Skip to content

Commit e5557c3

Browse files
committed
feat(@clayui/autocomplete): LPD-54854 Allow adding new items to the autocomplete dropdown
1 parent b857063 commit e5557c3

File tree

1 file changed

+65
-7
lines changed

1 file changed

+65
-7
lines changed

packages/clay-autocomplete/src/Autocomplete.tsx

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,24 @@ export interface IProps<T>
114114
* Messages for autocomplete.
115115
*/
116116
messages?: {
117+
allowsCustomValue?: string;
117118
listCount?: string;
118119
listCountPlural?: string;
119120
loading: string;
120121
notFound: string;
122+
setAsHTML?: boolean;
121123
};
122124

123125
/**
124126
* Callback for when the active state changes (controlled).
125127
*/
126128
onActiveChange?: InternalDispatch<boolean>;
127129

130+
/**
131+
* Callback called when an item is added to the autocomplete list.
132+
*/
133+
onAddNewItem?: () => void;
134+
128135
/**
129136
* Callback called when input value changes (controlled).
130137
*/
@@ -189,10 +196,12 @@ function hasItem<T extends Item>(
189196
const ESCAPE_REGEXP = /[.*+?^${}()|[\]\\]/g;
190197

191198
const defaultMessages = {
199+
allowsCustomValue: 'Add {0}',
192200
listCount: '{0} option available.',
193201
listCountPlural: '{0} options available.',
194202
loading: 'Loading...',
195203
notFound: 'No results found',
204+
setAsHTML: false,
196205
};
197206

198207
function AutocompleteInner<T extends Item>(
@@ -214,6 +223,7 @@ function AutocompleteInner<T extends Item>(
214223
menuTrigger = 'input',
215224
messages,
216225
onActiveChange,
226+
onAddNewItem,
217227
onChange,
218228
onItemsChange,
219229
onLoadMore,
@@ -280,6 +290,8 @@ function AutocompleteInner<T extends Item>(
280290
[value]
281291
);
282292

293+
const allowsCustomValueMessage = messages?.allowsCustomValue!;
294+
283295
useEffect(() => {
284296
// Validates that the initial value exists in the items.
285297
if (
@@ -404,13 +416,50 @@ function AutocompleteInner<T extends Item>(
404416
),
405417
items: filteredItems,
406418
notFound: (
407-
<DropDown.Item
408-
aria-disabled="true"
409-
className="disabled"
410-
roleItem="option"
411-
>
412-
{messages.notFound}
413-
</DropDown.Item>
419+
<>
420+
{allowsCustomValue && (
421+
<>
422+
<DropDown.Item
423+
className=""
424+
id={value}
425+
key={value}
426+
onClick={() => {
427+
if (allowsCustomValue && items && onAddNewItem) {
428+
onAddNewItem();
429+
430+
items.push(value);
431+
432+
inputElementRef.current?.focus();
433+
434+
setValue('');
435+
}
436+
}}
437+
roleItem="option"
438+
>
439+
{messages.setAsHTML &&
440+
typeof allowsCustomValueMessage === 'string' ? (
441+
<span
442+
dangerouslySetInnerHTML={{
443+
__html: sub(allowsCustomValueMessage, [
444+
value,
445+
]),
446+
}}
447+
/>
448+
) : (
449+
sub(allowsCustomValueMessage, [value])
450+
)}
451+
</DropDown.Item>
452+
<li className="dropdown-divider"></li>
453+
</>
454+
)}
455+
<DropDown.Item
456+
aria-disabled="true"
457+
className="disabled"
458+
roleItem="option"
459+
>
460+
{messages.notFound}
461+
</DropDown.Item>
462+
</>
414463
),
415464
suppressTextValueWarning: false,
416465
virtualizer: items ? virtualizer : undefined,
@@ -554,6 +603,14 @@ function AutocompleteInner<T extends Item>(
554603
break;
555604
}
556605
case Keys.Enter: {
606+
if (allowsCustomValue && items && onAddNewItem) {
607+
onAddNewItem();
608+
609+
items.push(value);
610+
611+
setValue('');
612+
}
613+
557614
setActive(false);
558615

559616
if (active && activeDescendant) {
@@ -607,6 +664,7 @@ function AutocompleteInner<T extends Item>(
607664
}
608665

609666
navigationProps.onKeyDown(event);
667+
610668
break;
611669
}
612670
default:

0 commit comments

Comments
 (0)