- Introduced a new KanbanGridLayout component for improved task organization and layout management. - Updated KanbanBoard to utilize the new grid layout, enhancing the visual structure of tasks. - Added CSS styles for grid layout and resizing handles to improve user interaction. - Refactored KanbanColumn to support additional customization options for headers and body styles. - Enhanced persistence flows to rely on repository abstractions, promoting better separation of concerns in storage management. - Updated package dependencies to include react-grid-layout and react-resizable for enhanced layout capabilities.
107 lines
3.1 KiB
TypeScript
107 lines
3.1 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
|
import {
|
|
createPersistedGridLayoutState,
|
|
mergeGridLayoutItems,
|
|
normalizePersistedGridLayoutState,
|
|
projectVisibleGridLayoutItems,
|
|
} from '@renderer/services/layout-system/gridLayoutSchema';
|
|
|
|
import type { GridLayoutRepository } from '@renderer/services/layout-system/GridLayoutRepository';
|
|
import type {
|
|
PersistedGridLayoutItem,
|
|
PersistedGridLayoutState,
|
|
} from '@renderer/services/layout-system/gridLayoutTypes';
|
|
|
|
interface UsePersistedGridLayoutOptions {
|
|
scopeKey: string;
|
|
allItemIds: string[];
|
|
visibleItemIds: string[];
|
|
cols: number;
|
|
repository: GridLayoutRepository<PersistedGridLayoutState>;
|
|
buildDefaultItems: (itemIds: string[]) => PersistedGridLayoutItem[];
|
|
}
|
|
|
|
interface UsePersistedGridLayoutResult {
|
|
allItems: PersistedGridLayoutItem[];
|
|
visibleItems: PersistedGridLayoutItem[];
|
|
isLoaded: boolean;
|
|
applyVisibleItems: (items: PersistedGridLayoutItem[], options?: { persist?: boolean }) => void;
|
|
}
|
|
|
|
export function usePersistedGridLayout({
|
|
scopeKey,
|
|
allItemIds,
|
|
visibleItemIds,
|
|
cols,
|
|
repository,
|
|
buildDefaultItems,
|
|
}: UsePersistedGridLayoutOptions): UsePersistedGridLayoutResult {
|
|
const defaultItems = useMemo(
|
|
() => buildDefaultItems(allItemIds),
|
|
[allItemIds, buildDefaultItems]
|
|
);
|
|
const [layoutState, setLayoutState] = useState<PersistedGridLayoutState>(() =>
|
|
normalizePersistedGridLayoutState(null, defaultItems)
|
|
);
|
|
const [loadedScopeKey, setLoadedScopeKey] = useState<string | null>(null);
|
|
const resolvedLayoutState = useMemo(
|
|
() => normalizePersistedGridLayoutState(layoutState, defaultItems),
|
|
[defaultItems, layoutState]
|
|
);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
void repository
|
|
.load(scopeKey)
|
|
.then((stored) => {
|
|
if (cancelled) return;
|
|
setLayoutState(normalizePersistedGridLayoutState(stored, defaultItems));
|
|
setLoadedScopeKey(scopeKey);
|
|
})
|
|
.catch(() => {
|
|
if (cancelled) return;
|
|
setLayoutState(normalizePersistedGridLayoutState(null, defaultItems));
|
|
setLoadedScopeKey(scopeKey);
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [defaultItems, repository, scopeKey]);
|
|
|
|
const visibleItems = useMemo(
|
|
() => projectVisibleGridLayoutItems(resolvedLayoutState.items, visibleItemIds, cols),
|
|
[cols, resolvedLayoutState.items, visibleItemIds]
|
|
);
|
|
|
|
const applyVisibleItems = useCallback(
|
|
(items: PersistedGridLayoutItem[], options?: { persist?: boolean }) => {
|
|
setLayoutState((current) => {
|
|
const mergedItems = mergeGridLayoutItems(
|
|
normalizePersistedGridLayoutState(current, defaultItems).items,
|
|
items
|
|
);
|
|
const nextState = normalizePersistedGridLayoutState(
|
|
createPersistedGridLayoutState(mergedItems),
|
|
defaultItems
|
|
);
|
|
|
|
if (options?.persist) {
|
|
void repository.save(scopeKey, nextState);
|
|
}
|
|
|
|
return nextState;
|
|
});
|
|
},
|
|
[defaultItems, repository, scopeKey]
|
|
);
|
|
|
|
return {
|
|
allItems: resolvedLayoutState.items,
|
|
visibleItems,
|
|
isLoaded: loadedScopeKey === scopeKey,
|
|
applyVisibleItems,
|
|
};
|
|
}
|