[DASHBOARDS] Widget duplication (#15979)

## Widget duplication

- Created a shared component for the option menu shared by the record
page, the dashboards and the workflows
- Fix arrow selection in the option menu in workflows, which wasn't
working
- Scroll to the new widget after creation and open the new widget
settings


https://github.com/user-attachments/assets/47b8dade-44bd-4ba2-a81c-01b09e8718d3
This commit is contained in:
Raphaël Bosi
2025-11-24 14:38:18 +01:00
committed by GitHub
parent 8923d2fa0a
commit 2b80d9e015
18 changed files with 526 additions and 217 deletions
@@ -1,5 +1,6 @@
import { Action } from '@/action-menu/actions/components/Action';
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
@@ -12,6 +13,8 @@ export const CancelDashboardSingleRecordAction = () => {
const pageLayoutId = selectedRecord?.pageLayoutId;
const { closeCommandMenu } = useCommandMenu();
const { setIsPageLayoutInEditMode } =
useSetIsPageLayoutInEditMode(pageLayoutId);
@@ -19,6 +22,8 @@ export const CancelDashboardSingleRecordAction = () => {
useResetDraftPageLayoutToPersistedPageLayout(pageLayoutId);
const handleClick = () => {
closeCommandMenu();
resetDraftPageLayoutToPersistedPageLayout();
setIsPageLayoutInEditMode(false);
};
@@ -1,5 +1,6 @@
import { Action } from '@/action-menu/actions/components/Action';
import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useSavePageLayout } from '@/page-layout/hooks/useSavePageLayout';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
@@ -17,8 +18,11 @@ export const SaveDashboardSingleRecordAction = () => {
const { setIsPageLayoutInEditMode } =
useSetIsPageLayoutInEditMode(pageLayoutId);
const { closeCommandMenu } = useCommandMenu();
const handleClick = async () => {
await savePageLayout();
closeCommandMenu();
setIsPageLayoutInEditMode(false);
};
@@ -3,20 +3,9 @@ import { ActionScope } from '@/action-menu/actions/types/ActionScope';
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
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 { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { OptionsDropdownMenu } from '@/ui/layout/dropdown/components/OptionsDropdownMenu';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { Button } from 'twenty-ui/input';
import { getOsControlSymbol } from 'twenty-ui/utilities';
export const CommandMenuActionMenuDropdown = () => {
const { actions } = useContext(ActionMenuContext);
@@ -25,31 +14,8 @@ export const CommandMenuActionMenuDropdown = () => {
ActionMenuComponentInstanceContext,
);
const theme = useTheme();
const dropdownId =
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId);
const { toggleDropdown } = useToggleDropdown();
const hotkeysConfig = {
keys: ['ctrl+o', 'meta+o'],
callback: () => {
toggleDropdown({
dropdownComponentInstanceIdFromProps: dropdownId,
});
},
dependencies: [toggleDropdown],
};
useHotkeysOnFocusedElement({
...hotkeysConfig,
focusId: SIDE_PANEL_FOCUS_ID,
});
useHotkeysOnFocusedElement({
...hotkeysConfig,
focusId: dropdownId,
});
const recordSelectionActions = actions.filter(
(action) => action.scope === ActionScope.RecordSelection,
@@ -59,43 +25,15 @@ export const CommandMenuActionMenuDropdown = () => {
(action) => action.key,
);
const { setSelectedItemId } = useSelectableList(actionMenuId);
return (
<Dropdown
<OptionsDropdownMenu
dropdownId={dropdownId}
data-select-disable
clickableComponent={
<Button
size="small"
title={t`Options`}
hotkeys={[getOsControlSymbol(), 'O']}
/>
}
dropdownPlacement="top-end"
dropdownOffset={{ y: parseInt(theme.spacing(2), 10) }}
globalHotkeysConfig={{
enableGlobalHotkeysWithModifiers: true,
enableGlobalHotkeysConflictingWithKeyboard: false,
}}
onOpen={() => {
setSelectedItemId(selectableItemIdArray[0]);
}}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<SelectableList
selectableListInstanceId={actionMenuId}
focusId={dropdownId}
selectableItemIdArray={selectableItemIdArray}
>
{recordSelectionActions.map((action) => (
<ActionComponent action={action} key={action.key} />
))}
</SelectableList>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
selectableListId={actionMenuId}
selectableItemIdArray={selectableItemIdArray}
>
{recordSelectionActions.map((action) => (
<ActionComponent action={action} key={action.key} />
))}
</OptionsDropdownMenu>
);
};