refactor(twenty-front): reorganize NavigationMenuItem module into common/display/edit subfolders (#18691)
## Summary - Reorganizes the `navigation-menu-item` frontend module from a flat structure into `common/`, `display/`, and `edit/` subfolders with type-specific subdirectories (`link/`, `folder/`, `object/`, `view/`, `record/`) - Every file is now in a leaf folder describing its type: `components/`, `hooks/`, `utils/`, `types/`, or `constants/` - Moves 14 NavigationMenuItem-related components out of `object-metadata/` and `side-panel/pages/` into the `navigation-menu-item` module where they belong - Creates type-specific display utility functions (e.g., `getLinkNavigationMenuItemLabel`, `getObjectNavigationMenuItemComputedLink`) to replace generic switch-based functions - Unifies the Favorites section drag-and-drop from `@hello-pangea/dnd` to `@dnd-kit/react`, matching the Workspace section's DnD library - Renames Favorites section components from `CurrentWorkspaceMember*` to `Favorites*` for clarity - Deletes unused `FavoritesDragDropProviderContent` and `NavbarDragProvider` ## Test plan - [x] `npx nx typecheck twenty-front` passes - [x] `npx nx lint:diff-with-main twenty-front` passes (0 warnings, 0 errors) - [x] `npx nx test twenty-front` passes (763 suites, 4467 tests) - [ ] Verify favorites drag-and-drop still works in the UI (reorder items, move between folders) - [ ] Verify workspace edit mode drag-and-drop still works - [ ] Verify "add to navigation" drag from command menu/side panel still works
This commit is contained in:
-174
@@ -1,174 +0,0 @@
|
||||
import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay';
|
||||
import { useObjectNavItemColor } from '@/navigation-menu-item/hooks/useObjectNavItemColor';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
|
||||
import { getNavigationMenuItemComputedLink } from '@/navigation-menu-item/utils/getNavigationMenuItemComputedLink';
|
||||
import { getNavigationMenuItemLabel } from '@/navigation-menu-item/utils/getNavigationMenuItemLabel';
|
||||
import { recordIdentifierToObjectRecordIdentifier } from '@/navigation-menu-item/utils/recordIdentifierToObjectRecordIdentifier';
|
||||
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import {
|
||||
AppPath,
|
||||
CoreObjectNameSingular,
|
||||
NavigationMenuItemType,
|
||||
} from 'twenty-shared/types';
|
||||
import { getAppPath, isDefined } from 'twenty-shared/utils';
|
||||
import { Avatar, useIcons } from 'twenty-ui/display';
|
||||
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
export type NavigationDrawerItemForObjectMetadataItemProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
navigationMenuItem?: NavigationMenuItem;
|
||||
isSelectedInEditMode?: boolean;
|
||||
onEditModeClick?: () => void;
|
||||
onActiveItemClickWhenNotInEditMode?: () => void;
|
||||
isDragging?: boolean;
|
||||
};
|
||||
|
||||
export const NavigationDrawerItemForObjectMetadataItem = ({
|
||||
objectMetadataItem,
|
||||
navigationMenuItem,
|
||||
isSelectedInEditMode = false,
|
||||
onEditModeClick,
|
||||
onActiveItemClickWhenNotInEditMode: _onActiveItemClickWhenNotInEditMode,
|
||||
isDragging = false,
|
||||
}: NavigationDrawerItemForObjectMetadataItemProps) => {
|
||||
const isNavigationMenuInEditMode = useAtomStateValue(
|
||||
isNavigationMenuInEditModeState,
|
||||
);
|
||||
const lastVisitedViewPerObjectMetadataItem = useAtomStateValue(
|
||||
lastVisitedViewPerObjectMetadataItemState,
|
||||
);
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
const views = useAtomStateValue(viewsSelector);
|
||||
|
||||
const lastVisitedViewId =
|
||||
lastVisitedViewPerObjectMetadataItem?.[objectMetadataItem.id];
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
const objectNavItemColor = useObjectNavItemColor(
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
const location = useLocation();
|
||||
const currentPath = location.pathname;
|
||||
const currentPathWithSearch = `${location.pathname}${location.search}`;
|
||||
|
||||
const isRecord = navigationMenuItem?.type === NavigationMenuItemType.RECORD;
|
||||
const isView = navigationMenuItem?.type === NavigationMenuItemType.VIEW;
|
||||
const isObject = navigationMenuItem?.type === NavigationMenuItemType.OBJECT;
|
||||
const hasCustomLink = isRecord || isView || isObject;
|
||||
|
||||
const navigationPath = hasCustomLink
|
||||
? getNavigationMenuItemComputedLink(
|
||||
navigationMenuItem!,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
)
|
||||
: getAppPath(
|
||||
AppPath.RecordIndexPage,
|
||||
{ objectNamePlural: objectMetadataItem.namePlural },
|
||||
lastVisitedViewId ? { viewId: lastVisitedViewId } : undefined,
|
||||
);
|
||||
|
||||
const computedLink = hasCustomLink ? navigationPath : '';
|
||||
|
||||
const isActive = hasCustomLink
|
||||
? (isView || isObject ? currentPathWithSearch : currentPath) ===
|
||||
computedLink
|
||||
: currentPath ===
|
||||
getAppPath(AppPath.RecordIndexPage, {
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
}) ||
|
||||
currentPath.includes(
|
||||
getAppPath(AppPath.RecordShowPage, {
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
objectRecordId: '',
|
||||
}) + '/',
|
||||
);
|
||||
|
||||
const handleClick = isNavigationMenuInEditMode ? onEditModeClick : undefined;
|
||||
|
||||
const shouldNavigate = !isNavigationMenuInEditMode;
|
||||
|
||||
const view = isDefined(navigationMenuItem?.viewId)
|
||||
? views.find((view) => view.id === navigationMenuItem!.viewId)
|
||||
: undefined;
|
||||
const viewKey = view?.key ?? null;
|
||||
|
||||
const isViewWithCustomName =
|
||||
isView && viewKey !== ViewKey.INDEX && isDefined(view);
|
||||
|
||||
const itemLabel = isDefined(navigationMenuItem)
|
||||
? getNavigationMenuItemLabel(navigationMenuItem, objectMetadataItems, views)
|
||||
: objectMetadataItem.labelPlural;
|
||||
|
||||
const label = isRecord
|
||||
? itemLabel
|
||||
: isViewWithCustomName
|
||||
? itemLabel
|
||||
: objectMetadataItem.labelPlural;
|
||||
|
||||
const recordIdentifier =
|
||||
isRecord && isDefined(navigationMenuItem?.targetRecordIdentifier)
|
||||
? recordIdentifierToObjectRecordIdentifier({
|
||||
recordIdentifier: navigationMenuItem!.targetRecordIdentifier!,
|
||||
objectMetadataItem,
|
||||
})
|
||||
: null;
|
||||
|
||||
const Icon = isRecord
|
||||
? () => (
|
||||
<Avatar
|
||||
type={
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Company
|
||||
? 'squared'
|
||||
: 'rounded'
|
||||
}
|
||||
avatarUrl={recordIdentifier?.avatarUrl}
|
||||
placeholderColorSeed={navigationMenuItem!.targetRecordId ?? undefined}
|
||||
placeholder={itemLabel}
|
||||
/>
|
||||
)
|
||||
: isViewWithCustomName && isDefined(view?.icon)
|
||||
? () => (
|
||||
<ObjectIconWithViewOverlay
|
||||
ObjectIcon={getIcon(objectMetadataItem.icon)}
|
||||
ViewIcon={getIcon(view!.icon)}
|
||||
objectColor={objectNavItemColor}
|
||||
/>
|
||||
)
|
||||
: getIcon(objectMetadataItem.icon);
|
||||
|
||||
const iconThemeColor = !isRecord ? objectNavItemColor : undefined;
|
||||
|
||||
const secondaryLabel =
|
||||
isRecord || isViewWithCustomName
|
||||
? objectMetadataItem.labelSingular
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<NavigationDrawerItem
|
||||
label={label}
|
||||
secondaryLabel={secondaryLabel}
|
||||
to={
|
||||
isNavigationMenuInEditMode || isDragging
|
||||
? undefined
|
||||
: shouldNavigate
|
||||
? navigationPath
|
||||
: undefined
|
||||
}
|
||||
onClick={handleClick}
|
||||
Icon={Icon}
|
||||
iconColor={iconThemeColor}
|
||||
active={isActive}
|
||||
isSelectedInEditMode={isSelectedInEditMode}
|
||||
isDragging={isDragging}
|
||||
triggerEvent={isNavigationMenuInEditMode ? 'CLICK' : undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { useWorkspaceNavigationMenuItems } from '@/navigation-menu-item/hooks/useWorkspaceNavigationMenuItems';
|
||||
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
const WORKFLOW_OBJECTS_IN_SIDEBAR = [
|
||||
CoreObjectNameSingular.Workflow,
|
||||
CoreObjectNameSingular.WorkflowRun,
|
||||
CoreObjectNameSingular.WorkflowVersion,
|
||||
];
|
||||
|
||||
export const NavigationDrawerOpenedSection = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
const filteredActiveNonSystemObjectMetadataItems =
|
||||
activeObjectMetadataItems.filter((item) => !item.isRemote);
|
||||
|
||||
const { workspaceNavigationMenuItemsObjectMetadataItems } =
|
||||
useWorkspaceNavigationMenuItems();
|
||||
|
||||
const {
|
||||
objectNamePlural: currentObjectNamePlural,
|
||||
objectNameSingular: currentObjectNameSingular,
|
||||
} = useParams();
|
||||
|
||||
if (!currentObjectNamePlural && !currentObjectNameSingular) {
|
||||
return;
|
||||
}
|
||||
|
||||
const objectMetadataItem = filteredActiveNonSystemObjectMetadataItems.find(
|
||||
(item) =>
|
||||
item.namePlural === currentObjectNamePlural ||
|
||||
item.nameSingular === currentObjectNameSingular,
|
||||
);
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isWorkflowObjectInSidebar = WORKFLOW_OBJECTS_IN_SIDEBAR.includes(
|
||||
objectMetadataItem.nameSingular as CoreObjectNameSingular,
|
||||
);
|
||||
|
||||
const shouldDisplayObjectInOpenedSection =
|
||||
!isWorkflowObjectInSidebar &&
|
||||
!workspaceNavigationMenuItemsObjectMetadataItems
|
||||
.map((item) => item.id)
|
||||
.includes(objectMetadataItem.id);
|
||||
|
||||
return (
|
||||
shouldDisplayObjectInOpenedSection && (
|
||||
<NavigationDrawerSectionForObjectMetadataItems
|
||||
sectionTitle={t`Opened`}
|
||||
objectMetadataItems={[objectMetadataItem]}
|
||||
isRemote={false}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { NavigationDrawerItemForObjectMetadataItem } from '@/object-metadata/components/NavigationDrawerItemForObjectMetadataItem';
|
||||
import { NavigationDrawerItemForObjectMetadataItem } from '@/navigation-menu-item/display/object/components/NavigationDrawerItemForObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { NavigationDrawerSectionForWorkspaceItemFolderContent } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemFolderContent';
|
||||
import { NavigationDrawerSectionForWorkspaceItemLinkContent } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemLinkContent';
|
||||
import { NavigationDrawerSectionForWorkspaceItemObjectContent } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemObjectContent';
|
||||
import type { WorkspaceSectionItemContentProps } from '@/object-metadata/components/WorkspaceSectionItemContentProps';
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemContentProps =
|
||||
WorkspaceSectionItemContentProps;
|
||||
|
||||
export const NavigationDrawerSectionForWorkspaceItemContent = ({
|
||||
item,
|
||||
editModeProps,
|
||||
isDragging,
|
||||
folderChildrenById,
|
||||
folderCount,
|
||||
selectedNavigationMenuItemId,
|
||||
onNavigationMenuItemClick,
|
||||
onActiveObjectMetadataItemClick,
|
||||
readOnly,
|
||||
}: NavigationDrawerSectionForWorkspaceItemContentProps) => {
|
||||
switch (item.type) {
|
||||
case NavigationMenuItemType.FOLDER:
|
||||
return (
|
||||
<NavigationDrawerSectionForWorkspaceItemFolderContent
|
||||
item={item}
|
||||
editModeProps={editModeProps}
|
||||
isDragging={isDragging}
|
||||
folderChildrenById={folderChildrenById}
|
||||
folderCount={folderCount}
|
||||
selectedNavigationMenuItemId={selectedNavigationMenuItemId}
|
||||
onNavigationMenuItemClick={onNavigationMenuItemClick}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
);
|
||||
case NavigationMenuItemType.LINK:
|
||||
return (
|
||||
<NavigationDrawerSectionForWorkspaceItemLinkContent
|
||||
item={item}
|
||||
editModeProps={editModeProps}
|
||||
isDragging={isDragging}
|
||||
folderChildrenById={folderChildrenById}
|
||||
folderCount={folderCount}
|
||||
selectedNavigationMenuItemId={selectedNavigationMenuItemId}
|
||||
onNavigationMenuItemClick={onNavigationMenuItemClick}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<NavigationDrawerSectionForWorkspaceItemObjectContent
|
||||
item={item}
|
||||
editModeProps={editModeProps}
|
||||
isDragging={isDragging}
|
||||
folderChildrenById={folderChildrenById}
|
||||
folderCount={folderCount}
|
||||
selectedNavigationMenuItemId={selectedNavigationMenuItemId}
|
||||
onNavigationMenuItemClick={onNavigationMenuItemClick}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
|
||||
import { WorkspaceFolderReadOnly } from '@/object-metadata/components/WorkspaceFolderReadOnly';
|
||||
import type { WorkspaceSectionItemContentProps } from '@/object-metadata/components/WorkspaceSectionItemContentProps';
|
||||
|
||||
const LazyWorkspaceNavigationMenuItemsFolder = lazy(() =>
|
||||
import(
|
||||
'@/navigation-menu-item/components/WorkspaceNavigationMenuItemsFolder'
|
||||
).then((m) => ({ default: m.WorkspaceNavigationMenuItemsFolder })),
|
||||
);
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemFolderContentProps =
|
||||
WorkspaceSectionItemContentProps;
|
||||
|
||||
export const NavigationDrawerSectionForWorkspaceItemFolderContent = ({
|
||||
item,
|
||||
editModeProps,
|
||||
isDragging,
|
||||
folderChildrenById,
|
||||
folderCount,
|
||||
selectedNavigationMenuItemId,
|
||||
onNavigationMenuItemClick,
|
||||
readOnly = false,
|
||||
}: NavigationDrawerSectionForWorkspaceItemFolderContentProps) => {
|
||||
const folderId = item.id;
|
||||
const folderName = item.name ?? 'Folder';
|
||||
const folderIconKey = item.icon;
|
||||
const folderColor = 'color' in item ? item.color : undefined;
|
||||
const navigationMenuItems = folderChildrenById.get(item.id) ?? [];
|
||||
const isGroup = folderCount > 1;
|
||||
|
||||
if (readOnly) {
|
||||
return (
|
||||
<WorkspaceFolderReadOnly
|
||||
folderId={folderId}
|
||||
folderName={folderName}
|
||||
folderIconKey={folderIconKey}
|
||||
folderColor={folderColor}
|
||||
navigationMenuItems={navigationMenuItems}
|
||||
isGroup={isGroup}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
<WorkspaceFolderReadOnly
|
||||
folderId={folderId}
|
||||
folderName={folderName}
|
||||
folderIconKey={folderIconKey}
|
||||
folderColor={folderColor}
|
||||
navigationMenuItems={navigationMenuItems}
|
||||
isGroup={isGroup}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LazyWorkspaceNavigationMenuItemsFolder
|
||||
folderId={folderId}
|
||||
folderName={folderName}
|
||||
folderIconKey={folderIconKey}
|
||||
folderColor={folderColor}
|
||||
navigationMenuItems={navigationMenuItems}
|
||||
isGroup={isGroup}
|
||||
isSelectedInEditMode={editModeProps.isSelectedInEditMode}
|
||||
onEditModeClick={editModeProps.onEditModeClick}
|
||||
onNavigationMenuItemClick={onNavigationMenuItemClick}
|
||||
selectedNavigationMenuItemId={selectedNavigationMenuItemId}
|
||||
isDragging={isDragging}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
|
||||
import { getNavigationMenuItemComputedLink } from '@/navigation-menu-item/utils/getNavigationMenuItemComputedLink';
|
||||
import { getNavigationMenuItemLabel } from '@/navigation-menu-item/utils/getNavigationMenuItemLabel';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import type { WorkspaceSectionItemContentProps } from '@/object-metadata/components/WorkspaceSectionItemContentProps';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
|
||||
import { IconArrowUpRight } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemLinkContentProps =
|
||||
WorkspaceSectionItemContentProps;
|
||||
|
||||
export const NavigationDrawerSectionForWorkspaceItemLinkContent = ({
|
||||
item,
|
||||
editModeProps,
|
||||
isDragging,
|
||||
}: NavigationDrawerSectionForWorkspaceItemLinkContentProps) => {
|
||||
const isNavigationMenuInEditMode = useAtomStateValue(
|
||||
isNavigationMenuInEditModeState,
|
||||
);
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
const views = useAtomStateValue(viewsSelector);
|
||||
|
||||
const label = getNavigationMenuItemLabel(item, objectMetadataItems, views);
|
||||
const computedLink = getNavigationMenuItemComputedLink(
|
||||
item,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
);
|
||||
|
||||
return (
|
||||
<NavigationDrawerItem
|
||||
label={label}
|
||||
to={isNavigationMenuInEditMode || isDragging ? undefined : computedLink}
|
||||
onClick={
|
||||
isNavigationMenuInEditMode ? editModeProps.onEditModeClick : undefined
|
||||
}
|
||||
Icon={() => <NavigationMenuItemIcon navigationMenuItem={item} />}
|
||||
active={false}
|
||||
isSelectedInEditMode={editModeProps.isSelectedInEditMode}
|
||||
isDragging={isDragging}
|
||||
triggerEvent="CLICK"
|
||||
rightOptions={
|
||||
!isNavigationMenuInEditMode && (
|
||||
<IconArrowUpRight
|
||||
size={themeCssVariables.icon.size.sm}
|
||||
stroke={themeCssVariables.icon.stroke.md}
|
||||
color={themeCssVariables.font.color.light}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/utils/getObjectMetadataForNavigationMenuItem';
|
||||
import { NavigationDrawerItemForObjectMetadataItem } from '@/object-metadata/components/NavigationDrawerItemForObjectMetadataItem';
|
||||
import type { WorkspaceSectionItemContentProps } from '@/object-metadata/components/WorkspaceSectionItemContentProps';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemObjectContentProps =
|
||||
WorkspaceSectionItemContentProps;
|
||||
|
||||
export const NavigationDrawerSectionForWorkspaceItemObjectContent = ({
|
||||
item,
|
||||
editModeProps,
|
||||
isDragging,
|
||||
onActiveObjectMetadataItemClick,
|
||||
}: NavigationDrawerSectionForWorkspaceItemObjectContentProps) => {
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
const views = useAtomStateValue(viewsSelector);
|
||||
const objectMetadataItem = getObjectMetadataForNavigationMenuItem(
|
||||
item,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
);
|
||||
if (!objectMetadataItem) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<NavigationDrawerItemForObjectMetadataItem
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
navigationMenuItem={item}
|
||||
isSelectedInEditMode={editModeProps.isSelectedInEditMode}
|
||||
onEditModeClick={editModeProps.onEditModeClick}
|
||||
isDragging={isDragging}
|
||||
onActiveItemClickWhenNotInEditMode={
|
||||
onActiveObjectMetadataItemClick
|
||||
? () => onActiveObjectMetadataItemClick(objectMetadataItem, item.id)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
import { NavigationDropTargetContext } from '@/navigation-menu-item/contexts/NavigationDropTargetContext';
|
||||
import React, { lazy, Suspense, useContext } from 'react';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
|
||||
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/constants/NavigationMenuItemDroppableIds';
|
||||
import { type NavigationMenuItemClickParams } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
|
||||
import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/utils/getObjectMetadataForNavigationMenuItem';
|
||||
import type { EditModeProps } from '@/object-metadata/components/EditModeProps';
|
||||
import { NavigationDrawerSectionForWorkspaceItemsListReadOnly } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemsListReadOnly';
|
||||
import { WorkspaceSectionListEditModeFallback } from '@/object-metadata/components/WorkspaceSectionListEditModeFallback';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const LazyWorkspaceSectionListDndKit = lazy(() =>
|
||||
import(
|
||||
'@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemsListDndKit'
|
||||
).then((m) => ({ default: m.WorkspaceSectionListDndKit })),
|
||||
);
|
||||
|
||||
const StyledWorkspaceSectionContentGapOffset = styled.div`
|
||||
margin-top: calc(-1 * ${themeCssVariables.betweenSiblingsGap});
|
||||
`;
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemsProps = {
|
||||
sectionTitle: string;
|
||||
items: NavigationMenuItem[];
|
||||
rightIcon?: React.ReactNode;
|
||||
selectedNavigationMenuItemId?: string | null;
|
||||
onNavigationMenuItemClick?: (params: NavigationMenuItemClickParams) => void;
|
||||
onActiveObjectMetadataItemClick?: (
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
navigationMenuItemId: string,
|
||||
) => void;
|
||||
};
|
||||
|
||||
export const NavigationDrawerSectionForWorkspaceItems = ({
|
||||
sectionTitle,
|
||||
items,
|
||||
rightIcon,
|
||||
selectedNavigationMenuItemId = null,
|
||||
onNavigationMenuItemClick,
|
||||
onActiveObjectMetadataItemClick,
|
||||
}: NavigationDrawerSectionForWorkspaceItemsProps) => {
|
||||
const isNavigationMenuInEditMode = useAtomStateValue(
|
||||
isNavigationMenuInEditModeState,
|
||||
);
|
||||
const { toggleNavigationSection, isNavigationSectionOpen } =
|
||||
useNavigationSection('Workspace');
|
||||
const views = useAtomStateValue(viewsSelector);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
const { addToNavigationFallbackDestination } = useContext(
|
||||
NavigationDropTargetContext,
|
||||
);
|
||||
|
||||
const flatItems = items.filter((item) => !isDefined(item.folderId));
|
||||
const isAddToNavigationDropTargetVisible =
|
||||
addToNavigationFallbackDestination?.droppableId ===
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS;
|
||||
const folderChildrenById = items.reduce<Map<string, NavigationMenuItem[]>>(
|
||||
(acc, item) => {
|
||||
const folderId = item.folderId;
|
||||
if (isDefined(folderId)) {
|
||||
const children = acc.get(folderId) ?? [];
|
||||
children.push(item);
|
||||
acc.set(folderId, children);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
new Map(),
|
||||
);
|
||||
|
||||
const filteredItems = flatItems.filter((item) => {
|
||||
const itemType = item.type;
|
||||
if (
|
||||
itemType === NavigationMenuItemType.FOLDER ||
|
||||
itemType === NavigationMenuItemType.LINK
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
itemType === NavigationMenuItemType.OBJECT ||
|
||||
itemType === NavigationMenuItemType.VIEW ||
|
||||
itemType === NavigationMenuItemType.RECORD
|
||||
) {
|
||||
const objectMetadataItem = getObjectMetadataForNavigationMenuItem(
|
||||
item,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
);
|
||||
return (
|
||||
isDefined(objectMetadataItem) &&
|
||||
getObjectPermissionsForObject(
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataItem.id,
|
||||
).canReadObjectRecords
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const getEditModeProps = (item: NavigationMenuItem): EditModeProps => {
|
||||
const itemId = item.id;
|
||||
return {
|
||||
isSelectedInEditMode: selectedNavigationMenuItemId === itemId,
|
||||
onEditModeClick: onNavigationMenuItemClick
|
||||
? () => {
|
||||
const itemType = item.type;
|
||||
const objectMetadataItem =
|
||||
itemType === 'OBJECT' ||
|
||||
itemType === 'VIEW' ||
|
||||
itemType === 'RECORD'
|
||||
? getObjectMetadataForNavigationMenuItem(
|
||||
item,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
)
|
||||
: null;
|
||||
onNavigationMenuItemClick({
|
||||
item,
|
||||
objectMetadataItem: objectMetadataItem ?? undefined,
|
||||
});
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
if (flatItems.length === 0 && !isAddToNavigationDropTargetVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationDrawerSection>
|
||||
<NavigationDrawerAnimatedCollapseWrapper>
|
||||
<NavigationDrawerSectionTitle
|
||||
label={sectionTitle}
|
||||
onClick={() => toggleNavigationSection()}
|
||||
rightIcon={rightIcon}
|
||||
alwaysShowRightIcon={isNavigationMenuInEditMode}
|
||||
isOpen={isNavigationSectionOpen}
|
||||
/>
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
<StyledWorkspaceSectionContentGapOffset>
|
||||
<AnimatedExpandableContainer
|
||||
isExpanded={
|
||||
isNavigationSectionOpen || isAddToNavigationDropTargetVisible
|
||||
}
|
||||
dimension="height"
|
||||
mode="fit-content"
|
||||
containAnimation
|
||||
initial={false}
|
||||
>
|
||||
{isNavigationMenuInEditMode ? (
|
||||
<Suspense
|
||||
fallback={
|
||||
<WorkspaceSectionListEditModeFallback
|
||||
filteredItems={filteredItems}
|
||||
folderChildrenById={folderChildrenById}
|
||||
onActiveObjectMetadataItemClick={
|
||||
onActiveObjectMetadataItemClick
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LazyWorkspaceSectionListDndKit
|
||||
filteredItems={filteredItems}
|
||||
getEditModeProps={getEditModeProps}
|
||||
folderChildrenById={folderChildrenById}
|
||||
selectedNavigationMenuItemId={selectedNavigationMenuItemId}
|
||||
onNavigationMenuItemClick={onNavigationMenuItemClick}
|
||||
onActiveObjectMetadataItemClick={
|
||||
onActiveObjectMetadataItemClick
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
) : (
|
||||
<NavigationDrawerSectionForWorkspaceItemsListReadOnly
|
||||
filteredItems={filteredItems}
|
||||
folderChildrenById={folderChildrenById}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
/>
|
||||
)}
|
||||
</AnimatedExpandableContainer>
|
||||
</StyledWorkspaceSectionContentGapOffset>
|
||||
</NavigationDrawerSection>
|
||||
);
|
||||
};
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
import { WorkspaceDndKitDroppableSlot } from '@/navigation-menu-item/components/WorkspaceDndKitDroppableSlot';
|
||||
import { WorkspaceDndKitSortableItem } from '@/navigation-menu-item/components/WorkspaceDndKitSortableItem';
|
||||
import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/constants/NavigationMenuItemDroppableIds';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { NavigationDropTargetContext } from '@/navigation-menu-item/contexts/NavigationDropTargetContext';
|
||||
import { useIsDropDisabledForSection } from '@/navigation-menu-item/hooks/useIsDropDisabledForSection';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { NavigationMenuItemDragContext } from '@/navigation-menu-item/contexts/NavigationMenuItemDragContext';
|
||||
import { NavigationDrawerSectionForWorkspaceItemContent } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemContent';
|
||||
import { WorkspaceOrphanDropTarget } from '@/object-metadata/components/WorkspaceOrphanDropTarget';
|
||||
import { WorkspaceSectionAddMenuItemButton } from '@/object-metadata/components/WorkspaceSectionAddMenuItemButton';
|
||||
import type { WorkspaceSectionListDndKitProps } from '@/object-metadata/components/WorkspaceSectionListDndKitProps';
|
||||
|
||||
const StyledList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.betweenSiblingsGap};
|
||||
padding-top: ${themeCssVariables.betweenSiblingsGap};
|
||||
`;
|
||||
|
||||
const StyledListItemRow = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
`;
|
||||
|
||||
export const WorkspaceSectionListDndKit = ({
|
||||
filteredItems,
|
||||
getEditModeProps,
|
||||
folderChildrenById,
|
||||
selectedNavigationMenuItemId,
|
||||
onNavigationMenuItemClick,
|
||||
onActiveObjectMetadataItemClick,
|
||||
}: WorkspaceSectionListDndKitProps) => {
|
||||
const isNavigationMenuInEditMode = useAtomStateValue(
|
||||
isNavigationMenuInEditModeState,
|
||||
);
|
||||
const workspaceDropDisabled = useIsDropDisabledForSection(true);
|
||||
const { isDragging } = useContext(NavigationMenuItemDragContext);
|
||||
const { addToNavigationFallbackDestination } = useContext(
|
||||
NavigationDropTargetContext,
|
||||
);
|
||||
const folderCount = filteredItems.filter(
|
||||
(item) => item.type === NavigationMenuItemType.FOLDER,
|
||||
).length;
|
||||
const isAddMenuItemButtonVisible = isNavigationMenuInEditMode;
|
||||
return (
|
||||
<StyledList>
|
||||
{filteredItems.map((item, index) => (
|
||||
<StyledListItemRow key={item.id}>
|
||||
<WorkspaceOrphanDropTarget index={index} compact />
|
||||
<WorkspaceDndKitSortableItem
|
||||
id={item.id}
|
||||
index={index}
|
||||
group={
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS
|
||||
}
|
||||
disabled={!isNavigationMenuInEditMode || workspaceDropDisabled}
|
||||
>
|
||||
<NavigationDrawerSectionForWorkspaceItemContent
|
||||
item={item}
|
||||
editModeProps={getEditModeProps(item)}
|
||||
isDragging={isDragging}
|
||||
folderChildrenById={folderChildrenById}
|
||||
folderCount={folderCount}
|
||||
selectedNavigationMenuItemId={selectedNavigationMenuItemId}
|
||||
onNavigationMenuItemClick={onNavigationMenuItemClick}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
/>
|
||||
</WorkspaceDndKitSortableItem>
|
||||
</StyledListItemRow>
|
||||
))}
|
||||
<WorkspaceDndKitDroppableSlot
|
||||
droppableId={
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS
|
||||
}
|
||||
index={filteredItems.length}
|
||||
disabled={workspaceDropDisabled}
|
||||
>
|
||||
<WorkspaceOrphanDropTarget
|
||||
index={filteredItems.length}
|
||||
compact={!isAddMenuItemButtonVisible}
|
||||
>
|
||||
{isAddMenuItemButtonVisible && <WorkspaceSectionAddMenuItemButton />}
|
||||
</WorkspaceOrphanDropTarget>
|
||||
</WorkspaceDndKitDroppableSlot>
|
||||
{addToNavigationFallbackDestination?.droppableId ===
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS &&
|
||||
addToNavigationFallbackDestination.index > filteredItems.length && (
|
||||
<WorkspaceDndKitDroppableSlot
|
||||
droppableId={
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS
|
||||
}
|
||||
index={addToNavigationFallbackDestination.index}
|
||||
disabled={workspaceDropDisabled}
|
||||
>
|
||||
<WorkspaceOrphanDropTarget
|
||||
index={addToNavigationFallbackDestination.index}
|
||||
compact
|
||||
/>
|
||||
</WorkspaceDndKitDroppableSlot>
|
||||
)}
|
||||
</StyledList>
|
||||
);
|
||||
};
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import type { NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import type { WorkspaceSectionListDndKitProps } from '@/object-metadata/components/WorkspaceSectionListDndKitProps';
|
||||
import { NavigationDrawerSectionForWorkspaceItemContent } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemContent';
|
||||
|
||||
const StyledList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.betweenSiblingsGap};
|
||||
padding-top: ${themeCssVariables.betweenSiblingsGap};
|
||||
`;
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemsListReadOnlyProps = Pick<
|
||||
WorkspaceSectionListDndKitProps,
|
||||
'filteredItems' | 'folderChildrenById' | 'onActiveObjectMetadataItemClick'
|
||||
>;
|
||||
|
||||
const READ_ONLY_EDIT_MODE_PROPS = {
|
||||
isSelectedInEditMode: false,
|
||||
onEditModeClick: undefined,
|
||||
} as const;
|
||||
|
||||
export const NavigationDrawerSectionForWorkspaceItemsListReadOnly = ({
|
||||
filteredItems,
|
||||
folderChildrenById,
|
||||
onActiveObjectMetadataItemClick,
|
||||
}: NavigationDrawerSectionForWorkspaceItemsListReadOnlyProps) => {
|
||||
const folderCount = filteredItems.filter(
|
||||
(item) => item.type === NavigationMenuItemType.FOLDER,
|
||||
).length;
|
||||
|
||||
return (
|
||||
<StyledList>
|
||||
{filteredItems.map((item: NavigationMenuItem) => (
|
||||
<NavigationDrawerSectionForWorkspaceItemContent
|
||||
key={item.id}
|
||||
item={item}
|
||||
editModeProps={READ_ONLY_EDIT_MODE_PROPS}
|
||||
isDragging={false}
|
||||
folderChildrenById={folderChildrenById}
|
||||
folderCount={folderCount}
|
||||
selectedNavigationMenuItemId={null}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
readOnly
|
||||
/>
|
||||
))}
|
||||
</StyledList>
|
||||
);
|
||||
};
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { IconChevronDown, IconChevronRight, useIcons } from 'twenty-ui/display';
|
||||
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
import { WorkspaceNavigationMenuItemFolderSubItem } from '@/navigation-menu-item/components/WorkspaceNavigationMenuItemFolderSubItem';
|
||||
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
|
||||
import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder';
|
||||
import { useWorkspaceFolderOpenState } from '@/navigation-menu-item/hooks/useWorkspaceFolderOpenState';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer';
|
||||
|
||||
const StyledFolderContainer = styled.div`
|
||||
border: 1px solid transparent;
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
`;
|
||||
|
||||
const StyledFolderContent = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const StyledFolderExpandableWrapper = styled.div`
|
||||
& > div {
|
||||
overflow: visible !important;
|
||||
}
|
||||
`;
|
||||
|
||||
type WorkspaceFolderReadOnlyProps = {
|
||||
folderId: string;
|
||||
folderName: string;
|
||||
folderIconKey?: string | null;
|
||||
folderColor?: string | null;
|
||||
navigationMenuItems: NavigationMenuItem[];
|
||||
isGroup: boolean;
|
||||
};
|
||||
|
||||
export const WorkspaceFolderReadOnly = ({
|
||||
folderId,
|
||||
folderName,
|
||||
folderIconKey,
|
||||
folderColor,
|
||||
navigationMenuItems,
|
||||
isGroup,
|
||||
}: WorkspaceFolderReadOnlyProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
const FolderIcon = getIcon(folderIconKey ?? FOLDER_ICON_DEFAULT);
|
||||
const isMobile = useIsMobile();
|
||||
const { isOpen, handleToggle, selectedNavigationMenuItemIndex } =
|
||||
useWorkspaceFolderOpenState({ folderId, navigationMenuItems });
|
||||
|
||||
const [skipInitialExpandAnimation] = useState(() => isOpen);
|
||||
|
||||
return (
|
||||
<StyledFolderContainer>
|
||||
<NavigationDrawerItemsCollapsableContainer isGroup={isGroup}>
|
||||
<NavigationDrawerItem
|
||||
label={folderName}
|
||||
Icon={FolderIcon}
|
||||
iconColor={
|
||||
isDefined(folderColor)
|
||||
? folderColor
|
||||
: DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER
|
||||
}
|
||||
active={!isOpen && selectedNavigationMenuItemIndex >= 0}
|
||||
onClick={handleToggle}
|
||||
className="navigation-drawer-item"
|
||||
triggerEvent="CLICK"
|
||||
preventCollapseOnMobile={isMobile}
|
||||
alwaysShowRightOptions
|
||||
rightOptions={
|
||||
isOpen ? (
|
||||
<IconChevronDown
|
||||
size={themeCssVariables.icon.size.sm}
|
||||
stroke={themeCssVariables.icon.stroke.sm}
|
||||
color={themeCssVariables.font.color.tertiary}
|
||||
/>
|
||||
) : (
|
||||
<IconChevronRight
|
||||
size={themeCssVariables.icon.size.sm}
|
||||
stroke={themeCssVariables.icon.stroke.sm}
|
||||
color={themeCssVariables.font.color.tertiary}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<StyledFolderExpandableWrapper>
|
||||
<AnimatedExpandableContainer
|
||||
isExpanded={isOpen}
|
||||
dimension="height"
|
||||
mode="fit-content"
|
||||
containAnimation
|
||||
initial={!skipInitialExpandAnimation}
|
||||
>
|
||||
<StyledFolderContent>
|
||||
{navigationMenuItems.map((navigationMenuItem, index) => (
|
||||
<WorkspaceNavigationMenuItemFolderSubItem
|
||||
key={navigationMenuItem.id}
|
||||
navigationMenuItem={navigationMenuItem}
|
||||
index={index}
|
||||
arrayLength={navigationMenuItems.length}
|
||||
selectedNavigationMenuItemIndex={
|
||||
selectedNavigationMenuItemIndex
|
||||
}
|
||||
onNavigationMenuItemClick={undefined}
|
||||
selectedNavigationMenuItemId={null}
|
||||
isContextDragging={false}
|
||||
/>
|
||||
))}
|
||||
</StyledFolderContent>
|
||||
</AnimatedExpandableContainer>
|
||||
</StyledFolderExpandableWrapper>
|
||||
</NavigationDrawerItemsCollapsableContainer>
|
||||
</StyledFolderContainer>
|
||||
);
|
||||
};
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import { NavigationItemDropTarget } from '@/navigation-menu-item/components/NavigationItemDropTarget';
|
||||
import { NavigationSections } from '@/navigation-menu-item/constants/NavigationSections.constants';
|
||||
import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/constants/NavigationMenuItemDroppableIds';
|
||||
import { getDndKitDropTargetId } from '@/navigation-menu-item/utils/getDndKitDropTargetId';
|
||||
|
||||
type WorkspaceOrphanDropTargetProps = {
|
||||
index: number;
|
||||
compact?: boolean;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export const WorkspaceOrphanDropTarget = ({
|
||||
index,
|
||||
compact = false,
|
||||
children,
|
||||
}: WorkspaceOrphanDropTargetProps) => (
|
||||
<NavigationItemDropTarget
|
||||
folderId={null}
|
||||
index={index}
|
||||
sectionId={NavigationSections.WORKSPACE}
|
||||
compact={compact}
|
||||
dropTargetIdOverride={getDndKitDropTargetId(
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS,
|
||||
index,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</NavigationItemDropTarget>
|
||||
);
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import React from 'react';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
import { IconColumnInsertRight, IconPlus } from 'twenty-ui/display';
|
||||
|
||||
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
|
||||
export const WorkspaceSectionAddMenuItemButton = () => {
|
||||
const { t } = useLingui();
|
||||
const { navigateSidePanel } = useNavigateSidePanel();
|
||||
const sidePanelPage = useAtomStateValue(sidePanelPageState);
|
||||
const addMenuItemInsertionContext = useAtomStateValue(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
const setAddMenuItemInsertionContext = useSetAtomState(
|
||||
addMenuItemInsertionContextState,
|
||||
);
|
||||
|
||||
const setSelectedNavigationMenuItemInEditMode = useSetAtomState(
|
||||
selectedNavigationMenuItemInEditModeState,
|
||||
);
|
||||
|
||||
const handleClick = (event?: React.MouseEvent) => {
|
||||
event?.stopPropagation();
|
||||
setAddMenuItemInsertionContext(null);
|
||||
setSelectedNavigationMenuItemInEditMode(null);
|
||||
navigateSidePanel({
|
||||
page: SidePanelPages.NavigationMenuAddItem,
|
||||
pageTitle: t`New sidebar item`,
|
||||
pageIcon: IconColumnInsertRight,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
};
|
||||
|
||||
const isSelected =
|
||||
sidePanelPage === SidePanelPages.NavigationMenuAddItem &&
|
||||
addMenuItemInsertionContext === null;
|
||||
|
||||
return (
|
||||
<NavigationDrawerItem
|
||||
Icon={IconPlus}
|
||||
label={t`Add menu item`}
|
||||
onClick={handleClick}
|
||||
triggerEvent="CLICK"
|
||||
variant="tertiary"
|
||||
isSelectedInEditMode={isSelected}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import type { NavigationMenuItemClickParams } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import type { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import type { NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
import type { EditModeProps } from '@/object-metadata/components/EditModeProps';
|
||||
|
||||
export type WorkspaceSectionItemContentProps = {
|
||||
item: NavigationMenuItem;
|
||||
editModeProps: EditModeProps;
|
||||
isDragging: boolean;
|
||||
folderChildrenById: Map<string, NavigationMenuItem[]>;
|
||||
folderCount: number;
|
||||
selectedNavigationMenuItemId: string | null;
|
||||
onNavigationMenuItemClick?: (params: NavigationMenuItemClickParams) => void;
|
||||
onActiveObjectMetadataItemClick?: (
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
navigationMenuItemId: string,
|
||||
) => void;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
import type { NavigationMenuItemClickParams } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import type { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import type { NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
import type { EditModeProps } from '@/object-metadata/components/EditModeProps';
|
||||
|
||||
export type WorkspaceSectionListDndKitProps = {
|
||||
filteredItems: NavigationMenuItem[];
|
||||
getEditModeProps: (item: NavigationMenuItem) => EditModeProps;
|
||||
folderChildrenById: Map<string, NavigationMenuItem[]>;
|
||||
selectedNavigationMenuItemId: string | null;
|
||||
onNavigationMenuItemClick?: (params: NavigationMenuItemClickParams) => void;
|
||||
onActiveObjectMetadataItemClick?: (
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
navigationMenuItemId: string,
|
||||
) => void;
|
||||
};
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { NavigationDrawerSectionForWorkspaceItemsListReadOnly } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemsListReadOnly';
|
||||
import { WorkspaceSectionAddMenuItemButton } from '@/object-metadata/components/WorkspaceSectionAddMenuItemButton';
|
||||
import type { WorkspaceSectionListDndKitProps } from '@/object-metadata/components/WorkspaceSectionListDndKitProps';
|
||||
import { NavigationDrawerSectionForWorkspaceItemsListReadOnly } from '@/navigation-menu-item/display/sections/components/NavigationDrawerSectionForWorkspaceItemsListReadOnly';
|
||||
import { WorkspaceSectionAddMenuItemButton } from '@/navigation-menu-item/edit/components/WorkspaceSectionAddMenuItemButton';
|
||||
import type { WorkspaceSectionListDndKitProps } from '@/navigation-menu-item/display/sections/types/WorkspaceSectionListDndKitProps';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledFallback = styled.div`
|
||||
|
||||
Reference in New Issue
Block a user