mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 20:27:55 +00:00 
			
		
		
		
	7339 implement contextual actions inside the commandmenu (#8000)
Closes #7339 https://github.com/user-attachments/assets/b623caa4-c1b3-448e-8880-4a8301802ba8
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; | ||||
| import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries'; | ||||
| import { ActionMenuType } from '@/action-menu/types/ActionMenuType'; | ||||
| import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; | ||||
| import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; | ||||
| import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters'; | ||||
| @@ -12,17 +12,15 @@ import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTabl | ||||
| import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; | ||||
| import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
| import { useCallback, useEffect, useState } from 'react'; | ||||
| import { useCallback, useContext, useEffect, useState } from 'react'; | ||||
| import { IconTrash, isDefined } from 'twenty-ui'; | ||||
|  | ||||
| export const DeleteRecordsActionEffect = ({ | ||||
|   position, | ||||
|   objectMetadataItem, | ||||
|   actionMenuType, | ||||
| }: { | ||||
|   position: number; | ||||
|   objectMetadataItem: ObjectMetadataItem; | ||||
|   actionMenuType: ActionMenuType; | ||||
| }) => { | ||||
|   const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries(); | ||||
|  | ||||
| @@ -93,6 +91,9 @@ export const DeleteRecordsActionEffect = ({ | ||||
|     contextStoreNumberOfSelectedRecords < DELETE_MAX_COUNT && | ||||
|     contextStoreNumberOfSelectedRecords > 0; | ||||
|  | ||||
|   const { isInRightDrawer, onActionExecutedCallback } = | ||||
|     useContext(ActionMenuContext); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (canDelete) { | ||||
|       addActionMenuEntry({ | ||||
| @@ -120,17 +121,14 @@ export const DeleteRecordsActionEffect = ({ | ||||
|             } can be recovered from the Options menu.`} | ||||
|             onConfirmClick={() => { | ||||
|               handleDeleteClick(); | ||||
|  | ||||
|               if (actionMenuType === 'recordShow') { | ||||
|               onActionExecutedCallback?.(); | ||||
|               if (isInRightDrawer) { | ||||
|                 closeRightDrawer(); | ||||
|               } | ||||
|             }} | ||||
|             deleteButtonText={`Delete ${ | ||||
|               contextStoreNumberOfSelectedRecords > 1 ? 'Records' : 'Record' | ||||
|             }`} | ||||
|             modalVariant={ | ||||
|               actionMenuType === 'recordShow' ? 'tertiary' : 'primary' | ||||
|             } | ||||
|           /> | ||||
|         ), | ||||
|       }); | ||||
| @@ -142,13 +140,14 @@ export const DeleteRecordsActionEffect = ({ | ||||
|       removeActionMenuEntry('delete'); | ||||
|     }; | ||||
|   }, [ | ||||
|     actionMenuType, | ||||
|     addActionMenuEntry, | ||||
|     canDelete, | ||||
|     closeRightDrawer, | ||||
|     contextStoreNumberOfSelectedRecords, | ||||
|     handleDeleteClick, | ||||
|     isDeleteRecordsModalOpen, | ||||
|     isInRightDrawer, | ||||
|     onActionExecutedCallback, | ||||
|     position, | ||||
|     removeActionMenuEntry, | ||||
|   ]); | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect'; | ||||
| import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect'; | ||||
| import { ActionMenuType } from '@/action-menu/types/ActionMenuType'; | ||||
| import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; | ||||
|  | ||||
| const actionEffects = [ExportRecordsActionEffect, DeleteRecordsActionEffect]; | ||||
|  | ||||
| export const MultipleRecordsActionMenuEntriesSetter = ({ | ||||
|   objectMetadataItem, | ||||
|   actionMenuType, | ||||
| }: { | ||||
|   objectMetadataItem: ObjectMetadataItem; | ||||
|   actionMenuType: ActionMenuType; | ||||
| }) => { | ||||
|   return ( | ||||
|     <> | ||||
|       {actionEffects.map((ActionEffect, index) => ( | ||||
|         <ActionEffect | ||||
|           key={index} | ||||
|           position={index} | ||||
|           objectMetadataItem={objectMetadataItem} | ||||
|           actionMenuType={actionMenuType} | ||||
|         /> | ||||
|       ))} | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
| @@ -1,16 +1,23 @@ | ||||
| import { MultipleRecordsActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/MultipleRecordsActionMenuEntriesSetter'; | ||||
| import { SingleRecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/SingleRecordActionMenuEntriesSetter'; | ||||
| import { ActionMenuType } from '@/action-menu/types/ActionMenuType'; | ||||
| import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect'; | ||||
| import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect'; | ||||
| import { ManageFavoritesActionEffect } from '@/action-menu/actions/record-actions/components/ManageFavoritesActionEffect'; | ||||
| import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; | ||||
| import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; | ||||
| import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
|  | ||||
| export const RecordActionMenuEntriesSetter = ({ | ||||
|   actionMenuType, | ||||
| }: { | ||||
|   actionMenuType: ActionMenuType; | ||||
| }) => { | ||||
| const singleRecordActionEffects = [ | ||||
|   ManageFavoritesActionEffect, | ||||
|   ExportRecordsActionEffect, | ||||
|   DeleteRecordsActionEffect, | ||||
| ]; | ||||
|  | ||||
| const multipleRecordActionEffects = [ | ||||
|   ExportRecordsActionEffect, | ||||
|   DeleteRecordsActionEffect, | ||||
| ]; | ||||
|  | ||||
| export const RecordActionMenuEntriesSetter = () => { | ||||
|   const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2( | ||||
|     contextStoreNumberOfSelectedRecordsComponentState, | ||||
|   ); | ||||
| @@ -33,19 +40,20 @@ export const RecordActionMenuEntriesSetter = ({ | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   if (contextStoreNumberOfSelectedRecords === 1) { | ||||
|     return ( | ||||
|       <SingleRecordActionMenuEntriesSetter | ||||
|         objectMetadataItem={objectMetadataItem} | ||||
|         actionMenuType={actionMenuType} | ||||
|       /> | ||||
|     ); | ||||
|   } | ||||
|   const actions = | ||||
|     contextStoreNumberOfSelectedRecords === 1 | ||||
|       ? singleRecordActionEffects | ||||
|       : multipleRecordActionEffects; | ||||
|  | ||||
|   return ( | ||||
|     <MultipleRecordsActionMenuEntriesSetter | ||||
|     <> | ||||
|       {actions.map((ActionEffect, index) => ( | ||||
|         <ActionEffect | ||||
|           key={index} | ||||
|           position={index} | ||||
|           objectMetadataItem={objectMetadataItem} | ||||
|       actionMenuType={actionMenuType} | ||||
|         /> | ||||
|       ))} | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -1,31 +0,0 @@ | ||||
| import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect'; | ||||
| import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect'; | ||||
| import { ManageFavoritesActionEffect } from '@/action-menu/actions/record-actions/components/ManageFavoritesActionEffect'; | ||||
| import { ActionMenuType } from '@/action-menu/types/ActionMenuType'; | ||||
| import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; | ||||
|  | ||||
| export const SingleRecordActionMenuEntriesSetter = ({ | ||||
|   objectMetadataItem, | ||||
|   actionMenuType, | ||||
| }: { | ||||
|   objectMetadataItem: ObjectMetadataItem; | ||||
|   actionMenuType: ActionMenuType; | ||||
| }) => { | ||||
|   const actionEffects = [ | ||||
|     ManageFavoritesActionEffect, | ||||
|     ExportRecordsActionEffect, | ||||
|     DeleteRecordsActionEffect, | ||||
|   ]; | ||||
|   return ( | ||||
|     <> | ||||
|       {actionEffects.map((ActionEffect, index) => ( | ||||
|         <ActionEffect | ||||
|           key={index} | ||||
|           position={index} | ||||
|           objectMetadataItem={objectMetadataItem} | ||||
|           actionMenuType={actionMenuType} | ||||
|         /> | ||||
|       ))} | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
| @@ -3,16 +3,12 @@ import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMen | ||||
| import { RecordIndexActionMenuBar } from '@/action-menu/components/RecordIndexActionMenuBar'; | ||||
| import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown'; | ||||
| import { RecordIndexActionMenuEffect } from '@/action-menu/components/RecordIndexActionMenuEffect'; | ||||
| import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; | ||||
|  | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
|  | ||||
| export const RecordIndexActionMenu = ({ | ||||
|   actionMenuId, | ||||
| }: { | ||||
|   actionMenuId: string; | ||||
| }) => { | ||||
| export const RecordIndexActionMenu = () => { | ||||
|   const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2( | ||||
|     contextStoreCurrentObjectMetadataIdComponentState, | ||||
|   ); | ||||
| @@ -20,15 +16,18 @@ export const RecordIndexActionMenu = ({ | ||||
|   return ( | ||||
|     <> | ||||
|       {contextStoreCurrentObjectMetadataId && ( | ||||
|         <ActionMenuComponentInstanceContext.Provider | ||||
|           value={{ instanceId: actionMenuId }} | ||||
|         <ActionMenuContext.Provider | ||||
|           value={{ | ||||
|             isInRightDrawer: false, | ||||
|             onActionExecutedCallback: () => {}, | ||||
|           }} | ||||
|         > | ||||
|           <RecordIndexActionMenuBar /> | ||||
|           <RecordIndexActionMenuDropdown /> | ||||
|           <ActionMenuConfirmationModals /> | ||||
|           <RecordIndexActionMenuEffect /> | ||||
|           <RecordActionMenuEntriesSetter actionMenuType="recordIndex" /> | ||||
|         </ActionMenuComponentInstanceContext.Provider> | ||||
|           <RecordActionMenuEntriesSetter /> | ||||
|         </ActionMenuContext.Provider> | ||||
|       )} | ||||
|     </> | ||||
|   ); | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| import { useActionMenu } from '@/action-menu/hooks/useActionMenu'; | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; | ||||
| import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; | ||||
| import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; | ||||
| import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; | ||||
| import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
| import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; | ||||
| @@ -25,6 +27,9 @@ export const RecordIndexActionMenuEffect = () => { | ||||
|       `action-menu-dropdown-${actionMenuId}`, | ||||
|     ), | ||||
|   ); | ||||
|   const { isRightDrawerOpen } = useRightDrawer(); | ||||
|  | ||||
|   const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (contextStoreNumberOfSelectedRecords > 0 && !isDropdownOpen) { | ||||
| @@ -43,5 +48,11 @@ export const RecordIndexActionMenuEffect = () => { | ||||
|     isDropdownOpen, | ||||
|   ]); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (isRightDrawerOpen || isCommandMenuOpened) { | ||||
|       closeActionBar(); | ||||
|     } | ||||
|   }, [closeActionBar, isRightDrawerOpen, isCommandMenuOpened]); | ||||
|  | ||||
|   return null; | ||||
| }; | ||||
|   | ||||
| @@ -1,30 +1,53 @@ | ||||
| import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter'; | ||||
| import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals'; | ||||
| import { RecordShowActionMenuBar } from '@/action-menu/components/RecordShowActionMenuBar'; | ||||
| import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; | ||||
|  | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; | ||||
| import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; | ||||
| import { ObjectRecord } from '@/object-record/types/ObjectRecord'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
| import { RecordShowPageBaseHeader } from '~/pages/object-record/RecordShowPageBaseHeader'; | ||||
|  | ||||
| export const RecordShowActionMenu = ({ | ||||
|   actionMenuId, | ||||
|   isFavorite, | ||||
|   handleFavoriteButtonClick, | ||||
|   record, | ||||
|   objectMetadataItem, | ||||
|   objectNameSingular, | ||||
| }: { | ||||
|   actionMenuId: string; | ||||
|   isFavorite: boolean; | ||||
|   handleFavoriteButtonClick: () => void; | ||||
|   record: ObjectRecord | undefined; | ||||
|   objectMetadataItem: ObjectMetadataItem; | ||||
|   objectNameSingular: string; | ||||
| }) => { | ||||
|   const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2( | ||||
|     contextStoreCurrentObjectMetadataIdComponentState, | ||||
|   ); | ||||
|  | ||||
|   // TODO: refactor RecordShowPageBaseHeader to use the context store | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       {contextStoreCurrentObjectMetadataId && ( | ||||
|         <ActionMenuComponentInstanceContext.Provider | ||||
|           value={{ instanceId: actionMenuId }} | ||||
|         <ActionMenuContext.Provider | ||||
|           value={{ | ||||
|             isInRightDrawer: false, | ||||
|             onActionExecutedCallback: () => {}, | ||||
|           }} | ||||
|         > | ||||
|           <RecordShowActionMenuBar /> | ||||
|           <RecordShowPageBaseHeader | ||||
|             {...{ | ||||
|               isFavorite, | ||||
|               handleFavoriteButtonClick, | ||||
|               record, | ||||
|               objectMetadataItem, | ||||
|               objectNameSingular, | ||||
|             }} | ||||
|           /> | ||||
|           <ActionMenuConfirmationModals /> | ||||
|           <RecordActionMenuEntriesSetter actionMenuType="recordShow" /> | ||||
|         </ActionMenuComponentInstanceContext.Provider> | ||||
|           <RecordActionMenuEntriesSetter /> | ||||
|         </ActionMenuContext.Provider> | ||||
|       )} | ||||
|     </> | ||||
|   ); | ||||
|   | ||||
| @@ -0,0 +1,30 @@ | ||||
| import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter'; | ||||
| import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals'; | ||||
| import { RecordShowRightDrawerActionMenuBar } from '@/action-menu/components/RecordShowRightDrawerActionMenuBar'; | ||||
| import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; | ||||
|  | ||||
| import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
|  | ||||
| export const RecordShowRightDrawerActionMenu = () => { | ||||
|   const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2( | ||||
|     contextStoreCurrentObjectMetadataIdComponentState, | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       {contextStoreCurrentObjectMetadataId && ( | ||||
|         <ActionMenuContext.Provider | ||||
|           value={{ | ||||
|             isInRightDrawer: true, | ||||
|             onActionExecutedCallback: () => {}, | ||||
|           }} | ||||
|         > | ||||
|           <RecordShowRightDrawerActionMenuBar /> | ||||
|           <ActionMenuConfirmationModals /> | ||||
|           <RecordActionMenuEntriesSetter /> | ||||
|         </ActionMenuContext.Provider> | ||||
|       )} | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
| @@ -2,7 +2,7 @@ import { RecordShowActionMenuBarEntry } from '@/action-menu/components/RecordSho | ||||
| import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; | ||||
| import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; | ||||
| 
 | ||||
| export const RecordShowActionMenuBar = () => { | ||||
| export const RecordShowRightDrawerActionMenuBar = () => { | ||||
|   const actionMenuEntries = useRecoilComponentValueV2( | ||||
|     actionMenuEntriesComponentSelector, | ||||
|   ); | ||||
| @@ -2,7 +2,7 @@ import { expect, jest } from '@storybook/jest'; | ||||
| import { Meta, StoryObj } from '@storybook/react'; | ||||
| import { RecoilRoot } from 'recoil'; | ||||
|  | ||||
| import { RecordShowActionMenuBar } from '@/action-menu/components/RecordShowActionMenuBar'; | ||||
| import { RecordShowRightDrawerActionMenuBar } from '@/action-menu/components/RecordShowRightDrawerActionMenuBar'; | ||||
| import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState'; | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; | ||||
| @@ -20,9 +20,9 @@ const deleteMock = jest.fn(); | ||||
| const addToFavoritesMock = jest.fn(); | ||||
| const exportMock = jest.fn(); | ||||
|  | ||||
| const meta: Meta<typeof RecordShowActionMenuBar> = { | ||||
|   title: 'Modules/ActionMenu/RecordShowActionMenuBar', | ||||
|   component: RecordShowActionMenuBar, | ||||
| const meta: Meta<typeof RecordShowRightDrawerActionMenuBar> = { | ||||
|   title: 'Modules/ActionMenu/RecordShowRightDrawerActionMenuBar', | ||||
|   component: RecordShowRightDrawerActionMenuBar, | ||||
|   decorators: [ | ||||
|     (Story) => ( | ||||
|       <RecoilRoot | ||||
| @@ -98,7 +98,7 @@ const meta: Meta<typeof RecordShowActionMenuBar> = { | ||||
|  | ||||
| export default meta; | ||||
|  | ||||
| type Story = StoryObj<typeof RecordShowActionMenuBar>; | ||||
| type Story = StoryObj<typeof RecordShowRightDrawerActionMenuBar>; | ||||
|  | ||||
| export const Default: Story = { | ||||
|   args: { | ||||
|   | ||||
| @@ -0,0 +1,11 @@ | ||||
| import { createContext } from 'react'; | ||||
|  | ||||
| type ActionMenuContextType = { | ||||
|   isInRightDrawer: boolean; | ||||
|   onActionExecutedCallback: () => void; | ||||
| }; | ||||
|  | ||||
| export const ActionMenuContext = createContext<ActionMenuContextType>({ | ||||
|   isInRightDrawer: false, | ||||
|   onActionExecutedCallback: () => {}, | ||||
| }); | ||||
| @@ -1 +0,0 @@ | ||||
| export type ActionMenuType = 'recordIndex' | 'recordShow'; | ||||
| @@ -10,6 +10,7 @@ import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchS | ||||
| import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; | ||||
| import { Command, CommandType } from '@/command-menu/types/Command'; | ||||
| import { Company } from '@/companies/types/Company'; | ||||
| import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId'; | ||||
| import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu'; | ||||
| import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; | ||||
| import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName'; | ||||
| @@ -287,6 +288,14 @@ export const CommandMenu = () => { | ||||
|         : true) && cmd.type === CommandType.Create, | ||||
|   ); | ||||
|  | ||||
|   const matchingActionCommands = commandMenuCommands.filter( | ||||
|     (cmd) => | ||||
|       (deferredCommandMenuSearch.length > 0 | ||||
|         ? checkInShortcuts(cmd, deferredCommandMenuSearch) || | ||||
|           checkInLabels(cmd, deferredCommandMenuSearch) | ||||
|         : true) && cmd.type === CommandType.Action, | ||||
|   ); | ||||
|  | ||||
|   useListenClickOutside({ | ||||
|     refs: [commandMenuRef], | ||||
|     callback: closeCommandMenu, | ||||
| @@ -312,6 +321,7 @@ export const CommandMenu = () => { | ||||
|  | ||||
|   const selectableItemIds = copilotCommands | ||||
|     .map((cmd) => cmd.id) | ||||
|     .concat(matchingActionCommands.map((cmd) => cmd.id)) | ||||
|     .concat(matchingCreateCommand.map((cmd) => cmd.id)) | ||||
|     .concat(matchingNavigateCommand.map((cmd) => cmd.id)) | ||||
|     .concat(people?.map((person) => person.id)) | ||||
| @@ -320,22 +330,28 @@ export const CommandMenu = () => { | ||||
|     .concat(notes?.map((note) => note.id)); | ||||
|  | ||||
|   const isNoResults = | ||||
|     !matchingActionCommands.length && | ||||
|     !matchingCreateCommand.length && | ||||
|     !matchingNavigateCommand.length && | ||||
|     !people?.length && | ||||
|     !companies?.length && | ||||
|     !notes?.length && | ||||
|     !opportunities?.length; | ||||
|  | ||||
|   const isLoading = | ||||
|     isPeopleLoading || | ||||
|     isNotesLoading || | ||||
|     isOpportunitiesLoading || | ||||
|     isCompaniesLoading; | ||||
|  | ||||
|   const mainContextStoreComponentInstanceId = useRecoilValue( | ||||
|     mainContextStoreComponentInstanceIdState, | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       {isCommandMenuOpened && ( | ||||
|         <StyledCommandMenu ref={commandMenuRef}> | ||||
|         <StyledCommandMenu ref={commandMenuRef} className="command-menu"> | ||||
|           <StyledInputContainer> | ||||
|             <StyledInput | ||||
|               autoFocus | ||||
| @@ -393,6 +409,23 @@ export const CommandMenu = () => { | ||||
|                       </SelectableItem> | ||||
|                     </CommandGroup> | ||||
|                   )} | ||||
|                   {mainContextStoreComponentInstanceId && ( | ||||
|                     <CommandGroup heading="Actions"> | ||||
|                       {matchingActionCommands?.map((actionCommand) => ( | ||||
|                         <SelectableItem | ||||
|                           itemId={actionCommand.id} | ||||
|                           key={actionCommand.id} | ||||
|                         > | ||||
|                           <CommandMenuItem | ||||
|                             id={actionCommand.id} | ||||
|                             label={actionCommand.label} | ||||
|                             Icon={actionCommand.Icon} | ||||
|                             onClick={actionCommand.onCommandClick} | ||||
|                           /> | ||||
|                         </SelectableItem> | ||||
|                       ))} | ||||
|                     </CommandGroup> | ||||
|                   )} | ||||
|                   <CommandGroup heading="Create"> | ||||
|                     {matchingCreateCommand.map((cmd) => ( | ||||
|                       <SelectableItem itemId={cmd.id} key={cmd.id}> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { isNonEmptyString } from '@sniptt/guards'; | ||||
| import { useCallback } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
| import { useRecoilCallback, useSetRecoilState } from 'recoil'; | ||||
| import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil'; | ||||
|  | ||||
| import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; | ||||
| import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; | ||||
| @@ -9,7 +9,9 @@ import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousH | ||||
| import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; | ||||
| import { isDefined } from '~/utils/isDefined'; | ||||
|  | ||||
| import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; | ||||
| import { COMMAND_MENU_COMMANDS } from '@/command-menu/constants/CommandMenuCommands'; | ||||
| import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId'; | ||||
| import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; | ||||
| import { ALL_ICONS } from '@ui/display/icon/providers/internal/AllIcons'; | ||||
| import { sortByProperty } from '~/utils/array/sortByProperty'; | ||||
| @@ -27,10 +29,43 @@ export const useCommandMenu = () => { | ||||
|     goBackToPreviousHotkeyScope, | ||||
|   } = usePreviousHotkeyScope(); | ||||
|  | ||||
|   const openCommandMenu = useCallback(() => { | ||||
|   const mainContextStoreComponentInstanceId = useRecoilValue( | ||||
|     mainContextStoreComponentInstanceIdState, | ||||
|   ); | ||||
|  | ||||
|   const openCommandMenu = useRecoilCallback( | ||||
|     ({ snapshot }) => | ||||
|       () => { | ||||
|         if (isDefined(mainContextStoreComponentInstanceId)) { | ||||
|           const actionMenuEntries = snapshot.getLoadable( | ||||
|             actionMenuEntriesComponentSelector.selectorFamily({ | ||||
|               instanceId: mainContextStoreComponentInstanceId, | ||||
|             }), | ||||
|           ); | ||||
|  | ||||
|           const actionCommands = actionMenuEntries | ||||
|             .getValue() | ||||
|             ?.map((actionMenuEntry) => ({ | ||||
|               id: actionMenuEntry.key, | ||||
|               label: actionMenuEntry.label, | ||||
|               Icon: actionMenuEntry.Icon, | ||||
|               onCommandClick: actionMenuEntry.onClick, | ||||
|               type: CommandType.Action, | ||||
|             })); | ||||
|  | ||||
|           setCommands(actionCommands); | ||||
|         } | ||||
|  | ||||
|         setIsCommandMenuOpened(true); | ||||
|         setHotkeyScopeAndMemorizePreviousScope(AppHotkeyScope.CommandMenuOpen); | ||||
|   }, [setHotkeyScopeAndMemorizePreviousScope, setIsCommandMenuOpened]); | ||||
|       }, | ||||
|     [ | ||||
|       mainContextStoreComponentInstanceId, | ||||
|       setCommands, | ||||
|       setHotkeyScopeAndMemorizePreviousScope, | ||||
|       setIsCommandMenuOpened, | ||||
|     ], | ||||
|   ); | ||||
|  | ||||
|   const closeCommandMenu = useRecoilCallback( | ||||
|     ({ snapshot }) => | ||||
|   | ||||
| @@ -3,13 +3,14 @@ import { IconComponent } from 'twenty-ui'; | ||||
| export enum CommandType { | ||||
|   Navigate = 'Navigate', | ||||
|   Create = 'Create', | ||||
|   Action = 'Action', | ||||
| } | ||||
|  | ||||
| export type Command = { | ||||
|   id: string; | ||||
|   to: string; | ||||
|   to?: string; | ||||
|   label: string; | ||||
|   type: CommandType.Navigate | CommandType.Create; | ||||
|   type: CommandType.Navigate | CommandType.Create | CommandType.Action; | ||||
|   Icon?: IconComponent; | ||||
|   firstHotKey?: string; | ||||
|   secondHotKey?: string; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import { mainContextStoreComponentInstanceIdState } from '@/context-store/states | ||||
| import { useContext, useEffect } from 'react'; | ||||
| import { useSetRecoilState } from 'recoil'; | ||||
| 
 | ||||
| export const SetMainContextStoreComponentInstanceIdEffect = () => { | ||||
| export const MainContextStoreComponentInstanceIdSetterEffect = () => { | ||||
|   const setMainContextStoreComponentInstanceId = useSetRecoilState( | ||||
|     mainContextStoreComponentInstanceIdState, | ||||
|   ); | ||||
| @@ -71,7 +71,7 @@ export const RecordBoard = () => { | ||||
|  | ||||
|   useListenClickOutsideByClassName({ | ||||
|     classNames: ['record-board-card'], | ||||
|     excludeClassNames: ['bottom-bar', 'action-menu-dropdown'], | ||||
|     excludeClassNames: ['bottom-bar', 'action-menu-dropdown', 'command-menu'], | ||||
|     callback: resetRecordSelection, | ||||
|   }); | ||||
|  | ||||
|   | ||||
| @@ -206,6 +206,7 @@ export const RecordIndexContainer = () => { | ||||
|             viewBarId={recordIndexId} | ||||
|           /> | ||||
|         </SpreadsheetImportProvider> | ||||
|  | ||||
|         {recordIndexViewType === ViewType.Table && ( | ||||
|           <> | ||||
|             <RecordIndexTableContainer | ||||
| @@ -232,7 +233,7 @@ export const RecordIndexContainer = () => { | ||||
|             /> | ||||
|           </StyledContainerWithPadding> | ||||
|         )} | ||||
|         <RecordIndexActionMenu actionMenuId={recordIndexId} /> | ||||
|         <RecordIndexActionMenu /> | ||||
|       </RecordFieldValueSelectorContextProvider> | ||||
|     </StyledContainer> | ||||
|   ); | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| import { useRecoilValue } from 'recoil'; | ||||
|  | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; | ||||
| import { isNewViewableRecordLoadingState } from '@/object-record/record-right-drawer/states/isNewViewableRecordLoading'; | ||||
| import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; | ||||
| import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; | ||||
| @@ -38,6 +40,14 @@ export const RightDrawerRecord = () => { | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <ContextStoreComponentInstanceContext.Provider | ||||
|       value={{ | ||||
|         instanceId: `record-show-${objectRecordId}`, | ||||
|       }} | ||||
|     > | ||||
|       <ActionMenuComponentInstanceContext.Provider | ||||
|         value={{ instanceId: `record-show-${objectRecordId}` }} | ||||
|       > | ||||
|         <StyledRightDrawerRecord> | ||||
|           <RecordFieldValueSelectorContextProvider> | ||||
|             {!isNewViewableRecordLoading && ( | ||||
| @@ -52,5 +62,7 @@ export const RightDrawerRecord = () => { | ||||
|             /> | ||||
|           </RecordFieldValueSelectorContextProvider> | ||||
|         </StyledRightDrawerRecord> | ||||
|       </ActionMenuComponentInstanceContext.Provider> | ||||
|     </ContextStoreComponentInstanceContext.Provider> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; | ||||
| import { ShowPageContainer } from '@/ui/layout/page/components/ShowPageContainer'; | ||||
|  | ||||
| import { SetMainContextStoreComponentInstanceIdEffect } from '@/context-store/components/SetMainContextStoreComponentInstanceIdEffect'; | ||||
| import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; | ||||
| import { MainContextStoreComponentInstanceIdSetterEffect } from '@/context-store/components/MainContextStoreComponentInstanceIdSetterEffect'; | ||||
| import { InformationBannerDeletedRecord } from '@/information-banner/components/deleted-record/InformationBannerDeletedRecord'; | ||||
| import { RecordShowContainerContextStoreEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreEffect'; | ||||
|  | ||||
| import { RecordShowContainerContextStoreObjectMetadataIdEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreObjectMetadataIdEffect'; | ||||
| import { RecordShowContainerContextStoreTargetedRecordsEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreTargetedRecordsEffect'; | ||||
| import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData'; | ||||
| import { useRecordShowContainerTabs } from '@/object-record/record-show/hooks/useRecordShowContainerTabs'; | ||||
| import { ShowPageSubContainer } from '@/ui/layout/show-page/components/ShowPageSubContainer'; | ||||
| @@ -41,16 +42,15 @@ export const RecordShowContainer = ({ | ||||
|   ); | ||||
|  | ||||
|   return ( | ||||
|     <ContextStoreComponentInstanceContext.Provider | ||||
|       value={{ | ||||
|         instanceId: 'record-show', | ||||
|       }} | ||||
|     > | ||||
|       <RecordShowContainerContextStoreEffect | ||||
|     <> | ||||
|       <RecordShowContainerContextStoreObjectMetadataIdEffect | ||||
|         recordId={objectRecordId} | ||||
|         objectNameSingular={objectNameSingular} | ||||
|       /> | ||||
|       {!isInRightDrawer && <SetMainContextStoreComponentInstanceIdEffect />} | ||||
|       <RecordShowContainerContextStoreTargetedRecordsEffect | ||||
|         recordId={objectRecordId} | ||||
|       /> | ||||
|       {!isInRightDrawer && <MainContextStoreComponentInstanceIdSetterEffect />} | ||||
|       {recordFromStore && recordFromStore.deletedAt && ( | ||||
|         <InformationBannerDeletedRecord | ||||
|           recordId={objectRecordId} | ||||
| @@ -69,6 +69,6 @@ export const RecordShowContainer = ({ | ||||
|           isNewRightDrawerItemLoading={isNewRightDrawerItemLoading} | ||||
|         /> | ||||
|       </ShowPageContainer> | ||||
|     </ContextStoreComponentInstanceContext.Provider> | ||||
|     </> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
| @@ -0,0 +1,30 @@ | ||||
| import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; | ||||
| import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; | ||||
| import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; | ||||
| import { useEffect } from 'react'; | ||||
|  | ||||
| export const RecordShowContainerContextStoreObjectMetadataIdEffect = ({ | ||||
|   recordId, | ||||
|   objectNameSingular, | ||||
| }: { | ||||
|   recordId: string; | ||||
|   objectNameSingular: string; | ||||
| }) => { | ||||
|   const setContextStoreCurrentObjectMetadataId = useSetRecoilComponentStateV2( | ||||
|     contextStoreCurrentObjectMetadataIdComponentState, | ||||
|   ); | ||||
|  | ||||
|   const { objectMetadataItem } = useObjectMetadataItem({ | ||||
|     objectNameSingular: objectNameSingular, | ||||
|   }); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     setContextStoreCurrentObjectMetadataId(objectMetadataItem?.id); | ||||
|  | ||||
|     return () => { | ||||
|       setContextStoreCurrentObjectMetadataId(null); | ||||
|     }; | ||||
|   }, [recordId, setContextStoreCurrentObjectMetadataId, objectMetadataItem.id]); | ||||
|  | ||||
|   return null; | ||||
| }; | ||||
| @@ -1,29 +1,17 @@ | ||||
| import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; | ||||
| import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; | ||||
| import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; | ||||
| import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; | ||||
| import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; | ||||
| import { useEffect } from 'react'; | ||||
| 
 | ||||
| export const RecordShowContainerContextStoreEffect = ({ | ||||
| export const RecordShowContainerContextStoreTargetedRecordsEffect = ({ | ||||
|   recordId, | ||||
|   objectNameSingular, | ||||
| }: { | ||||
|   recordId: string; | ||||
|   objectNameSingular: string; | ||||
| }) => { | ||||
|   const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2( | ||||
|     contextStoreTargetedRecordsRuleComponentState, | ||||
|   ); | ||||
| 
 | ||||
|   const setContextStoreCurrentObjectMetadataId = useSetRecoilComponentStateV2( | ||||
|     contextStoreCurrentObjectMetadataIdComponentState, | ||||
|   ); | ||||
| 
 | ||||
|   const { objectMetadataItem } = useObjectMetadataItem({ | ||||
|     objectNameSingular: objectNameSingular, | ||||
|   }); | ||||
| 
 | ||||
|   const setContextStoreNumberOfSelectedRecords = useSetRecoilComponentStateV2( | ||||
|     contextStoreNumberOfSelectedRecordsComponentState, | ||||
|   ); | ||||
| @@ -33,7 +21,6 @@ export const RecordShowContainerContextStoreEffect = ({ | ||||
|       mode: 'selection', | ||||
|       selectedRecordIds: [recordId], | ||||
|     }); | ||||
|     setContextStoreCurrentObjectMetadataId(objectMetadataItem?.id); | ||||
|     setContextStoreNumberOfSelectedRecords(1); | ||||
| 
 | ||||
|     return () => { | ||||
| @@ -41,14 +28,11 @@ export const RecordShowContainerContextStoreEffect = ({ | ||||
|         mode: 'selection', | ||||
|         selectedRecordIds: [], | ||||
|       }); | ||||
|       setContextStoreCurrentObjectMetadataId(null); | ||||
|       setContextStoreNumberOfSelectedRecords(0); | ||||
|     }; | ||||
|   }, [ | ||||
|     recordId, | ||||
|     setContextStoreTargetedRecordsRule, | ||||
|     setContextStoreCurrentObjectMetadataId, | ||||
|     objectMetadataItem?.id, | ||||
|     setContextStoreNumberOfSelectedRecords, | ||||
|   ]); | ||||
| 
 | ||||
| @@ -27,7 +27,7 @@ export const RecordTableInternalEffect = ({ | ||||
|  | ||||
|   useListenClickOutsideByClassName({ | ||||
|     classNames: ['entity-table-cell'], | ||||
|     excludeClassNames: ['bottom-bar', 'action-menu-dropdown'], | ||||
|     excludeClassNames: ['bottom-bar', 'action-menu-dropdown', 'command-menu'], | ||||
|     callback: () => { | ||||
|       leaveTableFocus(); | ||||
|     }, | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| import { useRecoilCallback } from 'recoil'; | ||||
|  | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState'; | ||||
| import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; | ||||
| import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState'; | ||||
| import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; | ||||
| import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; | ||||
| import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; | ||||
| import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; | ||||
| import { extractComponentFamilyState } from '@/ui/utilities/state/component-state/utils/extractComponentFamilyState'; | ||||
| import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; | ||||
|  | ||||
| @@ -14,6 +16,10 @@ export const useTriggerActionMenuDropdown = ({ | ||||
| }: { | ||||
|   recordTableId: string; | ||||
| }) => { | ||||
|   const actionMenuInstanceId = useAvailableComponentInstanceIdOrThrow( | ||||
|     ActionMenuComponentInstanceContext, | ||||
|   ); | ||||
|  | ||||
|   const triggerActionMenuDropdown = useRecoilCallback( | ||||
|     ({ set, snapshot }) => | ||||
|       (event: React.MouseEvent, recordId: string) => { | ||||
| @@ -24,7 +30,7 @@ export const useTriggerActionMenuDropdown = ({ | ||||
|         set( | ||||
|           extractComponentState( | ||||
|             recordIndexActionMenuDropdownPositionComponentState, | ||||
|             `action-menu-dropdown-${recordTableId}`, | ||||
|             `action-menu-dropdown-${actionMenuInstanceId}`, | ||||
|           ), | ||||
|           { | ||||
|             x: event.clientX, | ||||
| @@ -48,19 +54,19 @@ export const useTriggerActionMenuDropdown = ({ | ||||
|  | ||||
|         const isActionMenuDropdownOpenState = extractComponentState( | ||||
|           isDropdownOpenComponentState, | ||||
|           `action-menu-dropdown-${recordTableId}`, | ||||
|           `action-menu-dropdown-${actionMenuInstanceId}`, | ||||
|         ); | ||||
|  | ||||
|         const isActionBarOpenState = isBottomBarOpenedComponentState.atomFamily( | ||||
|           { | ||||
|             instanceId: `action-bar-${recordTableId}`, | ||||
|             instanceId: `action-bar-${actionMenuInstanceId}`, | ||||
|           }, | ||||
|         ); | ||||
|  | ||||
|         set(isActionBarOpenState, false); | ||||
|         set(isActionMenuDropdownOpenState, true); | ||||
|       }, | ||||
|     [recordTableId], | ||||
|     [actionMenuInstanceId, recordTableId], | ||||
|   ); | ||||
|  | ||||
|   return { triggerActionMenuDropdown }; | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| import styled from '@emotion/styled'; | ||||
|  | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; | ||||
| import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers'; | ||||
| import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/components/SignInBackgroundMockContainerEffect'; | ||||
| import { ViewBar } from '@/views/components/ViewBar'; | ||||
| @@ -21,6 +23,14 @@ export const SignInBackgroundMockContainer = () => { | ||||
|   return ( | ||||
|     <StyledContainer> | ||||
|       <ViewComponentInstanceContext.Provider value={{ instanceId: viewBarId }}> | ||||
|         <ContextStoreComponentInstanceContext.Provider | ||||
|           value={{ | ||||
|             instanceId: recordIndexId, | ||||
|           }} | ||||
|         > | ||||
|           <ActionMenuComponentInstanceContext.Provider | ||||
|             value={{ instanceId: recordIndexId }} | ||||
|           > | ||||
|             <ViewBar | ||||
|               viewBarId={viewBarId} | ||||
|               onCurrentViewChange={async () => {}} | ||||
| @@ -37,6 +47,8 @@ export const SignInBackgroundMockContainer = () => { | ||||
|               viewBarId={viewBarId} | ||||
|               updateRecordMutation={() => {}} | ||||
|             /> | ||||
|           </ActionMenuComponentInstanceContext.Provider> | ||||
|         </ContextStoreComponentInstanceContext.Provider> | ||||
|       </ViewComponentInstanceContext.Provider> | ||||
|     </StyledContainer> | ||||
|   ); | ||||
|   | ||||
| @@ -219,6 +219,7 @@ export const Modal = ({ | ||||
|  | ||||
|   return ( | ||||
|     <StyledBackDrop | ||||
|       className="modal-backdrop" | ||||
|       onMouseDown={stopEventPropagation} | ||||
|       modalVariant={modalVariant} | ||||
|     > | ||||
|   | ||||
| @@ -47,6 +47,10 @@ const StyledContainer = styled(motion.div)<{ isRightDrawerMinimized: boolean }>` | ||||
|   right: 0; | ||||
|   top: 0; | ||||
|   z-index: 100; | ||||
|  | ||||
|   .modal-backdrop { | ||||
|     background: ${({ theme }) => theme.background.overlayTertiary}; | ||||
|   } | ||||
| `; | ||||
|  | ||||
| const StyledRightDrawer = styled.div` | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { RecordShowActionMenu } from '@/action-menu/components/RecordShowActionMenu'; | ||||
| import { RecordShowRightDrawerActionMenu } from '@/action-menu/components/RecordShowRightDrawerActionMenu'; | ||||
| import { Calendar } from '@/activities/calendar/components/Calendar'; | ||||
| import { EmailThreads } from '@/activities/emails/components/EmailThreads'; | ||||
| import { Attachments } from '@/activities/files/components/Attachments'; | ||||
| @@ -221,7 +221,7 @@ export const ShowPageSubContainer = ({ | ||||
|         </StyledContentContainer> | ||||
|         {isInRightDrawer && recordFromStore && !recordFromStore.deletedAt && ( | ||||
|           <StyledButtonContainer> | ||||
|             <RecordShowActionMenu actionMenuId={'right-drawer-action-menu'} /> | ||||
|             <RecordShowRightDrawerActionMenu /> | ||||
|           </StyledButtonContainer> | ||||
|         )} | ||||
|       </StyledShowPageRightContainer> | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| import styled from '@emotion/styled'; | ||||
| import { useParams } from 'react-router-dom'; | ||||
|  | ||||
| import { SetMainContextStoreComponentInstanceIdEffect } from '@/context-store/components/SetMainContextStoreComponentInstanceIdEffect'; | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { MainContextStoreComponentInstanceIdSetterEffect } from '@/context-store/components/MainContextStoreComponentInstanceIdSetterEffect'; | ||||
| import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; | ||||
| import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; | ||||
| import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; | ||||
| @@ -81,13 +82,19 @@ export const RecordIndexPage = () => { | ||||
|             <StyledIndexContainer> | ||||
|               <ContextStoreComponentInstanceContext.Provider | ||||
|                 value={{ | ||||
|                   instanceId: 'record-index', | ||||
|                   instanceId: `record-index-${objectMetadataItem.id}`, | ||||
|                 }} | ||||
|               > | ||||
|                 <ActionMenuComponentInstanceContext.Provider | ||||
|                   value={{ | ||||
|                     instanceId: `record-index-${objectMetadataItem.id}`, | ||||
|                   }} | ||||
|                 > | ||||
|                   <RecordIndexContainerContextStoreObjectMetadataEffect /> | ||||
|                   <RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect /> | ||||
|                 <SetMainContextStoreComponentInstanceIdEffect /> | ||||
|                   <MainContextStoreComponentInstanceIdSetterEffect /> | ||||
|                   <RecordIndexContainer /> | ||||
|                 </ActionMenuComponentInstanceContext.Provider> | ||||
|               </ContextStoreComponentInstanceContext.Provider> | ||||
|             </StyledIndexContainer> | ||||
|           </PageBody> | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| import { useParams } from 'react-router-dom'; | ||||
|  | ||||
| import { RecordShowActionMenu } from '@/action-menu/components/RecordShowActionMenu'; | ||||
| import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; | ||||
| import { TimelineActivityContext } from '@/activities/timeline-activities/contexts/TimelineActivityContext'; | ||||
| import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; | ||||
| import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; | ||||
| import { RecordShowContainer } from '@/object-record/record-show/components/RecordShowContainer'; | ||||
| import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordShowPage'; | ||||
| @@ -11,7 +14,6 @@ import { PageContainer } from '@/ui/layout/page/components/PageContainer'; | ||||
| import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle'; | ||||
| import { RecordShowPageWorkflowHeader } from '@/workflow/components/RecordShowPageWorkflowHeader'; | ||||
| import { RecordShowPageWorkflowVersionHeader } from '@/workflow/components/RecordShowPageWorkflowVersionHeader'; | ||||
| import { RecordShowPageBaseHeader } from '~/pages/object-record/RecordShowPageBaseHeader'; | ||||
| import { RecordShowPageHeader } from '~/pages/object-record/RecordShowPageHeader'; | ||||
|  | ||||
| export const RecordShowPage = () => { | ||||
| @@ -38,6 +40,14 @@ export const RecordShowPage = () => { | ||||
|  | ||||
|   return ( | ||||
|     <RecordFieldValueSelectorContextProvider> | ||||
|       <ContextStoreComponentInstanceContext.Provider | ||||
|         value={{ | ||||
|           instanceId: `record-show-${objectRecordId}`, | ||||
|         }} | ||||
|       > | ||||
|         <ActionMenuComponentInstanceContext.Provider | ||||
|           value={{ instanceId: `record-show-${objectRecordId}` }} | ||||
|         > | ||||
|           <RecordValueSetterEffect recordId={objectRecordId} /> | ||||
|           <PageContainer> | ||||
|             <PageTitle title={pageTitle} /> | ||||
| @@ -55,7 +65,8 @@ export const RecordShowPage = () => { | ||||
|                     workflowVersionId={objectRecordId} | ||||
|                   /> | ||||
|                 ) : ( | ||||
|               <RecordShowPageBaseHeader | ||||
|                   <> | ||||
|                     <RecordShowActionMenu | ||||
|                       {...{ | ||||
|                         isFavorite, | ||||
|                         handleFavoriteButtonClick, | ||||
| @@ -64,6 +75,7 @@ export const RecordShowPage = () => { | ||||
|                         objectNameSingular, | ||||
|                       }} | ||||
|                     /> | ||||
|                   </> | ||||
|                 )} | ||||
|               </> | ||||
|             </RecordShowPageHeader> | ||||
| @@ -81,6 +93,8 @@ export const RecordShowPage = () => { | ||||
|               </TimelineActivityContext.Provider> | ||||
|             </PageBody> | ||||
|           </PageContainer> | ||||
|         </ActionMenuComponentInstanceContext.Provider> | ||||
|       </ContextStoreComponentInstanceContext.Provider> | ||||
|     </RecordFieldValueSelectorContextProvider> | ||||
|   ); | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Raphaël Bosi
					Raphaël Bosi