[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:
+76
-67
@@ -33,6 +33,13 @@ import {
|
||||
type PageLayoutWidget,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
const StyledCommandMenuContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledSidePanelInformationBanner = styled(SidePanelInformationBanner)`
|
||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
@@ -137,78 +144,80 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
|
||||
primaryAxisField?.type === FieldMetadataType.DATE_TIME;
|
||||
|
||||
return (
|
||||
<CommandMenuList commandGroups={[]} selectableItemIds={visibleItemIds}>
|
||||
<ChartTypeSelectionSection
|
||||
currentGraphType={currentGraphType}
|
||||
setCurrentGraphType={handleGraphTypeChange}
|
||||
/>
|
||||
{hasWidgetTooManyGroups && (
|
||||
<StyledSidePanelInformationBanner
|
||||
message={
|
||||
currentGraphType === GraphType.LINE
|
||||
? t`Undisplayed data: max ${LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS} data points per chart.`
|
||||
: t`Undisplayed data: max ${BAR_CHART_MAXIMUM_NUMBER_OF_BARS} bars per chart.`
|
||||
}
|
||||
tooltipMessage={
|
||||
isPrimaryAxisDate
|
||||
? t`Consider adding a filter or changing the date granularity to display more data.`
|
||||
: t`Consider adding a filter to display more data.`
|
||||
}
|
||||
variant="warning"
|
||||
<StyledCommandMenuContainer>
|
||||
<CommandMenuList commandGroups={[]} selectableItemIds={visibleItemIds}>
|
||||
<ChartTypeSelectionSection
|
||||
currentGraphType={currentGraphType}
|
||||
setCurrentGraphType={handleGraphTypeChange}
|
||||
/>
|
||||
)}
|
||||
{chartSettings.map((group) => {
|
||||
const visibleItems = group.items.filter(
|
||||
(item) =>
|
||||
!shouldHideChartSetting(
|
||||
item,
|
||||
widget.objectMetadataId,
|
||||
isGroupByEnabled as boolean,
|
||||
configuration,
|
||||
objectMetadataItem,
|
||||
),
|
||||
);
|
||||
{hasWidgetTooManyGroups && (
|
||||
<StyledSidePanelInformationBanner
|
||||
message={
|
||||
currentGraphType === GraphType.LINE
|
||||
? t`Undisplayed data: max ${LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS} data points per chart.`
|
||||
: t`Undisplayed data: max ${BAR_CHART_MAXIMUM_NUMBER_OF_BARS} bars per chart.`
|
||||
}
|
||||
tooltipMessage={
|
||||
isPrimaryAxisDate
|
||||
? t`Consider adding a filter or changing the date granularity to display more data.`
|
||||
: t`Consider adding a filter to display more data.`
|
||||
}
|
||||
variant="warning"
|
||||
/>
|
||||
)}
|
||||
{chartSettings.map((group) => {
|
||||
const visibleItems = group.items.filter(
|
||||
(item) =>
|
||||
!shouldHideChartSetting(
|
||||
item,
|
||||
widget.objectMetadataId,
|
||||
isGroupByEnabled as boolean,
|
||||
configuration,
|
||||
objectMetadataItem,
|
||||
),
|
||||
);
|
||||
|
||||
return (
|
||||
<CommandGroup key={group.heading} heading={group.heading}>
|
||||
{visibleItems.map((item) => {
|
||||
const handleItemToggleChange = () => {
|
||||
setSelectedItemId(item.id);
|
||||
updateChartSettingToggle(item.id);
|
||||
};
|
||||
return (
|
||||
<CommandGroup key={group.heading} heading={group.heading}>
|
||||
{visibleItems.map((item) => {
|
||||
const handleItemToggleChange = () => {
|
||||
setSelectedItemId(item.id);
|
||||
updateChartSettingToggle(item.id);
|
||||
};
|
||||
|
||||
const handleItemInputChange = (value: number | null) => {
|
||||
updateChartSettingInput(item.id, value);
|
||||
};
|
||||
const handleItemInputChange = (value: number | null) => {
|
||||
updateChartSettingInput(item.id, value);
|
||||
};
|
||||
|
||||
const handleItemDropdownOpen = () => {
|
||||
openDropdown({
|
||||
dropdownComponentInstanceIdFromProps: item.id,
|
||||
});
|
||||
};
|
||||
const handleItemDropdownOpen = () => {
|
||||
openDropdown({
|
||||
dropdownComponentInstanceIdFromProps: item.id,
|
||||
});
|
||||
};
|
||||
|
||||
const handleFilterClick = () => {
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutGraphFilter,
|
||||
});
|
||||
};
|
||||
const handleFilterClick = () => {
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutGraphFilter,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ChartSettingItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
configuration={configuration}
|
||||
getChartSettingsValues={getChartSettingsValues}
|
||||
onToggleChange={handleItemToggleChange}
|
||||
onInputChange={handleItemInputChange}
|
||||
onDropdownOpen={handleItemDropdownOpen}
|
||||
onFilterClick={handleFilterClick}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
);
|
||||
})}
|
||||
</CommandMenuList>
|
||||
return (
|
||||
<ChartSettingItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
configuration={configuration}
|
||||
getChartSettingsValues={getChartSettingsValues}
|
||||
onToggleChange={handleItemToggleChange}
|
||||
onInputChange={handleItemInputChange}
|
||||
onDropdownOpen={handleItemDropdownOpen}
|
||||
onFilterClick={handleFilterClick}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
);
|
||||
})}
|
||||
</CommandMenuList>
|
||||
</StyledCommandMenuContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+16
-5
@@ -1,11 +1,19 @@
|
||||
import { ChartSettings } from '@/command-menu/pages/page-layout/components/ChartSettings';
|
||||
import { WidgetSettingsFooter } from '@/command-menu/pages/page-layout/components/WidgetSettingsFooter';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { GraphWidgetComponentInstanceContext } from '@/page-layout/widgets/graph/states/contexts/GraphWidgetComponentInstanceContext';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import styled from '@emotion/styled';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export const CommandMenuPageLayoutGraphTypeSelect = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
|
||||
@@ -41,10 +49,13 @@ export const CommandMenuPageLayoutGraphTypeSelect = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<GraphWidgetComponentInstanceContext.Provider
|
||||
value={{ instanceId: widgetInEditMode.id }}
|
||||
>
|
||||
<ChartSettings widget={widgetInEditMode} />
|
||||
</GraphWidgetComponentInstanceContext.Provider>
|
||||
<StyledContainer>
|
||||
<GraphWidgetComponentInstanceContext.Provider
|
||||
value={{ instanceId: widgetInEditMode.id }}
|
||||
>
|
||||
<ChartSettings widget={widgetInEditMode} />
|
||||
<WidgetSettingsFooter />
|
||||
</GraphWidgetComponentInstanceContext.Provider>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { useDuplicatePageLayoutWidget } from '@/page-layout/hooks/useDuplicatePageLayoutWidget';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { OptionsDropdownMenu } from '@/ui/layout/dropdown/components/OptionsDropdownMenu';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDrawerFooter';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useId } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconCopyPlus } from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
export const WidgetSettingsFooter = () => {
|
||||
const dropdownId = useId();
|
||||
const { t } = useLingui();
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { duplicateWidget } = useDuplicatePageLayoutWidget(pageLayoutId);
|
||||
|
||||
const editingWidgetId = useRecoilComponentValue(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const handleDuplicateWidget = () => {
|
||||
if (isDefined(editingWidgetId)) {
|
||||
duplicateWidget(editingWidgetId);
|
||||
}
|
||||
closeDropdown(dropdownId);
|
||||
};
|
||||
|
||||
const selectedItemId = useRecoilComponentValue(
|
||||
selectedItemIdComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
return (
|
||||
<RightDrawerFooter
|
||||
actions={[
|
||||
<OptionsDropdownMenu
|
||||
key="options"
|
||||
dropdownId={dropdownId}
|
||||
selectableListId={dropdownId}
|
||||
selectableItemIdArray={['duplicate-widget']}
|
||||
>
|
||||
<SelectableListItem
|
||||
itemId="duplicate-widget"
|
||||
onEnter={handleDuplicateWidget}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === 'duplicate-widget'}
|
||||
onClick={handleDuplicateWidget}
|
||||
text={t`Duplicate widget`}
|
||||
LeftIcon={IconCopyPlus}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</OptionsDropdownMenu>,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user