Add Keyboard navigation on IconPicker (#2778)
* Add Add Keyboard navigation on IconPicker Co-authored-by: Matheus <matheus_benini@hotmail.com> * Add Keyboard navigation on IconPicker Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com> * Add Keyboard navigation on IconPicker Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com> * Add Keyboard navigation on IconPicker Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com> * Refactor according to review Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com> * Implement IconPicker * Remove onEnter clicked --------- Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com> Co-authored-by: Matheus <matheus_benini@hotmail.com> Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -4,7 +4,6 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||
import { Activity } from '@/activities/types/Activity';
|
||||
import { CommandMenuSelectableListEffect } from '@/command-menu/components/CommandMenuSelectableListEffect';
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
@ -86,7 +85,7 @@ export const StyledEmpty = styled.div`
|
||||
`;
|
||||
|
||||
export const CommandMenu = () => {
|
||||
const { toggleCommandMenu, closeCommandMenu } = useCommandMenu();
|
||||
const { toggleCommandMenu } = useCommandMenu();
|
||||
|
||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
|
||||
@ -109,17 +108,6 @@ export const CommandMenu = () => {
|
||||
[toggleCommandMenu, setSearch],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
'esc',
|
||||
() => {
|
||||
setSearch('');
|
||||
closeKeyboardShortcutMenu();
|
||||
closeCommandMenu();
|
||||
},
|
||||
AppHotkeyScope.CommandMenu,
|
||||
[toggleCommandMenu, setSearch],
|
||||
);
|
||||
|
||||
const { records: people } = useFindManyRecords<Person>({
|
||||
skip: !isCommandMenuOpened,
|
||||
objectNameSingular: 'person',
|
||||
@ -198,12 +186,13 @@ export const CommandMenu = () => {
|
||||
<StyledList>
|
||||
<ScrollWrapper>
|
||||
<StyledInnerList>
|
||||
<CommandMenuSelectableListEffect
|
||||
selectableItemIds={selectableItemIds}
|
||||
/>
|
||||
<SelectableList
|
||||
selectableListId="command-menu-list"
|
||||
selectableItemIds={selectableItemIds}
|
||||
selectableItemIds={[selectableItemIds]}
|
||||
hotkeyScope={AppHotkeyScope.CommandMenu}
|
||||
onEnter={(itemId) => {
|
||||
console.log(itemId);
|
||||
}}
|
||||
>
|
||||
{!matchingCreateCommand.length &&
|
||||
!matchingNavigateCommand.length &&
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { IconArrowUpRight } from '@/ui/display/icon';
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
@ -26,8 +24,7 @@ export const CommandMenuItem = ({
|
||||
firstHotKey,
|
||||
secondHotKey,
|
||||
}: CommandMenuItemProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { toggleCommandMenu } = useCommandMenu();
|
||||
const { onItemClick } = useCommandMenu();
|
||||
|
||||
if (to && !Icon) {
|
||||
Icon = IconArrowUpRight;
|
||||
@ -35,26 +32,13 @@ export const CommandMenuItem = ({
|
||||
|
||||
const { isSelectedItemId } = useSelectableList({ itemId: id });
|
||||
|
||||
const onItemClick = () => {
|
||||
toggleCommandMenu();
|
||||
|
||||
if (onClick) {
|
||||
onClick();
|
||||
return;
|
||||
}
|
||||
if (to) {
|
||||
navigate(to);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemCommand
|
||||
LeftIcon={Icon}
|
||||
text={label}
|
||||
firstHotKey={firstHotKey}
|
||||
secondHotKey={secondHotKey}
|
||||
onClick={onItemClick}
|
||||
onClick={() => onItemClick(onClick, to)}
|
||||
isSelected={isSelectedItemId}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
|
||||
type CommandMenuSelectableListEffectProps = {
|
||||
selectableItemIds: string[];
|
||||
};
|
||||
|
||||
export const CommandMenuSelectableListEffect = ({
|
||||
selectableItemIds,
|
||||
}: CommandMenuSelectableListEffectProps) => {
|
||||
const { setSelectableItemIds } = useSelectableList({
|
||||
selectableListId: 'command-menu-list',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setSelectableItemIds(selectableItemIds);
|
||||
}, [selectableItemIds, setSelectableItemIds]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
@ -1,4 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
@ -10,6 +11,7 @@ import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
||||
import { Command } from '../types/Command';
|
||||
|
||||
export const useCommandMenu = () => {
|
||||
const navigate = useNavigate();
|
||||
const setIsCommandMenuOpened = useSetRecoilState(isCommandMenuOpenedState);
|
||||
const setCommands = useSetRecoilState(commandMenuCommandsState);
|
||||
const {
|
||||
@ -50,11 +52,28 @@ export const useCommandMenu = () => {
|
||||
setCommands(commandMenuCommands);
|
||||
};
|
||||
|
||||
const onItemClick = useCallback(
|
||||
(onClick?: () => void, to?: string) => {
|
||||
toggleCommandMenu();
|
||||
|
||||
if (onClick) {
|
||||
onClick();
|
||||
return;
|
||||
}
|
||||
if (to) {
|
||||
navigate(to);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[navigate, toggleCommandMenu],
|
||||
);
|
||||
|
||||
return {
|
||||
openCommandMenu,
|
||||
closeCommandMenu,
|
||||
toggleCommandMenu,
|
||||
addToCommandMenu,
|
||||
onItemClick,
|
||||
setToIntitialCommandMenu,
|
||||
};
|
||||
};
|
||||
|
||||
@ -9,6 +9,10 @@ import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/Dropdow
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { arrayToChunks } from '~/utils/array/array-to-chunks';
|
||||
|
||||
import { IconButton, IconButtonVariant } from '../button/components/IconButton';
|
||||
import { LightIconButton } from '../button/components/LightIconButton';
|
||||
@ -44,6 +48,34 @@ const StyledLightIconButton = styled(LightIconButton)<{ isSelected?: boolean }>`
|
||||
const convertIconKeyToLabel = (iconKey: string) =>
|
||||
iconKey.replace(/[A-Z]/g, (letter) => ` ${letter}`).trim();
|
||||
|
||||
type IconPickerIconProps = {
|
||||
iconKey: string;
|
||||
onClick: () => void;
|
||||
selectedIconKey?: string;
|
||||
Icon: IconComponent;
|
||||
};
|
||||
|
||||
const IconPickerIcon = ({
|
||||
iconKey,
|
||||
onClick,
|
||||
selectedIconKey,
|
||||
Icon,
|
||||
}: IconPickerIconProps) => {
|
||||
const { isSelectedItemId } = useSelectableList({ itemId: iconKey });
|
||||
|
||||
return (
|
||||
<StyledLightIconButton
|
||||
key={iconKey}
|
||||
aria-label={convertIconKeyToLabel(iconKey)}
|
||||
size="medium"
|
||||
title={iconKey}
|
||||
isSelected={iconKey === selectedIconKey || isSelectedItemId}
|
||||
Icon={Icon}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const IconPicker = ({
|
||||
disabled,
|
||||
dropdownScopeId = 'icon-picker',
|
||||
@ -56,6 +88,10 @@ export const IconPicker = ({
|
||||
className,
|
||||
}: IconPickerProps) => {
|
||||
const [searchString, setSearchString] = useState('');
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const { closeDropdown } = useDropdown({ dropdownScopeId });
|
||||
|
||||
@ -79,6 +115,11 @@ export const IconPicker = ({
|
||||
).slice(0, 25);
|
||||
}, [icons, searchString, selectedIconKey]);
|
||||
|
||||
const iconKeys2d = useMemo(
|
||||
() => arrayToChunks(iconKeys.slice(), 5),
|
||||
[iconKeys],
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={dropdownScopeId}>
|
||||
<div className={className}>
|
||||
@ -93,36 +134,53 @@ export const IconPicker = ({
|
||||
}
|
||||
dropdownMenuWidth={176}
|
||||
dropdownComponents={
|
||||
<DropdownMenu width={176}>
|
||||
<DropdownMenuSearchInput
|
||||
placeholder="Search icon"
|
||||
autoFocus
|
||||
onChange={(event) => setSearchString(event.target.value)}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
{isLoading ? (
|
||||
<DropdownMenuSkeletonItem />
|
||||
) : (
|
||||
<StyledMenuIconItemsContainer>
|
||||
{iconKeys.map((iconKey) => (
|
||||
<StyledLightIconButton
|
||||
key={iconKey}
|
||||
aria-label={convertIconKeyToLabel(iconKey)}
|
||||
isSelected={selectedIconKey === iconKey}
|
||||
size="medium"
|
||||
title={iconKey}
|
||||
Icon={icons[iconKey]}
|
||||
onClick={() => {
|
||||
onChange({ iconKey, Icon: icons[iconKey] });
|
||||
closeDropdown();
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</StyledMenuIconItemsContainer>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownMenu>
|
||||
<SelectableList
|
||||
selectableListId="icon-list"
|
||||
selectableItemIds={iconKeys2d}
|
||||
hotkeyScope={IconPickerHotkeyScope.IconPicker}
|
||||
onEnter={(iconKey) => {
|
||||
onChange({ iconKey, Icon: icons[iconKey] });
|
||||
closeDropdown();
|
||||
}}
|
||||
>
|
||||
<DropdownMenu width={176}>
|
||||
<DropdownMenuSearchInput
|
||||
placeholder="Search icon"
|
||||
autoFocus
|
||||
onChange={(event) => setSearchString(event.target.value)}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<div
|
||||
onMouseEnter={() => {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
IconPickerHotkeyScope.IconPicker,
|
||||
);
|
||||
}}
|
||||
onMouseLeave={goBackToPreviousHotkeyScope}
|
||||
>
|
||||
<DropdownMenuItemsContainer>
|
||||
{isLoading ? (
|
||||
<DropdownMenuSkeletonItem />
|
||||
) : (
|
||||
<StyledMenuIconItemsContainer>
|
||||
{iconKeys.map((iconKey) => (
|
||||
<IconPickerIcon
|
||||
key={iconKey}
|
||||
iconKey={iconKey}
|
||||
onClick={() => {
|
||||
onChange({ iconKey, Icon: icons[iconKey] });
|
||||
closeDropdown();
|
||||
}}
|
||||
selectedIconKey={selectedIconKey}
|
||||
Icon={icons[iconKey]}
|
||||
/>
|
||||
))}
|
||||
</StyledMenuIconItemsContainer>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</div>
|
||||
</DropdownMenu>
|
||||
</SelectableList>
|
||||
}
|
||||
onClickOutside={onClickOutside}
|
||||
onClose={() => {
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { ReactNode, useEffect } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useSelectableListHotKeys } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListHotKeys';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { SelectableListScope } from '@/ui/layout/selectable-list/scopes/SelectableListScope';
|
||||
|
||||
type SelectableListProps = {
|
||||
children: ReactNode;
|
||||
selectableListId: string;
|
||||
selectableItemIds: string[];
|
||||
selectableItemIds: string[][];
|
||||
onSelect?: (selected: string) => void;
|
||||
hotkeyScope: string;
|
||||
onEnter?: (itemId: string) => void;
|
||||
};
|
||||
|
||||
const StyledSelectableItemsContainer = styled.div`
|
||||
@ -17,8 +21,23 @@ const StyledSelectableItemsContainer = styled.div`
|
||||
export const SelectableList = ({
|
||||
children,
|
||||
selectableListId,
|
||||
hotkeyScope,
|
||||
selectableItemIds,
|
||||
onEnter,
|
||||
}: SelectableListProps) => {
|
||||
useSelectableListHotKeys(selectableListId);
|
||||
useSelectableListHotKeys(selectableListId, hotkeyScope);
|
||||
|
||||
const { setSelectableItemIds, setSelectableListOnEnter } = useSelectableList({
|
||||
selectableListId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setSelectableListOnEnter(() => onEnter);
|
||||
}, [onEnter, setSelectableListOnEnter]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectableItemIds(selectableItemIds);
|
||||
}, [selectableItemIds, setSelectableItemIds]);
|
||||
|
||||
return (
|
||||
<SelectableListScope selectableListScopeId={selectableListId}>
|
||||
|
||||
@ -1,16 +1,37 @@
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { getSelectableListScopedStates } from '@/ui/layout/selectable-list/utils/internal/getSelectableListScopedStates';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
export const useSelectableListHotKeys = (scopeId: string) => {
|
||||
type Direction = 'up' | 'down' | 'left' | 'right';
|
||||
|
||||
export const useSelectableListHotKeys = (
|
||||
scopeId: string,
|
||||
hotkeyScope: string,
|
||||
) => {
|
||||
const findPosition = (
|
||||
selectableItemIds: string[][],
|
||||
selectedItemId?: string | null,
|
||||
) => {
|
||||
if (!selectedItemId) {
|
||||
// If nothing is selected, return the default position
|
||||
return { row: 0, col: 0 };
|
||||
}
|
||||
|
||||
for (let row = 0; row < selectableItemIds.length; row++) {
|
||||
const col = selectableItemIds[row].indexOf(selectedItemId);
|
||||
if (col !== -1) {
|
||||
return { row, col };
|
||||
}
|
||||
}
|
||||
return { row: 0, col: 0 };
|
||||
};
|
||||
|
||||
const handleSelect = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(direction: 'up' | 'down') => {
|
||||
(direction: Direction) => {
|
||||
const { selectedItemIdState, selectableItemIdsState } =
|
||||
getSelectableListScopedStates({
|
||||
selectableListScopeId: scopeId,
|
||||
@ -21,31 +42,54 @@ export const useSelectableListHotKeys = (scopeId: string) => {
|
||||
selectableItemIdsState,
|
||||
);
|
||||
|
||||
const computeNextId = (direction: 'up' | 'down') => {
|
||||
const { row: currentRow, col: currentCol } = findPosition(
|
||||
selectableItemIds,
|
||||
selectedItemId,
|
||||
);
|
||||
|
||||
const computeNextId = (direction: Direction) => {
|
||||
if (selectableItemIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNull(selectedItemId)) {
|
||||
return direction === 'up'
|
||||
? selectableItemIds[selectableItemIds.length - 1]
|
||||
: selectableItemIds[0];
|
||||
const isSingleRow = selectableItemIds.length === 1;
|
||||
|
||||
let nextRow: number;
|
||||
let nextCol: number;
|
||||
|
||||
switch (direction) {
|
||||
case 'up':
|
||||
nextRow = isSingleRow ? currentRow : Math.max(0, currentRow - 1);
|
||||
nextCol = isSingleRow ? Math.max(0, currentCol - 1) : currentCol;
|
||||
break;
|
||||
case 'down':
|
||||
nextRow = isSingleRow
|
||||
? currentRow
|
||||
: Math.min(selectableItemIds.length - 1, currentRow + 1);
|
||||
nextCol = isSingleRow
|
||||
? Math.min(
|
||||
selectableItemIds[currentRow].length - 1,
|
||||
currentCol + 1,
|
||||
)
|
||||
: currentCol;
|
||||
break;
|
||||
case 'left':
|
||||
nextRow = currentRow;
|
||||
nextCol = Math.max(0, currentCol - 1);
|
||||
break;
|
||||
case 'right':
|
||||
nextRow = currentRow;
|
||||
nextCol = Math.min(
|
||||
selectableItemIds[currentRow].length - 1,
|
||||
currentCol + 1,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
nextRow = currentRow;
|
||||
nextCol = currentCol;
|
||||
}
|
||||
|
||||
const currentIndex = selectableItemIds.indexOf(selectedItemId);
|
||||
if (currentIndex === -1) {
|
||||
return direction === 'up'
|
||||
? selectableItemIds[selectableItemIds.length - 1]
|
||||
: selectableItemIds[0];
|
||||
}
|
||||
|
||||
return direction === 'up'
|
||||
? currentIndex == 0
|
||||
? selectableItemIds[selectableItemIds.length - 1]
|
||||
: selectableItemIds[currentIndex - 1]
|
||||
: currentIndex == selectableItemIds.length - 1
|
||||
? selectableItemIds[0]
|
||||
: selectableItemIds[currentIndex + 1];
|
||||
return selectableItemIds[nextRow][nextCol];
|
||||
};
|
||||
|
||||
const nextId = computeNextId(direction);
|
||||
@ -70,17 +114,45 @@ export const useSelectableListHotKeys = (scopeId: string) => {
|
||||
[scopeId],
|
||||
);
|
||||
|
||||
useScopedHotkeys(Key.ArrowUp, () => handleSelect('up'), hotkeyScope, []);
|
||||
|
||||
useScopedHotkeys(Key.ArrowDown, () => handleSelect('down'), hotkeyScope, []);
|
||||
|
||||
useScopedHotkeys(Key.ArrowLeft, () => handleSelect('left'), hotkeyScope, []);
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.ArrowUp,
|
||||
() => handleSelect('up'),
|
||||
AppHotkeyScope.CommandMenu,
|
||||
Key.ArrowRight,
|
||||
() => handleSelect('right'),
|
||||
hotkeyScope,
|
||||
[],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.ArrowDown,
|
||||
() => handleSelect('down'),
|
||||
AppHotkeyScope.CommandMenu,
|
||||
Key.Enter,
|
||||
useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const { selectedItemIdState, selectableListOnEnterState } =
|
||||
getSelectableListScopedStates({
|
||||
selectableListScopeId: scopeId,
|
||||
});
|
||||
const selectedItemId = getSnapshotValue(
|
||||
snapshot,
|
||||
selectedItemIdState,
|
||||
);
|
||||
|
||||
const onEnter = getSnapshotValue(
|
||||
snapshot,
|
||||
selectableListOnEnterState,
|
||||
);
|
||||
|
||||
if (selectedItemId) {
|
||||
onEnter?.(selectedItemId);
|
||||
}
|
||||
},
|
||||
[scopeId],
|
||||
),
|
||||
hotkeyScope,
|
||||
[],
|
||||
);
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ export const useSelectableListScopedStates = (
|
||||
selectedItemIdState,
|
||||
selectableItemIdsState,
|
||||
isSelectedItemIdSelector,
|
||||
selectableListOnEnterState,
|
||||
} = getSelectableListScopedStates({
|
||||
selectableListScopeId: scopeId,
|
||||
itemId: itemId,
|
||||
@ -30,5 +31,6 @@ export const useSelectableListScopedStates = (
|
||||
isSelectedItemIdSelector,
|
||||
selectableItemIdsState,
|
||||
selectedItemIdState,
|
||||
selectableListOnEnterState,
|
||||
};
|
||||
};
|
||||
|
||||
@ -15,18 +15,25 @@ export const useSelectableList = (props?: UseSelectableListProps) => {
|
||||
props?.selectableListId,
|
||||
);
|
||||
|
||||
const { selectableItemIdsState, isSelectedItemIdSelector } =
|
||||
useSelectableListScopedStates({
|
||||
selectableListScopeId: scopeId,
|
||||
itemId: props?.itemId,
|
||||
});
|
||||
const {
|
||||
selectableItemIdsState,
|
||||
isSelectedItemIdSelector,
|
||||
selectableListOnEnterState,
|
||||
} = useSelectableListScopedStates({
|
||||
selectableListScopeId: scopeId,
|
||||
itemId: props?.itemId,
|
||||
});
|
||||
|
||||
const setSelectableItemIds = useSetRecoilState(selectableItemIdsState);
|
||||
const setSelectableListOnEnter = useSetRecoilState(
|
||||
selectableListOnEnterState,
|
||||
);
|
||||
const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector);
|
||||
|
||||
return {
|
||||
setSelectableItemIds,
|
||||
isSelectedItemId,
|
||||
setSelectableListOnEnter,
|
||||
selectableListId: scopeId,
|
||||
isSelectedItemIdSelector,
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const selectableItemIdsScopedState = createScopedState<string[]>({
|
||||
export const selectableItemIdsScopedState = createScopedState<string[][]>({
|
||||
key: 'selectableItemIdsScopedState',
|
||||
defaultValue: [],
|
||||
defaultValue: [[]],
|
||||
});
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const selectableListOnEnterScopedState = createScopedState<
|
||||
((itemId: string) => void) | undefined
|
||||
>({
|
||||
key: 'selectableListOnEnterScopedState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,4 +1,5 @@
|
||||
import { selectableItemIdsScopedState } from '@/ui/layout/selectable-list/states/selectableItemIdsScopedState';
|
||||
import { selectableListOnEnterScopedState } from '@/ui/layout/selectable-list/states/selectableListOnEnterScopedState';
|
||||
import { selectedItemIdScopedState } from '@/ui/layout/selectable-list/states/selectedItemIdScopedState';
|
||||
import { isSelectedItemIdScopedFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdScopedFamilySelector';
|
||||
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
|
||||
@ -27,9 +28,15 @@ export const getSelectableListScopedStates = ({
|
||||
selectableListScopeId,
|
||||
);
|
||||
|
||||
const selectableListOnEnterState = getScopedState(
|
||||
selectableListOnEnterScopedState,
|
||||
selectableListScopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
isSelectedItemIdSelector,
|
||||
selectableItemIdsState,
|
||||
selectedItemIdState,
|
||||
selectableListOnEnterState,
|
||||
};
|
||||
};
|
||||
|
||||
@ -74,6 +74,7 @@ export const useSetHotkeyScope = () =>
|
||||
}
|
||||
|
||||
scopesToSet.push(newHotkeyScope.scope);
|
||||
console.log(scopesToSet);
|
||||
set(internalHotkeysEnabledScopesState, scopesToSet);
|
||||
set(currentHotkeyScopeState, newHotkeyScope);
|
||||
},
|
||||
|
||||
11
front/src/utils/array/array-to-chunks.ts
Normal file
11
front/src/utils/array/array-to-chunks.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// split an array into subarrays of a given size
|
||||
export const arrayToChunks = <T>(array: T[], size: number) => {
|
||||
const arrayCopy = [...array];
|
||||
const results = [];
|
||||
|
||||
while (arrayCopy.length) {
|
||||
results.push(arrayCopy.splice(0, size));
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
Reference in New Issue
Block a user