summary
This commit is contained in:
@@ -41,6 +41,9 @@ export interface SelectionAssignmentMenuProps {
|
||||
onOpenMenu?: () => void;
|
||||
renderItemLabel?: (item: NormalizedSelectionAssignmentItem) => React.ReactNode;
|
||||
positionStrategy?: 'absolute' | 'fixed';
|
||||
closeOnSelection?: boolean;
|
||||
sortByState?: boolean;
|
||||
freezeSortOnOpen?: boolean;
|
||||
}
|
||||
|
||||
const STATE_ORDER: Record<AssignmentState, number> = {
|
||||
@@ -94,11 +97,15 @@ const SelectionAssignmentMenu: React.FC<SelectionAssignmentMenuProps> = ({
|
||||
onOpenMenu,
|
||||
renderItemLabel,
|
||||
positionStrategy = 'absolute',
|
||||
closeOnSelection = true,
|
||||
sortByState = true,
|
||||
freezeSortOnOpen = false,
|
||||
}) => {
|
||||
const anchorRef = useRef<HTMLButtonElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [query, setQuery] = useState('');
|
||||
const [pending, setPending] = useState(false);
|
||||
const [sortSnapshot, setSortSnapshot] = useState<Array<string | number> | null>(null);
|
||||
|
||||
const {
|
||||
isOpen,
|
||||
@@ -145,20 +152,55 @@ const SelectionAssignmentMenu: React.FC<SelectionAssignmentMenuProps> = ({
|
||||
|
||||
const normalizedItems = useMemo(() => normalizeItems(items), [items]);
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
const search = query.trim().toLowerCase();
|
||||
const sorted = normalizedItems.slice().sort((a, b) => {
|
||||
const sortedByStateItems = useMemo(() => {
|
||||
if (!sortByState) {
|
||||
return normalizedItems;
|
||||
}
|
||||
return normalizedItems.slice().sort((a, b) => {
|
||||
const stateDiff = STATE_ORDER[a.state] - STATE_ORDER[b.state];
|
||||
if (stateDiff !== 0) {
|
||||
return stateDiff;
|
||||
}
|
||||
return a.label.localeCompare(b.label);
|
||||
});
|
||||
if (!search) {
|
||||
return sorted;
|
||||
}, [normalizedItems, sortByState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !freezeSortOnOpen || !sortByState) {
|
||||
setSortSnapshot(null);
|
||||
return;
|
||||
}
|
||||
return sorted.filter((item) => item.label.toLowerCase().includes(search));
|
||||
}, [normalizedItems, query]);
|
||||
setSortSnapshot((prev) => prev ?? sortedByStateItems.map((item) => item.id));
|
||||
}, [isOpen, freezeSortOnOpen, sortByState, sortedByStateItems]);
|
||||
|
||||
const orderedItems = useMemo(() => {
|
||||
if (freezeSortOnOpen && sortSnapshot && sortByState) {
|
||||
const itemMap = new Map<string | number, NormalizedSelectionAssignmentItem>(
|
||||
sortedByStateItems.map((item) => [item.id, item]),
|
||||
);
|
||||
const seen = new Set<string | number>();
|
||||
const fromSnapshot = sortSnapshot
|
||||
.map((id) => {
|
||||
const entry = itemMap.get(id);
|
||||
if (entry) {
|
||||
seen.add(entry.id);
|
||||
}
|
||||
return entry || null;
|
||||
})
|
||||
.filter((entry): entry is NormalizedSelectionAssignmentItem => Boolean(entry));
|
||||
const remaining = sortedByStateItems.filter((item) => !seen.has(item.id));
|
||||
return [...fromSnapshot, ...remaining];
|
||||
}
|
||||
return sortedByStateItems;
|
||||
}, [freezeSortOnOpen, sortSnapshot, sortByState, sortedByStateItems]);
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
const search = query.trim().toLowerCase();
|
||||
if (!search) {
|
||||
return orderedItems;
|
||||
}
|
||||
return orderedItems.filter((item) => item.label.toLowerCase().includes(search));
|
||||
}, [orderedItems, query]);
|
||||
|
||||
const handleToggle = useCallback(
|
||||
async (item: NormalizedSelectionAssignmentItem) => {
|
||||
@@ -169,13 +211,15 @@ const SelectionAssignmentMenu: React.FC<SelectionAssignmentMenuProps> = ({
|
||||
try {
|
||||
await onToggle(item);
|
||||
setPending(false);
|
||||
close();
|
||||
if (closeOnSelection) {
|
||||
close();
|
||||
}
|
||||
} catch (error) {
|
||||
setPending(false);
|
||||
console.error('[selection-assignment] toggle failed', error);
|
||||
}
|
||||
},
|
||||
[onToggle, close],
|
||||
[onToggle, close, closeOnSelection],
|
||||
);
|
||||
|
||||
const handleCreate = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user