mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
Merge pull request #32825 from overleaf/mfb-autocomplete-component-design-review
[web] Autocomplete component design review fixes GitOrigin-RevId: f598f46a770c94512de5beddb8ff1997df354fae
This commit is contained in:
committed by
Copybot
parent
050f00a4b5
commit
df3f50d10b
@@ -107,22 +107,23 @@ function OLAutocompleteInternal({
|
||||
const showCreateOption =
|
||||
allowCreateForInput && internalInputValue && !exactMatch
|
||||
|
||||
const createDisplayItem: OLAutocompleteDisplayItem[] = showCreateOption
|
||||
? [{ type: 'create' as const, inputValue: internalInputValue }]
|
||||
: []
|
||||
|
||||
const displayItems: OLAutocompleteDisplayItem[] = [
|
||||
...(expandUp ? [] : createDisplayItem),
|
||||
...inputItems.map(item => ({
|
||||
type: 'item' as const,
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
})),
|
||||
...(showCreateOption
|
||||
? [
|
||||
{
|
||||
type: 'create' as const,
|
||||
inputValue: internalInputValue,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(expandUp ? createDisplayItem : []),
|
||||
]
|
||||
|
||||
const getDisplayIndex = (inputItemIndex: number) =>
|
||||
!expandUp && showCreateOption ? inputItemIndex + 1 : inputItemIndex
|
||||
|
||||
const hasGroupedItems = inputItems.some(item => Boolean(item.group))
|
||||
|
||||
const {
|
||||
@@ -141,6 +142,28 @@ function OLAutocompleteInternal({
|
||||
if (!item) return ''
|
||||
return item.type === 'create' ? item.inputValue : item.label
|
||||
},
|
||||
stateReducer: (_state, { type, changes }) => {
|
||||
if (type === useCombobox.stateChangeTypes.InputChange) {
|
||||
const newInputValue = changes.inputValue || ''
|
||||
const newAllowCreate =
|
||||
typeof allowCreate === 'function'
|
||||
? allowCreate(newInputValue)
|
||||
: allowCreate
|
||||
const hasExactMatch = items.some(
|
||||
item => item.label.toLowerCase() === newInputValue.toLowerCase()
|
||||
)
|
||||
const hasMatchingItems = items.some(item =>
|
||||
item.label.toLowerCase().includes(newInputValue.toLowerCase())
|
||||
)
|
||||
const newShowCreate = newAllowCreate && newInputValue && !hasExactMatch
|
||||
return {
|
||||
...changes,
|
||||
highlightedIndex:
|
||||
!expandUp && newShowCreate && hasMatchingItems ? 1 : 0,
|
||||
}
|
||||
}
|
||||
return changes
|
||||
},
|
||||
onSelectedItemChange: ({ selectedItem }) => {
|
||||
if (selectedItem) {
|
||||
if (selectedItem.type === 'create') {
|
||||
@@ -159,13 +182,41 @@ function OLAutocompleteInternal({
|
||||
|
||||
const shouldShowDropdown = isOpen && displayItems.length > 0
|
||||
|
||||
const renderCreateOption = (index: number) => (
|
||||
<>
|
||||
{hasGroupedItems && expandUp && (
|
||||
<li role="separator" className="dropdown-divider" />
|
||||
)}
|
||||
<li
|
||||
{...getItemProps({
|
||||
item: { type: 'create', inputValue: internalInputValue },
|
||||
index,
|
||||
})}
|
||||
>
|
||||
<OLButton
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={classnames('w-100', 'justify-content-start', {
|
||||
'dropdown-item-highlighted': highlightedIndex === index,
|
||||
})}
|
||||
>
|
||||
<span className="text-muted">{createOptionPrefix} </span>
|
||||
<strong>'{internalInputValue}'</strong>
|
||||
</OLButton>
|
||||
</li>
|
||||
{hasGroupedItems && !expandUp && (
|
||||
<li role="separator" className="dropdown-divider" />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
const handleClear = () => {
|
||||
selectItem(null)
|
||||
setInternalInputValue('')
|
||||
onChange('')
|
||||
}
|
||||
|
||||
const getSearchBar = () => (
|
||||
const renderSearchBar = () => (
|
||||
<div className={classnames({ 'mb-3': !expandUp, 'mt-3': expandUp })}>
|
||||
<OLFormLabel
|
||||
{...getLabelProps()}
|
||||
@@ -196,7 +247,7 @@ function OLAutocompleteInternal({
|
||||
</div>
|
||||
)
|
||||
|
||||
const getResultsList = () => (
|
||||
const renderResultsList = () => (
|
||||
<>
|
||||
<ul
|
||||
{...getMenuProps()}
|
||||
@@ -207,11 +258,13 @@ function OLAutocompleteInternal({
|
||||
>
|
||||
{hasGroupedItems ? (
|
||||
<>
|
||||
{!expandUp && showCreateOption && <>{renderCreateOption(0)}</>}
|
||||
{inputItems.map((item, index) => {
|
||||
const previousItem = inputItems[index - 1]
|
||||
const hasGroupHeader =
|
||||
item.group &&
|
||||
(!previousItem || previousItem.group !== item.group)
|
||||
const displayIndex = getDisplayIndex(index)
|
||||
|
||||
return (
|
||||
<Fragment key={`${item.value}${index}`}>
|
||||
@@ -230,14 +283,15 @@ function OLAutocompleteInternal({
|
||||
value: item.value,
|
||||
label: item.label,
|
||||
},
|
||||
index,
|
||||
index: displayIndex,
|
||||
})}
|
||||
>
|
||||
<DropdownItem
|
||||
as="span"
|
||||
role={undefined}
|
||||
className={classnames({
|
||||
'dropdown-item-highlighted': highlightedIndex === index,
|
||||
'dropdown-item-highlighted':
|
||||
highlightedIndex === displayIndex,
|
||||
})}
|
||||
>
|
||||
{item.label}
|
||||
@@ -246,49 +300,24 @@ function OLAutocompleteInternal({
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
{showCreateOption && (
|
||||
<>
|
||||
<li role="separator" className="dropdown-divider" />
|
||||
<li
|
||||
{...getItemProps({
|
||||
item: {
|
||||
type: 'create',
|
||||
inputValue: internalInputValue,
|
||||
},
|
||||
index: displayItems.length - 1,
|
||||
})}
|
||||
>
|
||||
<DropdownItem
|
||||
as="span"
|
||||
role={undefined}
|
||||
className={classnames({
|
||||
'dropdown-item-highlighted':
|
||||
highlightedIndex === displayItems.length - 1,
|
||||
})}
|
||||
>
|
||||
<span className="text-muted">{createOptionPrefix} </span>
|
||||
<strong>'{internalInputValue}'</strong>
|
||||
</DropdownItem>
|
||||
</li>
|
||||
</>
|
||||
{expandUp && showCreateOption && (
|
||||
<>{renderCreateOption(displayItems.length - 1)}</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
displayItems.map((item, index) => {
|
||||
const isCreateOption = item.type === 'create'
|
||||
const displayValue = isCreateOption ? item.inputValue : item.label
|
||||
if (item.type === 'create') {
|
||||
return (
|
||||
<Fragment key={`create-${item.inputValue}-${index}`}>
|
||||
{renderCreateOption(index)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
key={
|
||||
item.type === 'create'
|
||||
? `create-${item.inputValue}-${index}`
|
||||
: `${item.value}${index}`
|
||||
}
|
||||
{...getItemProps({
|
||||
item,
|
||||
index,
|
||||
})}
|
||||
key={`${item.value}${index}`}
|
||||
{...getItemProps({ item, index })}
|
||||
>
|
||||
<DropdownItem
|
||||
as="span"
|
||||
@@ -297,14 +326,7 @@ function OLAutocompleteInternal({
|
||||
'dropdown-item-highlighted': highlightedIndex === index,
|
||||
})}
|
||||
>
|
||||
{isCreateOption ? (
|
||||
<>
|
||||
<span className="text-muted">{createOptionPrefix} </span>
|
||||
<strong>'{displayValue}'</strong>
|
||||
</>
|
||||
) : (
|
||||
displayValue
|
||||
)}
|
||||
{item.label}
|
||||
</DropdownItem>
|
||||
</li>
|
||||
)
|
||||
@@ -318,13 +340,13 @@ function OLAutocompleteInternal({
|
||||
<div className={classnames('dropdown', 'd-block', 'ol-autocomplete')}>
|
||||
{expandUp ? (
|
||||
<>
|
||||
{getResultsList()}
|
||||
{getSearchBar()}
|
||||
{renderResultsList()}
|
||||
{renderSearchBar()}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{getSearchBar()}
|
||||
{getResultsList()}
|
||||
{renderSearchBar()}
|
||||
{renderResultsList()}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user