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