fix glitch for relation picker search (#8040)

Fix for #7957

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Sanskar Jain
2024-10-25 20:21:52 +05:30
committed by GitHub
parent f633f0d330
commit 9c923ba8d5
13 changed files with 162 additions and 92 deletions

View File

@@ -1,5 +1,5 @@
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
export type Maybe<T> = T | null; export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>; export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] }; export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
@@ -1160,6 +1160,7 @@ export type UpdateObjectPayload = {
labelSingular?: InputMaybe<Scalars['String']>; labelSingular?: InputMaybe<Scalars['String']>;
namePlural?: InputMaybe<Scalars['String']>; namePlural?: InputMaybe<Scalars['String']>;
nameSingular?: InputMaybe<Scalars['String']>; nameSingular?: InputMaybe<Scalars['String']>;
shortcut?: InputMaybe<Scalars['String']>;
shouldSyncLabelAndName?: InputMaybe<Scalars['Boolean']>; shouldSyncLabelAndName?: InputMaybe<Scalars['Boolean']>;
}; };
@@ -1477,6 +1478,7 @@ export type Object = {
labelSingular: Scalars['String']; labelSingular: Scalars['String'];
namePlural: Scalars['String']; namePlural: Scalars['String'];
nameSingular: Scalars['String']; nameSingular: Scalars['String'];
shortcut?: Maybe<Scalars['String']>;
shouldSyncLabelAndName: Scalars['Boolean']; shouldSyncLabelAndName: Scalars['Boolean'];
updatedAt: Scalars['DateTime']; updatedAt: Scalars['DateTime'];
}; };

View File

@@ -31,7 +31,7 @@ const StyledContainer = styled.div`
const StyledColumnContainer = styled.div` const StyledColumnContainer = styled.div`
display: flex; display: flex;
& > *:not(:first-child) { & > *:not(:first-of-type) {
border-left: 1px solid ${({ theme }) => theme.border.color.light}; border-left: 1px solid ${({ theme }) => theme.border.color.light};
} }
`; `;

View File

@@ -18,7 +18,7 @@ const StyledHeaderContainer = styled.div`
top: 0; top: 0;
} }
& > *:not(:first-child) { & > *:not(:first-of-type) {
border-left: 1px solid ${({ theme }) => theme.border.color.light}; border-left: 1px solid ${({ theme }) => theme.border.color.light};
} }
`; `;

View File

@@ -10,6 +10,7 @@ import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldM
import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect'; import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect';
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer';
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
type RelationFromManyFieldInputProps = { type RelationFromManyFieldInputProps = {
onSubmit?: FieldInputEvent; onSubmit?: FieldInputEvent;
@@ -50,6 +51,8 @@ export const RelationFromManyFieldInput = ({
recordId, recordId,
}); });
const { dropdownPlacement } = useDropdown(relationPickerScopeId);
return ( return (
<> <>
<RelationPickerScope relationPickerScopeId={relationPickerScopeId}> <RelationPickerScope relationPickerScopeId={relationPickerScopeId}>
@@ -58,6 +61,7 @@ export const RelationFromManyFieldInput = ({
onSubmit={handleSubmit} onSubmit={handleSubmit}
onChange={updateRelation} onChange={updateRelation}
onCreate={createNewRecordAndOpenRightDrawer} onCreate={createNewRecordAndOpenRightDrawer}
dropdownPlacement={dropdownPlacement}
/> />
</RelationPickerScope> </RelationPickerScope>
</> </>

View File

@@ -82,7 +82,8 @@ export const RecordDetailRelationSection = ({
const dropdownId = `record-field-card-relation-picker-${fieldDefinition.label}-${recordId}`; const dropdownId = `record-field-card-relation-picker-${fieldDefinition.label}-${recordId}`;
const { closeDropdown, isDropdownOpen } = useDropdown(dropdownId); const { closeDropdown, isDropdownOpen, dropdownPlacement } =
useDropdown(dropdownId);
const { setRelationPickerSearchFilter } = useRelationPicker({ const { setRelationPickerSearchFilter } = useRelationPicker({
relationPickerScopeId: dropdownId, relationPickerScopeId: dropdownId,
@@ -183,7 +184,7 @@ export const RecordDetailRelationSection = ({
<DropdownScope dropdownScopeId={dropdownId}> <DropdownScope dropdownScopeId={dropdownId}>
<StyledAddDropdown <StyledAddDropdown
dropdownId={dropdownId} dropdownId={dropdownId}
dropdownPlacement="right-start" dropdownPlacement="left-start"
onClose={handleCloseRelationPickerDropdown} onClose={handleCloseRelationPickerDropdown}
clickableComponent={ clickableComponent={
<LightIconButton <LightIconButton
@@ -204,6 +205,7 @@ export const RecordDetailRelationSection = ({
} }
relationPickerScopeId={dropdownId} relationPickerScopeId={dropdownId}
onCreate={createNewRecordAndOpenRightDrawer} onCreate={createNewRecordAndOpenRightDrawer}
dropdownPlacement={dropdownPlacement}
/> />
) : ( ) : (
<> <>
@@ -212,6 +214,7 @@ export const RecordDetailRelationSection = ({
onCreate={createNewRecordAndOpenRightDrawer} onCreate={createNewRecordAndOpenRightDrawer}
onChange={updateRelation} onChange={updateRelation}
onSubmit={closeDropdown} onSubmit={closeDropdown}
dropdownPlacement={dropdownPlacement}
/> />
</> </>
)} )}

View File

@@ -5,6 +5,7 @@ import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/r
import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates';
import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext';
import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton';
import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
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';
@@ -18,11 +19,12 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Placement } from '@floating-ui/react';
import { useCallback, useEffect, useRef } from 'react'; import { useCallback, useEffect, useRef } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { IconPlus, isDefined } from 'twenty-ui'; import { IconPlus, isDefined } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export const StyledSelectableItem = styled(SelectableItem)` export const StyledSelectableItem = styled(SelectableItem)`
height: 100%; height: 100%;
@@ -33,10 +35,12 @@ export const MultiRecordSelect = ({
onChange, onChange,
onSubmit, onSubmit,
onCreate, onCreate,
dropdownPlacement,
}: { }: {
onChange?: (changedRecordForSelectId: string) => void; onChange?: (changedRecordForSelectId: string) => void;
onSubmit?: () => void; onSubmit?: () => void;
onCreate?: ((searchInput?: string) => void) | (() => void); onCreate?: ((searchInput?: string) => void) | (() => void);
dropdownPlacement?: Placement | null;
}) => { }) => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const setHotkeyScope = useSetHotkeyScope(); const setHotkeyScope = useSetHotkeyScope();
@@ -55,6 +59,7 @@ export const MultiRecordSelect = ({
const recordMultiSelectIsLoading = useRecoilValue( const recordMultiSelectIsLoading = useRecoilValue(
recordMultiSelectIsLoadingState, recordMultiSelectIsLoadingState,
); );
const objectRecordsIdsMultiSelect = useRecoilValue( const objectRecordsIdsMultiSelect = useRecoilValue(
objectRecordsIdsMultiSelectState, objectRecordsIdsMultiSelectState,
); );
@@ -67,9 +72,6 @@ export const MultiRecordSelect = ({
const relationPickerSearchFilter = useRecoilValue( const relationPickerSearchFilter = useRecoilValue(
relationPickerSearchFilterState, relationPickerSearchFilterState,
); );
const debouncedSetSearchFilter = useDebouncedCallback(setSearchFilter, 100, {
leading: true,
});
useEffect(() => { useEffect(() => {
setHotkeyScope(relationPickerScopedId); setHotkeyScope(relationPickerScopedId);
@@ -86,16 +88,53 @@ export const MultiRecordSelect = ({
[onSubmit, goBackToPreviousHotkeyScope, resetSelectedItem], [onSubmit, goBackToPreviousHotkeyScope, resetSelectedItem],
); );
const debouncedOnCreate = useDebouncedCallback(
() => onCreate?.(relationPickerSearchFilter),
500,
);
const handleFilterChange = useCallback( const handleFilterChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => { (event: React.ChangeEvent<HTMLInputElement>) => {
debouncedSetSearchFilter(event.currentTarget.value); setSearchFilter(event.currentTarget.value);
}, },
[debouncedSetSearchFilter], [setSearchFilter],
);
const results = (
<DropdownMenuItemsContainer hasMaxHeight>
<SelectableList
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
selectableItemIdArray={objectRecordsIdsMultiSelect}
hotkeyScope={relationPickerScopedId}
onEnter={(selectedId) => {
onChange?.(selectedId);
resetSelectedItem();
}}
>
{objectRecordsIdsMultiSelect?.map((recordId) => {
return (
<MultipleObjectRecordSelectItem
key={recordId}
objectRecordId={recordId}
onChange={(recordId) => {
onChange?.(recordId);
resetSelectedItem();
}}
/>
);
})}
</SelectableList>
{objectRecordsIdsMultiSelect?.length === 0 &&
!recordMultiSelectIsLoading && <MenuItem text="No result" />}
</DropdownMenuItemsContainer>
);
const createNewButton = isDefined(onCreate) && (
<>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
<CreateNewButton
onClick={() => onCreate?.(relationPickerSearchFilter)}
LeftIcon={IconPlus}
text="Add New"
/>
</DropdownMenuItemsContainer>
</>
); );
return ( return (
@@ -107,55 +146,30 @@ export const MultiRecordSelect = ({
}} }}
/> />
<DropdownMenu ref={containerRef} data-select-disable> <DropdownMenu ref={containerRef} data-select-disable>
{dropdownPlacement?.includes('end') && (
<>
{createNewButton}
{results}
{recordMultiSelectIsLoading && !relationPickerSearchFilter && (
<DropdownMenuSkeletonItem />
)}
<DropdownMenuSeparator />
</>
)}
<DropdownMenuSearchInput <DropdownMenuSearchInput
value={relationPickerSearchFilter} value={relationPickerSearchFilter}
onChange={handleFilterChange} onChange={handleFilterChange}
autoFocus autoFocus
/> />
<DropdownMenuSeparator /> {(dropdownPlacement?.includes('start') ||
<DropdownMenuItemsContainer hasMaxHeight hasMinHeight> isUndefinedOrNull(dropdownPlacement)) && (
{recordMultiSelectIsLoading ? (
<MenuItem text="Loading..." />
) : (
<>
<SelectableList
selectableListId={MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID}
selectableItemIdArray={objectRecordsIdsMultiSelect}
hotkeyScope={relationPickerScopedId}
onEnter={(selectedId) => {
onChange?.(selectedId);
resetSelectedItem();
}}
>
{objectRecordsIdsMultiSelect?.map((recordId) => {
return (
<MultipleObjectRecordSelectItem
key={recordId}
objectRecordId={recordId}
onChange={(recordId) => {
onChange?.(recordId);
resetSelectedItem();
}}
/>
);
})}
</SelectableList>
{objectRecordsIdsMultiSelect?.length === 0 && (
<MenuItem text="No result" />
)}
</>
)}
</DropdownMenuItemsContainer>
{isDefined(onCreate) && (
<> <>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItemsContainer> {recordMultiSelectIsLoading && !relationPickerSearchFilter && (
<CreateNewButton <DropdownMenuSkeletonItem />
onClick={debouncedOnCreate} )}
LeftIcon={IconPlus} {results}
text="Add New" {createNewButton}
/>
</DropdownMenuItemsContainer>
</> </>
)} )}
</DropdownMenu> </DropdownMenu>

View File

@@ -36,6 +36,7 @@ export type SingleEntitySelectMenuItemsProps = {
isAllEntitySelectShown?: boolean; isAllEntitySelectShown?: boolean;
onAllEntitySelected?: () => void; onAllEntitySelected?: () => void;
hotkeyScope?: string; hotkeyScope?: string;
isFiltered: boolean;
}; };
export const SingleEntitySelectMenuItems = ({ export const SingleEntitySelectMenuItems = ({
@@ -54,6 +55,7 @@ export const SingleEntitySelectMenuItems = ({
isAllEntitySelectShown, isAllEntitySelectShown,
onAllEntitySelected, onAllEntitySelected,
hotkeyScope = RelationPickerHotkeyScope.RelationPicker, hotkeyScope = RelationPickerHotkeyScope.RelationPicker,
isFiltered,
}: SingleEntitySelectMenuItemsProps) => { }: SingleEntitySelectMenuItemsProps) => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
@@ -139,9 +141,11 @@ export const SingleEntitySelectMenuItems = ({
}} }}
> >
<DropdownMenuItemsContainer hasMaxHeight> <DropdownMenuItemsContainer hasMaxHeight>
{loading ? ( {loading && !isFiltered ? (
<DropdownMenuSkeletonItem /> <DropdownMenuSkeletonItem />
) : entitiesInDropdown.length === 0 && !isAllEntitySelectShown ? ( ) : entitiesInDropdown.length === 0 &&
!isAllEntitySelectShown &&
!loading ? (
<> <>
<MenuItem text="No result" /> <MenuItem text="No result" />
{entitiesToSelect.length > 0 && <DropdownMenuSeparator />} {entitiesToSelect.length > 0 && <DropdownMenuSeparator />}

View File

@@ -6,7 +6,9 @@ import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/use
import { useRelationPickerEntitiesOptions } from '@/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions'; import { useRelationPickerEntitiesOptions } from '@/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions';
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 { Placement } from '@floating-ui/react';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export type SingleEntitySelectMenuItemsWithSearchProps = { export type SingleEntitySelectMenuItemsWithSearchProps = {
excludedRelationRecordIds?: string[]; excludedRelationRecordIds?: string[];
@@ -14,6 +16,7 @@ export type SingleEntitySelectMenuItemsWithSearchProps = {
relationObjectNameSingular: string; relationObjectNameSingular: string;
relationPickerScopeId?: string; relationPickerScopeId?: string;
selectedRelationRecordIds: string[]; selectedRelationRecordIds: string[];
dropdownPlacement?: Placement | null;
} & Pick< } & Pick<
SingleEntitySelectMenuItemsProps, SingleEntitySelectMenuItemsProps,
| 'EmptyIcon' | 'EmptyIcon'
@@ -34,6 +37,7 @@ export const SingleEntitySelectMenuItemsWithSearch = ({
relationPickerScopeId = 'relation-picker', relationPickerScopeId = 'relation-picker',
selectedEntity, selectedEntity,
selectedRelationRecordIds, selectedRelationRecordIds,
dropdownPlacement,
}: SingleEntitySelectMenuItemsWithSearchProps) => { }: SingleEntitySelectMenuItemsWithSearchProps) => {
const { handleSearchFilterChange } = useEntitySelectSearch({ const { handleSearchFilterChange } = useEntitySelectSearch({
relationPickerScopeId, relationPickerScopeId,
@@ -62,29 +66,45 @@ export const SingleEntitySelectMenuItemsWithSearch = ({
}; };
} }
const results = (
<SingleEntitySelectMenuItems
entitiesToSelect={entities.entitiesToSelect}
loading={entities.loading}
selectedEntity={
selectedEntity ??
(entities.selectedEntities.length === 1
? entities.selectedEntities[0]
: undefined)
}
hotkeyScope={relationPickerScopeId}
onCreate={onCreateWithInput}
isFiltered={!!relationPickerSearchFilter}
{...{
EmptyIcon,
emptyLabel,
onCancel,
onEntitySelected,
showCreateButton,
}}
/>
);
return ( return (
<> <>
{dropdownPlacement?.includes('end') && (
<>
{results}
<DropdownMenuSeparator />
</>
)}
<DropdownMenuSearchInput onChange={handleSearchFilterChange} autoFocus /> <DropdownMenuSearchInput onChange={handleSearchFilterChange} autoFocus />
<DropdownMenuSeparator /> {(dropdownPlacement?.includes('start') ||
<SingleEntitySelectMenuItems isUndefinedOrNull(dropdownPlacement)) && (
entitiesToSelect={entities.entitiesToSelect} <>
loading={entities.loading} <DropdownMenuSeparator />
selectedEntity={ {results}
selectedEntity ?? </>
(entities.selectedEntities.length === 1 )}
? entities.selectedEntities[0]
: undefined)
}
hotkeyScope={relationPickerScopeId}
onCreate={onCreateWithInput}
{...{
EmptyIcon,
emptyLabel,
onCancel,
onEntitySelected,
showCreateButton,
}}
/>
</> </>
); );
}; };

View File

@@ -6,7 +6,7 @@ import {
Placement, Placement,
useFloating, useFloating,
} from '@floating-ui/react'; } from '@floating-ui/react';
import { MouseEvent, useRef } from 'react'; import { MouseEvent, useEffect, useRef } from 'react';
import { Keys } from 'react-hotkeys-hook'; import { Keys } from 'react-hotkeys-hook';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
@@ -64,8 +64,13 @@ export const Dropdown = ({
}: DropdownProps) => { }: DropdownProps) => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const { isDropdownOpen, toggleDropdown, closeDropdown, dropdownWidth } = const {
useDropdown(dropdownId); isDropdownOpen,
toggleDropdown,
closeDropdown,
dropdownWidth,
setDropdownPlacement,
} = useDropdown(dropdownId);
const offsetMiddlewares = []; const offsetMiddlewares = [];
@@ -77,13 +82,17 @@ export const Dropdown = ({
offsetMiddlewares.push(offset({ mainAxis: dropdownOffset.y })); offsetMiddlewares.push(offset({ mainAxis: dropdownOffset.y }));
} }
const { refs, floatingStyles } = useFloating({ const { refs, floatingStyles, placement } = useFloating({
placement: dropdownPlacement, placement: dropdownPlacement,
middleware: [flip(), ...offsetMiddlewares], middleware: [flip(), ...offsetMiddlewares],
whileElementsMounted: autoUpdate, whileElementsMounted: autoUpdate,
strategy: dropdownStrategy, strategy: dropdownStrategy,
}); });
useEffect(() => {
setDropdownPlacement(placement);
}, [placement, setDropdownPlacement]);
const handleHotkeyTriggered = () => { const handleHotkeyTriggered = () => {
toggleDropdown(); toggleDropdown();
}; };

View File

@@ -3,7 +3,6 @@ import styled from '@emotion/styled';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
const StyledDropdownMenuItemsExternalContainer = styled.div<{ const StyledDropdownMenuItemsExternalContainer = styled.div<{
hasMinHeight?: boolean;
hasMaxHeight?: boolean; hasMaxHeight?: boolean;
}>` }>`
--padding: ${({ theme }) => theme.spacing(1)}; --padding: ${({ theme }) => theme.spacing(1)};
@@ -13,7 +12,6 @@ const StyledDropdownMenuItemsExternalContainer = styled.div<{
flex-direction: column; flex-direction: column;
gap: 2px; gap: 2px;
min-height: ${({ hasMinHeight }) => (hasMinHeight ? '150px' : '100%')};
max-height: ${({ hasMaxHeight }) => (hasMaxHeight ? '188px' : 'none')}; max-height: ${({ hasMaxHeight }) => (hasMaxHeight ? '188px' : 'none')};
overflow-y: auto; overflow-y: auto;
@@ -38,18 +36,13 @@ const StyledDropdownMenuItemsInternalContainer = styled.div`
export const DropdownMenuItemsContainer = ({ export const DropdownMenuItemsContainer = ({
children, children,
hasMinHeight,
hasMaxHeight, hasMaxHeight,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
hasMinHeight?: boolean;
hasMaxHeight?: boolean; hasMaxHeight?: boolean;
}) => { }) => {
return ( return (
<StyledDropdownMenuItemsExternalContainer <StyledDropdownMenuItemsExternalContainer hasMaxHeight={hasMaxHeight}>
hasMaxHeight={hasMaxHeight}
hasMinHeight={hasMinHeight}
>
{hasMaxHeight ? ( {hasMaxHeight ? (
<StyledScrollWrapper contextProviderName="dropdownMenuItemsContainer"> <StyledScrollWrapper contextProviderName="dropdownMenuItemsContainer">
<StyledDropdownMenuItemsInternalContainer> <StyledDropdownMenuItemsInternalContainer>

View File

@@ -1,5 +1,6 @@
import { DropdownScopeInternalContext } from '@/ui/layout/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext'; import { DropdownScopeInternalContext } from '@/ui/layout/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext';
import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState'; import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState';
import { dropdownPlacementComponentState } from '@/ui/layout/dropdown/states/dropdownPlacementComponentState';
import { dropdownWidthComponentState } from '@/ui/layout/dropdown/states/dropdownWidthComponentState'; import { dropdownWidthComponentState } from '@/ui/layout/dropdown/states/dropdownWidthComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
@@ -19,6 +20,10 @@ export const useDropdownStates = ({
return { return {
scopeId, scopeId,
dropdownPlacementState: extractComponentState(
dropdownPlacementComponentState,
scopeId,
),
dropdownHotkeyScopeState: extractComponentState( dropdownHotkeyScopeState: extractComponentState(
dropdownHotkeyComponentState, dropdownHotkeyComponentState,
scopeId, scopeId,

View File

@@ -12,6 +12,7 @@ export const useDropdown = (dropdownId?: string) => {
dropdownHotkeyScopeState, dropdownHotkeyScopeState,
dropdownWidthState, dropdownWidthState,
isDropdownOpenState, isDropdownOpenState,
dropdownPlacementState,
} = useDropdownStates({ } = useDropdownStates({
dropdownScopeId: getScopeIdOrUndefinedFromComponentId(dropdownId), dropdownScopeId: getScopeIdOrUndefinedFromComponentId(dropdownId),
}); });
@@ -25,6 +26,10 @@ export const useDropdown = (dropdownId?: string) => {
const [dropdownWidth, setDropdownWidth] = useRecoilState(dropdownWidthState); const [dropdownWidth, setDropdownWidth] = useRecoilState(dropdownWidthState);
const [dropdownPlacement, setDropdownPlacement] = useRecoilState(
dropdownPlacementState,
);
const [isDropdownOpen, setIsDropdownOpen] = const [isDropdownOpen, setIsDropdownOpen] =
useRecoilState(isDropdownOpenState); useRecoilState(isDropdownOpenState);
@@ -59,5 +64,7 @@ export const useDropdown = (dropdownId?: string) => {
openDropdown, openDropdown,
dropdownWidth, dropdownWidth,
setDropdownWidth, setDropdownWidth,
dropdownPlacement,
setDropdownPlacement,
}; };
}; };

View File

@@ -0,0 +1,9 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { Placement } from '@floating-ui/react';
export const dropdownPlacementComponentState =
createComponentState<Placement | null>({
key: 'dropdownPlacementComponentState',
defaultValue: null,
});