Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ interface AutocompleteContextValue {
onInputChange: (value: string) => void;
renderTag?: (item: SelectItemType, onRemove: () => void) => ReactNode;
maxVisibleItems?: number;
isAtMax: boolean;
}

const AutocompleteContext = createContext<AutocompleteContextValue>({
Expand All @@ -65,6 +66,7 @@ const AutocompleteContext = createContext<AutocompleteContextValue>({
onRemove: () => {},
onInputChange: () => {},
maxVisibleItems: undefined,
isAtMax: false,
});

interface AutocompleteTriggerProps extends AriaGroupProps {
Expand All @@ -77,7 +79,8 @@ interface AutocompleteTriggerProps extends AriaGroupProps {
}

export interface AutocompleteProps
extends Omit<AriaComboBoxProps<SelectItemType>, 'children' | 'items'>,
extends
Omit<AriaComboBoxProps<SelectItemType>, 'children' | 'items'>,
RefAttributes<HTMLDivElement> {
hint?: string;
label?: string;
Expand All @@ -94,6 +97,7 @@ export interface AutocompleteProps
filterOption?: (item: SelectItemType, filterText: string) => boolean;
onSearchChange?: (value: string) => void;
maxVisibleItems?: number;
multiple?: boolean;
}

const renderChipIcon = (item: SelectItemType) => {
Expand Down Expand Up @@ -145,6 +149,12 @@ const InnerAutocomplete = ({
case 'ArrowRight':
focusManager?.focusNext({ wrap: false, tabbable: false });

break;
case 'ArrowDown':
if (comboBoxStateContext && !comboBoxStateContext.isOpen) {
comboBoxStateContext.open();
}

break;
}
};
Expand All @@ -157,7 +167,7 @@ const InnerAutocomplete = ({

const handleTagKeyDown = (
event: KeyboardEvent<HTMLButtonElement>,
value: Key
value: Key,
) => {
if (event.key === 'Tab') {
return;
Expand Down Expand Up @@ -195,7 +205,7 @@ const InnerAutocomplete = ({
};

const isSelectionEmpty = context?.selectedItems?.length === 0;
const { maxVisibleItems } = context;
const { maxVisibleItems, isAtMax } = context;
const allSelected = context?.selectedItems ?? [];
const visibleSelected =
maxVisibleItems === undefined
Expand Down Expand Up @@ -228,7 +238,7 @@ const InnerAutocomplete = ({
</Typography>
</div>
</BadgeWithButton>
)
),
)}

{overflowCount > 0 && (
Expand All @@ -240,10 +250,11 @@ const InnerAutocomplete = ({
<div
className={cx(
'tw:relative tw:flex tw:min-w-[20%] tw:flex-1 tw:flex-row tw:items-center',
!isSelectionEmpty && 'tw:ml-0.5'
!isSelectionEmpty && 'tw:ml-0.5',
isAtMax && 'tw:hidden',
)}>
<AriaInput
className="tw:w-full tw:flex-[1_0_0] tw:appearance-none tw:bg-transparent tw:text-md tw:text-ellipsis tw:text-primary tw:caret-alpha-black/90 tw:outline-none tw:placeholder:text-placeholder tw:focus:outline-hidden tw:disabled:cursor-not-allowed tw:disabled:text-disabled tw:disabled:placeholder:text-disabled"
className="tw:w-full tw:flex-[1_0_0] tw:appearance-none tw:bg-transparent tw:text-sm tw:text-ellipsis tw:text-primary tw:caret-alpha-black/90 tw:outline-none tw:placeholder:text-placeholder tw:focus:outline-hidden tw:disabled:cursor-not-allowed tw:disabled:text-disabled tw:disabled:placeholder:text-disabled"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, thanks for the clarification. The tw:text-sm change is intentional.

placeholder={placeholder}
onKeyDown={handleInputKeyDown}
onMouseDown={handleInputMouseDown}
Expand All @@ -258,6 +269,7 @@ const AutocompleteTrigger = ({
placeholder,
placeholderIcon: Icon = SearchLg,
isDisabled: _isDisabled,
isInvalid,
...otherProps
}: AutocompleteTriggerProps) => {
return (
Expand All @@ -267,10 +279,13 @@ const AutocompleteTrigger = ({
cx(
'tw:relative tw:flex tw:w-full tw:items-center tw:gap-2 tw:rounded-lg tw:bg-primary tw:shadow-xs tw:ring-1 tw:ring-primary tw:outline-hidden tw:transition tw:duration-100 tw:ease-linear tw:ring-inset',
isDisabled && 'tw:cursor-not-allowed tw:bg-disabled_subtle',
isInvalid && 'tw:ring-error_subtle',
isFocusWithin && 'tw:ring-2 tw:ring-brand',
sizes[size].root
isFocusWithin && isInvalid && 'tw:ring-2 tw:ring-error',
sizes[size].root,
)
}>
}
isInvalid={isInvalid}>
{({ isDisabled }) => (
<>
{Icon && (
Expand All @@ -289,7 +304,7 @@ const AutocompleteTrigger = ({
};

const resolveSelectedItems = (
value: SelectItemType[] | ListData<SelectItemType>
value: SelectItemType[] | ListData<SelectItemType>,
): SelectItemType[] => (Array.isArray(value) ? value : value.items);

export const AutocompleteBase = ({
Expand All @@ -298,13 +313,15 @@ export const AutocompleteBase = ({
label,
tooltip,
hint,
isInvalid,
selectedItems,
onItemCleared,
onItemInserted,
placeholder = 'Search',
popoverClassName,
renderTag,
filterOption,
multiple = true,
onSearchChange,
maxVisibleItems,
name: _name,
Expand All @@ -314,7 +331,7 @@ export const AutocompleteBase = ({
const { contains } = useFilter({ sensitivity: 'base' });

const [internalSelected, setInternalSelected] = useState<SelectItemType[]>(
resolveSelectedItems(selectedItems)
resolveSelectedItems(selectedItems),
);
const selectedKeys = internalSelected.map((item) => item.id);

Expand Down Expand Up @@ -344,7 +361,7 @@ export const AutocompleteBase = ({

const itemMap = useMemo(
() => new Map(allItems.map((item) => [item.id, item])),
[allItems]
[allItems],
);

const onRemove = useCallback(
Expand All @@ -357,13 +374,18 @@ export const AutocompleteBase = ({
onItemCleared?.(key);
setFilterText('');
},
[onItemCleared]
[onItemCleared],
);

const isAtMax = !multiple && internalSelected.length >= 1;

const onSelectionChange = (id: Key | null) => {
if (!id) {
return;
}
if (isAtMax) {
return;
}
const item = itemMap.get(id as string);
if (!item) {
return;
Expand All @@ -380,7 +402,7 @@ export const AutocompleteBase = ({
setFilterText(value);
onSearchChange?.(value);
},
[onSearchChange]
[onSearchChange],
);

const triggerRef = useRef<HTMLDivElement>(null);
Expand All @@ -407,6 +429,7 @@ export const AutocompleteBase = ({
onRemove,
renderTag,
maxVisibleItems,
isAtMax,
}),
[
selectedKeys,
Expand All @@ -415,7 +438,8 @@ export const AutocompleteBase = ({
onRemove,
renderTag,
maxVisibleItems,
]
isAtMax,
],
);

return (
Expand All @@ -425,7 +449,7 @@ export const AutocompleteBase = ({
allowsEmptyCollection
inputValue={filterText}
items={visibleItems}
menuTrigger="focus"
menuTrigger="input"
selectedKey={null}
onInputChange={onInputChange}
onSelectionChange={onSelectionChange}
Expand All @@ -440,6 +464,7 @@ export const AutocompleteBase = ({

<div className="tw:relative tw:w-full" ref={triggerRef}>
<AutocompleteTrigger
isInvalid={isInvalid}
placeholder={placeholder}
placeholderIcon={props.placeholderIcon}
size="sm"
Expand All @@ -460,7 +485,7 @@ export const AutocompleteBase = ({
</AriaListBox>
</Popover>

{hint && <HintText isInvalid={state.isInvalid}>{hint}</HintText>}
{hint && <HintText isInvalid={isInvalid}>{hint}</HintText>}
</div>
)}
</AriaComboBox>
Expand Down
Loading