Field widget edition (#19209)
https://github.com/user-attachments/assets/2f1a5847-3375-414f-a2b8-ce4b533b5512 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
136f362b24
commit
20ac5b7e84
+7
-2
@@ -52,11 +52,15 @@ export const useCreateRecordPageFieldWidget = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const availableRelationField = boxedRelationFieldMetadataItems.find(
|
||||
const unusedRelationField = boxedRelationFieldMetadataItems.find(
|
||||
(field) => !usedFieldMetadataIds.has(field.id),
|
||||
);
|
||||
|
||||
const fieldMetadataId = availableRelationField?.id ?? '';
|
||||
const selectedField =
|
||||
unusedRelationField ?? boxedRelationFieldMetadataItems[0];
|
||||
|
||||
const fieldMetadataId = selectedField?.id ?? '';
|
||||
const title = selectedField?.label ?? '';
|
||||
|
||||
const positionIndex = existingWidgets.length;
|
||||
const widgetId = uuidv4();
|
||||
@@ -64,6 +68,7 @@ export const useCreateRecordPageFieldWidget = () => {
|
||||
const newWidget = createDefaultFieldWidget({
|
||||
id: widgetId,
|
||||
pageLayoutTabId: tabId,
|
||||
title,
|
||||
fieldMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
positionIndex,
|
||||
|
||||
@@ -66,6 +66,16 @@ export const useEditPageLayoutWidget = (pageLayoutIdFromProps?: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widgetType === WidgetType.FIELD) {
|
||||
navigatePageLayoutSidePanel({
|
||||
sidePanelPage: SidePanelPages.PageLayoutFieldSettings,
|
||||
pageTitle: t`Field widget`,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
setPageLayoutEditingWidgetId(widgetId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (widgetType === WidgetType.RECORD_TABLE) {
|
||||
navigatePageLayoutSidePanel({
|
||||
sidePanelPage: SidePanelPages.PageLayoutRecordTableSettings,
|
||||
|
||||
@@ -9,12 +9,14 @@ import {
|
||||
export const createDefaultFieldWidget = ({
|
||||
id,
|
||||
pageLayoutTabId,
|
||||
title,
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
positionIndex,
|
||||
}: {
|
||||
id: string;
|
||||
pageLayoutTabId: string;
|
||||
title: string;
|
||||
fieldMetadataId: string;
|
||||
objectMetadataId: string;
|
||||
positionIndex: number;
|
||||
@@ -23,7 +25,7 @@ export const createDefaultFieldWidget = ({
|
||||
__typename: 'PageLayoutWidget',
|
||||
id,
|
||||
pageLayoutTabId,
|
||||
title: '',
|
||||
title,
|
||||
type: WidgetType.FIELD,
|
||||
configuration: {
|
||||
__typename: 'FieldConfiguration',
|
||||
|
||||
@@ -106,7 +106,8 @@ export const WidgetRenderer = ({ widget }: WidgetRendererProps) => {
|
||||
isPageLayoutInEditMode &&
|
||||
(!isRecordPageLayout ||
|
||||
(isRecordPageLayout && isRecordPageGlobalEditionEnabled) ||
|
||||
widget.type === WidgetType.FIELDS);
|
||||
widget.type === WidgetType.FIELDS ||
|
||||
widget.type === WidgetType.FIELD);
|
||||
|
||||
// TODO: when we have more widgets without headers, we should use a more generic approach to hide the header
|
||||
// each widget type could have metadata (e.g., hasHeader: boolean or headerMode: 'always' | 'editOnly' | 'never')
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
OverflowingTextWithTooltip,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
import { NavigationMenuItemType, SidePanelPages } from 'twenty-shared/types';
|
||||
import { useNavigationMenuItemSectionItems } from '@/navigation-menu-item/display/hooks/useNavigationMenuItemSectionItems';
|
||||
import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState';
|
||||
import { useNavigationMenuItemSectionItems } from '@/navigation-menu-item/display/hooks/useNavigationMenuItemSectionItems';
|
||||
import { SidePanelAskAIInfo } from '@/side-panel/components/SidePanelAskAIInfo';
|
||||
import { SidePanelFolderInfo } from '@/side-panel/components/SidePanelFolderInfo';
|
||||
import { SidePanelLinkInfo } from '@/side-panel/components/SidePanelLinkInfo';
|
||||
@@ -18,6 +17,7 @@ import { SidePanelPageLayoutInfo } from '@/side-panel/components/SidePanelPageLa
|
||||
import { SidePanelRecordInfo } from '@/side-panel/components/SidePanelRecordInfo';
|
||||
import { SidePanelWorkflowStepInfo } from '@/side-panel/components/SidePanelWorkflowStepInfo';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { NavigationMenuItemType, SidePanelPages } from 'twenty-shared/types';
|
||||
|
||||
import { type SidePanelContextChipProps } from '@/side-panel/components/SidePanelContextChip';
|
||||
import { useContext } from 'react';
|
||||
@@ -101,6 +101,7 @@ export const SidePanelPageInfo = ({ pageChip }: SidePanelPageInfoProps) => {
|
||||
SidePanelPages.PageLayoutIframeSettings,
|
||||
SidePanelPages.PageLayoutTabSettings,
|
||||
SidePanelPages.PageLayoutFieldsSettings,
|
||||
SidePanelPages.PageLayoutFieldSettings,
|
||||
SidePanelPages.PageLayoutRecordTableSettings,
|
||||
].includes(pageChip.page?.page)
|
||||
: false;
|
||||
|
||||
+30
@@ -175,6 +175,36 @@ export const usePageLayoutHeaderInfo = ({
|
||||
};
|
||||
}
|
||||
|
||||
case SidePanelPages.PageLayoutFieldSettings: {
|
||||
if (!isDefined(pageLayoutEditingWidgetId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const widgetInEditMode = draftPageLayout.tabs
|
||||
.flatMap((tab) => tab.widgets)
|
||||
.find((widget) => widget.id === pageLayoutEditingWidgetId);
|
||||
|
||||
if (!isDefined(widgetInEditMode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const title = isDefined(editedTitle)
|
||||
? editedTitle
|
||||
: isDefined(widgetInEditMode.title) && widgetInEditMode.title !== ''
|
||||
? widgetInEditMode.title
|
||||
: '';
|
||||
|
||||
return {
|
||||
headerIcon: IconList,
|
||||
headerIconColor: iconColor,
|
||||
headerType: t`Field Widget`,
|
||||
title,
|
||||
isReadonly: false,
|
||||
tab: undefined,
|
||||
widgetInEditMode,
|
||||
};
|
||||
}
|
||||
|
||||
case SidePanelPages.PageLayoutRecordTableSettings: {
|
||||
if (!isDefined(pageLayoutEditingWidgetId)) {
|
||||
return null;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { SidePanelCommandMenuItemEditPage } from '@/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage';
|
||||
import { SidePanelCommandMenuItemDisplayPage } from '@/command-menu-item/server-items/display/components/SidePanelCommandMenuItemDisplayPage';
|
||||
import { SidePanelCommandMenuItemEditPage } from '@/command-menu-item/server-items/edit/components/SidePanelCommandMenuItemEditPage';
|
||||
import { SidePanelNavigationMenuItemEditPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNavigationMenuItemEditPage';
|
||||
import { SidePanelNewSidebarItemPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemPage';
|
||||
import { SidePanelAIChatThreadsPage } from '@/side-panel/pages/ai-chat-threads/components/SidePanelAIChatThreadsPage';
|
||||
import { SidePanelAskAIPage } from '@/side-panel/pages/ask-ai/components/SidePanelAskAIPage';
|
||||
import { SidePanelCalendarEventPage } from '@/side-panel/pages/calendar-event/components/SidePanelCalendarEventPage';
|
||||
import { SidePanelFrontComponentPage } from '@/side-panel/pages/front-component/components/SidePanelFrontComponentPage';
|
||||
import { SidePanelMessageThreadPage } from '@/side-panel/pages/message-thread/components/SidePanelMessageThreadPage';
|
||||
import { SidePanelNavigationMenuItemEditPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNavigationMenuItemEditPage';
|
||||
import { SidePanelNewSidebarItemPage } from '@/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemPage';
|
||||
import { SidePanelPageLayoutChartSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutChartSettings';
|
||||
import { SidePanelPageLayoutFieldSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutFieldSettings';
|
||||
import { SidePanelPageLayoutFieldsSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutFieldsSettings';
|
||||
import { SidePanelPageLayoutRecordTableSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutRecordTableSettings';
|
||||
import { SidePanelPageLayoutIframeSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutIframeSettings';
|
||||
import { SidePanelPageLayoutRecordTableSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutRecordTableSettings';
|
||||
import { SidePanelPageLayoutTabSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutTabSettings';
|
||||
import { SidePanelPageLayoutWidgetTypeSelect } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutWidgetTypeSelect';
|
||||
import { SidePanelMergeRecordPage } from '@/side-panel/pages/record-page/components/SidePanelMergeRecordPage';
|
||||
@@ -82,6 +83,10 @@ export const SIDE_PANEL_PAGES_CONFIG = new Map<SidePanelPages, React.ReactNode>(
|
||||
SidePanelPages.PageLayoutFieldsSettings,
|
||||
<SidePanelPageLayoutFieldsSettings />,
|
||||
],
|
||||
[
|
||||
SidePanelPages.PageLayoutFieldSettings,
|
||||
<SidePanelPageLayoutFieldSettings />,
|
||||
],
|
||||
[
|
||||
SidePanelPages.PageLayoutRecordTableSettings,
|
||||
<SidePanelPageLayoutRecordTableSettings />,
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown';
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
|
||||
import { SidePanelList } from '@/side-panel/components/SidePanelList';
|
||||
import { FieldWidgetFieldDropdownContent } from '@/side-panel/pages/page-layout/components/dropdown-content/FieldWidgetFieldDropdownContent';
|
||||
import { FieldWidgetLayoutDropdownContent } from '@/side-panel/pages/page-layout/components/dropdown-content/FieldWidgetLayoutDropdownContent';
|
||||
import { WidgetSettingsFooter } from '@/side-panel/pages/page-layout/components/WidgetSettingsFooter';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconLayoutSidebarRight, IconListDetails } from 'twenty-ui/display';
|
||||
import {
|
||||
FieldDisplayMode,
|
||||
type FieldConfiguration,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const StyledSidePanelContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const SidePanelPageLayoutFieldSettings = () => {
|
||||
const { t } = useLingui();
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const fieldConfiguration = widgetInEditMode?.configuration as
|
||||
| FieldConfiguration
|
||||
| undefined;
|
||||
|
||||
const currentFieldMetadataId = fieldConfiguration?.fieldMetadataId;
|
||||
const currentDisplayMode = fieldConfiguration?.fieldDisplayMode;
|
||||
|
||||
const { fieldMetadataItem: currentFieldMetadataItem } =
|
||||
useFieldMetadataItemById(currentFieldMetadataId ?? '');
|
||||
|
||||
if (!isDefined(widgetInEditMode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fieldLabel = currentFieldMetadataItem?.label ?? '';
|
||||
|
||||
const displayModeLabels: Record<string, string> = {
|
||||
[FieldDisplayMode.FIELD]: t`Field`,
|
||||
[FieldDisplayMode.CARD]: t`Card`,
|
||||
};
|
||||
|
||||
const layoutLabel = isDefined(currentDisplayMode)
|
||||
? (displayModeLabels[currentDisplayMode] ?? '')
|
||||
: '';
|
||||
|
||||
const selectableItemIds = ['field', 'layout'];
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledSidePanelContainer>
|
||||
<SidePanelList commandGroups={[]} selectableItemIds={selectableItemIds}>
|
||||
<SidePanelGroup heading={t`Data and display`}>
|
||||
<SelectableListItem itemId="field">
|
||||
<CommandMenuItemDropdown
|
||||
id="field"
|
||||
label={t`Field`}
|
||||
Icon={IconListDetails}
|
||||
dropdownId="field"
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<FieldWidgetFieldDropdownContent />
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownPlacement="bottom-end"
|
||||
description={fieldLabel}
|
||||
contextualTextPosition="right"
|
||||
/>
|
||||
</SelectableListItem>
|
||||
<SelectableListItem itemId="layout">
|
||||
<CommandMenuItemDropdown
|
||||
id="layout"
|
||||
label={t`Layout`}
|
||||
Icon={IconLayoutSidebarRight}
|
||||
dropdownId="layout"
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<FieldWidgetLayoutDropdownContent />
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownPlacement="bottom-end"
|
||||
description={layoutLabel}
|
||||
contextualTextPosition="right"
|
||||
/>
|
||||
</SelectableListItem>
|
||||
</SidePanelGroup>
|
||||
</SidePanelList>
|
||||
</StyledSidePanelContainer>
|
||||
<WidgetSettingsFooter pageLayoutId={pageLayoutId} />
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { useFieldListFieldMetadataItems } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataItems';
|
||||
import { useUpdatePageLayoutWidget } from '@/page-layout/hooks/useUpdatePageLayoutWidget';
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import { type FieldConfiguration } from '~/generated-metadata/graphql';
|
||||
import { filterBySearchQuery } from '~/utils/filterBySearchQuery';
|
||||
|
||||
export const FieldWidgetFieldDropdownContent = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const { pageLayoutId, objectNameSingular } =
|
||||
usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const fieldConfiguration = widgetInEditMode?.configuration as
|
||||
| FieldConfiguration
|
||||
| undefined;
|
||||
|
||||
const currentFieldMetadataId = fieldConfiguration?.fieldMetadataId;
|
||||
|
||||
const { boxedRelationFieldMetadataItems } = useFieldListFieldMetadataItems({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const dropdownId = useAvailableComponentInstanceIdOrThrow(
|
||||
DropdownComponentInstanceContext,
|
||||
);
|
||||
|
||||
const selectedItemId = useAtomComponentStateValue(
|
||||
selectedItemIdComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
const { updatePageLayoutWidget } = useUpdatePageLayoutWidget(pageLayoutId);
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const availableFields = filterBySearchQuery({
|
||||
items: boxedRelationFieldMetadataItems,
|
||||
searchQuery,
|
||||
getSearchableValues: (item) => [item.label],
|
||||
});
|
||||
|
||||
const { fieldMetadataItem: currentFieldMetadataItem } =
|
||||
useFieldMetadataItemById(currentFieldMetadataId ?? '');
|
||||
|
||||
const handleSelectField = (fieldMetadataId: string) => {
|
||||
updateCurrentWidgetConfig({
|
||||
configToUpdate: {
|
||||
fieldMetadataId,
|
||||
},
|
||||
});
|
||||
|
||||
const selectedField = boxedRelationFieldMetadataItems.find(
|
||||
(field) => field.id === fieldMetadataId,
|
||||
);
|
||||
|
||||
if (widgetInEditMode && selectedField) {
|
||||
updatePageLayoutWidget(widgetInEditMode.id, {
|
||||
title: selectedField.label,
|
||||
});
|
||||
}
|
||||
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
type="text"
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event) => setSearchQuery(event.target.value)}
|
||||
value={searchQuery}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
selectableListInstanceId={dropdownId}
|
||||
focusId={dropdownId}
|
||||
selectableItemIdArray={availableFields.map((field) => field.id)}
|
||||
>
|
||||
{availableFields.map((fieldMetadataItem) => (
|
||||
<SelectableListItem
|
||||
key={fieldMetadataItem.id}
|
||||
itemId={fieldMetadataItem.id}
|
||||
onEnter={() => {
|
||||
handleSelectField(fieldMetadataItem.id);
|
||||
}}
|
||||
>
|
||||
<MenuItemSelect
|
||||
text={fieldMetadataItem.label}
|
||||
selected={currentFieldMetadataId === fieldMetadataItem.id}
|
||||
focused={selectedItemId === fieldMetadataItem.id}
|
||||
LeftIcon={getIcon(
|
||||
currentFieldMetadataId === fieldMetadataItem.id
|
||||
? currentFieldMetadataItem?.icon
|
||||
: fieldMetadataItem.icon,
|
||||
)}
|
||||
onClick={() => {
|
||||
handleSelectField(fieldMetadataItem.id);
|
||||
}}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
import { usePageLayoutIdFromContextStore } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdFromContextStore';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconLayoutKanban, IconListDetails } from 'twenty-ui/display';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import {
|
||||
FieldDisplayMode,
|
||||
type FieldConfiguration,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const LAYOUT_OPTIONS = [
|
||||
{
|
||||
id: FieldDisplayMode.FIELD,
|
||||
Icon: IconListDetails,
|
||||
},
|
||||
{
|
||||
id: FieldDisplayMode.CARD,
|
||||
Icon: IconLayoutKanban,
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const FieldWidgetLayoutDropdownContent = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStore();
|
||||
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
|
||||
const fieldConfiguration = widgetInEditMode?.configuration as
|
||||
| FieldConfiguration
|
||||
| undefined;
|
||||
|
||||
const currentDisplayMode = fieldConfiguration?.fieldDisplayMode;
|
||||
|
||||
const dropdownId = useAvailableComponentInstanceIdOrThrow(
|
||||
DropdownComponentInstanceContext,
|
||||
);
|
||||
|
||||
const selectedItemId = useAtomComponentStateValue(
|
||||
selectedItemIdComponentState,
|
||||
dropdownId,
|
||||
);
|
||||
|
||||
const { updateCurrentWidgetConfig } =
|
||||
useUpdateCurrentWidgetConfig(pageLayoutId);
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const handleSelectLayout = (fieldDisplayMode: FieldDisplayMode) => {
|
||||
updateCurrentWidgetConfig({
|
||||
configToUpdate: {
|
||||
fieldDisplayMode,
|
||||
},
|
||||
});
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
const layoutLabels: Record<(typeof LAYOUT_OPTIONS)[number]['id'], string> = {
|
||||
[FieldDisplayMode.FIELD]: t`Field`,
|
||||
[FieldDisplayMode.CARD]: t`Card`,
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
selectableListInstanceId={dropdownId}
|
||||
focusId={dropdownId}
|
||||
selectableItemIdArray={LAYOUT_OPTIONS.map((option) => option.id)}
|
||||
>
|
||||
{LAYOUT_OPTIONS.map((option) => (
|
||||
<SelectableListItem
|
||||
key={option.id}
|
||||
itemId={option.id}
|
||||
onEnter={() => {
|
||||
handleSelectLayout(option.id);
|
||||
}}
|
||||
>
|
||||
<MenuItemSelect
|
||||
text={layoutLabels[option.id]}
|
||||
selected={currentDisplayMode === option.id}
|
||||
focused={selectedItemId === option.id}
|
||||
LeftIcon={option.Icon}
|
||||
onClick={() => {
|
||||
handleSelectLayout(option.id);
|
||||
}}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
))}
|
||||
</SelectableList>
|
||||
</DropdownMenuItemsContainer>
|
||||
);
|
||||
};
|
||||
+1
@@ -6,4 +6,5 @@ export type PageLayoutSidePanelPage =
|
||||
| SidePanelPages.PageLayoutIframeSettings
|
||||
| SidePanelPages.PageLayoutTabSettings
|
||||
| SidePanelPages.PageLayoutFieldsSettings
|
||||
| SidePanelPages.PageLayoutFieldSettings
|
||||
| SidePanelPages.PageLayoutRecordTableSettings;
|
||||
|
||||
+2
@@ -21,6 +21,8 @@ export const getPageLayoutIcon = (page: PageLayoutSidePanelPage) => {
|
||||
return IconAppWindow;
|
||||
case SidePanelPages.PageLayoutFieldsSettings:
|
||||
return IconList;
|
||||
case SidePanelPages.PageLayoutFieldSettings:
|
||||
return IconList;
|
||||
case SidePanelPages.PageLayoutRecordTableSettings:
|
||||
return IconTable;
|
||||
default:
|
||||
|
||||
+2
@@ -15,6 +15,8 @@ export const getPageLayoutPageTitle = (page: PageLayoutSidePanelPage) => {
|
||||
return t`Tab Settings`;
|
||||
case SidePanelPages.PageLayoutFieldsSettings:
|
||||
return t`Fields Settings`;
|
||||
case SidePanelPages.PageLayoutFieldSettings:
|
||||
return t`Field widget`;
|
||||
case SidePanelPages.PageLayoutRecordTableSettings:
|
||||
return t`Record Table Settings`;
|
||||
default:
|
||||
|
||||
@@ -22,6 +22,7 @@ export enum SidePanelPages {
|
||||
PageLayoutTabSettings = 'page-layout-tab-settings',
|
||||
PageLayoutFieldsSettings = 'page-layout-fields-settings',
|
||||
PageLayoutRecordTableSettings = 'page-layout-record-table-settings',
|
||||
PageLayoutFieldSettings = 'page-layout-field-settings',
|
||||
ViewFrontComponent = 'view-front-component',
|
||||
NavigationMenuItemEdit = 'navigation-menu-item-edit',
|
||||
NavigationMenuAddItem = 'navigation-menu-add-item',
|
||||
|
||||
Reference in New Issue
Block a user