f1dfcfd163
## 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
129 lines
3.6 KiB
TypeScript
129 lines
3.6 KiB
TypeScript
import { styled } from '@linaria/react';
|
|
import { useLingui } from '@lingui/react/macro';
|
|
import { type ReactNode, lazy, Suspense, useState } from 'react';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
import { type IconComponent } from 'twenty-ui/display';
|
|
|
|
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
|
import { AddToNavigationDragHandle } from '@/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle';
|
|
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/states/addToNavPayloadRegistryState';
|
|
import type { AddToNavigationDragPayload } from '@/navigation-menu-item/common/types/add-to-navigation-drag-payload';
|
|
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
|
|
|
const CommandMenuItemWithAddToNavigationDragDndKit = lazy(() =>
|
|
import(
|
|
'@/command-menu/components/CommandMenuItemWithAddToNavigationDragDndKit'
|
|
).then((m) => ({
|
|
default: m.CommandMenuItemWithAddToNavigationDragDndKit,
|
|
})),
|
|
);
|
|
|
|
type SidePanelItemWithAddToNavigationDragProps = {
|
|
icon?: IconComponent;
|
|
customIconContent?: ReactNode;
|
|
label: string;
|
|
description?: string;
|
|
id: string;
|
|
onClick: () => void;
|
|
payload: AddToNavigationDragPayload;
|
|
dragIndex?: number;
|
|
disabled?: boolean;
|
|
disableDrag?: boolean;
|
|
};
|
|
|
|
const StyledDraggableMenuItem = styled.div<{
|
|
$disabled?: boolean;
|
|
$disableDrag?: boolean;
|
|
}>`
|
|
cursor: ${({ $disabled, $disableDrag }) =>
|
|
$disabled || $disableDrag ? 'default' : 'grab'};
|
|
pointer-events: ${({ $disabled }) => ($disabled ? 'none' : 'auto')};
|
|
width: 100%;
|
|
|
|
&:active {
|
|
cursor: ${({ $disabled, $disableDrag }) =>
|
|
$disabled || $disableDrag ? 'default' : 'grabbing'};
|
|
}
|
|
`;
|
|
|
|
export const SidePanelItemWithAddToNavigationDrag = ({
|
|
icon,
|
|
customIconContent,
|
|
label,
|
|
description,
|
|
id,
|
|
onClick,
|
|
payload,
|
|
dragIndex,
|
|
disabled = false,
|
|
disableDrag = false,
|
|
}: SidePanelItemWithAddToNavigationDragProps) => {
|
|
const { t } = useLingui();
|
|
const setAddToNavPayloadRegistry = useSetAtomState(
|
|
addToNavPayloadRegistryState,
|
|
);
|
|
const [isHovered, setIsHovered] = useState(false);
|
|
|
|
const showDragAffordance = !disabled && !disableDrag && isHovered;
|
|
const contextualDescription = showDragAffordance
|
|
? t`Drag to add to navbar`
|
|
: description;
|
|
|
|
const DragHandleIcon = () => (
|
|
<AddToNavigationDragHandle
|
|
icon={icon}
|
|
customIconContent={customIconContent}
|
|
payload={payload}
|
|
isHovered={showDragAffordance}
|
|
disabled={disabled}
|
|
disableDrag={disableDrag}
|
|
/>
|
|
);
|
|
|
|
const registerPayload = () => {
|
|
if (!disabled && !disableDrag && isDefined(dragIndex)) {
|
|
setAddToNavPayloadRegistry((prev) => new Map(prev).set(id, payload));
|
|
}
|
|
};
|
|
|
|
const menuItemContent = (
|
|
<StyledDraggableMenuItem
|
|
$disabled={disabled}
|
|
$disableDrag={disableDrag}
|
|
onMouseEnter={() => {
|
|
if (!disabled && !disableDrag) {
|
|
setIsHovered(true);
|
|
registerPayload();
|
|
}
|
|
}}
|
|
onMouseLeave={() => {
|
|
if (!disabled && !disableDrag) setIsHovered(false);
|
|
}}
|
|
onMouseDown={registerPayload}
|
|
>
|
|
<CommandMenuItem
|
|
Icon={DragHandleIcon}
|
|
label={label}
|
|
description={contextualDescription}
|
|
id={id}
|
|
onClick={onClick}
|
|
disabled={disabled}
|
|
/>
|
|
</StyledDraggableMenuItem>
|
|
);
|
|
|
|
if (!isDefined(dragIndex) || disableDrag) {
|
|
return menuItemContent;
|
|
}
|
|
|
|
return (
|
|
<Suspense fallback={menuItemContent}>
|
|
<CommandMenuItemWithAddToNavigationDragDndKit
|
|
id={id}
|
|
dragIndex={dragIndex}
|
|
menuItemContent={menuItemContent}
|
|
/>
|
|
</Suspense>
|
|
);
|
|
};
|