mirror of
https://github.com/OFFIS-ESC/constellation-analyzer
synced 2026-01-27 07:43:41 +00:00
Implements a comprehensive label system and completely redesigns all filtering (labels, actor types, relation types) to use intuitive positive filtering where empty selection shows all items. Label System Features: - Create, edit, delete labels with names, colors, and scope (actors/relations/both) - Inline editing with click-to-edit UI for quick modifications - Quick-add label forms in config modals - Autocomplete label selector with inline label creation - Label badges rendered on nodes and edges (no overflow limits) - Full undo/redo support for label operations - Label validation and cleanup when labels are deleted - Labels stored per-document in workspace system Filter System Redesign: - Changed from negative to positive filtering for all filter types - Empty selection = show all items (intuitive default) - Selected items = show only those items (positive filter) - Consistent behavior across actor types, relation types, and labels - Clear visual feedback with selection counts and helper text - Auto-zoom viewport adjustment works for all filter types including labels Label Cleanup & Validation: - When label deleted, automatically removed from all nodes/edges across all timeline states - Label references validated during node/edge updates - Unknown label IDs filtered out to maintain data integrity UI Improvements: - All labels rendered without overflow limits (removed +N more indicators) - Filter checkboxes start unchecked (select to filter, not hide) - Helper text explains current filter state - Selection counts displayed in filter section headers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
174 lines
4.3 KiB
TypeScript
174 lines
4.3 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
|
|
/**
|
|
* Panel Store - Manages state of collapsible side panels
|
|
*
|
|
* Features:
|
|
* - Left panel (tools) visibility and width
|
|
* - Right panel (properties) visibility and width
|
|
* - Panel state persistence to localStorage
|
|
* - Collapsed section state within panels
|
|
*/
|
|
|
|
interface PanelState {
|
|
// Left Panel
|
|
leftPanelVisible: boolean;
|
|
leftPanelWidth: number;
|
|
leftPanelCollapsed: boolean;
|
|
leftPanelSections: {
|
|
history: boolean;
|
|
addActors: boolean;
|
|
relations: boolean;
|
|
labels: boolean;
|
|
layout: boolean;
|
|
view: boolean;
|
|
search: boolean;
|
|
};
|
|
|
|
// Right Panel
|
|
rightPanelVisible: boolean;
|
|
rightPanelWidth: number;
|
|
rightPanelCollapsed: boolean;
|
|
|
|
// Bottom Panel (Timeline)
|
|
bottomPanelVisible: boolean;
|
|
bottomPanelHeight: number;
|
|
bottomPanelCollapsed: boolean;
|
|
|
|
// Actions
|
|
toggleLeftPanel: () => void;
|
|
toggleRightPanel: () => void;
|
|
setLeftPanelWidth: (width: number) => void;
|
|
setRightPanelWidth: (width: number) => void;
|
|
setBottomPanelHeight: (height: number) => void;
|
|
toggleLeftPanelSection: (section: keyof PanelState['leftPanelSections']) => void;
|
|
collapseLeftPanel: () => void;
|
|
expandLeftPanel: () => void;
|
|
collapseRightPanel: () => void;
|
|
expandRightPanel: () => void;
|
|
collapseBottomPanel: () => void;
|
|
expandBottomPanel: () => void;
|
|
}
|
|
|
|
const DEFAULT_LEFT_WIDTH = 280;
|
|
const DEFAULT_RIGHT_WIDTH = 320;
|
|
const DEFAULT_BOTTOM_HEIGHT = 200;
|
|
const MIN_LEFT_WIDTH = 240;
|
|
const MAX_LEFT_WIDTH = 400;
|
|
const MIN_RIGHT_WIDTH = 280;
|
|
const MAX_RIGHT_WIDTH = 500;
|
|
const MIN_BOTTOM_HEIGHT = 150;
|
|
const MAX_BOTTOM_HEIGHT = 500;
|
|
const COLLAPSED_LEFT_WIDTH = 40;
|
|
const COLLAPSED_BOTTOM_HEIGHT = 48;
|
|
|
|
export const usePanelStore = create<PanelState>()(
|
|
persist(
|
|
(set) => ({
|
|
// Initial state
|
|
leftPanelVisible: true,
|
|
leftPanelWidth: DEFAULT_LEFT_WIDTH,
|
|
leftPanelCollapsed: false,
|
|
leftPanelSections: {
|
|
history: true,
|
|
addActors: true,
|
|
relations: true,
|
|
labels: false,
|
|
layout: false,
|
|
view: false,
|
|
search: false,
|
|
},
|
|
|
|
rightPanelVisible: true,
|
|
rightPanelWidth: DEFAULT_RIGHT_WIDTH,
|
|
rightPanelCollapsed: false,
|
|
|
|
bottomPanelVisible: true, // Timeline panel is always visible (can be collapsed but not hidden)
|
|
bottomPanelHeight: DEFAULT_BOTTOM_HEIGHT,
|
|
bottomPanelCollapsed: false,
|
|
|
|
// Actions
|
|
toggleLeftPanel: () =>
|
|
set((state) => ({
|
|
leftPanelVisible: !state.leftPanelVisible,
|
|
})),
|
|
|
|
toggleRightPanel: () =>
|
|
set((state) => ({
|
|
rightPanelVisible: !state.rightPanelVisible,
|
|
})),
|
|
|
|
setLeftPanelWidth: (width: number) =>
|
|
set(() => ({
|
|
leftPanelWidth: Math.max(MIN_LEFT_WIDTH, Math.min(MAX_LEFT_WIDTH, width)),
|
|
})),
|
|
|
|
setRightPanelWidth: (width: number) =>
|
|
set(() => ({
|
|
rightPanelWidth: Math.max(MIN_RIGHT_WIDTH, Math.min(MAX_RIGHT_WIDTH, width)),
|
|
})),
|
|
|
|
setBottomPanelHeight: (height: number) =>
|
|
set(() => ({
|
|
bottomPanelHeight: Math.max(MIN_BOTTOM_HEIGHT, Math.min(MAX_BOTTOM_HEIGHT, height)),
|
|
})),
|
|
|
|
toggleLeftPanelSection: (section) =>
|
|
set((state) => ({
|
|
leftPanelSections: {
|
|
...state.leftPanelSections,
|
|
[section]: !state.leftPanelSections[section],
|
|
},
|
|
})),
|
|
|
|
collapseLeftPanel: () =>
|
|
set(() => ({
|
|
leftPanelCollapsed: true,
|
|
})),
|
|
|
|
expandLeftPanel: () =>
|
|
set(() => ({
|
|
leftPanelCollapsed: false,
|
|
})),
|
|
|
|
collapseRightPanel: () =>
|
|
set(() => ({
|
|
rightPanelCollapsed: true,
|
|
})),
|
|
|
|
expandRightPanel: () =>
|
|
set(() => ({
|
|
rightPanelCollapsed: false,
|
|
})),
|
|
|
|
collapseBottomPanel: () =>
|
|
set(() => ({
|
|
bottomPanelCollapsed: true,
|
|
})),
|
|
|
|
expandBottomPanel: () =>
|
|
set(() => ({
|
|
bottomPanelCollapsed: false,
|
|
})),
|
|
}),
|
|
{
|
|
name: 'constellation-panel-state',
|
|
}
|
|
)
|
|
);
|
|
|
|
// Export constants for use in components
|
|
export const PANEL_CONSTANTS = {
|
|
DEFAULT_LEFT_WIDTH,
|
|
DEFAULT_RIGHT_WIDTH,
|
|
DEFAULT_BOTTOM_HEIGHT,
|
|
MIN_LEFT_WIDTH,
|
|
MAX_LEFT_WIDTH,
|
|
MIN_RIGHT_WIDTH,
|
|
MAX_RIGHT_WIDTH,
|
|
MIN_BOTTOM_HEIGHT,
|
|
MAX_BOTTOM_HEIGHT,
|
|
COLLAPSED_LEFT_WIDTH,
|
|
COLLAPSED_BOTTOM_HEIGHT,
|
|
};
|