CreateComponentFamilyState -> createComponentFamilyStateV2 (#11546)
Refacto of createComponentFamilyState --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -9,10 +11,12 @@ export const CommandMenuDefaultSelectionEffect = ({
|
||||
}: {
|
||||
selectableItemIds: string[];
|
||||
}) => {
|
||||
const { setSelectedItemId, selectedItemIdState } =
|
||||
useSelectableList('command-menu-list');
|
||||
const { setSelectedItemId } = useSelectableList('command-menu-list');
|
||||
|
||||
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||
const selectedItemId = useRecoilComponentValueV2(
|
||||
selectedItemIdComponentState,
|
||||
'command-menu-list',
|
||||
);
|
||||
|
||||
const hasUserSelectedCommand = useRecoilValue(hasUserSelectedCommandState);
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { ReactNode } from 'react';
|
||||
import { IconArrowUpRight, IconComponent } from 'twenty-ui/display';
|
||||
import { MenuItemCommand } from 'twenty-ui/navigation';
|
||||
@ -35,8 +35,10 @@ export const CommandMenuItem = ({
|
||||
Icon = IconArrowUpRight;
|
||||
}
|
||||
|
||||
const { isSelectedItemIdSelector } = useSelectableList();
|
||||
const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector(id));
|
||||
const isSelectedItemId = useRecoilComponentFamilyValueV2(
|
||||
isSelectedItemIdComponentFamilySelector,
|
||||
id,
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItemCommand
|
||||
|
||||
@ -79,7 +79,7 @@ export const CommandMenuList = ({
|
||||
<ScrollWrapper componentInstanceId={`scroll-wrapper-command-menu`}>
|
||||
<StyledInnerList>
|
||||
<SelectableList
|
||||
selectableListId="command-menu-list"
|
||||
selectableListInstanceId="command-menu-list"
|
||||
hotkeyScope={AppHotkeyScope.CommandMenuOpen}
|
||||
selectableItemIdArray={selectableItemIds}
|
||||
onEnter={(itemId) => {
|
||||
|
||||
@ -144,7 +144,7 @@ export const AdvancedFilterFieldSelectMenu = ({
|
||||
<SelectableList
|
||||
hotkeyScope={FiltersHotkeyScope.ObjectFilterDropdownButton}
|
||||
selectableItemIdArray={selectableFieldMetadataItemIds}
|
||||
selectableListId={OBJECT_FILTER_DROPDOWN_ID}
|
||||
selectableListInstanceId={OBJECT_FILTER_DROPDOWN_ID}
|
||||
onEnter={handleEnter}
|
||||
>
|
||||
<DropdownMenuItemsContainer>
|
||||
|
||||
@ -90,7 +90,7 @@ export const ObjectFilterDropdownBooleanSelect = () => {
|
||||
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListId="boolean-select"
|
||||
selectableListInstanceId="boolean-select"
|
||||
selectableItemIdArray={options.map((option) => option.toString())}
|
||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
||||
onEnter={(itemId) => {
|
||||
|
||||
@ -150,7 +150,7 @@ export const ObjectFilterDropdownFilterSelect = ({
|
||||
<SelectableList
|
||||
hotkeyScope={FiltersHotkeyScope.ObjectFilterDropdownButton}
|
||||
selectableItemIdArray={selectableFieldMetadataItemIds}
|
||||
selectableListId={OBJECT_FILTER_DROPDOWN_ID}
|
||||
selectableListInstanceId={OBJECT_FILTER_DROPDOWN_ID}
|
||||
onEnter={handleEnter}
|
||||
>
|
||||
<DropdownMenuItemsContainer>
|
||||
|
||||
@ -15,14 +15,15 @@ import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-re
|
||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
|
||||
export type ObjectFilterDropdownFilterSelectMenuItemProps = {
|
||||
fieldMetadataItemToSelect: FieldMetadataItem;
|
||||
@ -48,12 +49,11 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({
|
||||
objectFilterDropdownFilterIsSelectedComponentState,
|
||||
);
|
||||
|
||||
const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList(
|
||||
OBJECT_FILTER_DROPDOWN_ID,
|
||||
);
|
||||
const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID);
|
||||
|
||||
const isSelectedItem = useRecoilValue(
|
||||
isSelectedItemIdSelector(fieldMetadataItemToSelect.id),
|
||||
const isSelectedItem = useRecoilComponentFamilyValueV2(
|
||||
isSelectedItemIdComponentFamilySelector,
|
||||
fieldMetadataItemToSelect.id,
|
||||
);
|
||||
|
||||
const setSelectedOperandInDropdown = useSetRecoilComponentStateV2(
|
||||
|
||||
@ -3,9 +3,10 @@ import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdow
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
|
||||
export type ObjectFilterDropdownFilterSelectMenuItemV2Props = {
|
||||
fieldMetadataItemToSelect: FieldMetadataItem;
|
||||
@ -16,12 +17,11 @@ export const ObjectFilterDropdownFilterSelectMenuItemV2 = ({
|
||||
fieldMetadataItemToSelect,
|
||||
onClick,
|
||||
}: ObjectFilterDropdownFilterSelectMenuItemV2Props) => {
|
||||
const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList(
|
||||
OBJECT_FILTER_DROPDOWN_ID,
|
||||
);
|
||||
const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID);
|
||||
|
||||
const isSelectedItem = useRecoilValue(
|
||||
isSelectedItemIdSelector(fieldMetadataItemToSelect.id),
|
||||
const isSelectedItem = useRecoilComponentFamilyValueV2(
|
||||
isSelectedItemIdComponentFamilySelector,
|
||||
fieldMetadataItemToSelect.id,
|
||||
);
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@ -8,7 +7,7 @@ import { useOptionsForSelect } from '@/object-record/object-filter-dropdown/hook
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
|
||||
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
@ -20,6 +19,7 @@ import { selectedFilterComponentState } from '@/object-record/object-filter-drop
|
||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||
import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter';
|
||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
@ -62,13 +62,12 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
||||
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
const { selectedItemIdState } = useSelectableListStates({
|
||||
selectableListScopeId: componentInstanceId,
|
||||
});
|
||||
|
||||
const { resetSelectedItem } = useSelectableList(componentInstanceId);
|
||||
|
||||
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||
const selectedItemId = useRecoilComponentValueV2(
|
||||
selectedItemIdComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const fieldMetaDataId = fieldMetadataItemUsedInDropdown?.id ?? '';
|
||||
|
||||
@ -164,7 +163,7 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
||||
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListId={componentInstanceId}
|
||||
selectableListInstanceId={componentInstanceId}
|
||||
selectableItemIdArray={objectRecordsIds}
|
||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
||||
onEnter={(itemId) => {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getAvatarType } from '@/object-metadata/utils/getAvatarType';
|
||||
@ -8,12 +7,12 @@ import { multipleRecordPickerIsSelectedComponentFamilySelector } from '@/object-
|
||||
import { getMultipleRecordPickerSelectableListId } from '@/object-record/record-picker/multiple-record-picker/utils/getMultipleRecordPickerSelectableListId';
|
||||
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { SearchRecord } from '~/generated-metadata/graphql';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
|
||||
import { SearchRecord } from '~/generated-metadata/graphql';
|
||||
|
||||
export const StyledSelectableItem = styled(SelectableItem)`
|
||||
height: 100%;
|
||||
@ -38,14 +37,12 @@ export const MultipleRecordPickerMenuItemContent = ({
|
||||
const selectableListComponentInstanceId =
|
||||
getMultipleRecordPickerSelectableListId(componentInstanceId);
|
||||
|
||||
const { isSelectedItemIdSelector } = useSelectableList(
|
||||
const isSelectedByKeyboard = useRecoilComponentFamilyValueV2(
|
||||
isSelectedItemIdComponentFamilySelector,
|
||||
searchRecord.recordId,
|
||||
selectableListComponentInstanceId,
|
||||
);
|
||||
|
||||
const isSelectedByKeyboard = useRecoilValue(
|
||||
isSelectedItemIdSelector(searchRecord.recordId),
|
||||
);
|
||||
|
||||
const isRecordSelectedWithObjectItem = useRecoilComponentFamilyValueV2(
|
||||
multipleRecordPickerIsSelectedComponentFamilySelector,
|
||||
searchRecord.recordId,
|
||||
|
||||
@ -114,7 +114,7 @@ export const MultipleRecordPickerMenuItems = ({
|
||||
return (
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
<SelectableList
|
||||
selectableListId={selectableListComponentInstanceId}
|
||||
selectableListInstanceId={selectableListComponentInstanceId}
|
||||
selectableItemIdArray={pickableRecordIds}
|
||||
hotkeyScope={MultipleRecordPickerHotkeyScope.MultipleRecordPicker}
|
||||
onEnter={handleEnter}
|
||||
|
||||
@ -12,7 +12,6 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
export const SINGLE_RECORD_PICKER_LISTENER_ID = 'single-record-select';
|
||||
|
||||
export type SingleRecordPickerProps = {
|
||||
width?: number;
|
||||
componentInstanceId: string;
|
||||
} & SingleRecordPickerMenuItemsWithSearchProps;
|
||||
|
||||
@ -24,7 +23,6 @@ export const SingleRecordPicker = ({
|
||||
onCreate,
|
||||
onRecordSelected,
|
||||
objectNameSingular,
|
||||
width = 200,
|
||||
componentInstanceId,
|
||||
layoutDirection,
|
||||
}: SingleRecordPickerProps) => {
|
||||
@ -51,7 +49,7 @@ export const SingleRecordPicker = ({
|
||||
<SingleRecordPickerComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
>
|
||||
<DropdownMenu ref={containerRef} width={width} data-select-disable>
|
||||
<DropdownMenu ref={containerRef} data-select-disable>
|
||||
<SingleRecordPickerMenuItemsWithSearch
|
||||
{...{
|
||||
EmptyIcon,
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { SingleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/single-record-picker/states/contexts/SingleRecordPickerComponentInstanceContext';
|
||||
import { SingleRecordPickerRecord } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerRecord';
|
||||
import { getSingleRecordPickerSelectableListId } from '@/object-record/record-picker/single-record-picker/utils/getSingleRecordPickerSelectableListId';
|
||||
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { MenuItemSelectAvatar } from 'twenty-ui/navigation';
|
||||
|
||||
@ -33,12 +33,12 @@ export const SingleRecordPickerMenuItem = ({
|
||||
const selectableListComponentInstanceId =
|
||||
getSingleRecordPickerSelectableListId(recordPickerComponentInstanceId);
|
||||
|
||||
const { isSelectedItemIdSelector } = useSelectableList(
|
||||
const isSelectedItemId = useRecoilComponentFamilyValueV2(
|
||||
isSelectedItemIdComponentFamilySelector,
|
||||
record.id,
|
||||
selectableListComponentInstanceId,
|
||||
);
|
||||
|
||||
const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector(record.id));
|
||||
|
||||
return (
|
||||
<StyledSelectableItem itemId={record.id} key={record.id}>
|
||||
<MenuItemSelectAvatar
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { isNonEmptyString, isUndefined } from '@sniptt/guards';
|
||||
import { useRef } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
||||
@ -15,7 +14,9 @@ import { singleRecordPickerSelectedIdComponentState } from '@/object-record/reco
|
||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||
import { SingleRecordPickerRecord } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerRecord';
|
||||
import { getSingleRecordPickerSelectableListId } from '@/object-record/record-picker/single-record-picker/utils/getSingleRecordPickerSelectableListId';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import styled from '@emotion/styled';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -76,12 +77,14 @@ export const SingleRecordPickerMenuItems = ({
|
||||
const selectableListComponentInstanceId =
|
||||
getSingleRecordPickerSelectableListId(recordPickerComponentInstanceId);
|
||||
|
||||
const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList(
|
||||
const { resetSelectedItem } = useSelectableList(
|
||||
selectableListComponentInstanceId,
|
||||
);
|
||||
|
||||
const isSelectedSelectNoneButton = useRecoilValue(
|
||||
isSelectedItemIdSelector('select-none'),
|
||||
const isSelectedSelectNoneButton = useRecoilComponentFamilyValueV2(
|
||||
isSelectedItemIdComponentFamilySelector,
|
||||
selectableListComponentInstanceId,
|
||||
'select-none',
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
@ -102,7 +105,7 @@ export const SingleRecordPickerMenuItems = ({
|
||||
return (
|
||||
<StyledContainer ref={containerRef}>
|
||||
<SelectableList
|
||||
selectableListId={selectableListComponentInstanceId}
|
||||
selectableListInstanceId={selectableListComponentInstanceId}
|
||||
selectableItemIdArray={selectableItemIds}
|
||||
hotkeyScope={hotkeyScope}
|
||||
onEnter={(itemId) => {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip';
|
||||
@ -8,9 +7,10 @@ import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
|
||||
|
||||
export const MultipleSelectDropdown = ({
|
||||
@ -35,13 +35,13 @@ export const MultipleSelectDropdown = ({
|
||||
loadingItems: boolean;
|
||||
}) => {
|
||||
const { closeDropdown } = useDropdown();
|
||||
const { selectedItemIdState } = useSelectableListStates({
|
||||
selectableListScopeId: selectableListId,
|
||||
});
|
||||
|
||||
const { resetSelectedItem } = useSelectableList(selectableListId);
|
||||
|
||||
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||
const selectedItemId = useRecoilComponentValueV2(
|
||||
selectedItemIdComponentState,
|
||||
selectableListId,
|
||||
);
|
||||
|
||||
const handleItemSelectChange = (
|
||||
itemToSelect: SelectableItem,
|
||||
@ -90,7 +90,7 @@ export const MultipleSelectDropdown = ({
|
||||
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListId={selectableListId}
|
||||
selectableListInstanceId={selectableListId}
|
||||
selectableItemIdArray={selectableItemIds}
|
||||
hotkeyScope={hotkeyScope}
|
||||
onEnter={(itemId) => {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
@ -8,14 +7,16 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
|
||||
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
|
||||
import { MenuItemMultiSelectTag } from 'twenty-ui/navigation';
|
||||
import { SelectOption } from 'twenty-ui/input';
|
||||
import { MenuItemMultiSelectTag } from 'twenty-ui/navigation';
|
||||
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
|
||||
|
||||
type MultiSelectInputProps = {
|
||||
selectableListComponentInstanceId: string;
|
||||
@ -34,14 +35,14 @@ export const MultiSelectInput = ({
|
||||
onCancel,
|
||||
onOptionSelected,
|
||||
}: MultiSelectInputProps) => {
|
||||
const { selectedItemIdState } = useSelectableListStates({
|
||||
selectableListScopeId: selectableListComponentInstanceId,
|
||||
});
|
||||
const { resetSelectedItem } = useSelectableList(
|
||||
selectableListComponentInstanceId,
|
||||
);
|
||||
|
||||
const selectedItemId = useRecoilValue(selectedItemIdState);
|
||||
const selectedItemId = useRecoilComponentValueV2(
|
||||
selectedItemIdComponentState,
|
||||
selectableListComponentInstanceId,
|
||||
);
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -97,7 +98,7 @@ export const MultiSelectInput = ({
|
||||
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListId={selectableListComponentInstanceId}
|
||||
selectableListInstanceId={selectableListComponentInstanceId}
|
||||
selectableItemIdArray={optionIds}
|
||||
hotkeyScope={hotkeyScope}
|
||||
onEnter={(itemId) => {
|
||||
|
||||
@ -31,7 +31,7 @@ export const SelectInput = ({
|
||||
}: SelectInputProps) => {
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListId={selectableListComponentInstanceId}
|
||||
selectableListInstanceId={selectableListComponentInstanceId}
|
||||
selectableItemIdArray={selectableItemIdArray}
|
||||
hotkeyScope={hotkeyScope}
|
||||
onEnter={onEnter}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
@ -9,10 +8,11 @@ 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 { 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/arrayToChunks';
|
||||
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconApps, IconComponent, useIcons } from 'twenty-ui/display';
|
||||
import {
|
||||
@ -64,9 +64,10 @@ const IconPickerIcon = ({
|
||||
selectedIconKey,
|
||||
Icon,
|
||||
}: IconPickerIconProps) => {
|
||||
const { isSelectedItemIdSelector } = useSelectableList();
|
||||
|
||||
const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector(iconKey));
|
||||
const isSelectedItemId = useRecoilComponentValueV2(
|
||||
selectedItemIdComponentState,
|
||||
iconKey,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledLightIconButton
|
||||
@ -74,7 +75,7 @@ const IconPickerIcon = ({
|
||||
aria-label={convertIconKeyToLabel(iconKey)}
|
||||
size="medium"
|
||||
title={iconKey}
|
||||
isSelected={iconKey === selectedIconKey || isSelectedItemId}
|
||||
isSelected={iconKey === selectedIconKey || !!isSelectedItemId}
|
||||
Icon={Icon}
|
||||
onClick={onClick}
|
||||
/>
|
||||
@ -175,7 +176,7 @@ export const IconPicker = ({
|
||||
dropdownWidth={176}
|
||||
dropdownComponents={
|
||||
<SelectableList
|
||||
selectableListId="icon-list"
|
||||
selectableListInstanceId="icon-list"
|
||||
selectableItemIdMatrix={iconKeys2d}
|
||||
hotkeyScope={IconPickerHotkeyScope.IconPicker}
|
||||
onEnter={(iconKey) => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ReactNode, useEffect, useRef } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@ -20,9 +20,10 @@ export const SelectableItem = ({
|
||||
children,
|
||||
className,
|
||||
}: SelectableItemProps) => {
|
||||
const { isSelectedItemIdSelector } = useSelectableList();
|
||||
|
||||
const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector(itemId));
|
||||
const isSelectedItemId = useRecoilComponentFamilyValueV2(
|
||||
isSelectedItemIdComponentFamilySelector,
|
||||
itemId,
|
||||
);
|
||||
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
@ -1,34 +1,43 @@
|
||||
import { ReactNode, useEffect } from 'react';
|
||||
|
||||
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';
|
||||
import { arrayToChunks } from '~/utils/array/arrayToChunks';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState';
|
||||
import { selectableListOnEnterComponentState } from '@/ui/layout/selectable-list/states/selectableListOnEnterComponentState';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { arrayToChunks } from '~/utils/array/arrayToChunks';
|
||||
|
||||
type SelectableListProps = {
|
||||
children: ReactNode;
|
||||
selectableListId: string;
|
||||
selectableItemIdArray?: string[];
|
||||
selectableItemIdMatrix?: string[][];
|
||||
onSelect?: (selected: string) => void;
|
||||
hotkeyScope: string;
|
||||
onEnter?: (itemId: string) => void;
|
||||
selectableListInstanceId: string;
|
||||
};
|
||||
|
||||
export const SelectableList = ({
|
||||
children,
|
||||
selectableListId,
|
||||
hotkeyScope,
|
||||
selectableItemIdArray,
|
||||
selectableItemIdMatrix,
|
||||
selectableListInstanceId,
|
||||
onEnter,
|
||||
onSelect,
|
||||
}: SelectableListProps) => {
|
||||
useSelectableListHotKeys(selectableListId, hotkeyScope, onSelect);
|
||||
useSelectableListHotKeys(selectableListInstanceId, hotkeyScope, onSelect);
|
||||
|
||||
const { setSelectableItemIds, setSelectableListOnEnter, setSelectedItemId } =
|
||||
useSelectableList(selectableListId);
|
||||
const setSelectableListOnEnter = useSetRecoilComponentStateV2(
|
||||
selectableListOnEnterComponentState,
|
||||
selectableListInstanceId,
|
||||
);
|
||||
|
||||
const setSelectableItemIds = useSetRecoilComponentStateV2(
|
||||
selectableItemIdsComponentState,
|
||||
selectableListInstanceId,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectableListOnEnter(() => onEnter);
|
||||
@ -48,16 +57,15 @@ export const SelectableList = ({
|
||||
if (isDefined(selectableItemIdArray)) {
|
||||
setSelectableItemIds(arrayToChunks(selectableItemIdArray, 1));
|
||||
}
|
||||
}, [
|
||||
selectableItemIdArray,
|
||||
selectableItemIdMatrix,
|
||||
setSelectableItemIds,
|
||||
setSelectedItemId,
|
||||
]);
|
||||
}, [selectableItemIdArray, selectableItemIdMatrix, setSelectableItemIds]);
|
||||
|
||||
return (
|
||||
<SelectableListScope selectableListScopeId={selectableListId}>
|
||||
<SelectableListComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: selectableListInstanceId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SelectableListScope>
|
||||
</SelectableListComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
|
||||
const selectableListScopeId = 'testId';
|
||||
const testArr = [['1'], ['2'], ['3']];
|
||||
@ -13,15 +16,15 @@ describe('useSelectableList', () => {
|
||||
it('Should setSelectableItemIds', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const { setSelectableItemIds } = useSelectableList(
|
||||
const setSelectableItemIds = useSetRecoilComponentStateV2(
|
||||
selectableItemIdsComponentState,
|
||||
selectableListScopeId,
|
||||
);
|
||||
|
||||
const { selectableItemIdsState } = useSelectableListStates({
|
||||
const selectableItemIds = useRecoilComponentValueV2(
|
||||
selectableItemIdsComponentState,
|
||||
selectableListScopeId,
|
||||
});
|
||||
|
||||
const selectableItemIds = useRecoilValue(selectableItemIdsState);
|
||||
);
|
||||
|
||||
return {
|
||||
setSelectableItemIds,
|
||||
@ -47,13 +50,14 @@ describe('useSelectableList', () => {
|
||||
() => {
|
||||
const { resetSelectedItem } = useSelectableList(selectableListScopeId);
|
||||
|
||||
const { selectedItemIdState } = useSelectableListStates({
|
||||
const selectedItemId = useRecoilComponentValueV2(
|
||||
selectedItemIdComponentState,
|
||||
selectableListScopeId,
|
||||
});
|
||||
|
||||
const [selectedItemId, setSelectedItemId] =
|
||||
useRecoilState(selectedItemIdState);
|
||||
|
||||
);
|
||||
const setSelectedItemId = useSetRecoilComponentStateV2(
|
||||
selectedItemIdComponentState,
|
||||
selectableListScopeId,
|
||||
);
|
||||
return {
|
||||
resetSelectedItem,
|
||||
selectedItemId,
|
||||
|
||||
@ -2,14 +2,17 @@ import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
|
||||
import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState';
|
||||
import { selectableListOnEnterComponentState } from '@/ui/layout/selectable-list/states/selectableListOnEnterComponentState';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
type Direction = 'up' | 'down' | 'left' | 'right';
|
||||
|
||||
export const useSelectableListHotKeys = (
|
||||
scopeId: string,
|
||||
instanceId: string,
|
||||
hotkeyScope: string,
|
||||
onSelect?: (itemId: string) => void,
|
||||
) => {
|
||||
@ -29,22 +32,20 @@ export const useSelectableListHotKeys = (
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
selectedItemIdState,
|
||||
selectableItemIdsState,
|
||||
isSelectedItemIdSelector,
|
||||
selectableListOnEnterState,
|
||||
} = useSelectableListStates({
|
||||
selectableListScopeId: scopeId,
|
||||
});
|
||||
|
||||
const handleSelect = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(direction: Direction) => {
|
||||
const selectedItemId = getSnapshotValue(snapshot, selectedItemIdState);
|
||||
const selectedItemId = getSnapshotValue(
|
||||
snapshot,
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: instanceId,
|
||||
}),
|
||||
);
|
||||
const selectableItemIds = getSnapshotValue(
|
||||
snapshot,
|
||||
selectableItemIdsState,
|
||||
selectableItemIdsComponentState.atomFamily({
|
||||
instanceId: instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
const currentPosition = findPosition(selectableItemIds, selectedItemId);
|
||||
@ -104,22 +105,34 @@ export const useSelectableListHotKeys = (
|
||||
|
||||
if (selectedItemId !== nextId) {
|
||||
if (isNonEmptyString(nextId)) {
|
||||
set(isSelectedItemIdSelector(nextId), true);
|
||||
set(selectedItemIdState, nextId);
|
||||
set(
|
||||
isSelectedItemIdComponentFamilySelector.selectorFamily({
|
||||
instanceId: instanceId,
|
||||
familyKey: nextId,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
set(
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: instanceId,
|
||||
}),
|
||||
nextId,
|
||||
);
|
||||
onSelect?.(nextId);
|
||||
}
|
||||
|
||||
if (isNonEmptyString(selectedItemId)) {
|
||||
set(isSelectedItemIdSelector(selectedItemId), false);
|
||||
set(
|
||||
isSelectedItemIdComponentFamilySelector.selectorFamily({
|
||||
instanceId: instanceId,
|
||||
familyKey: selectedItemId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
isSelectedItemIdSelector,
|
||||
onSelect,
|
||||
selectableItemIdsState,
|
||||
selectedItemIdState,
|
||||
],
|
||||
[instanceId, onSelect],
|
||||
);
|
||||
|
||||
useScopedHotkeys(Key.ArrowUp, () => handleSelect('up'), hotkeyScope, []);
|
||||
@ -142,18 +155,22 @@ export const useSelectableListHotKeys = (
|
||||
() => {
|
||||
const selectedItemId = getSnapshotValue(
|
||||
snapshot,
|
||||
selectedItemIdState,
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: instanceId,
|
||||
}),
|
||||
);
|
||||
const onEnter = getSnapshotValue(
|
||||
snapshot,
|
||||
selectableListOnEnterState,
|
||||
selectableListOnEnterComponentState.atomFamily({
|
||||
instanceId: instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isNonEmptyString(selectedItemId)) {
|
||||
onEnter?.(selectedItemId);
|
||||
}
|
||||
},
|
||||
[selectableListOnEnterState, selectedItemIdState],
|
||||
[instanceId],
|
||||
),
|
||||
hotkeyScope,
|
||||
[],
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import { SelectableListScopeInternalContext } from '@/ui/layout/selectable-list/scopes/scope-internal-context/SelectableListScopeInternalContext';
|
||||
import { selectableItemIdsComponentState } from '@/ui/layout/selectable-list/states/selectableItemIdsComponentState';
|
||||
import { selectableListOnEnterComponentState } from '@/ui/layout/selectable-list/states/selectableListOnEnterComponentState';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { isSelectedItemIdFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdFamilySelector';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { extractComponentFamilyState } from '@/ui/utilities/state/component-state/utils/extractComponentFamilyState';
|
||||
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
|
||||
|
||||
type useSelectableListStatesProps = {
|
||||
selectableListScopeId?: string;
|
||||
};
|
||||
|
||||
export const useSelectableListStates = ({
|
||||
selectableListScopeId,
|
||||
}: useSelectableListStatesProps) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
SelectableListScopeInternalContext,
|
||||
selectableListScopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
isSelectedItemIdSelector: extractComponentFamilyState(
|
||||
isSelectedItemIdFamilySelector,
|
||||
scopeId,
|
||||
),
|
||||
selectableItemIdsState: extractComponentState(
|
||||
selectableItemIdsComponentState,
|
||||
scopeId,
|
||||
),
|
||||
selectableListOnEnterState: extractComponentState(
|
||||
selectableListOnEnterComponentState,
|
||||
scopeId,
|
||||
),
|
||||
selectedItemIdState: extractComponentState(
|
||||
selectedItemIdComponentState,
|
||||
scopeId,
|
||||
),
|
||||
};
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
@ -7,15 +7,15 @@ import { Key } from 'ts-key-enum';
|
||||
|
||||
export const useListenToEnterHotkeyOnListItem = ({
|
||||
hotkeyScope,
|
||||
|
||||
itemId,
|
||||
onEnter,
|
||||
}: {
|
||||
hotkeyScope: string;
|
||||
|
||||
itemId: string;
|
||||
onEnter: () => void;
|
||||
}) => {
|
||||
const { selectedItemIdState } = useSelectableList();
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
useRecoilCallback(
|
||||
@ -23,17 +23,19 @@ export const useListenToEnterHotkeyOnListItem = ({
|
||||
() => {
|
||||
const selectedItemId = getSnapshotValue(
|
||||
snapshot,
|
||||
selectedItemIdState,
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: itemId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isNonEmptyString(selectedItemId) && selectedItemId === itemId) {
|
||||
onEnter?.();
|
||||
}
|
||||
},
|
||||
[itemId, onEnter, selectedItemIdState],
|
||||
[itemId, onEnter],
|
||||
),
|
||||
hotkeyScope,
|
||||
[selectedItemIdState, itemId, onEnter],
|
||||
[itemId, onEnter],
|
||||
{
|
||||
preventDefault: false,
|
||||
},
|
||||
|
||||
@ -1,60 +1,85 @@
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useSelectableListStates } from '@/ui/layout/selectable-list/hooks/internal/useSelectableListStates';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useSelectableList = (selectableListId?: string) => {
|
||||
const {
|
||||
scopeId,
|
||||
selectableItemIdsState,
|
||||
selectableListOnEnterState,
|
||||
isSelectedItemIdSelector,
|
||||
selectedItemIdState,
|
||||
} = useSelectableListStates({
|
||||
selectableListScopeId: selectableListId,
|
||||
});
|
||||
|
||||
const setSelectableItemIds = useSetRecoilState(selectableItemIdsState);
|
||||
const setSelectableListOnEnter = useSetRecoilState(
|
||||
selectableListOnEnterState,
|
||||
export const useSelectableList = (instanceId?: string) => {
|
||||
const selectableListInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
SelectableListComponentInstanceContext,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
const resetSelectedItem = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
() => {
|
||||
const selectedItemId = getSnapshotValue(snapshot, selectedItemIdState);
|
||||
const selectedItemId = getSnapshotValue(
|
||||
snapshot,
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: selectableListInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isDefined(selectedItemId)) {
|
||||
set(selectedItemIdState, null);
|
||||
set(isSelectedItemIdSelector(selectedItemId), false);
|
||||
set(
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: selectableListInstanceId,
|
||||
}),
|
||||
null,
|
||||
);
|
||||
set(
|
||||
isSelectedItemIdComponentFamilySelector.selectorFamily({
|
||||
instanceId: selectableListInstanceId,
|
||||
familyKey: selectedItemId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
}
|
||||
},
|
||||
[selectedItemIdState, isSelectedItemIdSelector],
|
||||
[selectableListInstanceId],
|
||||
);
|
||||
|
||||
const setSelectedItemId = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(itemId: string) => {
|
||||
const selectedItemId = getSnapshotValue(snapshot, selectedItemIdState);
|
||||
const selectedItemId = getSnapshotValue(
|
||||
snapshot,
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: selectableListInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isDefined(selectedItemId)) {
|
||||
set(isSelectedItemIdSelector(selectedItemId), false);
|
||||
set(
|
||||
isSelectedItemIdComponentFamilySelector.selectorFamily({
|
||||
instanceId: selectableListInstanceId,
|
||||
familyKey: selectedItemId,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
set(selectedItemIdState, itemId);
|
||||
set(isSelectedItemIdSelector(itemId), true);
|
||||
set(
|
||||
selectedItemIdComponentState.atomFamily({
|
||||
instanceId: selectableListInstanceId,
|
||||
}),
|
||||
itemId,
|
||||
);
|
||||
set(
|
||||
isSelectedItemIdComponentFamilySelector.selectorFamily({
|
||||
instanceId: selectableListInstanceId,
|
||||
familyKey: itemId,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
},
|
||||
[selectedItemIdState, isSelectedItemIdSelector],
|
||||
[selectableListInstanceId],
|
||||
);
|
||||
|
||||
return {
|
||||
selectableListId: scopeId,
|
||||
setSelectableItemIds,
|
||||
isSelectedItemIdSelector,
|
||||
setSelectableListOnEnter,
|
||||
resetSelectedItem,
|
||||
setSelectedItemId,
|
||||
selectedItemIdState,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { SelectableListScopeInternalContext } from './scope-internal-context/SelectableListScopeInternalContext';
|
||||
|
||||
type SelectableListScopeProps = {
|
||||
children: ReactNode;
|
||||
selectableListScopeId: string;
|
||||
};
|
||||
|
||||
export const SelectableListScope = ({
|
||||
children,
|
||||
selectableListScopeId,
|
||||
}: SelectableListScopeProps) => {
|
||||
return (
|
||||
<SelectableListScopeInternalContext.Provider
|
||||
value={{ scopeId: selectableListScopeId }}
|
||||
>
|
||||
{children}
|
||||
</SelectableListScopeInternalContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey';
|
||||
|
||||
type SelectableListScopeInternalContextProps = RecoilComponentStateKey;
|
||||
|
||||
export const SelectableListScopeInternalContext =
|
||||
createScopeInternalContext<SelectableListScopeInternalContextProps>();
|
||||
@ -0,0 +1,4 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
|
||||
export const SelectableListComponentInstanceContext =
|
||||
createComponentInstanceContext();
|
||||
@ -1,9 +1,9 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2';
|
||||
|
||||
export const isSelectedItemIdComponentFamilyState = createComponentFamilyState<
|
||||
boolean,
|
||||
string
|
||||
>({
|
||||
key: 'isSelectedItemIdComponentFamilyState',
|
||||
defaultValue: false,
|
||||
});
|
||||
export const isSelectedItemIdComponentFamilyState =
|
||||
createComponentFamilyStateV2<boolean, string>({
|
||||
key: 'isSelectedItemIdComponentFamilyState',
|
||||
defaultValue: false,
|
||||
componentInstanceContext: SelectableListComponentInstanceContext,
|
||||
});
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||
|
||||
export const selectableItemIdsComponentState = createComponentState<string[][]>(
|
||||
{
|
||||
key: 'selectableItemIdsComponentState',
|
||||
defaultValue: [[]],
|
||||
},
|
||||
);
|
||||
export const selectableItemIdsComponentState = createComponentStateV2<
|
||||
string[][]
|
||||
>({
|
||||
key: 'selectableItemIdsComponentState',
|
||||
defaultValue: [[]],
|
||||
componentInstanceContext: SelectableListComponentInstanceContext,
|
||||
});
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||
|
||||
export const selectableListOnEnterComponentState = createComponentState<
|
||||
export const selectableListOnEnterComponentState = createComponentStateV2<
|
||||
((itemId: string) => void) | undefined
|
||||
>({
|
||||
key: 'selectableListOnEnterComponentState',
|
||||
defaultValue: undefined,
|
||||
componentInstanceContext: SelectableListComponentInstanceContext,
|
||||
});
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||
|
||||
export const selectedItemIdComponentState = createComponentState<string | null>(
|
||||
{
|
||||
key: 'selectedItemIdComponentState',
|
||||
defaultValue: null,
|
||||
},
|
||||
);
|
||||
export const selectedItemIdComponentState = createComponentStateV2<
|
||||
string | null
|
||||
>({
|
||||
key: 'selectedItemIdComponentState',
|
||||
defaultValue: null,
|
||||
componentInstanceContext: SelectableListComponentInstanceContext,
|
||||
});
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
|
||||
import { createComponentFamilySelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelectorV2';
|
||||
|
||||
export const isSelectedItemIdComponentFamilySelector =
|
||||
createComponentFamilySelectorV2<boolean, string>({
|
||||
key: 'isSelectedItemIdComponentFamilySelector',
|
||||
componentInstanceContext: SelectableListComponentInstanceContext,
|
||||
get:
|
||||
({ instanceId, familyKey }: { instanceId: string; familyKey: string }) =>
|
||||
({ get }) =>
|
||||
get(
|
||||
isSelectedItemIdComponentFamilyState.atomFamily({
|
||||
instanceId: instanceId,
|
||||
familyKey: familyKey,
|
||||
}),
|
||||
),
|
||||
set:
|
||||
({ instanceId, familyKey }: { instanceId: string; familyKey: string }) =>
|
||||
({ set }, newValue) =>
|
||||
set(
|
||||
isSelectedItemIdComponentFamilyState.atomFamily({
|
||||
instanceId: instanceId,
|
||||
familyKey: familyKey,
|
||||
}),
|
||||
newValue,
|
||||
),
|
||||
});
|
||||
@ -1,28 +0,0 @@
|
||||
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
|
||||
import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector';
|
||||
|
||||
export const isSelectedItemIdFamilySelector = createComponentFamilySelector<
|
||||
boolean,
|
||||
string
|
||||
>({
|
||||
key: 'isSelectedItemIdFamilySelector',
|
||||
get:
|
||||
({ scopeId, familyKey }: { scopeId: string; familyKey: string }) =>
|
||||
({ get }) =>
|
||||
get(
|
||||
isSelectedItemIdComponentFamilyState({
|
||||
scopeId: scopeId,
|
||||
familyKey: familyKey,
|
||||
}),
|
||||
),
|
||||
set:
|
||||
({ scopeId, familyKey }: { scopeId: string; familyKey: string }) =>
|
||||
({ set }, newValue) =>
|
||||
set(
|
||||
isSelectedItemIdComponentFamilyState({
|
||||
scopeId: scopeId,
|
||||
familyKey: familyKey,
|
||||
}),
|
||||
newValue,
|
||||
),
|
||||
});
|
||||
@ -1,4 +1,5 @@
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { ComponentFamilySelectorV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilySelectorV2';
|
||||
import { ComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateV2';
|
||||
import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap';
|
||||
import { SerializableParam, useRecoilState } from 'recoil';
|
||||
@ -7,7 +8,9 @@ export const useRecoilComponentFamilyStateV2 = <
|
||||
StateType,
|
||||
FamilyKey extends SerializableParam,
|
||||
>(
|
||||
componentState: ComponentFamilyStateV2<StateType, FamilyKey>,
|
||||
componentState:
|
||||
| ComponentFamilyStateV2<StateType, FamilyKey>
|
||||
| ComponentFamilySelectorV2<StateType, FamilyKey>,
|
||||
familyKey: FamilyKey,
|
||||
instanceIdFromProps?: string,
|
||||
) => {
|
||||
@ -26,5 +29,10 @@ export const useRecoilComponentFamilyStateV2 = <
|
||||
instanceIdFromProps,
|
||||
);
|
||||
|
||||
return useRecoilState(componentState.atomFamily({ instanceId, familyKey }));
|
||||
const familySelector =
|
||||
componentState.type === 'ComponentFamilyState'
|
||||
? componentState.atomFamily({ instanceId, familyKey })
|
||||
: componentState.selectorFamily({ instanceId, familyKey });
|
||||
|
||||
return useRecoilState(familySelector);
|
||||
};
|
||||
|
||||
@ -16,6 +16,7 @@ export function createComponentFamilySelectorV2<
|
||||
>(options: {
|
||||
key: string;
|
||||
get: SelectorGetter<ValueType, ComponentFamilyStateKeyV2<FamilyKey>>;
|
||||
set?: never;
|
||||
componentInstanceContext: ComponentInstanceStateContext<any> | null;
|
||||
}): ComponentFamilyReadOnlySelectorV2<ValueType, FamilyKey>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user