From 2a0f5fb609c8f57a4f725fb1f794bbcb85207827 Mon Sep 17 00:00:00 2001 From: bosiraphael Date: Tue, 19 Nov 2024 15:58:34 +0100 Subject: [PATCH] create context chip for command menu --- .../CommandMenuContextRecordChip.tsx | 38 +++++++++++++ .../components/CommandMenuTopBar.tsx | 7 ++- ...textStoreCurrentObjectMetadataIdOrThrow.ts | 18 ++++++ .../hooks/useContextStoreSelectedRecords.ts | 55 +++++++++++++++++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordChip.tsx create mode 100644 packages/twenty-front/src/modules/context-store/hooks/useContextStoreCurrentObjectMetadataIdOrThrow.ts create mode 100644 packages/twenty-front/src/modules/context-store/hooks/useContextStoreSelectedRecords.ts diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordChip.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordChip.tsx new file mode 100644 index 000000000..57c31e072 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordChip.tsx @@ -0,0 +1,38 @@ +import { useContextStoreSelectedRecords } from '@/context-store/hooks/useContextStoreSelectedRecords'; +import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId'; +import styled from '@emotion/styled'; +import { useRecoilValue } from 'recoil'; + +const StyledChip = styled.div` + align-items: center; + background: ${({ theme }) => theme.background.transparent.light}; + border: 1px solid ${({ theme }) => theme.border.color.medium}; + border-radius: ${({ theme }) => theme.border.radius.md}; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; + height: ${({ theme }) => theme.spacing(8)}; + padding: 0 ${({ theme }) => theme.spacing(2)}; + font-size: ${({ theme }) => theme.font.size.sm}; + font-weight: ${({ theme }) => theme.font.weight.medium}; + line-height: ${({ theme }) => theme.text.lineHeight.lg}; +`; + +export const CommandMenuContextRecordChip = () => { + const mainContextStoreComponentInstanceId = useRecoilValue( + mainContextStoreComponentInstanceIdState, + ); + + const { records, loading, totalCount } = useContextStoreSelectedRecords( + mainContextStoreComponentInstanceId ?? undefined, + ); + + if (loading || !totalCount) { + return null; + } + + return ( + + {totalCount === 1 ? records[0].name : `${totalCount} records`} + + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx index 98b11b53b..2385683fd 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx @@ -1,3 +1,4 @@ +import { CommandMenuContextRecordChip } from '@/command-menu/components/CommandMenuContextRecordChip'; import { COMMAND_MENU_SEARCH_BAR_HEIGHT } from '@/command-menu/constants/CommandMenuSearchBarHeight'; import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/CommandMenuSearchBarPadding'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; @@ -19,6 +20,7 @@ const StyledInputContainer = styled.div` position: relative; padding: 0 ${({ theme }) => theme.spacing(COMMAND_MENU_SEARCH_BAR_PADDING)}; + gap: ${({ theme }) => theme.spacing(1)}; `; const StyledInput = styled.input` @@ -31,7 +33,7 @@ const StyledInput = styled.input` outline: none; height: 24px; padding: 0; - width: ${({ theme }) => `calc(100% - ${theme.spacing(8)})`}; + flex: 1; &::placeholder { color: ${({ theme }) => theme.font.color.light}; @@ -65,10 +67,11 @@ export const CommandMenuTopBar = ({ return ( + {!isMobile && ( diff --git a/packages/twenty-front/src/modules/context-store/hooks/useContextStoreCurrentObjectMetadataIdOrThrow.ts b/packages/twenty-front/src/modules/context-store/hooks/useContextStoreCurrentObjectMetadataIdOrThrow.ts new file mode 100644 index 000000000..cf147833a --- /dev/null +++ b/packages/twenty-front/src/modules/context-store/hooks/useContextStoreCurrentObjectMetadataIdOrThrow.ts @@ -0,0 +1,18 @@ +import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +export const useContextStoreCurrentObjectMetadataIdOrThrow = ( + instanceId?: string, +) => { + const contextStoreCurrentObjectMetadataIdComponent = + useRecoilComponentValueV2( + contextStoreCurrentObjectMetadataIdComponentState, + instanceId, + ); + + if (!contextStoreCurrentObjectMetadataIdComponent) { + throw new Error('contextStoreCurrentObjectMetadataIdComponent is not set'); + } + + return contextStoreCurrentObjectMetadataIdComponent; +}; diff --git a/packages/twenty-front/src/modules/context-store/hooks/useContextStoreSelectedRecords.ts b/packages/twenty-front/src/modules/context-store/hooks/useContextStoreSelectedRecords.ts new file mode 100644 index 000000000..b61a8c546 --- /dev/null +++ b/packages/twenty-front/src/modules/context-store/hooks/useContextStoreSelectedRecords.ts @@ -0,0 +1,55 @@ +import { useContextStoreCurrentObjectMetadataIdOrThrow } from '@/context-store/hooks/useContextStoreCurrentObjectMetadataIdOrThrow'; +import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState'; +import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; +import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters'; +import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; +import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +export const useContextStoreSelectedRecords = ( + instanceId?: string, + limit = 3, +) => { + const objectMetadataId = + useContextStoreCurrentObjectMetadataIdOrThrow(instanceId); + + const { objectMetadataItem } = useObjectMetadataItemById({ + objectId: objectMetadataId, + }); + + const contextStoreTargetedRecordsRule = useRecoilComponentValueV2( + contextStoreTargetedRecordsRuleComponentState, + instanceId, + ); + + const contextStoreFilters = useRecoilComponentValueV2( + contextStoreFiltersComponentState, + instanceId, + ); + + const queryFilter = computeContextStoreFilters( + contextStoreTargetedRecordsRule, + contextStoreFilters, + objectMetadataItem, + ); + + const { records, loading, totalCount } = useFindManyRecords({ + objectNameSingular: objectMetadataItem.nameSingular, + filter: queryFilter, + orderBy: [ + { + createdAt: 'DescNullsFirst', + }, + ], + skip: + contextStoreTargetedRecordsRule.mode === 'selection' && + contextStoreTargetedRecordsRule.selectedRecordIds.length === 0, + limit, + }); + + return { + records, + totalCount, + loading, + }; +};