[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
@@ -0,0 +1,95 @@
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 { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { type ReactNode, useId } from 'react';
import { Button } from 'twenty-ui/input';
import { getOsControlSymbol } from 'twenty-ui/utilities';
type OptionsDropdownMenuProps = {
dropdownId?: string;
selectableListId?: string;
selectableItemIdArray?: string[];
onOpen?: () => void;
children: ReactNode;
};
export const OptionsDropdownMenu = ({
dropdownId: dropdownIdFromProps,
selectableListId,
selectableItemIdArray = [],
onOpen,
children,
}: OptionsDropdownMenuProps) => {
const generatedDropdownId = useId();
const dropdownId = dropdownIdFromProps ?? generatedDropdownId;
const { t } = useLingui();
const theme = useTheme();
const { toggleDropdown } = useToggleDropdown();
const listId = selectableListId ?? dropdownId;
const { setSelectedItemId } = useSelectableList(listId);
const handleOpen = () => {
if (selectableItemIdArray.length > 0) {
setSelectedItemId(selectableItemIdArray[0]);
}
onOpen?.();
};
const hotkeysConfig = {
keys: ['ctrl+o', 'meta+o'],
callback: () => {
toggleDropdown({
dropdownComponentInstanceIdFromProps: dropdownId,
});
},
dependencies: [toggleDropdown, dropdownId],
};
useHotkeysOnFocusedElement({
...hotkeysConfig,
focusId: SIDE_PANEL_FOCUS_ID,
});
useHotkeysOnFocusedElement({
...hotkeysConfig,
focusId: dropdownId,
});
return (
<Dropdown
dropdownId={dropdownId}
data-select-disable
clickableComponent={
<Button title={t`Options`} hotkeys={[getOsControlSymbol(), 'O']} />
}
dropdownPlacement="top-end"
dropdownOffset={{ y: parseInt(theme.spacing(2), 10) }}
globalHotkeysConfig={{
enableGlobalHotkeysWithModifiers: true,
enableGlobalHotkeysConflictingWithKeyboard: false,
}}
onOpen={handleOpen}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<SelectableList
selectableListInstanceId={listId}
focusId={dropdownId}
selectableItemIdArray={selectableItemIdArray}
>
{children}
</SelectableList>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
);
};