Replace hotkey scopes by focus stack (Part 1 - Dropdowns and Side Panel) (#12673)
This PR is the first part of a refactoring aiming to deprecate the hotkey scopes api in favor of the new focus stack api which is more robust. The refactored components in this PR are the dropdowns and the side panel/command menu. - Replaced `useScopedHotkeys` by `useHotkeysOnFocusedElement` for all dropdown components, selectable lists and the command menu - Introduced `focusId` for all dropdowns and created a common hotkey scope `DropdownHotkeyScope` for backward compatibility - Replaced `setHotkeyScopeAndMemorizePreviousScope` occurrences with `usePushFocusItemToFocusStack` and `goBackToPreviousHotkeyScope` with `removeFocusItemFromFocusStack` Note: Test that the shorcuts and arrow key navigation still work properly when interacting with dropdowns and the command menu. Bugs that I have spotted during the QA but which are already present on main: - Icon picker select with arrow keys doesn’t work inside dropdowns - Some dropdowns are not selectable with arrow keys (no selectable list) - Dropdowns in dropdowns don’t reset the hotkey scope correctly when closing - The table click outside is not triggered after closing a table cell and clicking outside of the table
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||||
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { Button } from 'twenty-ui/input';
|
import { Button } from 'twenty-ui/input';
|
||||||
@ -13,12 +14,13 @@ export const CmdEnterActionButton = ({
|
|||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
[`${Key.Control}+${Key.Enter}`, `${Key.Meta}+${Key.Enter}`],
|
keys: [`${Key.Control}+${Key.Enter}`, `${Key.Meta}+${Key.Enter}`],
|
||||||
() => onClick(),
|
callback: () => onClick(),
|
||||||
AppHotkeyScope.CommandMenuOpen,
|
focusId: SIDE_PANEL_FOCUS_ID,
|
||||||
[onClick],
|
scope: AppHotkeyScope.CommandMenuOpen,
|
||||||
);
|
dependencies: [onClick],
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -2,15 +2,16 @@ import { ActionComponent } from '@/action-menu/actions/display/components/Action
|
|||||||
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
import { ActionScope } from '@/action-menu/actions/types/ActionScope';
|
||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { CommandMenuActionMenuDropdownHotkeyScope } from '@/action-menu/types/CommandMenuActionMenuDropdownHotkeyScope';
|
|
||||||
import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId';
|
import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId';
|
||||||
|
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
||||||
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 { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
@ -25,24 +26,30 @@ export const CommandMenuActionMenuDropdown = () => {
|
|||||||
ActionMenuComponentInstanceContext,
|
ActionMenuComponentInstanceContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { toggleDropdown } = useDropdownV2();
|
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
useScopedHotkeys(
|
const dropdownId =
|
||||||
['ctrl+o,meta+o'],
|
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId);
|
||||||
() => {
|
const { toggleDropdown } = useDropdownV2();
|
||||||
toggleDropdown(
|
|
||||||
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId),
|
const hotkeysConfig = {
|
||||||
{
|
keys: ['ctrl+o', 'meta+o'],
|
||||||
scope:
|
callback: () => {
|
||||||
CommandMenuActionMenuDropdownHotkeyScope.CommandMenuActionMenuDropdown,
|
toggleDropdown(dropdownId);
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
AppHotkeyScope.CommandMenuOpen,
|
scope: AppHotkeyScope.CommandMenuOpen,
|
||||||
[toggleDropdown],
|
dependencies: [toggleDropdown],
|
||||||
);
|
};
|
||||||
|
|
||||||
|
useHotkeysOnFocusedElement({
|
||||||
|
...hotkeysConfig,
|
||||||
|
focusId: SIDE_PANEL_FOCUS_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
useHotkeysOnFocusedElement({
|
||||||
|
...hotkeysConfig,
|
||||||
|
focusId: dropdownId,
|
||||||
|
});
|
||||||
|
|
||||||
const recordSelectionActions = actions.filter(
|
const recordSelectionActions = actions.filter(
|
||||||
(action) => action.scope === ActionScope.RecordSelection,
|
(action) => action.scope === ActionScope.RecordSelection,
|
||||||
@ -56,19 +63,17 @@ export const CommandMenuActionMenuDropdown = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={getRightDrawerActionMenuDropdownIdFromActionMenuId(
|
dropdownId={dropdownId}
|
||||||
actionMenuId,
|
|
||||||
)}
|
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope:
|
|
||||||
CommandMenuActionMenuDropdownHotkeyScope.CommandMenuActionMenuDropdown,
|
|
||||||
}}
|
|
||||||
data-select-disable
|
data-select-disable
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<Button title="Options" hotkeys={[getOsControlSymbol(), 'O']} />
|
<Button title="Options" hotkeys={[getOsControlSymbol(), 'O']} />
|
||||||
}
|
}
|
||||||
dropdownPlacement="top-end"
|
dropdownPlacement="top-end"
|
||||||
dropdownOffset={{ y: parseInt(theme.spacing(2), 10) }}
|
dropdownOffset={{ y: parseInt(theme.spacing(2), 10) }}
|
||||||
|
globalHotkeysConfig={{
|
||||||
|
enableGlobalHotkeysWithModifiers: true,
|
||||||
|
enableGlobalHotkeysConflictingWithKeyboard: false,
|
||||||
|
}}
|
||||||
onOpen={() => {
|
onOpen={() => {
|
||||||
setSelectedItemId(selectableItemIdArray[0]);
|
setSelectedItemId(selectableItemIdArray[0]);
|
||||||
}}
|
}}
|
||||||
@ -77,10 +82,9 @@ export const CommandMenuActionMenuDropdown = () => {
|
|||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={actionMenuId}
|
selectableListInstanceId={actionMenuId}
|
||||||
hotkeyScope={
|
focusId={dropdownId}
|
||||||
CommandMenuActionMenuDropdownHotkeyScope.CommandMenuActionMenuDropdown
|
|
||||||
}
|
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{recordSelectionActions.map((action) => (
|
{recordSelectionActions.map((action) => (
|
||||||
<ActionComponent action={action} key={action.key} />
|
<ActionComponent action={action} key={action.key} />
|
||||||
|
|||||||
@ -5,12 +5,12 @@ import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/A
|
|||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
||||||
import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope';
|
|
||||||
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
@ -72,9 +72,6 @@ export const RecordIndexActionMenuDropdown = () => {
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown,
|
|
||||||
}}
|
|
||||||
data-select-disable
|
data-select-disable
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownStrategy="absolute"
|
dropdownStrategy="absolute"
|
||||||
@ -89,9 +86,10 @@ export const RecordIndexActionMenuDropdown = () => {
|
|||||||
>
|
>
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
hotkeyScope={ActionMenuDropdownHotkeyScope.ActionMenuDropdown}
|
focusId={dropdownId}
|
||||||
selectableItemIdArray={selectedItemIdArray}
|
selectableItemIdArray={selectedItemIdArray}
|
||||||
selectableListInstanceId={dropdownId}
|
selectableListInstanceId={dropdownId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{recordIndexActions.map((action) => (
|
{recordIndexActions.map((action) => (
|
||||||
<ActionComponent action={action} key={action.key} />
|
<ActionComponent action={action} key={action.key} />
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId';
|
import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId';
|
||||||
|
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
|
import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext';
|
||||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||||
@ -11,7 +12,7 @@ import { AppPath } from '@/types/AppPath';
|
|||||||
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
||||||
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
|
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
|
||||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||||
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
|
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
|
||||||
@ -118,12 +119,13 @@ export const RecordShowRightDrawerOpenRecordButton = ({
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
['ctrl+Enter,meta+Enter'],
|
keys: ['ctrl+Enter,meta+Enter'],
|
||||||
handleOpenRecord,
|
callback: handleOpenRecord,
|
||||||
AppHotkeyScope.CommandMenuOpen,
|
focusId: SIDE_PANEL_FOCUS_ID,
|
||||||
[handleOpenRecord],
|
scope: AppHotkeyScope.CommandMenuOpen,
|
||||||
);
|
dependencies: [handleOpenRecord],
|
||||||
|
});
|
||||||
|
|
||||||
if (!isDefined(record)) {
|
if (!isDefined(record)) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
export enum ActionMenuDropdownHotkeyScope {
|
|
||||||
ActionMenuDropdown = 'action-menu-dropdown',
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export enum CommandMenuActionMenuDropdownHotkeyScope {
|
|
||||||
CommandMenuActionMenuDropdown = 'command-menu-action-menu-dropdown',
|
|
||||||
}
|
|
||||||
@ -72,7 +72,6 @@ export const AttachmentDropdown = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -70,6 +70,7 @@ export const ActivityTargetsInlineCell = ({
|
|||||||
labelWidth: fieldDefinition?.labelWidth,
|
labelWidth: fieldDefinition?.labelWidth,
|
||||||
editModeContent: (
|
editModeContent: (
|
||||||
<MultipleRecordPicker
|
<MultipleRecordPicker
|
||||||
|
focusId={componentInstanceId}
|
||||||
componentInstanceId={componentInstanceId}
|
componentInstanceId={componentInstanceId}
|
||||||
onClickOutside={() => {
|
onClickOutside={() => {
|
||||||
closeInlineCell();
|
closeInlineCell();
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-pic
|
|||||||
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
||||||
import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState';
|
import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState';
|
||||||
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
||||||
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||||
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
type OpenActivityTargetCellEditModeProps = {
|
type OpenActivityTargetCellEditModeProps = {
|
||||||
@ -19,7 +20,7 @@ export const useOpenActivityTargetCellEditMode = () => {
|
|||||||
const { performSearch: multipleRecordPickerPerformSearch } =
|
const { performSearch: multipleRecordPickerPerformSearch } =
|
||||||
useMultipleRecordPickerPerformSearch();
|
useMultipleRecordPickerPerformSearch();
|
||||||
|
|
||||||
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||||
|
|
||||||
const openActivityTargetCellEditMode = useRecoilCallback(
|
const openActivityTargetCellEditMode = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
@ -84,11 +85,19 @@ export const useOpenActivityTargetCellEditMode = () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope({
|
pushFocusItemToFocusStack({
|
||||||
scope: MultipleRecordPickerHotkeyScope.MultipleRecordPicker,
|
focusId: recordPickerInstanceId,
|
||||||
|
component: {
|
||||||
|
type: FocusComponentType.DROPDOWN,
|
||||||
|
instanceId: recordPickerInstanceId,
|
||||||
|
},
|
||||||
|
hotkeyScope: {
|
||||||
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
|
},
|
||||||
|
memoizeKey: recordPickerInstanceId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[multipleRecordPickerPerformSearch, setHotkeyScopeAndMemorizePreviousScope],
|
[multipleRecordPickerPerformSearch, pushFocusItemToFocusStack],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { openActivityTargetCellEditMode };
|
return { openActivityTargetCellEditMode };
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
|||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
|
||||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { MenuItem } from 'twenty-ui/navigation';
|
import { MenuItem } from 'twenty-ui/navigation';
|
||||||
import {
|
import {
|
||||||
@ -72,9 +71,6 @@ export const CommandMenuContextChipGroups = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: AppHotkeyScope.CommandMenu,
|
|
||||||
}}
|
|
||||||
dropdownId={COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID}
|
dropdownId={COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
></Dropdown>
|
></Dropdown>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { ActionGroupConfig } from '@/command-menu/components/CommandMenu';
|
|||||||
import { CommandMenuDefaultSelectionEffect } from '@/command-menu/components/CommandMenuDefaultSelectionEffect';
|
import { CommandMenuDefaultSelectionEffect } from '@/command-menu/components/CommandMenuDefaultSelectionEffect';
|
||||||
import { COMMAND_MENU_SEARCH_BAR_HEIGHT } from '@/command-menu/constants/CommandMenuSearchBarHeight';
|
import { COMMAND_MENU_SEARCH_BAR_HEIGHT } from '@/command-menu/constants/CommandMenuSearchBarHeight';
|
||||||
import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/CommandMenuSearchBarPadding';
|
import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/CommandMenuSearchBarPadding';
|
||||||
|
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||||
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
|
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||||
@ -75,8 +76,9 @@ export const CommandMenuList = ({
|
|||||||
<StyledInnerList>
|
<StyledInnerList>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId="command-menu-list"
|
selectableListInstanceId="command-menu-list"
|
||||||
hotkeyScope={AppHotkeyScope.CommandMenuOpen}
|
focusId={SIDE_PANEL_FOCUS_ID}
|
||||||
selectableItemIdArray={selectableItemIds}
|
selectableItemIdArray={selectableItemIds}
|
||||||
|
hotkeyScope={AppHotkeyScope.CommandMenuOpen}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setHasUserSelectedCommand(true);
|
setHasUserSelectedCommand(true);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export const SIDE_PANEL_FOCUS_ID = 'command-menu';
|
||||||
@ -3,12 +3,13 @@ import { useRecoilCallback } from 'recoil';
|
|||||||
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
|
||||||
|
|
||||||
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
||||||
|
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||||
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
|
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
|
||||||
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
|
import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState';
|
||||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||||
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
|
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
|
||||||
import { isDragSelectionStartEnabledState } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState';
|
import { isDragSelectionStartEnabledState } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { useRemoveFocusItemFromFocusStack } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStack';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { IconDotsVertical } from 'twenty-ui/display';
|
import { IconDotsVertical } from 'twenty-ui/display';
|
||||||
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
||||||
@ -16,7 +17,8 @@ import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
|
|||||||
export const useCommandMenu = () => {
|
export const useCommandMenu = () => {
|
||||||
const { navigateCommandMenu } = useNavigateCommandMenu();
|
const { navigateCommandMenu } = useNavigateCommandMenu();
|
||||||
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
|
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
|
||||||
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
|
|
||||||
|
const { removeFocusItemFromFocusStack } = useRemoveFocusItemFromFocusStack();
|
||||||
|
|
||||||
const closeCommandMenu = useRecoilCallback(
|
const closeCommandMenu = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
@ -30,10 +32,13 @@ export const useCommandMenu = () => {
|
|||||||
set(isCommandMenuClosingState, true);
|
set(isCommandMenuClosingState, true);
|
||||||
set(isDragSelectionStartEnabledState, true);
|
set(isDragSelectionStartEnabledState, true);
|
||||||
closeAnyOpenDropdown();
|
closeAnyOpenDropdown();
|
||||||
goBackToPreviousHotkeyScope(COMMAND_MENU_COMPONENT_INSTANCE_ID);
|
removeFocusItemFromFocusStack({
|
||||||
|
focusId: SIDE_PANEL_FOCUS_ID,
|
||||||
|
memoizeKey: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[closeAnyOpenDropdown, goBackToPreviousHotkeyScope],
|
[closeAnyOpenDropdown, removeFocusItemFromFocusStack],
|
||||||
);
|
);
|
||||||
|
|
||||||
const openCommandMenu = useCallback(() => {
|
const openCommandMenu = useCallback(() => {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
||||||
|
import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId';
|
||||||
import { useCommandMenuCloseAnimationCompleteCleanup } from '@/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup';
|
import { useCommandMenuCloseAnimationCompleteCleanup } from '@/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup';
|
||||||
import { useCopyContextStoreStates } from '@/command-menu/hooks/useCopyContextStoreAndActionMenuStates';
|
import { useCopyContextStoreStates } from '@/command-menu/hooks/useCopyContextStoreAndActionMenuStates';
|
||||||
import { commandMenuNavigationMorphItemByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsState';
|
import { commandMenuNavigationMorphItemByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsState';
|
||||||
@ -13,7 +14,8 @@ import { CommandMenuHotkeyScope } from '@/command-menu/types/CommandMenuHotkeySc
|
|||||||
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||||
import { isDragSelectionStartEnabledState } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState';
|
import { isDragSelectionStartEnabledState } from '@/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||||
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
import { IconComponent } from 'twenty-ui/display';
|
import { IconComponent } from 'twenty-ui/display';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
@ -27,13 +29,13 @@ export type CommandMenuNavigationStackItem = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useNavigateCommandMenu = () => {
|
export const useNavigateCommandMenu = () => {
|
||||||
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
|
|
||||||
|
|
||||||
const { copyContextStoreStates } = useCopyContextStoreStates();
|
const { copyContextStoreStates } = useCopyContextStoreStates();
|
||||||
|
|
||||||
const { commandMenuCloseAnimationCompleteCleanup } =
|
const { commandMenuCloseAnimationCompleteCleanup } =
|
||||||
useCommandMenuCloseAnimationCompleteCleanup();
|
useCommandMenuCloseAnimationCompleteCleanup();
|
||||||
|
|
||||||
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||||
|
|
||||||
const openCommandMenu = useRecoilCallback(
|
const openCommandMenu = useRecoilCallback(
|
||||||
({ snapshot, set }) =>
|
({ snapshot, set }) =>
|
||||||
() => {
|
() => {
|
||||||
@ -53,10 +55,17 @@ export const useNavigateCommandMenu = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope({
|
pushFocusItemToFocusStack({
|
||||||
scope: CommandMenuHotkeyScope.CommandMenuFocused,
|
focusId: SIDE_PANEL_FOCUS_ID,
|
||||||
customScopes: {
|
component: {
|
||||||
commandMenuOpen: true,
|
type: FocusComponentType.SIDE_PANEL,
|
||||||
|
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||||
|
},
|
||||||
|
hotkeyScope: {
|
||||||
|
scope: CommandMenuHotkeyScope.CommandMenuFocused,
|
||||||
|
customScopes: {
|
||||||
|
commandMenuOpen: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
memoizeKey: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
memoizeKey: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||||
});
|
});
|
||||||
@ -73,7 +82,7 @@ export const useNavigateCommandMenu = () => {
|
|||||||
[
|
[
|
||||||
copyContextStoreStates,
|
copyContextStoreStates,
|
||||||
commandMenuCloseAnimationCompleteCleanup,
|
commandMenuCloseAnimationCompleteCleanup,
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
pushFocusItemToFocusStack,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
|
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
@ -33,9 +32,6 @@ export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={`favorite-folder-edit-${folderId}`}
|
dropdownId={`favorite-folder-edit-${folderId}`}
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: FavoriteFolderHotkeyScope.FavoriteFolderRightIconDropdown,
|
|
||||||
}}
|
|
||||||
data-select-disable
|
data-select-disable
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||||
|
|||||||
@ -9,7 +9,8 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
|||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
|
|
||||||
@ -57,22 +58,23 @@ export const FavoriteFolderPicker = ({
|
|||||||
!favoriteFoldersSearchFilter ||
|
!favoriteFoldersSearchFilter ||
|
||||||
'no folder'.includes(favoriteFoldersSearchFilter.toLowerCase());
|
'no folder'.includes(favoriteFoldersSearchFilter.toLowerCase());
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
Key.Escape,
|
keys: [Key.Escape],
|
||||||
() => {
|
callback: () => {
|
||||||
if (isFavoriteFolderCreating) {
|
if (isFavoriteFolderCreating) {
|
||||||
setIsFavoriteFolderCreating(false);
|
setIsFavoriteFolderCreating(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSubmit?.();
|
onSubmit?.();
|
||||||
},
|
},
|
||||||
instanceId,
|
focusId: dropdownId,
|
||||||
[onSubmit, isFavoriteFolderCreating],
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
);
|
dependencies: [onSubmit, isFavoriteFolderCreating],
|
||||||
|
});
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
Key.Enter,
|
keys: [Key.Enter],
|
||||||
() => {
|
callback: () => {
|
||||||
if (filteredFolders.length === 1 && !showNoFolderOption) {
|
if (filteredFolders.length === 1 && !showNoFolderOption) {
|
||||||
toggleFolderSelection(filteredFolders[0].id);
|
toggleFolderSelection(filteredFolders[0].id);
|
||||||
onSubmit?.();
|
onSubmit?.();
|
||||||
@ -85,9 +87,15 @@ export const FavoriteFolderPicker = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
instanceId,
|
focusId: instanceId,
|
||||||
[filteredFolders, showNoFolderOption, toggleFolderSelection, onSubmit],
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
);
|
dependencies: [
|
||||||
|
filteredFolders,
|
||||||
|
showNoFolderOption,
|
||||||
|
toggleFolderSelection,
|
||||||
|
onSubmit,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownContent>
|
<DropdownContent>
|
||||||
|
|||||||
@ -180,7 +180,6 @@ export const AdvancedFilterAddFilterRuleSelect = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
dropdownOffset={{ y: 8, x: 0 }}
|
dropdownOffset={{ y: 8, x: 0 }}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
|
|||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
type AdvancedFilterDropdownFilterInputProps = {
|
type AdvancedFilterDropdownFilterInputProps = {
|
||||||
filterDropdownId?: string;
|
filterDropdownId: string;
|
||||||
recordFilter: RecordFilter;
|
recordFilter: RecordFilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -57,12 +57,15 @@ export const AdvancedFilterDropdownFilterInput = ({
|
|||||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||||
<ObjectFilterDropdownSearchInput />
|
<ObjectFilterDropdownSearchInput />
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<ObjectFilterDropdownRecordSelect recordFilterId={recordFilter.id} />
|
<ObjectFilterDropdownRecordSelect
|
||||||
|
recordFilterId={recordFilter.id}
|
||||||
|
dropdownId={filterDropdownId}
|
||||||
|
/>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
)}
|
)}
|
||||||
{filterType === 'ACTOR' &&
|
{filterType === 'ACTOR' &&
|
||||||
(isActorSourceCompositeFilter ? (
|
(isActorSourceCompositeFilter ? (
|
||||||
<ObjectFilterDropdownSourceSelect />
|
<ObjectFilterDropdownSourceSelect dropdownId={filterDropdownId} />
|
||||||
) : (
|
) : (
|
||||||
<ObjectFilterDropdownTextInput />
|
<ObjectFilterDropdownTextInput />
|
||||||
))}
|
))}
|
||||||
@ -70,7 +73,7 @@ export const AdvancedFilterDropdownFilterInput = ({
|
|||||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||||
<ObjectFilterDropdownSearchInput />
|
<ObjectFilterDropdownSearchInput />
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<ObjectFilterDropdownOptionSelect />
|
<ObjectFilterDropdownOptionSelect focusId={filterDropdownId} />
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
)}
|
)}
|
||||||
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
||||||
|
|||||||
@ -34,7 +34,6 @@ export const AdvancedFilterFieldSelectDropdownButton = ({
|
|||||||
recordFilterId={recordFilterId}
|
recordFilterId={recordFilterId}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: advancedFilterFieldSelectDropdownId }}
|
|
||||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/uti
|
|||||||
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
|
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/DropdownMenuSectionLabel';
|
import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/DropdownMenuSectionLabel';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
@ -144,9 +145,10 @@ export const AdvancedFilterFieldSelectMenu = ({
|
|||||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||||
<AdvancedFilterFieldSelectSearchInput />
|
<AdvancedFilterFieldSelectSearchInput />
|
||||||
<SelectableList
|
<SelectableList
|
||||||
hotkeyScope={advancedFilterFieldSelectDropdownId}
|
focusId={advancedFilterFieldSelectDropdownId}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
selectableListInstanceId={advancedFilterFieldSelectDropdownId}
|
selectableListInstanceId={advancedFilterFieldSelectDropdownId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{shouldShowVisibleFields && (
|
{shouldShowVisibleFields && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -65,7 +65,6 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
dropdownOffset={{ y: 2, x: 0 }}
|
dropdownOffset={{ y: 2, x: 0 }}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { SelectControl } from '@/ui/input/components/SelectControl';
|
|||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
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';
|
||||||
@ -63,11 +64,12 @@ export const AdvancedFilterRecordFilterOperandSelectContent = ({
|
|||||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
hotkeyScope={dropdownId}
|
focusId={dropdownId}
|
||||||
selectableItemIdArray={operandsForFilterType.map(
|
selectableItemIdArray={operandsForFilterType.map(
|
||||||
(operand) => operand,
|
(operand) => operand,
|
||||||
)}
|
)}
|
||||||
selectableListInstanceId={dropdownId}
|
selectableListInstanceId={dropdownId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{operandsForFilterType.map((filterOperand, index) => (
|
{operandsForFilterType.map((filterOperand, index) => (
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
@ -90,7 +92,6 @@ export const AdvancedFilterRecordFilterOperandSelectContent = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -85,7 +85,6 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { CompositeFieldSubFieldName } from '@/settings/data-model/types/Composit
|
|||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
@ -124,9 +125,10 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
|||||||
</DropdownMenuHeader>
|
</DropdownMenuHeader>
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
hotkeyScope={advancedFilterFieldSelectDropdownId}
|
focusId={advancedFilterFieldSelectDropdownId}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
selectableListInstanceId={advancedFilterFieldSelectDropdownId}
|
selectableListInstanceId={advancedFilterFieldSelectDropdownId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{compositeFieldTypeIsFilterableByAnySubField && (
|
{compositeFieldTypeIsFilterableByAnySubField && (
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
|
|||||||
@ -104,9 +104,11 @@ export const AdvancedFilterValueInput = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<AdvancedFilterDropdownFilterInput recordFilter={recordFilter} />
|
<AdvancedFilterDropdownFilterInput
|
||||||
|
recordFilter={recordFilter}
|
||||||
|
filterDropdownId={dropdownId}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
dropdownOffset={dropdownContentOffset}
|
dropdownOffset={dropdownContentOffset}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
onClose={handleFilterValueDropdownClose}
|
onClose={handleFilterValueDropdownClose}
|
||||||
|
|||||||
@ -13,10 +13,10 @@ import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
|||||||
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
|
import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType';
|
||||||
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands';
|
||||||
import { isCompositeTypeNonFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeNonFilterableByAnySubField';
|
import { isCompositeTypeNonFilterableByAnySubField } from '@/object-record/record-filter/utils/isCompositeTypeNonFilterableByAnySubField';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
|
||||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||||
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||||
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 { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
@ -44,7 +44,7 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
|||||||
currentRecordFiltersComponentState,
|
currentRecordFiltersComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||||
|
|
||||||
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
|
||||||
|
|
||||||
@ -76,7 +76,17 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => {
|
|||||||
fieldMetadataItem.type === 'RELATION' ||
|
fieldMetadataItem.type === 'RELATION' ||
|
||||||
fieldMetadataItem.type === 'SELECT'
|
fieldMetadataItem.type === 'SELECT'
|
||||||
) {
|
) {
|
||||||
setHotkeyScope(SingleRecordPickerHotkeyScope.SingleRecordPicker);
|
pushFocusItemToFocusStack({
|
||||||
|
focusId: fieldMetadataItem.id,
|
||||||
|
component: {
|
||||||
|
type: FocusComponentType.DROPDOWN,
|
||||||
|
instanceId: fieldMetadataItem.id,
|
||||||
|
},
|
||||||
|
hotkeyScope: {
|
||||||
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
|
},
|
||||||
|
memoizeKey: fieldMetadataItem.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
||||||
|
|||||||
@ -3,10 +3,10 @@ import styled from '@emotion/styled';
|
|||||||
|
|
||||||
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
|
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
|
||||||
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
|
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
|
||||||
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
|
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
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';
|
||||||
@ -57,7 +57,8 @@ export const ObjectFilterDropdownBooleanSelect = () => {
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId="boolean-select"
|
selectableListInstanceId="boolean-select"
|
||||||
selectableItemIdArray={options.map((option) => option.toString())}
|
selectableItemIdArray={options.map((option) => option.toString())}
|
||||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
focusId="boolean-select"
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
|
|||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
type ObjectFilterDropdownFilterInputProps = {
|
type ObjectFilterDropdownFilterInputProps = {
|
||||||
filterDropdownId?: string;
|
filterDropdownId: string;
|
||||||
recordFilterId?: string;
|
recordFilterId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,7 +113,10 @@ export const ObjectFilterDropdownFilterInput = ({
|
|||||||
<>
|
<>
|
||||||
<ObjectFilterDropdownSearchInput />
|
<ObjectFilterDropdownSearchInput />
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<ObjectFilterDropdownRecordSelect recordFilterId={recordFilterId} />
|
<ObjectFilterDropdownRecordSelect
|
||||||
|
recordFilterId={recordFilterId}
|
||||||
|
dropdownId={filterDropdownId}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{filterType === 'ACTOR' && <ObjectFilterDropdownTextInput />}
|
{filterType === 'ACTOR' && <ObjectFilterDropdownTextInput />}
|
||||||
@ -123,7 +126,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
|||||||
<>
|
<>
|
||||||
<ObjectFilterDropdownSearchInput />
|
<ObjectFilterDropdownSearchInput />
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<ObjectFilterDropdownOptionSelect />
|
<ObjectFilterDropdownOptionSelect focusId={filterDropdownId} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
{filterType === 'BOOLEAN' && <ObjectFilterDropdownBooleanSelect />}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu
|
|||||||
import { ObjectFilterDropdownOperandSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect';
|
import { ObjectFilterDropdownOperandSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect';
|
||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
|
|
||||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
|
import { ClickOutsideListenerContext } from '@/ui/utilities/pointer-event/contexts/ClickOutsideListenerContext';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
@ -47,9 +46,6 @@ export const ObjectFilterDropdownOperandDropdown = ({
|
|||||||
</StyledDropdownMenuHeader>
|
</StyledDropdownMenuHeader>
|
||||||
}
|
}
|
||||||
dropdownComponents={<ObjectFilterDropdownOperandSelect />}
|
dropdownComponents={<ObjectFilterDropdownOperandSelect />}
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: FiltersHotkeyScope.ObjectFilterDropdownOperandDropdown,
|
|
||||||
}}
|
|
||||||
dropdownOffset={{ x: parseInt(theme.spacing(2), 10) }}
|
dropdownOffset={{ x: parseInt(theme.spacing(2), 10) }}
|
||||||
/>
|
/>
|
||||||
</ClickOutsideListenerContext.Provider>
|
</ClickOutsideListenerContext.Provider>
|
||||||
|
|||||||
@ -14,9 +14,9 @@ import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/ob
|
|||||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
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';
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
@ -30,7 +30,11 @@ type SelectOptionForFilter = FieldMetadataItemOption & {
|
|||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ObjectFilterDropdownOptionSelect = () => {
|
export const ObjectFilterDropdownOptionSelect = ({
|
||||||
|
focusId,
|
||||||
|
}: {
|
||||||
|
focusId: string;
|
||||||
|
}) => {
|
||||||
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
|
||||||
fieldMetadataItemUsedInDropdownComponentSelector,
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
);
|
);
|
||||||
@ -92,15 +96,16 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
|||||||
}
|
}
|
||||||
}, [selectedOptions, selectOptions]);
|
}, [selectedOptions, selectOptions]);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
[Key.Escape],
|
keys: [Key.Escape],
|
||||||
() => {
|
callback: () => {
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
resetSelectedItem();
|
resetSelectedItem();
|
||||||
},
|
},
|
||||||
SingleRecordPickerHotkeyScope.SingleRecordPicker,
|
focusId,
|
||||||
[closeDropdown, resetSelectedItem],
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
);
|
dependencies: [closeDropdown, resetSelectedItem],
|
||||||
|
});
|
||||||
|
|
||||||
const handleMultipleOptionSelectChange = (
|
const handleMultipleOptionSelectChange = (
|
||||||
optionChanged: SelectOptionForFilter,
|
optionChanged: SelectOptionForFilter,
|
||||||
@ -148,7 +153,8 @@ export const ObjectFilterDropdownOptionSelect = () => {
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={componentInstanceId}
|
selectableListInstanceId={componentInstanceId}
|
||||||
selectableItemIdArray={objectRecordsIds}
|
selectableItemIdArray={objectRecordsIds}
|
||||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
focusId={focusId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{showNoResult ? (
|
{showNoResult ? (
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
|
|||||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||||
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
|
||||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
|
||||||
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
|
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
|
||||||
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
|
import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect';
|
||||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||||
@ -29,10 +28,12 @@ export const MAX_RECORDS_TO_DISPLAY = 3;
|
|||||||
|
|
||||||
type ObjectFilterDropdownRecordSelectProps = {
|
type ObjectFilterDropdownRecordSelectProps = {
|
||||||
recordFilterId?: string;
|
recordFilterId?: string;
|
||||||
|
dropdownId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ObjectFilterDropdownRecordSelect = ({
|
export const ObjectFilterDropdownRecordSelect = ({
|
||||||
recordFilterId,
|
recordFilterId,
|
||||||
|
dropdownId,
|
||||||
}: ObjectFilterDropdownRecordSelectProps) => {
|
}: ObjectFilterDropdownRecordSelectProps) => {
|
||||||
const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2(
|
const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2(
|
||||||
fieldMetadataItemUsedInDropdownComponentSelector,
|
fieldMetadataItemUsedInDropdownComponentSelector,
|
||||||
@ -220,7 +221,7 @@ export const ObjectFilterDropdownRecordSelect = ({
|
|||||||
)}
|
)}
|
||||||
<MultipleSelectDropdown
|
<MultipleSelectDropdown
|
||||||
selectableListId="object-filter-record-select-id"
|
selectableListId="object-filter-record-select-id"
|
||||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
focusId={dropdownId}
|
||||||
itemsToSelect={recordsToSelect}
|
itemsToSelect={recordsToSelect}
|
||||||
filteredSelectedItems={filteredSelectedRecords}
|
filteredSelectedItems={filteredSelectedRecords}
|
||||||
selectedItems={selectedRecords}
|
selectedItems={selectedRecords}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-recor
|
|||||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||||
import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions';
|
import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
|
||||||
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
|
import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown';
|
||||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
@ -15,7 +14,11 @@ import { isDefined } from 'twenty-shared/utils';
|
|||||||
export const EMPTY_FILTER_VALUE = '[]';
|
export const EMPTY_FILTER_VALUE = '[]';
|
||||||
export const MAX_ITEMS_TO_DISPLAY = 3;
|
export const MAX_ITEMS_TO_DISPLAY = 3;
|
||||||
|
|
||||||
export const ObjectFilterDropdownSourceSelect = () => {
|
export const ObjectFilterDropdownSourceSelect = ({
|
||||||
|
dropdownId,
|
||||||
|
}: {
|
||||||
|
dropdownId: string;
|
||||||
|
}) => {
|
||||||
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
|
const objectFilterDropdownSearchInput = useRecoilComponentValueV2(
|
||||||
objectFilterDropdownSearchInputComponentState,
|
objectFilterDropdownSearchInputComponentState,
|
||||||
);
|
);
|
||||||
@ -78,7 +81,7 @@ export const ObjectFilterDropdownSourceSelect = () => {
|
|||||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||||
<MultipleSelectDropdown
|
<MultipleSelectDropdown
|
||||||
selectableListId="object-filter-source-select-id"
|
selectableListId="object-filter-source-select-id"
|
||||||
hotkeyScope={SingleRecordPickerHotkeyScope.SingleRecordPicker}
|
focusId={dropdownId}
|
||||||
itemsToSelect={sourceTypes.filter(
|
itemsToSelect={sourceTypes.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
!filteredSelectedItems.some((selected) => selected.id === item.id),
|
!filteredSelectedItems.some((selected) => selected.id === item.id),
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
export enum FiltersHotkeyScope {
|
|
||||||
ObjectFilterDropdownButton = 'filter-dropdown-button',
|
|
||||||
ObjectSortDropdownButton = 'sort-dropdown-button',
|
|
||||||
ObjectFilterDropdownOperandDropdown = 'filter-dropdown-operand-dropdown',
|
|
||||||
}
|
|
||||||
@ -7,7 +7,6 @@ import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dro
|
|||||||
import { ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
|
import { ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
|
||||||
import { RecordGroupReorderConfirmationModal } from '@/object-record/record-group/components/RecordGroupReorderConfirmationModal';
|
import { RecordGroupReorderConfirmationModal } from '@/object-record/record-group/components/RecordGroupReorderConfirmationModal';
|
||||||
import { useRecordGroupReorderConfirmationModal } from '@/object-record/record-group/hooks/useRecordGroupReorderConfirmationModal';
|
import { useRecordGroupReorderConfirmationModal } from '@/object-record/record-group/hooks/useRecordGroupReorderConfirmationModal';
|
||||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
@ -40,7 +39,6 @@ export const ObjectOptionsDropdown = ({
|
|||||||
<>
|
<>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={OBJECT_OPTIONS_DROPDOWN_ID}
|
dropdownId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
dropdownHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
|
||||||
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
|
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledHeaderDropdownButton isUnfolded={isDropdownOpen}>
|
<StyledHeaderDropdownButton isUnfolded={isDropdownOpen}>
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hook
|
|||||||
import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-options-dropdown/hooks/useSetViewTypeFromLayoutOptionsMenu';
|
import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-options-dropdown/hooks/useSetViewTypeFromLayoutOptionsMenu';
|
||||||
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
||||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
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 { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
@ -108,8 +108,9 @@ export const ObjectOptionsDropdownLayoutContent = () => {
|
|||||||
{!!currentView && (
|
{!!currentView && (
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
hotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
focusId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemsContainer scrollable={false}>
|
<DropdownMenuItemsContainer scrollable={false}>
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
|
|||||||
@ -2,11 +2,11 @@ import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropd
|
|||||||
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
||||||
import { useUpdateObjectViewOptions } from '@/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions';
|
import { useUpdateObjectViewOptions } from '@/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions';
|
||||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
@ -53,8 +53,9 @@ export const ObjectOptionsDropdownLayoutOpenInContent = () => {
|
|||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
hotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
focusId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
itemId={ViewOpenRecordInType.SIDE_PANEL}
|
itemId={ViewOpenRecordInType.SIDE_PANEL}
|
||||||
|
|||||||
@ -1,20 +1,17 @@
|
|||||||
import { Key } from 'ts-key-enum';
|
|
||||||
|
|
||||||
import { ObjectOptionsDropdownMenuViewName } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuViewName';
|
import { ObjectOptionsDropdownMenuViewName } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuViewName';
|
||||||
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
|
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
|
||||||
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
|
||||||
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
|
||||||
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
||||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
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 { 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 { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||||
@ -48,14 +45,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
|||||||
(isDefined(currentView?.viewGroups) && currentView.viewGroups.length > 0) ||
|
(isDefined(currentView?.viewGroups) && currentView.viewGroups.length > 0) ||
|
||||||
currentView?.key !== 'INDEX';
|
currentView?.key !== 'INDEX';
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
[Key.Escape],
|
|
||||||
() => {
|
|
||||||
closeDropdown();
|
|
||||||
},
|
|
||||||
TableOptionsHotkeyScope.Dropdown,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { visibleBoardFields } = useObjectOptionsForBoard({
|
const { visibleBoardFields } = useObjectOptionsForBoard({
|
||||||
objectNameSingular: objectMetadataItem.nameSingular,
|
objectNameSingular: objectMetadataItem.nameSingular,
|
||||||
recordBoardId: recordIndexId,
|
recordBoardId: recordIndexId,
|
||||||
@ -101,8 +90,9 @@ export const ObjectOptionsDropdownMenuContent = () => {
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
hotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
focusId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemsContainer scrollable={false}>
|
<DropdownMenuItemsContainer scrollable={false}>
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
|
|||||||
@ -4,12 +4,13 @@ import { useUpdateObjectViewOptions } from '@/object-record/object-options-dropd
|
|||||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
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 { View } from '@/views/types/View';
|
import { View } from '@/views/types/View';
|
||||||
import { ViewsHotkeyScope } from '@/views/types/ViewsHotkeyScope';
|
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
|
||||||
import { useUpdateViewFromCurrentState } from '@/views/view-picker/hooks/useUpdateViewFromCurrentState';
|
import { useUpdateViewFromCurrentState } from '@/views/view-picker/hooks/useUpdateViewFromCurrentState';
|
||||||
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
|
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
|
||||||
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
|
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
|
||||||
@ -75,17 +76,19 @@ export const ObjectOptionsDropdownMenuViewName = ({
|
|||||||
const { updateViewFromCurrentState } = useUpdateViewFromCurrentState();
|
const { updateViewFromCurrentState } = useUpdateViewFromCurrentState();
|
||||||
const [viewName, setViewName] = useState(currentView?.name);
|
const [viewName, setViewName] = useState(currentView?.name);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
Key.Enter,
|
keys: [Key.Enter],
|
||||||
async () => {
|
callback: async () => {
|
||||||
if (viewPickerIsPersisting) {
|
if (viewPickerIsPersisting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateViewFromCurrentState();
|
await updateViewFromCurrentState();
|
||||||
},
|
},
|
||||||
ViewsHotkeyScope.ListDropdown,
|
focusId: VIEW_PICKER_DROPDOWN_ID,
|
||||||
);
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
|
dependencies: [viewPickerIsPersisting, updateViewFromCurrentState],
|
||||||
|
});
|
||||||
|
|
||||||
const handleIconChange = ({ iconKey }: { iconKey: string }) => {
|
const handleIconChange = ({ iconKey }: { iconKey: string }) => {
|
||||||
setViewPickerIsDirty(true);
|
setViewPickerIsDirty(true);
|
||||||
|
|||||||
@ -5,11 +5,11 @@ import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hook
|
|||||||
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
|
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
|
||||||
import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort';
|
import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort';
|
||||||
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
|
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
|
||||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
@ -73,8 +73,9 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
|
|||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
hotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
focusId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
itemId={RecordGroupSort.Manual}
|
itemId={RecordGroupSort.Manual}
|
||||||
|
|||||||
@ -9,12 +9,12 @@ import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-gr
|
|||||||
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
|
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
|
||||||
import { recordIndexRecordGroupHideComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentFamilyState';
|
import { recordIndexRecordGroupHideComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentFamilyState';
|
||||||
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
|
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
|
||||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
|
||||||
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
@ -94,6 +94,8 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
|||||||
'HideEmptyGroups',
|
'HideEmptyGroups',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const hiddenGroupsSelectableListId = `${OBJECT_OPTIONS_DROPDOWN_ID}-hidden-groups`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownContent>
|
<DropdownContent>
|
||||||
<DropdownMenuHeader
|
<DropdownMenuHeader
|
||||||
@ -109,8 +111,9 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
|||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
selectableListInstanceId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
hotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
focusId={OBJECT_OPTIONS_DROPDOWN_ID}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{currentView?.key !== 'INDEX' && (
|
{currentView?.key !== 'INDEX' && (
|
||||||
<>
|
<>
|
||||||
@ -175,9 +178,10 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItemsContainer scrollable={false}>
|
<DropdownMenuItemsContainer scrollable={false}>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={`${OBJECT_OPTIONS_DROPDOWN_ID}-hidden-groups`}
|
selectableListInstanceId={hiddenGroupsSelectableListId}
|
||||||
hotkeyScope={TableOptionsHotkeyScope.Dropdown}
|
focusId={hiddenGroupsSelectableListId}
|
||||||
selectableItemIdArray={['HiddenGroups']}
|
selectableItemIdArray={['HiddenGroups']}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
itemId="HiddenGroups"
|
itemId="HiddenGroups"
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { OBJECT_SORT_DROPDOWN_ID } from '@/object-record/object-sort-dropdown/co
|
|||||||
import { useCloseSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useCloseSortDropdown';
|
import { useCloseSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useCloseSortDropdown';
|
||||||
import { useResetRecordSortDropdownSearchInput } from '@/object-record/object-sort-dropdown/hooks/useResetRecordSortDropdownSearchInput';
|
import { useResetRecordSortDropdownSearchInput } from '@/object-record/object-sort-dropdown/hooks/useResetRecordSortDropdownSearchInput';
|
||||||
import { useResetSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useResetSortDropdown';
|
import { useResetSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useResetSortDropdown';
|
||||||
import { useToggleSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useToggleSortDropdown';
|
|
||||||
import { isRecordSortDirectionDropdownMenuUnfoldedComponentState } from '@/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState';
|
import { isRecordSortDirectionDropdownMenuUnfoldedComponentState } from '@/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState';
|
||||||
import { objectSortDropdownSearchInputComponentState } from '@/object-record/object-sort-dropdown/states/objectSortDropdownSearchInputComponentState';
|
import { objectSortDropdownSearchInputComponentState } from '@/object-record/object-sort-dropdown/states/objectSortDropdownSearchInputComponentState';
|
||||||
import { selectedRecordSortDirectionComponentState } from '@/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState';
|
import { selectedRecordSortDirectionComponentState } from '@/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState';
|
||||||
@ -25,6 +24,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
|
|||||||
import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/DropdownMenuSectionLabel';
|
import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/DropdownMenuSectionLabel';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
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';
|
||||||
@ -86,11 +86,7 @@ export type ObjectSortDropdownButtonProps = {
|
|||||||
hotkeyScope: HotkeyScope;
|
hotkeyScope: HotkeyScope;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ObjectSortDropdownButton = ({
|
export const ObjectSortDropdownButton = () => {
|
||||||
hotkeyScope,
|
|
||||||
}: ObjectSortDropdownButtonProps) => {
|
|
||||||
const { toggleSortDropdown } = useToggleSortDropdown();
|
|
||||||
|
|
||||||
const { resetRecordSortDropdownSearchInput } =
|
const { resetRecordSortDropdownSearchInput } =
|
||||||
useResetRecordSortDropdownSearchInput();
|
useResetRecordSortDropdownSearchInput();
|
||||||
|
|
||||||
@ -162,15 +158,16 @@ export const ObjectSortDropdownButton = ({
|
|||||||
const shouldShowSeparator =
|
const shouldShowSeparator =
|
||||||
visibleFieldMetadataItems.length > 0 && hiddenFieldMetadataItems.length > 0;
|
visibleFieldMetadataItems.length > 0 && hiddenFieldMetadataItems.length > 0;
|
||||||
|
|
||||||
const handleButtonClick = () => {
|
|
||||||
toggleSortDropdown();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDropdownButtonClose = () => {
|
const handleDropdownButtonClose = () => {
|
||||||
resetRecordSortDropdownSearchInput();
|
resetRecordSortDropdownSearchInput();
|
||||||
resetSortDropdown();
|
resetSortDropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDropdownOpen = () => {
|
||||||
|
resetSortDropdown();
|
||||||
|
setSelectedItemId(selectableItemIdArray[0]);
|
||||||
|
};
|
||||||
|
|
||||||
const { closeSortDropdown } = useCloseSortDropdown();
|
const { closeSortDropdown } = useCloseSortDropdown();
|
||||||
|
|
||||||
const { upsertRecordSort } = useUpsertRecordSort();
|
const { upsertRecordSort } = useUpsertRecordSort();
|
||||||
@ -224,16 +221,10 @@ export const ObjectSortDropdownButton = ({
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={OBJECT_SORT_DROPDOWN_ID}
|
dropdownId={OBJECT_SORT_DROPDOWN_ID}
|
||||||
dropdownHotkeyScope={hotkeyScope}
|
|
||||||
dropdownOffset={{ y: 8 }}
|
dropdownOffset={{ y: 8 }}
|
||||||
|
onOpen={handleDropdownOpen}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledHeaderDropdownButton
|
<StyledHeaderDropdownButton isUnfolded={isDropdownOpen}>
|
||||||
onClick={() => {
|
|
||||||
handleButtonClick();
|
|
||||||
setSelectedItemId(selectableItemIdArray[0]);
|
|
||||||
}}
|
|
||||||
isUnfolded={isDropdownOpen}
|
|
||||||
>
|
|
||||||
<Trans>Sort</Trans>
|
<Trans>Sort</Trans>
|
||||||
</StyledHeaderDropdownButton>
|
</StyledHeaderDropdownButton>
|
||||||
}
|
}
|
||||||
@ -241,8 +232,9 @@ export const ObjectSortDropdownButton = ({
|
|||||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
|
selectableListInstanceId={OBJECT_SORT_DROPDOWN_ID}
|
||||||
hotkeyScope={hotkeyScope.scope}
|
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
focusId={OBJECT_SORT_DROPDOWN_ID}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{isRecordSortDirectionMenuUnfolded && (
|
{isRecordSortDirectionMenuUnfolded && (
|
||||||
<StyledSelectedSortDirectionContainer>
|
<StyledSelectedSortDirectionContainer>
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/r
|
|||||||
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
||||||
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
|
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
|
||||||
|
|
||||||
import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope';
|
|
||||||
import { useActiveRecordBoardCard } from '@/object-record/record-board/hooks/useActiveRecordBoardCard';
|
import { useActiveRecordBoardCard } from '@/object-record/record-board/hooks/useActiveRecordBoardCard';
|
||||||
import { useFocusedRecordBoardCard } from '@/object-record/record-board/hooks/useFocusedRecordBoardCard';
|
import { useFocusedRecordBoardCard } from '@/object-record/record-board/hooks/useFocusedRecordBoardCard';
|
||||||
import { RecordBoardCardBody } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBody';
|
import { RecordBoardCardBody } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBody';
|
||||||
@ -155,7 +154,8 @@ export const RecordBoardCard = () => {
|
|||||||
y: event.clientY,
|
y: event.clientY,
|
||||||
});
|
});
|
||||||
openDropdown(actionMenuDropdownId, {
|
openDropdown(actionMenuDropdownId, {
|
||||||
scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown,
|
enableGlobalHotkeysWithModifiers: true,
|
||||||
|
enableGlobalHotkeysConflictingWithKeyboard: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext } from
|
|||||||
import { RecordBoardColumnHeaderAggregateDropdownButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton';
|
import { RecordBoardColumnHeaderAggregateDropdownButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton';
|
||||||
import { AggregateDropdownContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContent';
|
import { AggregateDropdownContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContent';
|
||||||
import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
|
import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
|
||||||
import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope';
|
|
||||||
import { RecordBoardColumnHeaderAggregateContentId } from '@/object-record/record-board/types/RecordBoardColumnHeaderAggregateContentId';
|
import { RecordBoardColumnHeaderAggregateContentId } from '@/object-record/record-board/types/RecordBoardColumnHeaderAggregateContentId';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
@ -42,9 +41,6 @@ export const RecordBoardColumnHeaderAggregateDropdown = ({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
onClose={handleResetContent}
|
onClose={handleResetContent}
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: RecordBoardColumnHotkeyScope.ColumnHeader,
|
|
||||||
}}
|
|
||||||
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
|
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<RecordBoardColumnHeaderAggregateDropdownButton
|
<RecordBoardColumnHeaderAggregateDropdownButton
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldM
|
|||||||
import { MultiSelectDisplay } from '@/ui/field/display/components/MultiSelectDisplay';
|
import { MultiSelectDisplay } from '@/ui/field/display/components/MultiSelectDisplay';
|
||||||
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
|
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
|
||||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||||
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
||||||
@ -21,7 +22,6 @@ import { isDefined } from 'twenty-shared/utils';
|
|||||||
import { VisibilityHidden } from 'twenty-ui/accessibility';
|
import { VisibilityHidden } from 'twenty-ui/accessibility';
|
||||||
import { IconChevronDown } from 'twenty-ui/display';
|
import { IconChevronDown } from 'twenty-ui/display';
|
||||||
import { SelectOption } from 'twenty-ui/input';
|
import { SelectOption } from 'twenty-ui/input';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
|
||||||
|
|
||||||
type FormMultiSelectFieldInputProps = {
|
type FormMultiSelectFieldInputProps = {
|
||||||
label?: string;
|
label?: string;
|
||||||
@ -256,7 +256,7 @@ export const FormMultiSelectFieldInput = ({
|
|||||||
selectableListComponentInstanceId={
|
selectableListComponentInstanceId={
|
||||||
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
||||||
}
|
}
|
||||||
hotkeyScope={hotkeyScope}
|
focusId={hotkeyScope}
|
||||||
options={options}
|
options={options}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onOptionSelected={onOptionSelected}
|
onOptionSelected={onOptionSelected}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { singleRecordPickerSelectedIdComponentState } from '@/object-record/reco
|
|||||||
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 { InputLabel } from '@/ui/input/components/InputLabel';
|
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
||||||
@ -18,7 +19,6 @@ import styled from '@emotion/styled';
|
|||||||
import { useCallback, useId } from 'react';
|
import { useCallback, useId } from 'react';
|
||||||
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
import { isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||||
import { IconChevronDown, IconForbid } from 'twenty-ui/display';
|
import { IconChevronDown, IconForbid } from 'twenty-ui/display';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
|
||||||
|
|
||||||
const StyledFormSelectContainer = styled(FormFieldInputInnerContainer)<{
|
const StyledFormSelectContainer = styled(FormFieldInputInnerContainer)<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
@ -189,6 +189,7 @@ export const FormSingleRecordPicker = ({
|
|||||||
}
|
}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<SingleRecordPicker
|
<SingleRecordPicker
|
||||||
|
focusId={dropdownId}
|
||||||
componentInstanceId={dropdownId}
|
componentInstanceId={dropdownId}
|
||||||
EmptyIcon={IconForbid}
|
EmptyIcon={IconForbid}
|
||||||
emptyLabel={'No ' + objectNameSingular}
|
emptyLabel={'No ' + objectNameSingular}
|
||||||
@ -199,7 +200,6 @@ export const FormSingleRecordPicker = ({
|
|||||||
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
|
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isDefined(VariablePicker) && !disabled && (
|
{isDefined(VariablePicker) && !disabled && (
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { getActivityTargetObjectRecords } from '@/activities/utils/getActivityTa
|
|||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { useOpenRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useOpenRelationFromManyFieldInput';
|
import { useOpenRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useOpenRelationFromManyFieldInput';
|
||||||
import { useOpenRelationToOneFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useOpenRelationToOneFieldInput';
|
import { useOpenRelationToOneFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useOpenRelationToOneFieldInput';
|
||||||
|
import { getRelationFromManyFieldInputInstanceId } from '@/object-record/record-field/meta-types/input/utils/getRelationFromManyFieldInputInstanceId';
|
||||||
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
||||||
import {
|
import {
|
||||||
FieldMetadata,
|
FieldMetadata,
|
||||||
@ -15,11 +16,13 @@ import {
|
|||||||
} from '@/object-record/record-field/types/FieldMetadata';
|
} from '@/object-record/record-field/types/FieldMetadata';
|
||||||
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects';
|
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects';
|
||||||
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
|
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
|
||||||
|
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||||
import { INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY } from '@/object-record/record-inline-cell/constants/InlineCellHotkeyScopeMemoizeKey';
|
import { INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY } from '@/object-record/record-inline-cell/constants/InlineCellHotkeyScopeMemoizeKey';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||||
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
@ -31,7 +34,7 @@ export const useOpenFieldInputEditMode = () => {
|
|||||||
const { openActivityTargetCellEditMode } =
|
const { openActivityTargetCellEditMode } =
|
||||||
useOpenActivityTargetCellEditMode();
|
useOpenActivityTargetCellEditMode();
|
||||||
|
|
||||||
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||||
|
|
||||||
const openFieldInput = useRecoilCallback(
|
const openFieldInput = useRecoilCallback(
|
||||||
({ snapshot }) =>
|
({ snapshot }) =>
|
||||||
@ -72,7 +75,10 @@ export const useOpenFieldInputEditMode = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
openActivityTargetCellEditMode({
|
openActivityTargetCellEditMode({
|
||||||
recordPickerInstanceId: `relation-from-many-field-input-${recordId}`,
|
recordPickerInstanceId: getRelationFromManyFieldInputInstanceId({
|
||||||
|
recordId,
|
||||||
|
fieldName: fieldDefinition.metadata.fieldName,
|
||||||
|
}),
|
||||||
activityTargetObjectRecords,
|
activityTargetObjectRecords,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -103,9 +109,22 @@ export const useOpenFieldInputEditMode = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope({
|
pushFocusItemToFocusStack({
|
||||||
scope: DEFAULT_CELL_SCOPE.scope,
|
focusId: getFieldInputInstanceId(
|
||||||
customScopes: DEFAULT_CELL_SCOPE.customScopes,
|
recordId,
|
||||||
|
fieldDefinition.metadata.fieldName,
|
||||||
|
),
|
||||||
|
component: {
|
||||||
|
type: FocusComponentType.OPEN_FIELD_INPUT,
|
||||||
|
instanceId: getFieldInputInstanceId(
|
||||||
|
recordId,
|
||||||
|
fieldDefinition.metadata.fieldName,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
hotkeyScope: {
|
||||||
|
scope: DEFAULT_CELL_SCOPE.scope,
|
||||||
|
customScopes: DEFAULT_CELL_SCOPE.customScopes,
|
||||||
|
},
|
||||||
memoizeKey: INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY,
|
memoizeKey: INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -113,7 +132,7 @@ export const useOpenFieldInputEditMode = () => {
|
|||||||
openActivityTargetCellEditMode,
|
openActivityTargetCellEditMode,
|
||||||
openRelationFromManyFieldInput,
|
openRelationFromManyFieldInput,
|
||||||
openRelationToOneFieldInput,
|
openRelationToOneFieldInput,
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
pushFocusItemToFocusStack,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,7 @@ export const useMultiSelectField = () => {
|
|||||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
recordId,
|
||||||
fieldDefinition,
|
fieldDefinition,
|
||||||
persistField,
|
persistField,
|
||||||
fieldValues: fieldMultiSelectValues,
|
fieldValues: fieldMultiSelectValues,
|
||||||
|
|||||||
@ -34,6 +34,7 @@ export const useSelectField = () => {
|
|||||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
recordId,
|
||||||
fieldDefinition,
|
fieldDefinition,
|
||||||
persistField,
|
persistField,
|
||||||
fieldValue: fieldSelectValue,
|
fieldValue: fieldSelectValue,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
|
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
|
||||||
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
|
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
|
||||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||||
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
|
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
|
||||||
|
|
||||||
type MultiSelectFieldInputProps = {
|
type MultiSelectFieldInputProps = {
|
||||||
@ -10,14 +10,18 @@ type MultiSelectFieldInputProps = {
|
|||||||
export const MultiSelectFieldInput = ({
|
export const MultiSelectFieldInput = ({
|
||||||
onCancel,
|
onCancel,
|
||||||
}: MultiSelectFieldInputProps) => {
|
}: MultiSelectFieldInputProps) => {
|
||||||
const { persistField, fieldDefinition, fieldValues } = useMultiSelectField();
|
const { persistField, fieldDefinition, fieldValues, recordId } =
|
||||||
|
useMultiSelectField();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultiSelectInput
|
<MultiSelectInput
|
||||||
selectableListComponentInstanceId={
|
selectableListComponentInstanceId={
|
||||||
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
||||||
}
|
}
|
||||||
hotkeyScope={DEFAULT_CELL_SCOPE.scope}
|
focusId={getFieldInputInstanceId(
|
||||||
|
recordId,
|
||||||
|
fieldDefinition.metadata.fieldName,
|
||||||
|
)}
|
||||||
options={fieldDefinition.metadata.options}
|
options={fieldDefinition.metadata.options}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onOptionSelected={persistField}
|
onOptionSelected={persistField}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
|
|||||||
import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField';
|
import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField';
|
||||||
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
|
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
|
||||||
import { useUpdateRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useUpdateRelationFromManyFieldInput';
|
import { useUpdateRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useUpdateRelationFromManyFieldInput';
|
||||||
|
import { getRelationFromManyFieldInputInstanceId } from '@/object-record/record-field/meta-types/input/utils/getRelationFromManyFieldInputInstanceId';
|
||||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
||||||
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
||||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||||
@ -25,7 +26,10 @@ export const RelationFromManyFieldInput = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
}: RelationFromManyFieldInputProps) => {
|
}: RelationFromManyFieldInputProps) => {
|
||||||
const { fieldDefinition, recordId } = useContext(FieldContext);
|
const { fieldDefinition, recordId } = useContext(FieldContext);
|
||||||
const recordPickerInstanceId = `relation-from-many-field-input-${recordId}`;
|
const recordPickerInstanceId = getRelationFromManyFieldInputInstanceId({
|
||||||
|
recordId,
|
||||||
|
fieldName: fieldDefinition.metadata.fieldName,
|
||||||
|
});
|
||||||
|
|
||||||
const { updateRelation } = useUpdateRelationFromManyFieldInput();
|
const { updateRelation } = useUpdateRelationFromManyFieldInput();
|
||||||
const fieldName = fieldDefinition.metadata.fieldName;
|
const fieldName = fieldDefinition.metadata.fieldName;
|
||||||
@ -84,6 +88,7 @@ export const RelationFromManyFieldInput = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MultipleRecordPicker
|
<MultipleRecordPicker
|
||||||
|
focusId={recordPickerInstanceId}
|
||||||
componentInstanceId={recordPickerInstanceId}
|
componentInstanceId={recordPickerInstanceId}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onChange={(morphItem) => {
|
onChange={(morphItem) => {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useRelationField } from '../../hooks/useRelationField';
|
|||||||
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
|
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
|
||||||
|
import { getRelationToOneFieldInputInstanceId } from '@/object-record/record-field/meta-types/input/utils/getRelationToOneFieldInputInstanceId';
|
||||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
||||||
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
||||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||||
@ -25,7 +26,10 @@ export const RelationToOneFieldInput = ({
|
|||||||
|
|
||||||
const persistField = usePersistField();
|
const persistField = usePersistField();
|
||||||
|
|
||||||
const recordPickerInstanceId = `relation-to-one-field-input-${recordId}-${fieldDefinition.metadata.fieldName}`;
|
const recordPickerInstanceId = getRelationToOneFieldInputInstanceId({
|
||||||
|
recordId,
|
||||||
|
fieldName: fieldDefinition.metadata.fieldName,
|
||||||
|
});
|
||||||
|
|
||||||
const handleRecordSelected = (
|
const handleRecordSelected = (
|
||||||
selectedRecord: SingleRecordPickerRecord | null | undefined,
|
selectedRecord: SingleRecordPickerRecord | null | undefined,
|
||||||
@ -64,6 +68,7 @@ export const RelationToOneFieldInput = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleRecordPicker
|
<SingleRecordPicker
|
||||||
|
focusId={recordPickerInstanceId}
|
||||||
componentInstanceId={recordPickerInstanceId}
|
componentInstanceId={recordPickerInstanceId}
|
||||||
EmptyIcon={IconForbid}
|
EmptyIcon={IconForbid}
|
||||||
emptyLabel={'No ' + fieldDefinition.label}
|
emptyLabel={'No ' + fieldDefinition.label}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useClearField } from '@/object-record/record-field/hooks/useClearField'
|
|||||||
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
|
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
|
||||||
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
|
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
|
||||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||||
|
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||||
import { SelectInput } from '@/ui/field/input/components/SelectInput';
|
import { SelectInput } from '@/ui/field/input/components/SelectInput';
|
||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
@ -20,7 +21,8 @@ export const SelectFieldInput = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
}: SelectFieldInputProps) => {
|
}: SelectFieldInputProps) => {
|
||||||
const { persistField, fieldDefinition, fieldValue } = useSelectField();
|
const { persistField, fieldDefinition, fieldValue, recordId } =
|
||||||
|
useSelectField();
|
||||||
|
|
||||||
const [filteredOptions, setFilteredOptions] = useState<SelectOption[]>([]);
|
const [filteredOptions, setFilteredOptions] = useState<SelectOption[]>([]);
|
||||||
|
|
||||||
@ -65,7 +67,10 @@ export const SelectFieldInput = ({
|
|||||||
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
||||||
}
|
}
|
||||||
selectableItemIdArray={optionIds}
|
selectableItemIdArray={optionIds}
|
||||||
hotkeyScope={DEFAULT_CELL_SCOPE.scope}
|
focusId={getFieldInputInstanceId(
|
||||||
|
recordId,
|
||||||
|
fieldDefinition.metadata.fieldName,
|
||||||
|
)}
|
||||||
onEnter={(itemId) => {
|
onEnter={(itemId) => {
|
||||||
const option = filteredOptions.find(
|
const option = filteredOptions.find(
|
||||||
(option) => option.value === itemId,
|
(option) => option.value === itemId,
|
||||||
|
|||||||
@ -18,9 +18,10 @@ import {
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||||
import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode';
|
import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode';
|
||||||
|
import { getRelationFromManyFieldInputInstanceId } from '@/object-record/record-field/meta-types/input/utils/getRelationFromManyFieldInputInstanceId';
|
||||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||||
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
|
|
||||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { RelationType } from '~/generated-metadata/graphql';
|
import { RelationType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ const RelationManyFieldInputWithContext = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRecordStoreFieldValue([]);
|
setRecordStoreFieldValue([]);
|
||||||
|
|
||||||
setHotKeyScope(MultipleRecordPickerHotkeyScope.MultipleRecordPicker);
|
setHotKeyScope(DropdownHotkeyScope.Dropdown);
|
||||||
openFieldInput({
|
openFieldInput({
|
||||||
fieldDefinition,
|
fieldDefinition,
|
||||||
recordId: 'recordId',
|
recordId: 'recordId',
|
||||||
@ -87,7 +88,10 @@ const RelationManyFieldInputWithContext = () => {
|
|||||||
<div>
|
<div>
|
||||||
<RecordFieldComponentInstanceContext.Provider
|
<RecordFieldComponentInstanceContext.Provider
|
||||||
value={{
|
value={{
|
||||||
instanceId: 'relation-from-many-field-record-id-people',
|
instanceId: getRelationFromManyFieldInputInstanceId({
|
||||||
|
recordId: 'recordId',
|
||||||
|
fieldName: 'people',
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FieldContext.Provider
|
<FieldContext.Provider
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { useSetRecoilState } from 'recoil';
|
|||||||
|
|
||||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
|
||||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||||
@ -17,9 +16,12 @@ import {
|
|||||||
|
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||||
|
import { getRelationToOneFieldInputInstanceId } from '@/object-record/record-field/meta-types/input/utils/getRelationToOneFieldInputInstanceId';
|
||||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||||
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||||
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { getCanvasElementForDropdownTesting } from 'twenty-ui/testing';
|
import { getCanvasElementForDropdownTesting } from 'twenty-ui/testing';
|
||||||
@ -62,11 +64,30 @@ const RelationToOneFieldInputWithContext = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
onCancel,
|
||||||
}: RelationToOneFieldInputWithContextProps) => {
|
}: RelationToOneFieldInputWithContextProps) => {
|
||||||
const setHotKeyScope = useSetHotkeyScope();
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHotKeyScope(SingleRecordPickerHotkeyScope.SingleRecordPicker);
|
pushFocusItemToFocusStack({
|
||||||
}, [setHotKeyScope]);
|
focusId: getRelationToOneFieldInputInstanceId({
|
||||||
|
recordId: '123',
|
||||||
|
fieldName: 'Relation',
|
||||||
|
}),
|
||||||
|
component: {
|
||||||
|
type: FocusComponentType.DROPDOWN,
|
||||||
|
instanceId: getRelationToOneFieldInputInstanceId({
|
||||||
|
recordId: '123',
|
||||||
|
fieldName: 'Relation',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
hotkeyScope: {
|
||||||
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
|
},
|
||||||
|
memoizeKey: getRelationToOneFieldInputInstanceId({
|
||||||
|
recordId: '123',
|
||||||
|
fieldName: 'Relation',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}, [pushFocusItemToFocusStack]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
import { getRelationFromManyFieldInputInstanceId } from '@/object-record/record-field/meta-types/input/utils/getRelationFromManyFieldInputInstanceId';
|
||||||
import {
|
import {
|
||||||
FieldRelationFromManyValue,
|
FieldRelationFromManyValue,
|
||||||
FieldRelationValue,
|
FieldRelationValue,
|
||||||
@ -6,17 +7,18 @@ import {
|
|||||||
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
|
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
|
||||||
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
||||||
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
||||||
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
|
|
||||||
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||||
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
export const useOpenRelationFromManyFieldInput = () => {
|
export const useOpenRelationFromManyFieldInput = () => {
|
||||||
const { performSearch } = useMultipleRecordPickerPerformSearch();
|
const { performSearch } = useMultipleRecordPickerPerformSearch();
|
||||||
|
|
||||||
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||||
|
|
||||||
const openRelationFromManyFieldInput = useRecoilCallback(
|
const openRelationFromManyFieldInput = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
@ -29,7 +31,10 @@ export const useOpenRelationFromManyFieldInput = () => {
|
|||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
recordId: string;
|
recordId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const recordPickerInstanceId = `relation-from-many-field-input-${recordId}`;
|
const recordPickerInstanceId = getRelationFromManyFieldInputInstanceId({
|
||||||
|
recordId,
|
||||||
|
fieldName,
|
||||||
|
});
|
||||||
|
|
||||||
const fieldValue = snapshot
|
const fieldValue = snapshot
|
||||||
.getLoadable<FieldRelationValue<FieldRelationFromManyValue>>(
|
.getLoadable<FieldRelationValue<FieldRelationFromManyValue>>(
|
||||||
@ -88,11 +93,19 @@ export const useOpenRelationFromManyFieldInput = () => {
|
|||||||
forcePickableMorphItems: pickableMorphItems,
|
forcePickableMorphItems: pickableMorphItems,
|
||||||
});
|
});
|
||||||
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope({
|
pushFocusItemToFocusStack({
|
||||||
scope: MultipleRecordPickerHotkeyScope.MultipleRecordPicker,
|
focusId: recordPickerInstanceId,
|
||||||
|
component: {
|
||||||
|
type: FocusComponentType.DROPDOWN,
|
||||||
|
instanceId: recordPickerInstanceId,
|
||||||
|
},
|
||||||
|
hotkeyScope: {
|
||||||
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
|
},
|
||||||
|
memoizeKey: recordPickerInstanceId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[performSearch, setHotkeyScopeAndMemorizePreviousScope],
|
[performSearch, pushFocusItemToFocusStack],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { openRelationFromManyFieldInput };
|
return { openRelationFromManyFieldInput };
|
||||||
|
|||||||
@ -1,21 +1,26 @@
|
|||||||
|
import { getRelationToOneFieldInputInstanceId } from '@/object-record/record-field/meta-types/input/utils/getRelationToOneFieldInputInstanceId';
|
||||||
import {
|
import {
|
||||||
FieldRelationToOneValue,
|
FieldRelationToOneValue,
|
||||||
FieldRelationValue,
|
FieldRelationValue,
|
||||||
} from '@/object-record/record-field/types/FieldMetadata';
|
} from '@/object-record/record-field/types/FieldMetadata';
|
||||||
import { singleRecordPickerSelectedIdComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSelectedIdComponentState';
|
import { singleRecordPickerSelectedIdComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSelectedIdComponentState';
|
||||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
|
||||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
|
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||||
|
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
export const useOpenRelationToOneFieldInput = () => {
|
export const useOpenRelationToOneFieldInput = () => {
|
||||||
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
|
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||||
|
|
||||||
const openRelationToOneFieldInput = useRecoilCallback(
|
const openRelationToOneFieldInput = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
({ fieldName, recordId }: { fieldName: string; recordId: string }) => {
|
({ fieldName, recordId }: { fieldName: string; recordId: string }) => {
|
||||||
const recordPickerInstanceId = `relation-to-one-field-input-${recordId}-${fieldName}`;
|
const recordPickerInstanceId = getRelationToOneFieldInputInstanceId({
|
||||||
|
recordId,
|
||||||
|
fieldName,
|
||||||
|
});
|
||||||
const fieldValue = snapshot
|
const fieldValue = snapshot
|
||||||
.getLoadable<FieldRelationValue<FieldRelationToOneValue>>(
|
.getLoadable<FieldRelationValue<FieldRelationToOneValue>>(
|
||||||
recordStoreFamilySelector({
|
recordStoreFamilySelector({
|
||||||
@ -34,11 +39,18 @@ export const useOpenRelationToOneFieldInput = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope({
|
pushFocusItemToFocusStack({
|
||||||
scope: SingleRecordPickerHotkeyScope.SingleRecordPicker,
|
focusId: recordPickerInstanceId,
|
||||||
|
component: {
|
||||||
|
type: FocusComponentType.OPEN_FIELD_INPUT,
|
||||||
|
instanceId: recordPickerInstanceId,
|
||||||
|
},
|
||||||
|
// TODO: Remove this once we've fully migrated away from hotkey scopes
|
||||||
|
hotkeyScope: { scope: DropdownHotkeyScope.Dropdown },
|
||||||
|
memoizeKey: recordPickerInstanceId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setHotkeyScopeAndMemorizePreviousScope],
|
[pushFocusItemToFocusStack],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { openRelationToOneFieldInput };
|
return { openRelationToOneFieldInput };
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
export enum RelationPickerHotkeyScope {
|
|
||||||
AddNew = 'add-new',
|
|
||||||
}
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
export const getRelationFromManyFieldInputInstanceId = ({
|
||||||
|
recordId,
|
||||||
|
fieldName,
|
||||||
|
}: {
|
||||||
|
recordId: string;
|
||||||
|
fieldName: string;
|
||||||
|
}): string => {
|
||||||
|
return `relation-from-many-field-input-${recordId}-${fieldName}`;
|
||||||
|
};
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
export const getRelationToOneFieldInputInstanceId = ({
|
||||||
|
recordId,
|
||||||
|
fieldName,
|
||||||
|
}: {
|
||||||
|
recordId: string;
|
||||||
|
fieldName: string;
|
||||||
|
}): string => {
|
||||||
|
return `relation-to-one-field-input-${recordId}-${fieldName}`;
|
||||||
|
};
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
export const getFieldInputInstanceId = (
|
||||||
|
recordId: string,
|
||||||
|
fieldName: string,
|
||||||
|
) => {
|
||||||
|
return `${recordId}-${fieldName}`;
|
||||||
|
};
|
||||||
@ -4,7 +4,6 @@ import { MultipleRecordPickerSearchInput } from '@/object-record/record-picker/m
|
|||||||
import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/multiple-record-picker/states/contexts/MultipleRecordPickerComponentInstanceContext';
|
import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/multiple-record-picker/states/contexts/MultipleRecordPickerComponentInstanceContext';
|
||||||
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
||||||
import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState';
|
import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState';
|
||||||
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
|
|
||||||
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 { RecordPickerLayoutDirection } from '@/object-record/record-picker/types/RecordPickerLayoutDirection';
|
import { RecordPickerLayoutDirection } from '@/object-record/record-picker/types/RecordPickerLayoutDirection';
|
||||||
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||||
@ -12,10 +11,11 @@ import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObj
|
|||||||
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
|
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|
||||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
@ -36,6 +36,7 @@ type MultipleRecordPickerProps = {
|
|||||||
layoutDirection?: RecordPickerLayoutDirection;
|
layoutDirection?: RecordPickerLayoutDirection;
|
||||||
componentInstanceId: string;
|
componentInstanceId: string;
|
||||||
onClickOutside: () => void;
|
onClickOutside: () => void;
|
||||||
|
focusId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MultipleRecordPicker = ({
|
export const MultipleRecordPicker = ({
|
||||||
@ -45,6 +46,7 @@ export const MultipleRecordPicker = ({
|
|||||||
onClickOutside,
|
onClickOutside,
|
||||||
layoutDirection = 'search-bar-on-bottom',
|
layoutDirection = 'search-bar-on-bottom',
|
||||||
componentInstanceId,
|
componentInstanceId,
|
||||||
|
focusId,
|
||||||
}: MultipleRecordPickerProps) => {
|
}: MultipleRecordPickerProps) => {
|
||||||
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
|
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
|
||||||
|
|
||||||
@ -94,14 +96,15 @@ export const MultipleRecordPicker = ({
|
|||||||
resetState();
|
resetState();
|
||||||
};
|
};
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
Key.Escape,
|
keys: [Key.Escape],
|
||||||
() => {
|
callback: () => {
|
||||||
handleSubmit();
|
handleSubmit();
|
||||||
},
|
},
|
||||||
MultipleRecordPickerHotkeyScope.MultipleRecordPicker,
|
focusId,
|
||||||
[handleSubmit],
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
);
|
dependencies: [handleSubmit],
|
||||||
|
});
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -140,13 +143,19 @@ export const MultipleRecordPicker = ({
|
|||||||
{layoutDirection === 'search-bar-on-bottom' && (
|
{layoutDirection === 'search-bar-on-bottom' && (
|
||||||
<>
|
<>
|
||||||
{createNewButtonSection}
|
{createNewButtonSection}
|
||||||
<MultipleRecordPickerItemsDisplay onChange={onChange} />
|
<MultipleRecordPickerItemsDisplay
|
||||||
|
onChange={onChange}
|
||||||
|
focusId={focusId}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<MultipleRecordPickerSearchInput />
|
<MultipleRecordPickerSearchInput />
|
||||||
{layoutDirection === 'search-bar-on-top' && (
|
{layoutDirection === 'search-bar-on-top' && (
|
||||||
<>
|
<>
|
||||||
<MultipleRecordPickerItemsDisplay onChange={onChange} />
|
<MultipleRecordPickerItemsDisplay
|
||||||
|
onChange={onChange}
|
||||||
|
focusId={focusId}
|
||||||
|
/>
|
||||||
{createNewButtonSection}
|
{createNewButtonSection}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -10,8 +10,10 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
|
|||||||
|
|
||||||
export const MultipleRecordPickerItemsDisplay = ({
|
export const MultipleRecordPickerItemsDisplay = ({
|
||||||
onChange,
|
onChange,
|
||||||
|
focusId,
|
||||||
}: {
|
}: {
|
||||||
onChange?: (morphItem: RecordPickerPickableMorphItem) => void;
|
onChange?: (morphItem: RecordPickerPickableMorphItem) => void;
|
||||||
|
focusId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||||
MultipleRecordPickerComponentInstanceContext,
|
MultipleRecordPickerComponentInstanceContext,
|
||||||
@ -33,7 +35,7 @@ export const MultipleRecordPickerItemsDisplay = ({
|
|||||||
{isLoading && itemsLength === 0 ? (
|
{isLoading && itemsLength === 0 ? (
|
||||||
<DropdownMenuSkeletonItem />
|
<DropdownMenuSkeletonItem />
|
||||||
) : (
|
) : (
|
||||||
<MultipleRecordPickerMenuItems onChange={onChange} />
|
<MultipleRecordPickerMenuItems onChange={onChange} focusId={focusId} />
|
||||||
)}
|
)}
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -6,10 +6,10 @@ import { MultipleRecordPickerMenuItem } from '@/object-record/record-picker/mult
|
|||||||
import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/multiple-record-picker/states/contexts/MultipleRecordPickerComponentInstanceContext';
|
import { MultipleRecordPickerComponentInstanceContext } from '@/object-record/record-picker/multiple-record-picker/states/contexts/MultipleRecordPickerComponentInstanceContext';
|
||||||
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
||||||
import { multipleRecordPickerPickableRecordIdsMatchingSearchComponentSelector } from '@/object-record/record-picker/multiple-record-picker/states/selectors/multipleRecordPickerPickableRecordIdsMatchingSearchComponentSelector';
|
import { multipleRecordPickerPickableRecordIdsMatchingSearchComponentSelector } from '@/object-record/record-picker/multiple-record-picker/states/selectors/multipleRecordPickerPickableRecordIdsMatchingSearchComponentSelector';
|
||||||
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
|
|
||||||
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 { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
@ -25,10 +25,12 @@ export const StyledSelectableItem = styled(SelectableListItem)`
|
|||||||
|
|
||||||
type MultipleRecordPickerMenuItemsProps = {
|
type MultipleRecordPickerMenuItemsProps = {
|
||||||
onChange?: (morphItem: RecordPickerPickableMorphItem) => void;
|
onChange?: (morphItem: RecordPickerPickableMorphItem) => void;
|
||||||
|
focusId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MultipleRecordPickerMenuItems = ({
|
export const MultipleRecordPickerMenuItems = ({
|
||||||
onChange,
|
onChange,
|
||||||
|
focusId,
|
||||||
}: MultipleRecordPickerMenuItemsProps) => {
|
}: MultipleRecordPickerMenuItemsProps) => {
|
||||||
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||||
MultipleRecordPickerComponentInstanceContext,
|
MultipleRecordPickerComponentInstanceContext,
|
||||||
@ -85,7 +87,8 @@ export const MultipleRecordPickerMenuItems = ({
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={selectableListComponentInstanceId}
|
selectableListInstanceId={selectableListComponentInstanceId}
|
||||||
selectableItemIdArray={pickableRecordIds}
|
selectableItemIdArray={pickableRecordIds}
|
||||||
hotkeyScope={MultipleRecordPickerHotkeyScope.MultipleRecordPicker}
|
focusId={focusId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{pickableRecordIds.map((recordId) => {
|
{pickableRecordIds.map((recordId) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
export enum MultipleRecordPickerHotkeyScope {
|
|
||||||
MultipleRecordPicker = 'multiple-record-picker',
|
|
||||||
}
|
|
||||||
@ -28,6 +28,7 @@ export const SingleRecordPicker = ({
|
|||||||
componentInstanceId,
|
componentInstanceId,
|
||||||
layoutDirection,
|
layoutDirection,
|
||||||
dropdownWidth,
|
dropdownWidth,
|
||||||
|
focusId,
|
||||||
}: SingleRecordPickerProps) => {
|
}: SingleRecordPickerProps) => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -71,6 +72,7 @@ export const SingleRecordPicker = ({
|
|||||||
>
|
>
|
||||||
<DropdownContent ref={containerRef} widthInPixels={dropdownWidth}>
|
<DropdownContent ref={containerRef} widthInPixels={dropdownWidth}>
|
||||||
<SingleRecordPickerMenuItemsWithSearch
|
<SingleRecordPickerMenuItemsWithSearch
|
||||||
|
focusId={focusId}
|
||||||
{...{
|
{...{
|
||||||
EmptyIcon,
|
EmptyIcon,
|
||||||
emptyLabel,
|
emptyLabel,
|
||||||
|
|||||||
@ -4,16 +4,16 @@ 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';
|
||||||
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 { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|
||||||
|
|
||||||
import { SingleRecordPickerMenuItem } from '@/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem';
|
import { SingleRecordPickerMenuItem } from '@/object-record/record-picker/single-record-picker/components/SingleRecordPickerMenuItem';
|
||||||
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 { singleRecordPickerSelectedIdComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSelectedIdComponentState';
|
import { singleRecordPickerSelectedIdComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSelectedIdComponentState';
|
||||||
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 { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
|
||||||
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
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 { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||||
@ -29,7 +29,7 @@ export type SingleRecordPickerMenuItemsProps = {
|
|||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
onRecordSelected: (entity?: SingleRecordPickerRecord) => void;
|
onRecordSelected: (entity?: SingleRecordPickerRecord) => void;
|
||||||
selectedRecord?: SingleRecordPickerRecord;
|
selectedRecord?: SingleRecordPickerRecord;
|
||||||
hotkeyScope?: string;
|
focusId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SingleRecordPickerMenuItems = ({
|
export const SingleRecordPickerMenuItems = ({
|
||||||
@ -40,7 +40,7 @@ export const SingleRecordPickerMenuItems = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
onRecordSelected,
|
onRecordSelected,
|
||||||
selectedRecord,
|
selectedRecord,
|
||||||
hotkeyScope = SingleRecordPickerHotkeyScope.SingleRecordPicker,
|
focusId,
|
||||||
}: SingleRecordPickerMenuItemsProps) => {
|
}: SingleRecordPickerMenuItemsProps) => {
|
||||||
const selectNone = emptyLabel
|
const selectNone = emptyLabel
|
||||||
? {
|
? {
|
||||||
@ -77,15 +77,16 @@ export const SingleRecordPickerMenuItems = ({
|
|||||||
'select-none',
|
'select-none',
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
[Key.Escape],
|
keys: Key.Escape,
|
||||||
() => {
|
callback: () => {
|
||||||
resetSelectedItem();
|
resetSelectedItem();
|
||||||
onCancel?.();
|
onCancel?.();
|
||||||
},
|
},
|
||||||
hotkeyScope,
|
focusId,
|
||||||
[onCancel, resetSelectedItem],
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
);
|
dependencies: [onCancel, resetSelectedItem],
|
||||||
|
});
|
||||||
|
|
||||||
const selectableItemIds = recordsInDropdown.map((entity) => entity.id);
|
const selectableItemIds = recordsInDropdown.map((entity) => entity.id);
|
||||||
const [selectedRecordId, setSelectedRecordId] = useRecoilComponentStateV2(
|
const [selectedRecordId, setSelectedRecordId] = useRecoilComponentStateV2(
|
||||||
@ -96,7 +97,8 @@ export const SingleRecordPickerMenuItems = ({
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={selectableListComponentInstanceId}
|
selectableListInstanceId={selectableListComponentInstanceId}
|
||||||
selectableItemIdArray={selectableItemIds}
|
selectableItemIdArray={selectableItemIds}
|
||||||
hotkeyScope={hotkeyScope}
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
|
focusId={focusId}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<DropdownMenuSkeletonItem />
|
<DropdownMenuSkeletonItem />
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export type SingleRecordPickerMenuItemsWithSearchProps = {
|
|||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
recordPickerInstanceId?: string;
|
recordPickerInstanceId?: string;
|
||||||
layoutDirection?: RecordPickerLayoutDirection;
|
layoutDirection?: RecordPickerLayoutDirection;
|
||||||
|
focusId: string;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
SingleRecordPickerMenuItemsProps,
|
SingleRecordPickerMenuItemsProps,
|
||||||
| 'EmptyIcon'
|
| 'EmptyIcon'
|
||||||
@ -44,6 +45,7 @@ export const SingleRecordPickerMenuItemsWithSearch = ({
|
|||||||
onRecordSelected,
|
onRecordSelected,
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
layoutDirection = 'search-bar-on-top',
|
layoutDirection = 'search-bar-on-top',
|
||||||
|
focusId,
|
||||||
}: SingleRecordPickerMenuItemsWithSearchProps) => {
|
}: SingleRecordPickerMenuItemsWithSearchProps) => {
|
||||||
const { handleSearchFilterChange } = useSingleRecordPickerSearch();
|
const { handleSearchFilterChange } = useSingleRecordPickerSearch();
|
||||||
|
|
||||||
@ -96,9 +98,11 @@ export const SingleRecordPickerMenuItemsWithSearch = ({
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{searchHasNoResults && <RecordPickerNoRecordFoundMenuItem />}
|
{searchHasNoResults && <RecordPickerNoRecordFoundMenuItem />}
|
||||||
<SingleRecordPickerMenuItems
|
<SingleRecordPickerMenuItems
|
||||||
|
focusId={focusId}
|
||||||
recordsToSelect={records.recordsToSelect}
|
recordsToSelect={records.recordsToSelect}
|
||||||
loading={records.loading}
|
loading={records.loading}
|
||||||
selectedRecord={records.selectedRecords?.[0]}
|
selectedRecord={records.selectedRecords?.[0]}
|
||||||
@ -123,6 +127,7 @@ export const SingleRecordPickerMenuItemsWithSearch = ({
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
<SingleRecordPickerMenuItems
|
<SingleRecordPickerMenuItems
|
||||||
|
focusId={focusId}
|
||||||
recordsToSelect={records.recordsToSelect}
|
recordsToSelect={records.recordsToSelect}
|
||||||
loading={records.loading}
|
loading={records.loading}
|
||||||
selectedRecord={records.selectedRecords?.[0]}
|
selectedRecord={records.selectedRecords?.[0]}
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
export enum SingleRecordPickerHotkeyScope {
|
|
||||||
SingleRecordPicker = 'single-record-picker',
|
|
||||||
}
|
|
||||||
@ -285,7 +285,6 @@ export const RecordDetailRelationRecordsListItem = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownScopeId }}
|
|
||||||
/>
|
/>
|
||||||
</DropdownScope>
|
</DropdownScope>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -216,10 +216,10 @@ export const RecordDetailRelationSectionDropdown = ({
|
|||||||
accent="tertiary"
|
accent="tertiary"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
isToOneObject ? (
|
isToOneObject ? (
|
||||||
<SingleRecordPicker
|
<SingleRecordPicker
|
||||||
|
focusId={dropdownId}
|
||||||
componentInstanceId={dropdownId}
|
componentInstanceId={dropdownId}
|
||||||
EmptyIcon={IconForbid}
|
EmptyIcon={IconForbid}
|
||||||
onRecordSelected={handleRelationPickerEntitySelected}
|
onRecordSelected={handleRelationPickerEntitySelected}
|
||||||
@ -235,6 +235,7 @@ export const RecordDetailRelationSectionDropdown = ({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<MultipleRecordPicker
|
<MultipleRecordPicker
|
||||||
|
focusId={dropdownId}
|
||||||
componentInstanceId={dropdownId}
|
componentInstanceId={dropdownId}
|
||||||
onCreate={() => {
|
onCreate={() => {
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useC
|
|||||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
import { useCloseCurrentTableCellInEditMode } from '@/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode';
|
import { useCloseCurrentTableCellInEditMode } from '@/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode';
|
||||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||||
|
import { useResetFocusStack } from '@/ui/utilities/focus/hooks/useResetFocusStack';
|
||||||
|
|
||||||
export const useCloseRecordTableCellInGroup = () => {
|
export const useCloseRecordTableCellInGroup = () => {
|
||||||
const { recordTableId } = useRecordTableContextOrThrow();
|
const { recordTableId } = useRecordTableContextOrThrow();
|
||||||
@ -22,11 +23,17 @@ export const useCloseRecordTableCellInGroup = () => {
|
|||||||
const closeCurrentTableCellInEditMode =
|
const closeCurrentTableCellInEditMode =
|
||||||
useCloseCurrentTableCellInEditMode(recordTableId);
|
useCloseCurrentTableCellInEditMode(recordTableId);
|
||||||
|
|
||||||
|
const { resetFocusStack } = useResetFocusStack();
|
||||||
|
|
||||||
const closeTableCellInGroup = useRecoilCallback(
|
const closeTableCellInGroup = useRecoilCallback(
|
||||||
() => () => {
|
() => () => {
|
||||||
toggleClickOutside(true);
|
toggleClickOutside(true);
|
||||||
setDragSelectionStartEnabled(true);
|
setDragSelectionStartEnabled(true);
|
||||||
closeCurrentTableCellInEditMode();
|
closeCurrentTableCellInEditMode();
|
||||||
|
|
||||||
|
// TODO: Remove this once we've fully migrated away from hotkey scopes
|
||||||
|
resetFocusStack();
|
||||||
|
|
||||||
setHotkeyScope(TableHotkeyScope.TableFocus, {
|
setHotkeyScope(TableHotkeyScope.TableFocus, {
|
||||||
goto: true,
|
goto: true,
|
||||||
keyboardShortcutMenu: true,
|
keyboardShortcutMenu: true,
|
||||||
@ -35,6 +42,7 @@ export const useCloseRecordTableCellInGroup = () => {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
closeCurrentTableCellInEditMode,
|
closeCurrentTableCellInEditMode,
|
||||||
|
resetFocusStack,
|
||||||
setDragSelectionStartEnabled,
|
setDragSelectionStartEnabled,
|
||||||
setHotkeyScope,
|
setHotkeyScope,
|
||||||
toggleClickOutside,
|
toggleClickOutside,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useC
|
|||||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
import { useCloseCurrentTableCellInEditMode } from '@/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode';
|
import { useCloseCurrentTableCellInEditMode } from '@/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode';
|
||||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||||
|
import { useResetFocusStack } from '@/ui/utilities/focus/hooks/useResetFocusStack';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
export const useCloseRecordTableCellNoGroup = () => {
|
export const useCloseRecordTableCellNoGroup = () => {
|
||||||
@ -22,10 +23,16 @@ export const useCloseRecordTableCellNoGroup = () => {
|
|||||||
const closeCurrentTableCellInEditMode =
|
const closeCurrentTableCellInEditMode =
|
||||||
useCloseCurrentTableCellInEditMode(recordTableId);
|
useCloseCurrentTableCellInEditMode(recordTableId);
|
||||||
|
|
||||||
|
const { resetFocusStack } = useResetFocusStack();
|
||||||
|
|
||||||
const closeTableCellNoGroup = useCallback(() => {
|
const closeTableCellNoGroup = useCallback(() => {
|
||||||
toggleClickOutside(true);
|
toggleClickOutside(true);
|
||||||
setDragSelectionStartEnabled(true);
|
setDragSelectionStartEnabled(true);
|
||||||
closeCurrentTableCellInEditMode();
|
closeCurrentTableCellInEditMode();
|
||||||
|
|
||||||
|
// TODO: Remove this once we've fully migrated away from hotkey scopes
|
||||||
|
resetFocusStack();
|
||||||
|
|
||||||
setHotkeyScope(TableHotkeyScope.TableFocus, {
|
setHotkeyScope(TableHotkeyScope.TableFocus, {
|
||||||
goto: true,
|
goto: true,
|
||||||
keyboardShortcutMenu: true,
|
keyboardShortcutMenu: true,
|
||||||
@ -33,6 +40,7 @@ export const useCloseRecordTableCellNoGroup = () => {
|
|||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
closeCurrentTableCellInEditMode,
|
closeCurrentTableCellInEditMode,
|
||||||
|
resetFocusStack,
|
||||||
setDragSelectionStartEnabled,
|
setDragSelectionStartEnabled,
|
||||||
setHotkeyScope,
|
setHotkeyScope,
|
||||||
toggleClickOutside,
|
toggleClickOutside,
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useRecoilCallback } from 'recoil';
|
|||||||
|
|
||||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||||
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
||||||
import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope';
|
|
||||||
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
||||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||||
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
|
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
|
||||||
@ -58,9 +57,7 @@ export const useTriggerActionMenuDropdown = ({
|
|||||||
|
|
||||||
closeCommandMenu();
|
closeCommandMenu();
|
||||||
|
|
||||||
openDropdown({
|
openDropdown();
|
||||||
scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
recordIndexActionMenuDropdownPositionState,
|
recordIndexActionMenuDropdownPositionState,
|
||||||
|
|||||||
@ -76,7 +76,6 @@ export const RecordTableColumnFooterWithDropdown = ({
|
|||||||
}
|
}
|
||||||
dropdownOffset={{ x: -1 }}
|
dropdownOffset={{ x: -1 }}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,7 +35,6 @@ export const RecordTableColumnHeadWithDropdown = ({
|
|||||||
dropdownComponents={<RecordTableColumnHeadDropdownMenu column={column} />}
|
dropdownComponents={<RecordTableColumnHeadDropdownMenu column={column} />}
|
||||||
dropdownOffset={{ x: -1 }}
|
dropdownOffset={{ x: -1 }}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownHotkeyScope={{ scope: column.fieldMetadataId + '-header' }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -50,9 +50,6 @@ const StyledDropdownContainer = styled.div`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const HIDDEN_TABLE_COLUMN_DROPDOWN_HOTKEY_SCOPE_ID =
|
|
||||||
'hidden-table-columns-dropdown-hotkey-scope-id';
|
|
||||||
|
|
||||||
export const RecordTableHeaderLastColumn = () => {
|
export const RecordTableHeaderLastColumn = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -89,9 +86,6 @@ export const RecordTableHeaderLastColumn = () => {
|
|||||||
}
|
}
|
||||||
dropdownComponents={<RecordTableHeaderPlusButtonContent />}
|
dropdownComponents={<RecordTableHeaderPlusButtonContent />}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: HIDDEN_TABLE_COLUMN_DROPDOWN_HOTKEY_SCOPE_ID,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</StyledDropdownContainer>
|
</StyledDropdownContainer>
|
||||||
</StyledPlusIconHeaderCell>
|
</StyledPlusIconHeaderCell>
|
||||||
|
|||||||
@ -3,19 +3,20 @@ import { Key } from 'ts-key-enum';
|
|||||||
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
import { SelectableItem } from '@/object-record/select/types/SelectableItem';
|
||||||
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
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 { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
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 { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { Avatar } from 'twenty-ui/display';
|
import { Avatar } from 'twenty-ui/display';
|
||||||
import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
|
import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
|
||||||
|
|
||||||
export const MultipleSelectDropdown = ({
|
export const MultipleSelectDropdown = ({
|
||||||
selectableListId,
|
selectableListId,
|
||||||
hotkeyScope,
|
focusId,
|
||||||
itemsToSelect,
|
itemsToSelect,
|
||||||
loadingItems,
|
loadingItems,
|
||||||
filteredSelectedItems,
|
filteredSelectedItems,
|
||||||
@ -23,7 +24,7 @@ export const MultipleSelectDropdown = ({
|
|||||||
searchFilter,
|
searchFilter,
|
||||||
}: {
|
}: {
|
||||||
selectableListId: string;
|
selectableListId: string;
|
||||||
hotkeyScope: string;
|
focusId: string;
|
||||||
itemsToSelect: SelectableItem[];
|
itemsToSelect: SelectableItem[];
|
||||||
filteredSelectedItems: SelectableItem[];
|
filteredSelectedItems: SelectableItem[];
|
||||||
selectedItems: SelectableItem[];
|
selectedItems: SelectableItem[];
|
||||||
@ -61,15 +62,16 @@ export const MultipleSelectDropdown = ({
|
|||||||
...(itemsToSelect ?? []),
|
...(itemsToSelect ?? []),
|
||||||
];
|
];
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
[Key.Escape],
|
keys: [Key.Escape],
|
||||||
() => {
|
callback: () => {
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
resetSelectedItem();
|
resetSelectedItem();
|
||||||
},
|
},
|
||||||
hotkeyScope,
|
focusId,
|
||||||
[closeDropdown, resetSelectedItem],
|
scope: DropdownHotkeyScope.Dropdown,
|
||||||
);
|
dependencies: [closeDropdown, resetSelectedItem],
|
||||||
|
});
|
||||||
|
|
||||||
const showNoResult =
|
const showNoResult =
|
||||||
itemsToSelect?.length === 0 &&
|
itemsToSelect?.length === 0 &&
|
||||||
@ -83,7 +85,8 @@ export const MultipleSelectDropdown = ({
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={selectableListId}
|
selectableListInstanceId={selectableListId}
|
||||||
selectableItemIdArray={selectableItemIds}
|
selectableItemIdArray={selectableItemIds}
|
||||||
hotkeyScope={hotkeyScope}
|
focusId={focusId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{itemsInDropdown?.map((item) => {
|
{itemsInDropdown?.map((item) => {
|
||||||
|
|||||||
@ -51,7 +51,6 @@ export const SettingsAccountsRowDropdownMenu = ({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownPlacement="right-start"
|
dropdownPlacement="right-start"
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { Select } from '@/ui/input/components/Select';
|
|||||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||||
import { TextArea } from '@/ui/input/components/TextArea';
|
import { TextArea } from '@/ui/input/components/TextArea';
|
||||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||||
import { SelectHotkeyScope } from '@/ui/input/types/SelectHotkeyScope';
|
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
@ -96,7 +95,6 @@ export const ConfigVariableDatabaseInput = ({
|
|||||||
{options && Array.isArray(options) ? (
|
{options && Array.isArray(options) ? (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId="config-variable-array-dropdown"
|
dropdownId="config-variable-array-dropdown"
|
||||||
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
|
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownOffset={{
|
dropdownOffset={{
|
||||||
y: 8,
|
y: 8,
|
||||||
|
|||||||
@ -46,7 +46,6 @@ export const ConfigVariableFilterDropdown = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownId="env-var-options-dropdown"
|
dropdownId="env-var-options-dropdown"
|
||||||
dropdownHotkeyScope={{ scope: 'env-var-options' }}
|
|
||||||
dropdownOffset={{ x: 0, y: 10 }}
|
dropdownOffset={{ x: 0, y: 10 }}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<ConfigVariableOptionsDropdownContent
|
<ConfigVariableOptionsDropdownContent
|
||||||
|
|||||||
@ -130,7 +130,6 @@ export const SettingsDataModelNewFieldBreadcrumbDropDown = () => {
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -117,7 +117,6 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={SELECT_COLOR_DROPDOWN_ID}
|
dropdownId={SELECT_COLOR_DROPDOWN_ID}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
dropdownHotkeyScope={{ scope: SELECT_COLOR_DROPDOWN_ID }}
|
|
||||||
clickableComponent={<StyledColorSample colorName={option.color} />}
|
clickableComponent={<StyledColorSample colorName={option.color} />}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<DropdownContent>
|
<DropdownContent>
|
||||||
@ -160,7 +159,6 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={SELECT_ACTIONS_DROPDOWN_ID}
|
dropdownId={SELECT_ACTIONS_DROPDOWN_ID}
|
||||||
dropdownPlacement="right-start"
|
dropdownPlacement="right-start"
|
||||||
dropdownHotkeyScope={{ scope: SELECT_ACTIONS_DROPDOWN_ID }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledLightIconButton accent="tertiary" Icon={IconDotsVertical} />
|
<StyledLightIconButton accent="tertiary" Icon={IconDotsVertical} />
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,7 +82,6 @@ export const SettingsObjectFieldActiveActionDropdown = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -86,9 +86,6 @@ export const SettingsObjectFieldInactiveActionDropdown = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: dropdownId,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -63,7 +63,6 @@ export const SettingsObjectInactiveMenuDropDown = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -54,7 +54,6 @@ export const SettingsIntegrationDatabaseConnectionSummaryCard = ({
|
|||||||
/>
|
/>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||||
}
|
}
|
||||||
|
|||||||
@ -238,7 +238,6 @@ export const SettingsRoleAssignment = ({
|
|||||||
<StyledAssignToMemberContainer>
|
<StyledAssignToMemberContainer>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId="role-member-select"
|
dropdownId="role-member-select"
|
||||||
dropdownHotkeyScope={{ scope: 'roleAssignment' }}
|
|
||||||
dropdownOffset={{ x: 0, y: 4 }}
|
dropdownOffset={{ x: 0, y: 4 }}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -69,7 +69,6 @@ export const SettingsSecuritySSORowDropdownMenu = ({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownPlacement="right-start"
|
dropdownPlacement="right-start"
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,7 +58,6 @@ export const SettingsSecurityApprovedAccessDomainRowDropdownMenu = ({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownPlacement="right-start"
|
dropdownPlacement="right-start"
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||||
}
|
}
|
||||||
|
|||||||
@ -131,7 +131,6 @@ export const SettingsServerlessFunctionTabEnvironmentVariableTableRow = ({
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropDownId }}
|
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</StyledTableRow>
|
</StyledTableRow>
|
||||||
|
|||||||
@ -123,9 +123,6 @@ export const MatchColumnToFieldSelect = ({
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownHotkeyScope={{
|
|
||||||
scope: dropdownId,
|
|
||||||
}}
|
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledMenuItem
|
<StyledMenuItem
|
||||||
|
|||||||
@ -52,7 +52,6 @@ export const SubMatchingSelectRowRightDropdown = <T extends string>({
|
|||||||
return (
|
return (
|
||||||
<StyledDropdownContainer>
|
<StyledDropdownContainer>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
|
|||||||
@ -44,7 +44,6 @@ export const SupportDropdown = () => {
|
|||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,15 +2,17 @@ import { useRef, useState } from 'react';
|
|||||||
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';
|
||||||
|
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
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 { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
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 { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||||
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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
@ -21,7 +23,7 @@ import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmp
|
|||||||
type MultiSelectInputProps = {
|
type MultiSelectInputProps = {
|
||||||
selectableListComponentInstanceId: string;
|
selectableListComponentInstanceId: string;
|
||||||
values: FieldMultiSelectValue;
|
values: FieldMultiSelectValue;
|
||||||
hotkeyScope: string;
|
focusId: string;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
options: SelectOption[];
|
options: SelectOption[];
|
||||||
onOptionSelected: (value: FieldMultiSelectValue) => void;
|
onOptionSelected: (value: FieldMultiSelectValue) => void;
|
||||||
@ -32,7 +34,7 @@ export const MultiSelectInput = ({
|
|||||||
selectableListComponentInstanceId,
|
selectableListComponentInstanceId,
|
||||||
values,
|
values,
|
||||||
options,
|
options,
|
||||||
hotkeyScope,
|
focusId,
|
||||||
onCancel,
|
onCancel,
|
||||||
onOptionSelected,
|
onOptionSelected,
|
||||||
dropdownWidth,
|
dropdownWidth,
|
||||||
@ -69,15 +71,16 @@ export const MultiSelectInput = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useScopedHotkeys(
|
useHotkeysOnFocusedElement({
|
||||||
Key.Escape,
|
keys: Key.Escape,
|
||||||
() => {
|
callback: () => {
|
||||||
onCancel?.();
|
onCancel?.();
|
||||||
resetSelectedItem();
|
resetSelectedItem();
|
||||||
},
|
},
|
||||||
hotkeyScope,
|
focusId,
|
||||||
[onCancel, resetSelectedItem],
|
scope: DEFAULT_CELL_SCOPE.scope,
|
||||||
);
|
dependencies: [onCancel, resetSelectedItem],
|
||||||
|
});
|
||||||
|
|
||||||
useListenClickOutside({
|
useListenClickOutside({
|
||||||
refs: [containerRef],
|
refs: [containerRef],
|
||||||
@ -102,7 +105,8 @@ export const MultiSelectInput = ({
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={selectableListComponentInstanceId}
|
selectableListInstanceId={selectableListComponentInstanceId}
|
||||||
selectableItemIdArray={optionIds}
|
selectableItemIdArray={optionIds}
|
||||||
hotkeyScope={hotkeyScope}
|
focusId={focusId}
|
||||||
|
hotkeyScope={DEFAULT_CELL_SCOPE.scope}
|
||||||
>
|
>
|
||||||
<DropdownContent
|
<DropdownContent
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
@ -122,17 +126,25 @@ export const MultiSelectInput = ({
|
|||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{filteredOptionsInDropDown.map((option) => {
|
{filteredOptionsInDropDown.map((option) => {
|
||||||
return (
|
return (
|
||||||
<MenuItemMultiSelectTag
|
<SelectableListItem
|
||||||
key={option.value}
|
key={option.value}
|
||||||
selected={values?.includes(option.value) || false}
|
itemId={option.value}
|
||||||
text={option.label}
|
onEnter={() => {
|
||||||
color={option.color ?? 'transparent'}
|
onOptionSelected(formatNewSelectedOptions(option.value));
|
||||||
Icon={option.Icon ?? undefined}
|
}}
|
||||||
onClick={() =>
|
>
|
||||||
onOptionSelected(formatNewSelectedOptions(option.value))
|
<MenuItemMultiSelectTag
|
||||||
}
|
key={option.value}
|
||||||
isKeySelected={selectedItemId === option.value}
|
selected={values?.includes(option.value) || false}
|
||||||
/>
|
text={option.label}
|
||||||
|
color={option.color ?? 'transparent'}
|
||||||
|
Icon={option.Icon ?? undefined}
|
||||||
|
onClick={() =>
|
||||||
|
onOptionSelected(formatNewSelectedOptions(option.value))
|
||||||
|
}
|
||||||
|
isKeySelected={selectedItemId === option.value}
|
||||||
|
/>
|
||||||
|
</SelectableListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||||
import { SelectInput as SelectBaseInput } from '@/ui/input/components/SelectInput';
|
import { SelectInput as SelectBaseInput } from '@/ui/input/components/SelectInput';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
import { SelectOption } from 'twenty-ui/input';
|
import { SelectOption } from 'twenty-ui/input';
|
||||||
@ -5,7 +6,7 @@ import { SelectOption } from 'twenty-ui/input';
|
|||||||
type SelectInputProps = {
|
type SelectInputProps = {
|
||||||
selectableListComponentInstanceId: string;
|
selectableListComponentInstanceId: string;
|
||||||
selectableItemIdArray: string[];
|
selectableItemIdArray: string[];
|
||||||
hotkeyScope: string;
|
focusId: string;
|
||||||
onEnter: (itemId: string) => void;
|
onEnter: (itemId: string) => void;
|
||||||
onOptionSelected: (selectedOption: SelectOption) => void;
|
onOptionSelected: (selectedOption: SelectOption) => void;
|
||||||
options: SelectOption[];
|
options: SelectOption[];
|
||||||
@ -19,7 +20,7 @@ type SelectInputProps = {
|
|||||||
export const SelectInput = ({
|
export const SelectInput = ({
|
||||||
selectableListComponentInstanceId,
|
selectableListComponentInstanceId,
|
||||||
selectableItemIdArray,
|
selectableItemIdArray,
|
||||||
hotkeyScope,
|
focusId,
|
||||||
onOptionSelected,
|
onOptionSelected,
|
||||||
options,
|
options,
|
||||||
onCancel,
|
onCancel,
|
||||||
@ -32,7 +33,8 @@ export const SelectInput = ({
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId={selectableListComponentInstanceId}
|
selectableListInstanceId={selectableListComponentInstanceId}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
hotkeyScope={hotkeyScope}
|
focusId={focusId}
|
||||||
|
hotkeyScope={DEFAULT_CELL_SCOPE.scope}
|
||||||
>
|
>
|
||||||
<SelectBaseInput
|
<SelectBaseInput
|
||||||
onOptionSelected={onOptionSelected}
|
onOptionSelected={onOptionSelected}
|
||||||
@ -42,7 +44,7 @@ export const SelectInput = ({
|
|||||||
onFilterChange={onFilterChange}
|
onFilterChange={onFilterChange}
|
||||||
onClear={onClear}
|
onClear={onClear}
|
||||||
clearLabel={clearLabel}
|
clearLabel={clearLabel}
|
||||||
hotkeyScope={hotkeyScope}
|
focusId={focusId}
|
||||||
/>
|
/>
|
||||||
</SelectableList>
|
</SelectableList>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { arrayToChunks } from '~/utils/array/arrayToChunks';
|
|||||||
|
|
||||||
import { ICON_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/ui/input/components/constants/IconPickerDropdownContentWidth';
|
import { ICON_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/ui/input/components/constants/IconPickerDropdownContentWidth';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { useSelectableListListenToEnterHotkeyOnItem } from '@/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem';
|
import { useSelectableListListenToEnterHotkeyOnItem } from '@/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem';
|
||||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
@ -71,9 +72,10 @@ const IconPickerIcon = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useSelectableListListenToEnterHotkeyOnItem({
|
useSelectableListListenToEnterHotkeyOnItem({
|
||||||
hotkeyScope: IconPickerHotkeyScope.IconPicker,
|
focusId: iconKey,
|
||||||
itemId: iconKey,
|
itemId: iconKey,
|
||||||
onEnter: onClick,
|
onEnter: onClick,
|
||||||
|
hotkeyScope: DropdownHotkeyScope.Dropdown,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -184,7 +186,6 @@ export const IconPicker = ({
|
|||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
dropdownHotkeyScope={{ scope: IconPickerHotkeyScope.IconPicker }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<IconButton
|
<IconButton
|
||||||
ariaLabel={`Click to select icon ${
|
ariaLabel={`Click to select icon ${
|
||||||
@ -203,7 +204,8 @@ export const IconPicker = ({
|
|||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListInstanceId="icon-list"
|
selectableListInstanceId="icon-list"
|
||||||
selectableItemIdMatrix={iconKeys2d}
|
selectableItemIdMatrix={iconKeys2d}
|
||||||
hotkeyScope={IconPickerHotkeyScope.IconPicker}
|
focusId={dropdownId}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
<DropdownMenuSearchInput
|
<DropdownMenuSearchInput
|
||||||
placeholder={t`Search icon`}
|
placeholder={t`Search icon`}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|||||||
|
|
||||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||||
|
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||||
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
|
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
|
||||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||||
@ -19,7 +20,6 @@ import { isDefined } from 'twenty-shared/utils';
|
|||||||
import { IconComponent } from 'twenty-ui/display';
|
import { IconComponent } from 'twenty-ui/display';
|
||||||
import { SelectOption } from 'twenty-ui/input';
|
import { SelectOption } from 'twenty-ui/input';
|
||||||
import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation';
|
import { MenuItem, MenuItemSelect } from 'twenty-ui/navigation';
|
||||||
import { SelectHotkeyScope } from '../types/SelectHotkeyScope';
|
|
||||||
|
|
||||||
export type SelectSizeVariant = 'small' | 'default';
|
export type SelectSizeVariant = 'small' | 'default';
|
||||||
|
|
||||||
@ -166,9 +166,10 @@ export const Select = <Value extends SelectValue>({
|
|||||||
{!!filteredOptions.length && (
|
{!!filteredOptions.length && (
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
<SelectableList
|
<SelectableList
|
||||||
hotkeyScope={SelectHotkeyScope.Select}
|
|
||||||
selectableListInstanceId={dropdownId}
|
selectableListInstanceId={dropdownId}
|
||||||
|
focusId={dropdownId}
|
||||||
selectableItemIdArray={selectableItemIdArray}
|
selectableItemIdArray={selectableItemIdArray}
|
||||||
|
hotkeyScope={DropdownHotkeyScope.Dropdown}
|
||||||
>
|
>
|
||||||
{filteredOptions.map((option) => (
|
{filteredOptions.map((option) => (
|
||||||
<SelectableListItem
|
<SelectableListItem
|
||||||
@ -211,7 +212,6 @@ export const Select = <Value extends SelectValue>({
|
|||||||
)}
|
)}
|
||||||
</DropdownContent>
|
</DropdownContent>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: SelectHotkeyScope.Select }}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
|||||||
@ -2,10 +2,13 @@ import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent
|
|||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
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 { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||||
|
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||||
|
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
|
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Key } from 'ts-key-enum';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { TagColor } from 'twenty-ui/components';
|
import { TagColor } from 'twenty-ui/components';
|
||||||
import { SelectOption } from 'twenty-ui/input';
|
import { SelectOption } from 'twenty-ui/input';
|
||||||
@ -19,7 +22,7 @@ interface SelectInputProps {
|
|||||||
onFilterChange?: (filteredOptions: SelectOption[]) => void;
|
onFilterChange?: (filteredOptions: SelectOption[]) => void;
|
||||||
onClear?: () => void;
|
onClear?: () => void;
|
||||||
clearLabel?: string;
|
clearLabel?: string;
|
||||||
hotkeyScope: string;
|
focusId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectInput = ({
|
export const SelectInput = ({
|
||||||
@ -30,10 +33,19 @@ export const SelectInput = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
defaultOption,
|
defaultOption,
|
||||||
onFilterChange,
|
onFilterChange,
|
||||||
hotkeyScope,
|
|
||||||
}: SelectInputProps) => {
|
}: SelectInputProps) => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Get the SelectableList instance id from context
|
||||||
|
const selectableListInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||||
|
SelectableListComponentInstanceContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedItemId = useRecoilComponentValueV2(
|
||||||
|
selectedItemIdComponentState,
|
||||||
|
selectableListInstanceId,
|
||||||
|
);
|
||||||
|
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
const [searchFilter, setSearchFilter] = useState('');
|
||||||
const [selectedOption, setSelectedOption] = useState<
|
const [selectedOption, setSelectedOption] = useState<
|
||||||
SelectOption | undefined
|
SelectOption | undefined
|
||||||
@ -61,6 +73,11 @@ export const SelectInput = ({
|
|||||||
onOptionSelected(option);
|
onOptionSelected(option);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClearOption = () => {
|
||||||
|
setSelectedOption(undefined);
|
||||||
|
onClear?.();
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onFilterChange?.(optionsInDropDown);
|
onFilterChange?.(optionsInDropDown);
|
||||||
}, [onFilterChange, optionsInDropDown]);
|
}, [onFilterChange, optionsInDropDown]);
|
||||||
@ -81,20 +98,6 @@ export const SelectInput = ({
|
|||||||
listenerId: 'select-input',
|
listenerId: 'select-input',
|
||||||
});
|
});
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
Key.Enter,
|
|
||||||
() => {
|
|
||||||
const selectedOption = optionsInDropDown.find((option) =>
|
|
||||||
option.label.toLowerCase().includes(searchFilter.toLowerCase()),
|
|
||||||
);
|
|
||||||
if (isDefined(selectedOption)) {
|
|
||||||
handleOptionChange(selectedOption);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hotkeyScope,
|
|
||||||
[searchFilter, optionsInDropDown],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownContent ref={containerRef} selectDisabled>
|
<DropdownContent ref={containerRef} selectDisabled>
|
||||||
<DropdownMenuSearchInput
|
<DropdownMenuSearchInput
|
||||||
@ -105,27 +108,37 @@ export const SelectInput = ({
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{onClear && clearLabel && (
|
{onClear && clearLabel && (
|
||||||
<MenuItemSelectTag
|
<SelectableListItem
|
||||||
key={`No ${clearLabel}`}
|
itemId={`No ${clearLabel}`}
|
||||||
text={`No ${clearLabel}`}
|
onEnter={handleClearOption}
|
||||||
color="transparent"
|
>
|
||||||
variant={'outline'}
|
<MenuItemSelectTag
|
||||||
onClick={() => {
|
key={`No ${clearLabel}`}
|
||||||
setSelectedOption(undefined);
|
text={`No ${clearLabel}`}
|
||||||
onClear();
|
color="transparent"
|
||||||
}}
|
variant={'outline'}
|
||||||
/>
|
onClick={handleClearOption}
|
||||||
|
isKeySelected={selectedItemId === `No ${clearLabel}`}
|
||||||
|
/>
|
||||||
|
</SelectableListItem>
|
||||||
)}
|
)}
|
||||||
{optionsInDropDown.map((option) => {
|
{optionsInDropDown.map((option) => {
|
||||||
return (
|
return (
|
||||||
<MenuItemSelectTag
|
<SelectableListItem
|
||||||
key={option.value}
|
key={option.value}
|
||||||
focused={selectedOption?.value === option.value}
|
itemId={option.value}
|
||||||
text={option.label}
|
onEnter={() => handleOptionChange(option)}
|
||||||
color={(option.color as TagColor) ?? 'transparent'}
|
>
|
||||||
onClick={() => handleOptionChange(option)}
|
<MenuItemSelectTag
|
||||||
LeftIcon={option.Icon}
|
key={option.value}
|
||||||
/>
|
selected={selectedOption?.value === option.value}
|
||||||
|
text={option.label}
|
||||||
|
color={(option.color as TagColor) ?? 'transparent'}
|
||||||
|
onClick={() => handleOptionChange(option)}
|
||||||
|
LeftIcon={option.Icon}
|
||||||
|
isKeySelected={selectedItemId === option.value}
|
||||||
|
/>
|
||||||
|
</SelectableListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
|
|||||||
@ -70,7 +70,6 @@ export const CurrencyPickerDropdownButton = ({
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId="currency-picker-dropdown-id"
|
dropdownId="currency-picker-dropdown-id"
|
||||||
dropdownHotkeyScope={{ scope: CurrencyPickerHotkeyScope.CurrencyPicker }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledDropdownButtonContainer>
|
<StyledDropdownButtonContainer>
|
||||||
<StyledIconContainer>
|
<StyledIconContainer>
|
||||||
|
|||||||
@ -6,8 +6,6 @@ import { useTheme } from '@emotion/react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { CountryPickerHotkeyScope } from '../types/CountryPickerHotkeyScope';
|
|
||||||
|
|
||||||
import { PhoneCountryPickerDropdownSelect } from './PhoneCountryPickerDropdownSelect';
|
import { PhoneCountryPickerDropdownSelect } from './PhoneCountryPickerDropdownSelect';
|
||||||
|
|
||||||
import 'react-phone-number-input/style.css';
|
import 'react-phone-number-input/style.css';
|
||||||
@ -98,7 +96,6 @@ export const PhoneCountryPickerDropdownButton = ({
|
|||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId="country-picker-dropdown-id"
|
dropdownId="country-picker-dropdown-id"
|
||||||
dropdownHotkeyScope={{ scope: CountryPickerHotkeyScope.CountryPicker }}
|
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
|
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
|
||||||
<StyledIconContainer>
|
<StyledIconContainer>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user