Navbar drag drop using dnd kit (#18288)
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
export type EditModeProps = {
|
||||
isSelectedInEditMode: boolean;
|
||||
onEditModeClick?: () => void;
|
||||
};
|
||||
+9
-16
@@ -3,6 +3,7 @@ import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/
|
||||
import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay';
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { useObjectNavItemColor } from '@/navigation-menu-item/hooks/useObjectNavItemColor';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
|
||||
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
|
||||
import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
|
||||
@@ -28,7 +29,6 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
export type NavigationDrawerItemForObjectMetadataItemProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
navigationMenuItem?: ProcessedNavigationMenuItem;
|
||||
isEditMode?: boolean;
|
||||
isSelectedInEditMode?: boolean;
|
||||
onEditModeClick?: () => void;
|
||||
onActiveItemClickWhenNotInEditMode?: () => void;
|
||||
@@ -38,12 +38,14 @@ export type NavigationDrawerItemForObjectMetadataItemProps = {
|
||||
export const NavigationDrawerItemForObjectMetadataItem = ({
|
||||
objectMetadataItem,
|
||||
navigationMenuItem,
|
||||
isEditMode = false,
|
||||
isSelectedInEditMode = false,
|
||||
onEditModeClick,
|
||||
onActiveItemClickWhenNotInEditMode,
|
||||
onActiveItemClickWhenNotInEditMode: _onActiveItemClickWhenNotInEditMode,
|
||||
isDragging = false,
|
||||
}: NavigationDrawerItemForObjectMetadataItemProps) => {
|
||||
const isNavigationMenuInEditMode = useAtomStateValue(
|
||||
isNavigationMenuInEditModeState,
|
||||
);
|
||||
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
|
||||
);
|
||||
@@ -99,18 +101,9 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
|
||||
}) + '/',
|
||||
);
|
||||
|
||||
const shouldUseClickHandler = isEditMode
|
||||
? Boolean(onEditModeClick)
|
||||
: isActive && Boolean(onActiveItemClickWhenNotInEditMode);
|
||||
const handleClick = isNavigationMenuInEditMode ? onEditModeClick : undefined;
|
||||
|
||||
const handleClick = shouldUseClickHandler
|
||||
? isEditMode
|
||||
? onEditModeClick
|
||||
: onActiveItemClickWhenNotInEditMode
|
||||
: undefined;
|
||||
|
||||
const shouldNavigate =
|
||||
!isEditMode && !(isActive && onActiveItemClickWhenNotInEditMode);
|
||||
const shouldNavigate = !isNavigationMenuInEditMode;
|
||||
|
||||
const isViewWithCustomName =
|
||||
isView &&
|
||||
@@ -218,7 +211,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
|
||||
label={label}
|
||||
secondaryLabel={secondaryLabel}
|
||||
to={
|
||||
isEditMode || isDragging
|
||||
isNavigationMenuInEditMode || isDragging
|
||||
? undefined
|
||||
: shouldNavigate
|
||||
? navigationPath
|
||||
@@ -230,7 +223,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({
|
||||
active={isActive}
|
||||
isSelectedInEditMode={isSelectedInEditMode}
|
||||
isDragging={isDragging}
|
||||
triggerEvent={isEditMode ? 'CLICK' : undefined}
|
||||
triggerEvent={isNavigationMenuInEditMode ? 'CLICK' : undefined}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+16
-6
@@ -3,10 +3,12 @@ import { useParams } from 'react-router-dom';
|
||||
import { useWorkspaceFavorites } from '@/favorites/hooks/useWorkspaceFavorites';
|
||||
import { useWorkspaceNavigationMenuItems } from '@/navigation-menu-item/hooks/useWorkspaceNavigationMenuItems';
|
||||
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
|
||||
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
@@ -24,14 +26,22 @@ export const NavigationDrawerOpenedSection = () => {
|
||||
const filteredActiveNonSystemObjectMetadataItems =
|
||||
activeObjectMetadataItems.filter((item) => !item.isRemote);
|
||||
|
||||
const loading = useIsPrefetchLoading();
|
||||
const isPrefetchLoading = useIsPrefetchLoading();
|
||||
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
|
||||
);
|
||||
const prefetchIsLoaded = useAtomFamilyStateValue(
|
||||
prefetchIsLoadedFamilyState,
|
||||
PrefetchKey.AllNavigationMenuItems,
|
||||
);
|
||||
|
||||
const loading =
|
||||
isPrefetchLoading ||
|
||||
(isNavigationMenuItemEditingEnabled && !prefetchIsLoaded);
|
||||
|
||||
const { workspaceFavoritesObjectMetadataItems } = useWorkspaceFavorites();
|
||||
const { workspaceNavigationMenuItemsObjectMetadataItems } =
|
||||
useWorkspaceNavigationMenuItems();
|
||||
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
|
||||
);
|
||||
|
||||
const {
|
||||
objectNamePlural: currentObjectNamePlural,
|
||||
@@ -67,7 +77,7 @@ export const NavigationDrawerOpenedSection = () => {
|
||||
.includes(objectMetadataItem.id);
|
||||
|
||||
if (loading) {
|
||||
return <NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader />;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
+1
-3
@@ -28,7 +28,6 @@ type NavigationDrawerSectionForObjectMetadataItemsProps = {
|
||||
isRemote: boolean;
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
rightIcon?: React.ReactNode;
|
||||
isEditMode?: boolean;
|
||||
selectedObjectMetadataItemId?: string | null;
|
||||
onObjectMetadataItemClick?: (objectMetadataItem: ObjectMetadataItem) => void;
|
||||
onActiveObjectMetadataItemClick?: (
|
||||
@@ -41,7 +40,6 @@ export const NavigationDrawerSectionForObjectMetadataItems = ({
|
||||
isRemote,
|
||||
objectMetadataItems,
|
||||
rightIcon,
|
||||
isEditMode = false,
|
||||
selectedObjectMetadataItemId = null,
|
||||
onObjectMetadataItemClick,
|
||||
onActiveObjectMetadataItemClick,
|
||||
@@ -119,6 +117,7 @@ export const NavigationDrawerSectionForObjectMetadataItems = ({
|
||||
label={sectionTitle}
|
||||
onClick={() => toggleNavigationSection()}
|
||||
rightIcon={rightIcon}
|
||||
isOpen={isNavigationSectionOpen}
|
||||
/>
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
{isNavigationSectionOpen &&
|
||||
@@ -127,7 +126,6 @@ export const NavigationDrawerSectionForObjectMetadataItems = ({
|
||||
<NavigationDrawerItemForObjectMetadataItem
|
||||
key={`navigation-drawer-item-${objectMetadataItem.id}`}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
isEditMode={isEditMode}
|
||||
isSelectedInEditMode={
|
||||
selectedObjectMetadataItemId === objectMetadataItem.id
|
||||
}
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
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.itemType) {
|
||||
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
@@ -0,0 +1,72 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon';
|
||||
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
|
||||
import type { ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
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 { 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 linkItem = item as ProcessedNavigationMenuItem;
|
||||
return (
|
||||
<NavigationDrawerItem
|
||||
label={linkItem.labelIdentifier}
|
||||
to={isNavigationMenuInEditMode || isDragging ? undefined : linkItem.link}
|
||||
onClick={
|
||||
isNavigationMenuInEditMode ? editModeProps.onEditModeClick : undefined
|
||||
}
|
||||
Icon={() => <NavigationMenuItemIcon navigationMenuItem={linkItem} />}
|
||||
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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/utils/getObjectMetadataForNavigationMenuItem';
|
||||
import type { ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
import { NavigationDrawerItemForObjectMetadataItem } from '@/object-metadata/components/NavigationDrawerItemForObjectMetadataItem';
|
||||
import type { WorkspaceSectionItemContentProps } from '@/object-metadata/components/WorkspaceSectionItemContentProps';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemObjectContentProps =
|
||||
WorkspaceSectionItemContentProps;
|
||||
|
||||
export const NavigationDrawerSectionForWorkspaceItemObjectContent = ({
|
||||
item,
|
||||
editModeProps,
|
||||
isDragging,
|
||||
onActiveObjectMetadataItemClick,
|
||||
}: NavigationDrawerSectionForWorkspaceItemObjectContentProps) => {
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsState);
|
||||
const coreViews = useAtomStateValue(coreViewsState);
|
||||
const views = coreViews.map(convertCoreViewToView);
|
||||
const objectMetadataItem = getObjectMetadataForNavigationMenuItem(
|
||||
item as ProcessedNavigationMenuItem,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
);
|
||||
if (!objectMetadataItem) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<NavigationDrawerItemForObjectMetadataItem
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
navigationMenuItem={item as ProcessedNavigationMenuItem}
|
||||
isSelectedInEditMode={editModeProps.isSelectedInEditMode}
|
||||
onEditModeClick={editModeProps.onEditModeClick}
|
||||
isDragging={isDragging}
|
||||
onActiveItemClickWhenNotInEditMode={
|
||||
onActiveObjectMetadataItemClick
|
||||
? () => onActiveObjectMetadataItemClick(objectMetadataItem, item.id)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+54
-218
@@ -1,33 +1,25 @@
|
||||
import { NavigationDropTargetContext } from '@/navigation-menu-item/contexts/NavigationDropTargetContext';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Droppable } from '@hello-pangea/dnd';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useContext } from 'react';
|
||||
import React, { lazy, Suspense, useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconLink, IconPlus } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { AnimatedExpandableContainer } from 'twenty-ui/layout';
|
||||
|
||||
import { NavigationItemDropTarget } from '@/navigation-menu-item/components/NavigationItemDropTarget';
|
||||
import { WorkspaceNavigationMenuItemsFolder } from '@/navigation-menu-item/components/WorkspaceNavigationMenuItemsFolder';
|
||||
import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/constants/NavigationMenuItemDroppableIds';
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { NavigationSections } from '@/navigation-menu-item/constants/NavigationSections.constants';
|
||||
import { NavigationMenuItemDragContext } from '@/navigation-menu-item/contexts/NavigationMenuItemDragContext';
|
||||
import { useIsDropDisabledForSection } from '@/navigation-menu-item/hooks/useIsDropDisabledForSection';
|
||||
import {
|
||||
type FlatWorkspaceItem,
|
||||
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 ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
import { NavigationDrawerItemForObjectMetadataItem } from '@/object-metadata/components/NavigationDrawerItemForObjectMetadataItem';
|
||||
import type { EditModeProps } from '@/object-metadata/components/EditModeProps';
|
||||
import { NavigationDrawerSectionForWorkspaceItemsListReadOnly } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemsListReadOnly';
|
||||
import { WorkspaceSectionListEditModeFallback } from '@/object-metadata/components/WorkspaceSectionListEditModeFallback';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
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';
|
||||
@@ -35,18 +27,16 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
|
||||
const StyledWorkspaceDroppableList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.betweenSiblingsGap};
|
||||
`;
|
||||
const LazyWorkspaceSectionListDndKit = lazy(() =>
|
||||
import(
|
||||
'@/object-metadata/components/NavigationDrawerSectionForWorkspaceItemsListDndKit'
|
||||
).then((m) => ({ default: m.WorkspaceSectionListDndKit })),
|
||||
);
|
||||
|
||||
type NavigationDrawerSectionForWorkspaceItemsProps = {
|
||||
sectionTitle: string;
|
||||
items: FlatWorkspaceItem[];
|
||||
rightIcon?: React.ReactNode;
|
||||
onAddMenuItem?: () => void;
|
||||
isEditMode?: boolean;
|
||||
selectedNavigationMenuItemId?: string | null;
|
||||
onNavigationMenuItemClick?: (params: NavigationMenuItemClickParams) => void;
|
||||
onActiveObjectMetadataItemClick?: (
|
||||
@@ -59,14 +49,13 @@ export const NavigationDrawerSectionForWorkspaceItems = ({
|
||||
sectionTitle,
|
||||
items,
|
||||
rightIcon,
|
||||
onAddMenuItem,
|
||||
isEditMode = false,
|
||||
selectedNavigationMenuItemId = null,
|
||||
onNavigationMenuItemClick,
|
||||
onActiveObjectMetadataItemClick,
|
||||
}: NavigationDrawerSectionForWorkspaceItemsProps) => {
|
||||
const { t } = useLingui();
|
||||
const workspaceDropDisabled = useIsDropDisabledForSection(true);
|
||||
const isNavigationMenuInEditMode = useAtomStateValue(
|
||||
isNavigationMenuInEditModeState,
|
||||
);
|
||||
const { toggleNavigationSection, isNavigationSectionOpen } =
|
||||
useNavigationSection('Workspace');
|
||||
const coreViews = useAtomStateValue(coreViewsState);
|
||||
@@ -74,7 +63,6 @@ export const NavigationDrawerSectionForWorkspaceItems = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsState);
|
||||
const { isDragging } = useContext(NavigationMenuItemDragContext);
|
||||
const { addToNavigationFallbackDestination } = useContext(
|
||||
NavigationDropTargetContext,
|
||||
);
|
||||
@@ -95,10 +83,6 @@ export const NavigationDrawerSectionForWorkspaceItems = ({
|
||||
return acc;
|
||||
}, new Map());
|
||||
|
||||
const folderCount = flatItems.filter(
|
||||
(item) => item.itemType === NavigationMenuItemType.FOLDER,
|
||||
).length;
|
||||
|
||||
const filteredItems = flatItems.filter((item) => {
|
||||
const type = item.itemType;
|
||||
if (
|
||||
@@ -127,7 +111,7 @@ export const NavigationDrawerSectionForWorkspaceItems = ({
|
||||
return false;
|
||||
});
|
||||
|
||||
const getEditModeProps = (item: FlatWorkspaceItem) => {
|
||||
const getEditModeProps = (item: FlatWorkspaceItem): EditModeProps => {
|
||||
const itemId = item.id;
|
||||
return {
|
||||
isSelectedInEditMode: selectedNavigationMenuItemId === itemId,
|
||||
@@ -151,9 +135,6 @@ export const NavigationDrawerSectionForWorkspaceItems = ({
|
||||
};
|
||||
};
|
||||
|
||||
const isAddMenuItemButtonVisible =
|
||||
isEditMode && isDefined(onAddMenuItem) && !isDragging;
|
||||
|
||||
if (flatItems.length === 0 && !isAddToNavigationDropTargetVisible) {
|
||||
return null;
|
||||
}
|
||||
@@ -165,193 +146,48 @@ export const NavigationDrawerSectionForWorkspaceItems = ({
|
||||
label={sectionTitle}
|
||||
onClick={() => toggleNavigationSection()}
|
||||
rightIcon={rightIcon}
|
||||
alwaysShowRightIcon={isEditMode}
|
||||
alwaysShowRightIcon={isNavigationMenuInEditMode}
|
||||
isOpen={isNavigationSectionOpen}
|
||||
/>
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
{(isNavigationSectionOpen || isAddToNavigationDropTargetVisible) && (
|
||||
<Droppable
|
||||
droppableId={
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS
|
||||
}
|
||||
isDropDisabled={workspaceDropDisabled}
|
||||
>
|
||||
{(provided) => (
|
||||
<StyledWorkspaceDroppableList
|
||||
ref={provided.innerRef}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{filteredItems.map((item, index) => {
|
||||
const type = item.itemType;
|
||||
const editModeProps = getEditModeProps(item);
|
||||
|
||||
if (type === 'folder') {
|
||||
return (
|
||||
<NavigationItemDropTarget
|
||||
key={item.id}
|
||||
folderId={null}
|
||||
index={index}
|
||||
sectionId={NavigationSections.WORKSPACE}
|
||||
>
|
||||
<DraggableItem
|
||||
draggableId={item.id}
|
||||
index={index}
|
||||
isInsideScrollableContainer
|
||||
isDragDisabled={!isEditMode}
|
||||
disableInteractiveElementBlocking={isEditMode}
|
||||
itemComponent={
|
||||
<WorkspaceNavigationMenuItemsFolder
|
||||
folderId={item.id}
|
||||
folderName={item.name ?? 'Folder'}
|
||||
folderIconKey={item.Icon}
|
||||
folderColor={
|
||||
'color' in item ? item.color : undefined
|
||||
}
|
||||
navigationMenuItems={
|
||||
folderChildrenById.get(item.id) ?? []
|
||||
}
|
||||
isGroup={folderCount > 1}
|
||||
isEditMode={isEditMode}
|
||||
isSelectedInEditMode={
|
||||
editModeProps.isSelectedInEditMode
|
||||
}
|
||||
onEditModeClick={editModeProps.onEditModeClick}
|
||||
onNavigationMenuItemClick={
|
||||
onNavigationMenuItemClick
|
||||
}
|
||||
selectedNavigationMenuItemId={
|
||||
selectedNavigationMenuItemId
|
||||
}
|
||||
isDragging={isDragging}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</NavigationItemDropTarget>
|
||||
);
|
||||
<AnimatedExpandableContainer
|
||||
isExpanded={
|
||||
isNavigationSectionOpen || isAddToNavigationDropTargetVisible
|
||||
}
|
||||
dimension="height"
|
||||
mode="fit-content"
|
||||
containAnimation
|
||||
initial={false}
|
||||
>
|
||||
{isNavigationMenuInEditMode ? (
|
||||
<Suspense
|
||||
fallback={
|
||||
<WorkspaceSectionListEditModeFallback
|
||||
filteredItems={filteredItems}
|
||||
folderChildrenById={folderChildrenById}
|
||||
onActiveObjectMetadataItemClick={
|
||||
onActiveObjectMetadataItemClick
|
||||
}
|
||||
|
||||
if (type === 'link') {
|
||||
const linkItem = item as ProcessedNavigationMenuItem;
|
||||
return (
|
||||
<NavigationItemDropTarget
|
||||
key={item.id}
|
||||
folderId={null}
|
||||
index={index}
|
||||
sectionId={NavigationSections.WORKSPACE}
|
||||
>
|
||||
<DraggableItem
|
||||
draggableId={item.id}
|
||||
index={index}
|
||||
isInsideScrollableContainer
|
||||
isDragDisabled={!isEditMode}
|
||||
disableInteractiveElementBlocking={isEditMode}
|
||||
itemComponent={
|
||||
<NavigationDrawerItem
|
||||
label={linkItem.labelIdentifier}
|
||||
to={
|
||||
isEditMode || isDragging
|
||||
? undefined
|
||||
: linkItem.link
|
||||
}
|
||||
onClick={
|
||||
isEditMode
|
||||
? editModeProps.onEditModeClick
|
||||
: undefined
|
||||
}
|
||||
Icon={IconLink}
|
||||
iconColor={linkItem.color}
|
||||
active={false}
|
||||
isSelectedInEditMode={
|
||||
editModeProps.isSelectedInEditMode
|
||||
}
|
||||
isDragging={isDragging}
|
||||
triggerEvent="CLICK"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</NavigationItemDropTarget>
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadataItem =
|
||||
getObjectMetadataForNavigationMenuItem(
|
||||
item as ProcessedNavigationMenuItem,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
);
|
||||
if (!objectMetadataItem) return null;
|
||||
|
||||
return (
|
||||
<NavigationItemDropTarget
|
||||
key={item.id}
|
||||
folderId={null}
|
||||
index={index}
|
||||
sectionId={NavigationSections.WORKSPACE}
|
||||
>
|
||||
<DraggableItem
|
||||
draggableId={item.id}
|
||||
index={index}
|
||||
isInsideScrollableContainer
|
||||
isDragDisabled={!isEditMode}
|
||||
disableInteractiveElementBlocking={isEditMode}
|
||||
itemComponent={
|
||||
<NavigationDrawerItemForObjectMetadataItem
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
navigationMenuItem={
|
||||
item as ProcessedNavigationMenuItem
|
||||
}
|
||||
isEditMode={isEditMode}
|
||||
isSelectedInEditMode={
|
||||
editModeProps.isSelectedInEditMode
|
||||
}
|
||||
onEditModeClick={editModeProps.onEditModeClick}
|
||||
isDragging={isDragging}
|
||||
onActiveItemClickWhenNotInEditMode={
|
||||
onActiveObjectMetadataItemClick
|
||||
? () =>
|
||||
onActiveObjectMetadataItemClick(
|
||||
objectMetadataItem,
|
||||
item.id,
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</NavigationItemDropTarget>
|
||||
);
|
||||
})}
|
||||
<NavigationItemDropTarget
|
||||
folderId={null}
|
||||
index={filteredItems.length}
|
||||
sectionId={NavigationSections.WORKSPACE}
|
||||
compact={!isAddMenuItemButtonVisible}
|
||||
>
|
||||
{isAddMenuItemButtonVisible && (
|
||||
<NavigationDrawerItem
|
||||
Icon={IconPlus}
|
||||
label={t`Add menu item`}
|
||||
onClick={onAddMenuItem}
|
||||
triggerEvent="CLICK"
|
||||
/>
|
||||
)}
|
||||
</NavigationItemDropTarget>
|
||||
{addToNavigationFallbackDestination?.droppableId ===
|
||||
NavigationMenuItemDroppableIds.WORKSPACE_ORPHAN_NAVIGATION_MENU_ITEMS &&
|
||||
addToNavigationFallbackDestination.index >
|
||||
filteredItems.length && (
|
||||
<NavigationItemDropTarget
|
||||
folderId={null}
|
||||
index={addToNavigationFallbackDestination.index}
|
||||
sectionId={NavigationSections.WORKSPACE}
|
||||
compact
|
||||
/>
|
||||
)}
|
||||
{provided.placeholder}
|
||||
</StyledWorkspaceDroppableList>
|
||||
)}
|
||||
</Droppable>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LazyWorkspaceSectionListDndKit
|
||||
filteredItems={filteredItems}
|
||||
getEditModeProps={getEditModeProps}
|
||||
folderChildrenById={folderChildrenById}
|
||||
selectedNavigationMenuItemId={selectedNavigationMenuItemId}
|
||||
onNavigationMenuItemClick={onNavigationMenuItemClick}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
/>
|
||||
</Suspense>
|
||||
) : (
|
||||
<NavigationDrawerSectionForWorkspaceItemsListReadOnly
|
||||
filteredItems={filteredItems}
|
||||
folderChildrenById={folderChildrenById}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
/>
|
||||
)}
|
||||
</AnimatedExpandableContainer>
|
||||
</NavigationDrawerSection>
|
||||
);
|
||||
};
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
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 '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { NavigationDropTargetContext } from '@/navigation-menu-item/contexts/NavigationDropTargetContext';
|
||||
import { NavigationMenuItemDragContext } from '@/navigation-menu-item/contexts/NavigationMenuItemDragContext';
|
||||
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 { 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};
|
||||
`;
|
||||
|
||||
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.itemType === 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>
|
||||
);
|
||||
};
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import type { FlatWorkspaceItem } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
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};
|
||||
`;
|
||||
|
||||
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.itemType === NavigationMenuItemType.FOLDER,
|
||||
).length;
|
||||
|
||||
return (
|
||||
<StyledList>
|
||||
{filteredItems.map((item: FlatWorkspaceItem) => (
|
||||
<NavigationDrawerSectionForWorkspaceItemContent
|
||||
key={item.id}
|
||||
item={item}
|
||||
editModeProps={READ_ONLY_EDIT_MODE_PROPS}
|
||||
isDragging={false}
|
||||
folderChildrenById={folderChildrenById}
|
||||
folderCount={folderCount}
|
||||
selectedNavigationMenuItemId={null}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
readOnly
|
||||
/>
|
||||
))}
|
||||
</StyledList>
|
||||
);
|
||||
};
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
import { styled } from '@linaria/react';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTitleSkeleton = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: ${themeCssVariables.spacing[5]};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
padding-right: ${themeCssVariables.spacing['0.5']};
|
||||
`;
|
||||
|
||||
const StyledRowsContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
export const NavigationDrawerWorkspaceSectionSkeletonLoader = () => {
|
||||
return (
|
||||
<NavigationDrawerSection>
|
||||
<SkeletonTheme
|
||||
baseColor={themeCssVariables.background.tertiary}
|
||||
highlightColor={themeCssVariables.background.transparent.light}
|
||||
borderRadius={4}
|
||||
>
|
||||
<StyledTitleSkeleton>
|
||||
<Skeleton
|
||||
width={72}
|
||||
height={SKELETON_LOADER_HEIGHT_SIZES.standard.xs}
|
||||
/>
|
||||
</StyledTitleSkeleton>
|
||||
<StyledRowsContainer>
|
||||
<Skeleton
|
||||
width={196}
|
||||
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
|
||||
/>
|
||||
<Skeleton
|
||||
width={196}
|
||||
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
|
||||
/>
|
||||
<Skeleton
|
||||
width={196}
|
||||
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
|
||||
/>
|
||||
</StyledRowsContainer>
|
||||
</SkeletonTheme>
|
||||
</NavigationDrawerSection>
|
||||
);
|
||||
};
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
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 { 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 type { ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
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: ProcessedNavigationMenuItem[];
|
||||
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
@@ -0,0 +1,31 @@
|
||||
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
@@ -0,0 +1,55 @@
|
||||
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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import type {
|
||||
FlatWorkspaceItem,
|
||||
NavigationMenuItemClickParams,
|
||||
} from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import type { ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
import type { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
import type { EditModeProps } from '@/object-metadata/components/EditModeProps';
|
||||
|
||||
export type WorkspaceSectionItemContentProps = {
|
||||
item: FlatWorkspaceItem;
|
||||
editModeProps: EditModeProps;
|
||||
isDragging: boolean;
|
||||
folderChildrenById: Map<string, ProcessedNavigationMenuItem[]>;
|
||||
folderCount: number;
|
||||
selectedNavigationMenuItemId: string | null;
|
||||
onNavigationMenuItemClick?: (params: NavigationMenuItemClickParams) => void;
|
||||
onActiveObjectMetadataItemClick?: (
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
navigationMenuItemId: string,
|
||||
) => void;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import type {
|
||||
FlatWorkspaceItem,
|
||||
NavigationMenuItemClickParams,
|
||||
} from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import type { ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems';
|
||||
import type { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
import type { EditModeProps } from '@/object-metadata/components/EditModeProps';
|
||||
|
||||
export type WorkspaceSectionListDndKitProps = {
|
||||
filteredItems: FlatWorkspaceItem[];
|
||||
getEditModeProps: (item: FlatWorkspaceItem) => EditModeProps;
|
||||
folderChildrenById: Map<string, ProcessedNavigationMenuItem[]>;
|
||||
selectedNavigationMenuItemId: string | null;
|
||||
onNavigationMenuItemClick?: (params: NavigationMenuItemClickParams) => void;
|
||||
onActiveObjectMetadataItemClick?: (
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
navigationMenuItemId: string,
|
||||
) => void;
|
||||
};
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
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 { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledFallback = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
type WorkspaceSectionListEditModeFallbackProps = Pick<
|
||||
WorkspaceSectionListDndKitProps,
|
||||
'filteredItems' | 'folderChildrenById' | 'onActiveObjectMetadataItemClick'
|
||||
>;
|
||||
|
||||
export const WorkspaceSectionListEditModeFallback = ({
|
||||
filteredItems,
|
||||
folderChildrenById,
|
||||
onActiveObjectMetadataItemClick,
|
||||
}: WorkspaceSectionListEditModeFallbackProps) => (
|
||||
<StyledFallback>
|
||||
<NavigationDrawerSectionForWorkspaceItemsListReadOnly
|
||||
filteredItems={filteredItems}
|
||||
folderChildrenById={folderChildrenById}
|
||||
onActiveObjectMetadataItemClick={onActiveObjectMetadataItemClick}
|
||||
/>
|
||||
<WorkspaceSectionAddMenuItemButton />
|
||||
</StyledFallback>
|
||||
);
|
||||
Reference in New Issue
Block a user