Skip to content

Commit

Permalink
Merge pull request #162 from reaviz/select-onpaste
Browse files Browse the repository at this point in the history
[Select] Create options onPaste event
  • Loading branch information
amcdnl authored Apr 30, 2024
2 parents 06e6246 + 45b1166 commit e4c8e9a
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/form/Select/MultiSelect.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const Createable = () => {
multiple
closeOnSelect={false}
createable
selectOnPaste
selectOnKeys={['Enter', 'Space', 'Comma']}
searchOptions={{ threshold: 0 }}
placeholder="Add some categories or pick existing one..."
Expand Down
60 changes: 58 additions & 2 deletions src/form/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import { CloneElement, useId } from '../../utils';
import { SelectInput, SelectInputProps, SelectInputRef } from './SelectInput';
import { SelectMenu, SelectMenuProps } from './SelectMenu';
import { SelectOptionProps, SelectValue } from './SelectOption';
import { useWidth } from './utils/useWidth';
import { useFuzzy } from '@reaviz/react-use-fuzzy';
import { createOptions, getGroups } from './utils';
import { createOptions, getGroups, useWidth, keyNameToCode } from './utils';
import isEqual from 'react-fast-compare';

export interface SelectProps {
Expand Down Expand Up @@ -120,6 +119,11 @@ export interface SelectProps {
*/
createable?: boolean;

/**
* Select options when paste text inside input.
*/
selectOnPaste?: boolean;

/**
* The list of KeyCodes for creating select values.
* The default is ['Enter']
Expand Down Expand Up @@ -229,6 +233,7 @@ export const Select: FC<Partial<SelectProps>> = ({
placeholder,
disabled,
createable,
selectOnPaste,
selectOnKeys,
loading,
multiple,
Expand Down Expand Up @@ -638,6 +643,56 @@ export const Select: FC<Partial<SelectProps>> = ({
[createable, menuDisabled, onInputBlur, toggleSelectedOption]
);

const onPasteHandler = useCallback(
(e: React.ClipboardEvent<HTMLInputElement>) => {
if (selectOnPaste) {
const inputElement = e.target as HTMLInputElement;
const inputValue = inputElement.value;
const clipboardValue = e.clipboardData.getData('Text');
const value = `${inputValue}${clipboardValue}`.trim();

if (multiple) {
const separators = selectOnKeys?.map(key =>
String.fromCharCode(keyNameToCode[key])
);
const expression = `[${separators}]`;
const regex = new RegExp(expression, 'g');
const items = value.split(regex);
const result = toggleSelectedMultiOption(
items.map(item => ({ value: item, children: item }))
);
const optionsToSelect = createable
? result.newOptions
: result.newSelectedOptions;
if (result.newOptions?.length) {
onOptionsChange?.([...options, ...optionsToSelect]);
}
setInternalValue(result.newValue);
onChange?.(result.newValue);
} else {
toggleSelectedOption({ value: value, children: value });
setInternalValue(value);
onChange?.(value);
}

resetInput();
e.preventDefault();
}
},
[
createable,
selectOnPaste,
multiple,
onChange,
onOptionsChange,
options,
resetInput,
selectOnKeys,
toggleSelectedMultiOption,
toggleSelectedOption
]
);

const onMenuSelectedChange = useCallback(
(option: SelectValue) => {
toggleSelectedOption(option);
Expand Down Expand Up @@ -728,6 +783,7 @@ export const Select: FC<Partial<SelectProps>> = ({
onBlur={onInputBlured}
onFocus={onInputFocused}
onRefresh={onRefresh}
onPaste={onPasteHandler}
/>
</ConnectedOverlay>
);
Expand Down
7 changes: 7 additions & 0 deletions src/form/Select/SelectInput/SelectInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ export interface SelectInputProps {
*/
onInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;

/**
* The function to handle input paste event.
*/
onPaste: (event: React.ClipboardEvent<HTMLInputElement>) => void;

/**
* The function to handle refresh.
*/
Expand Down Expand Up @@ -253,6 +258,7 @@ export const SelectInput: FC<Partial<SelectInputProps>> = ({
onFocus,
onBlur,
onRefresh,
onPaste,
chip,
theme: customTheme
}) => {
Expand Down Expand Up @@ -521,6 +527,7 @@ export const SelectInput: FC<Partial<SelectInputProps>> = ({
onChange={onChange}
onFocus={onInputFocus}
onBlur={onBlur}
onPaste={onPaste}
placeholderIsMinWidth={false}
/>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/form/Select/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './grouping';
export * from './options';
export * from './useWidth';
export * from './keyboard';
102 changes: 102 additions & 0 deletions src/form/Select/utils/keyboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
export const keyNameToCode = {
Backspace: 8,
Tab: 9,
Enter: 13,
Shift: 16,
Ctrl: 17,
Alt: 18,
'Pause/Break': 19,
'Caps Lock': 20,
Esc: 27,
Space: 32,
'Page Up': 33,
'Page Down': 34,
End: 35,
Home: 36,
Left: 37,
Up: 38,
Right: 39,
Down: 40,
Insert: 45,
Delete: 46,
'0': 48,
'1': 49,
'2': 50,
'3': 51,
'4': 52,
'5': 53,
'6': 54,
'7': 55,
'8': 56,
'9': 57,
A: 65,
B: 66,
C: 67,
D: 68,
E: 69,
F: 70,
G: 71,
H: 72,
I: 73,
J: 74,
K: 75,
L: 76,
M: 77,
N: 78,
O: 79,
P: 80,
Q: 81,
R: 82,
S: 83,
T: 84,
U: 85,
V: 86,
W: 87,
X: 88,
Y: 89,
Z: 90,
Windows: 91,
'Right Click': 93,
'Numpad 0': 96,
'Numpad 1': 97,
'Numpad 2': 98,
'Numpad 3': 99,
'Numpad 4': 100,
'Numpad 5': 101,
'Numpad 6': 102,
'Numpad 7': 103,
'Numpad 8': 104,
'Numpad 9': 105,
'Numpad *': 106,
'Numpad +': 107,
'Numpad -': 109,
'Numpad .': 110,
'Numpad /': 111,
F1: 112,
F2: 113,
F3: 114,
F4: 115,
F5: 116,
F6: 117,
F7: 118,
F8: 119,
F9: 120,
F10: 121,
F11: 122,
F12: 123,
'Num Lock': 144,
'Scroll Lock': 145,
'My Computer': 182,
'My Calculator': 183,
';': 186,
'=': 187,
',': 188,
'-': 189,
'.': 190,
'/': 191,
'`': 192,
'[': 219,
'\\': 220,
']': 221,
"'": 222
};

0 comments on commit e4c8e9a

Please sign in to comment.