sortable sidebar collapse

This commit is contained in:
Nate Kelley 2025-03-13 13:21:36 -06:00
parent 96fe76cfe6
commit f923c3a428
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
5 changed files with 26 additions and 14 deletions

View File

@ -209,6 +209,10 @@ GlobalModals.displayName = 'GlobalModals';
const favoritesDropdown = (favorites: BusterUserFavorite[]): ISidebarGroup => { const favoritesDropdown = (favorites: BusterUserFavorite[]): ISidebarGroup => {
return { return {
label: 'Favorites', label: 'Favorites',
isSortable: true,
onItemsReorder: (itemIds) => {
console.warn('onItemsReorder', itemIds);
},
items: favorites.map((favorite) => { items: favorites.map((favorite) => {
const Icon = assetTypeToIcon(favorite.asset_type); const Icon = assetTypeToIcon(favorite.asset_type);
const route = assetTypeToRoute(favorite.asset_type, favorite.id); const route = assetTypeToRoute(favorite.asset_type, favorite.id);

View File

@ -2,21 +2,21 @@ import React from 'react';
import { ISidebarGroup, ISidebarList, SidebarProps } from './interfaces'; import { ISidebarGroup, ISidebarList, SidebarProps } from './interfaces';
import { SidebarCollapsible } from './SidebarCollapsible'; import { SidebarCollapsible } from './SidebarCollapsible';
import { SidebarItem } from './SidebarItem'; import { SidebarItem } from './SidebarItem';
import { useMemoizedFn } from '@/hooks';
export const Sidebar: React.FC<SidebarProps> = React.memo( export const Sidebar: React.FC<SidebarProps> = React.memo(
({ header, content, footer, activeItem, isSortable = false }) => { ({ header, content, footer, activeItem }) => {
const onItemsReorder = useMemoizedFn((ids: string[], contentIndex: number) => {
console.log('onItemsReorder', ids);
});
return ( return (
<div className="flex h-full flex-col overflow-hidden px-3.5 pt-4.5"> <div className="flex h-full flex-col overflow-hidden px-3.5 pt-4.5">
<div className="flex flex-col space-y-4.5 overflow-hidden"> <div className="flex flex-col space-y-4.5 overflow-hidden">
<div className="mb-5"> {header}</div> <div className="mb-5"> {header}</div>
<div className="flex flex-grow flex-col space-y-4.5 overflow-y-auto pb-3"> <div className="flex flex-grow flex-col space-y-4.5 overflow-y-auto pb-3">
{content.map((item, index) => ( {content.map((item, index) => (
<ContentSelector <ContentSelector key={index} content={item} activeItem={activeItem} />
key={index}
content={item}
activeItem={activeItem}
isSortable={isSortable}
/>
))} ))}
</div> </div>
</div> </div>
@ -31,10 +31,9 @@ Sidebar.displayName = 'Sidebar';
const ContentSelector: React.FC<{ const ContentSelector: React.FC<{
content: SidebarProps['content'][number]; content: SidebarProps['content'][number];
activeItem: SidebarProps['activeItem']; activeItem: SidebarProps['activeItem'];
isSortable: SidebarProps['isSortable']; }> = React.memo(({ content, activeItem }) => {
}> = React.memo(({ content, activeItem, isSortable }) => {
if (isSidebarGroup(content)) { if (isSidebarGroup(content)) {
return <SidebarCollapsible {...content} activeItem={activeItem} isSortable={isSortable} />; return <SidebarCollapsible {...content} activeItem={activeItem} />;
} }
return <SidebarList items={content.items} activeItem={activeItem} />; return <SidebarList items={content.items} activeItem={activeItem} />;

View File

@ -3,6 +3,7 @@ import { SidebarCollapsible } from './SidebarCollapsible';
import { HouseModern, MapSettings, User } from '../icons/NucleoIconOutlined'; import { HouseModern, MapSettings, User } from '../icons/NucleoIconOutlined';
import { BusterRoutes } from '../../../routes'; import { BusterRoutes } from '../../../routes';
import React from 'react'; import React from 'react';
import { fn } from '@storybook/test';
const meta: Meta<typeof SidebarCollapsible> = { const meta: Meta<typeof SidebarCollapsible> = {
title: 'UI/Sidebar/SidebarCollapsible', title: 'UI/Sidebar/SidebarCollapsible',
@ -50,7 +51,9 @@ export const Default: Story = {
export const Sortable: Story = { export const Sortable: Story = {
args: { args: {
...Default.args, label: 'Sortable',
isSortable: true isSortable: true,
items: Default.args!.items!,
onItemsReorder: fn()
} }
}; };

View File

@ -100,12 +100,15 @@ const SortableSidebarItem: React.FC<SortableSidebarItemProps> = React.memo(({ it
SortableSidebarItem.displayName = 'SortableSidebarItem'; SortableSidebarItem.displayName = 'SortableSidebarItem';
export const SidebarCollapsible: React.FC<ISidebarGroup & { activeItem?: string }> = React.memo( export const SidebarCollapsible: React.FC<
ISidebarGroup & { activeItem?: string; onItemsReorder?: (ids: string[]) => void }
> = React.memo(
({ ({
label, label,
items, items,
isSortable = false, isSortable = false,
activeItem, activeItem,
onItemsReorder,
variant = 'collapsible', variant = 'collapsible',
icon, icon,
defaultOpen = true defaultOpen = true
@ -133,7 +136,9 @@ export const SidebarCollapsible: React.FC<ISidebarGroup & { activeItem?: string
setSortedItems((items) => { setSortedItems((items) => {
const oldIndex = items.findIndex((item) => item.id === active.id); const oldIndex = items.findIndex((item) => item.id === active.id);
const newIndex = items.findIndex((item) => item.id === over?.id); const newIndex = items.findIndex((item) => item.id === over?.id);
return arrayMove(items, oldIndex, newIndex); const moveddArray = arrayMove(items, oldIndex, newIndex);
onItemsReorder?.(moveddArray.map((item) => item.id));
return moveddArray;
}); });
} }
}); });

View File

@ -18,6 +18,7 @@ export interface ISidebarGroup {
variant?: 'collapsible' | 'icon'; //default is collapsible variant?: 'collapsible' | 'icon'; //default is collapsible
defaultOpen?: boolean; //will default to true defaultOpen?: boolean; //will default to true
isSortable?: boolean; isSortable?: boolean;
onItemsReorder?: (ids: string[]) => void;
} }
export interface ISidebarList { export interface ISidebarList {