@e-llm-studio/creative-workspace-ui-component
v0.0.14
Published
A hierarchical tree navigation component for rendering nested scope/node structures with expandable sections, search, and configurable actions. The component is part of the `@e-llm-studio/creative-workspace-ui-component` package and can be used independen
Maintainers
Keywords
Readme
TreeSidebar Component
A hierarchical tree navigation component for rendering nested scope/node structures with expandable sections, search, and configurable actions. The component is part of the @e-llm-studio/creative-workspace-ui-component package and can be used independently in any React application.
Introduction
TreeSidebar is a standalone component from the @e-llm-studio/creative-workspace-ui-component package designed to provide a fully generic, hierarchical tree navigation panel. Its primary purpose is to render a tree of nodes (e.g. Global → Scope → Subscope) with expandable sections, search filtering, and optional action handlers. The consuming team controls what actions are available by passing or omitting handler props — if a handler is not passed, the corresponding button is not rendered.
Features
- Hierarchical tree rendering — Supports unlimited nesting levels (Global → Scope → Subscope → ...)
- Expand/collapse — Each node is independently expandable; root node auto-expands on load
- Section items — Each node can show configurable section tabs (e.g. Learnings, Stations) below it when expanded
- Search filtering — Pass a
searchQueryto filter nodes by name; non-matching subtrees are hidden - Configurable node types — Control labels, add-child labels and child types via
nodeTypesConfig - Configurable sections — Control which sections appear per node via
sectionsConfig - Conditional actions — Add, Rename, Delete buttons only render when the corresponding handler prop is passed
- Children count badge — Optional badge showing number of direct children per node
- Section badge — Optional badge on section items showing metrics (e.g. learnings count)
- Inline styles only — No Tailwind or MUI dependency, works in any consuming app regardless of styling setup
Installation
The TreeSidebar component is part of the @e-llm-studio/creative-workspace-ui-component package.
Using npm
npm install @e-llm-studio/creative-workspace-ui-componentUsing yarn
yarn add @e-llm-studio/creative-workspace-ui-componentImport Statement
import { TreeSidebar } from "@e-llm-studio/creative-workspace-ui-component";
// or via subpath
import { TreeSidebar } from "@e-llm-studio/creative-workspace-ui-component/TreeSidebar";Usage
Basic Example
Here's a simple example demonstrating how to use TreeSidebar with minimal props:
import { TreeSidebar } from "@e-llm-studio/creative-workspace-ui-component";
import { BookOpen } from "lucide-react";
const treeData = {
id: "root",
name: "Global Scope",
type: "GLOBAL" as const,
metrics: { learningsCount: 10 },
children: [
{
id: "scope-1",
name: "Scope 1",
type: "SCOPE" as const,
metrics: { learningsCount: 5 },
children: [],
},
],
};
export default function App() {
return (
<TreeSidebar
data={treeData}
sectionsConfig={[
{
id: "learnings",
label: "Learnings",
icon: BookOpen,
badgeKey: "learningsCount",
},
]}
nodeTypesConfig={[
{ type: "GLOBAL", label: "Global", addChildLabel: "Add Scope", addChildType: "SCOPE" },
{ type: "SCOPE", label: "Scope" },
]}
onSectionSelect={(nodeId, sectionId) => console.log(nodeId, sectionId)}
/>
);
}Key Props Explained:
data: The root node of the tree — passnullto render nothingsectionsConfig: Defines which section tabs appear under each expanded nodenodeTypesConfig: Defines node labels and add-child behavior per depth levelonSectionSelect: Fired when a section tab is clicked — returns the selectednodeIdandsectionId
Advanced Example
This example demonstrates all action handlers and badge visibility controls:
import { TreeSidebar } from "@e-llm-studio/creative-workspace-ui-component";
import { BookOpen } from "lucide-react";
export default function AdvancedExample() {
return (
<TreeSidebar
data={treeData}
sectionsConfig={[
{
id: "learnings",
label: "Learnings",
icon: BookOpen,
badgeKey: "learningsCount",
},
]}
nodeTypesConfig={[
{ type: "GLOBAL", label: "Global", addChildLabel: "Add Scope", addChildType: "SCOPE" },
{ type: "SCOPE", label: "Scope" },
]}
onSectionSelect={(nodeId, sectionId) => handleSelect(nodeId, sectionId)}
onAddChild={(parentId, childType, parentName) => handleAdd(parentId, childType, parentName)}
onRenameChild={(scopeId, initialName) => handleRename(scopeId, initialName)}
onDeleteChild={(scopeId, scopeName, learningsCount) => handleDelete(scopeId, scopeName, learningsCount)}
showChildrenCount={true}
showSectionBadge={true}
/>
);
}Customization Tips:
- Only pass the action handlers (
onAddChild,onRenameChild,onDeleteChild) your use case needs — unneeded buttons won't render - Use
showChildrenCount={false}to hide the children count badge on node headers - Use
showSectionBadge={false}to hide the metric badge on section items - Use
searchQuerywithonSearchCountChangeto implement a search bar above the tree
API Reference
Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| data | ScopeTreeNode \| null | ✅ | — | Root node of the tree. Pass null to render nothing. |
| sectionsConfig | TreeSectionConfig[] | ❌ | [] | Defines section tabs shown under each expanded node. |
| nodeTypesConfig | NodeTypeConfig[] | ❌ | [] | Defines node labels and add-child behavior per depth level. |
| defaultExpandedIds | string[] | ❌ | [] | Node IDs to expand by default on first render. |
| searchQuery | string | ❌ | '' | Filters nodes by name. Non-matching subtrees are hidden. |
| onSearchCountChange | (count: number) => void | ❌ | — | Fired when the search result count changes. |
| onSectionSelect | (nodeId: string, sectionId: string) => void | ❌ | — | Fired when a section tab is clicked. Returns the node ID and section ID. |
| onAddChild | (parentId: string, childType: 'SCOPE' \| 'SUBSCOPE', parentName?: string) => void | ❌ | — | If passed, an Add button appears in the node menu. |
| onRenameChild | (scopeId: string, initialName: string) => void | ❌ | — | If passed, a Rename button appears in the node menu. |
| onDeleteChild | (scopeId: string, scopeName: string, learningsCount: number) => void | ❌ | — | If passed, a Delete button appears in the node menu. |
| selectedRuleSetId | string \| null | ❌ | — | ID of the node to auto-select on load. |
| activeSectionId | string | ❌ | — | Section ID to auto-activate alongside selectedRuleSetId. |
| showChildrenCount | boolean | ❌ | true | Whether to show the children count badge on node headers. |
| showSectionBadge | boolean | ❌ | true | Whether to show the metric badge on section items. |
TreeSectionConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | ✅ | Unique section identifier. |
| label | string | ✅ | Display label for the section tab. |
| icon | ElementType | ✅ | Lucide icon component to show next to the label. |
| badgeKey | keyof NodeMetrics | ❌ | Maps to a key in node.metrics to show as a badge count. |
| isVisible | (node: ScopeTreeNode, depth: number) => boolean | ❌ | Optional visibility check per node. If omitted, section is visible for all nodes. |
NodeTypeConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| type | string | ✅ | Node type identifier (e.g. 'GLOBAL', 'SCOPE', 'SUBSCOPE'). |
| label | string | ✅ | Display label used in delete confirmation text. |
| addChildLabel | string | ❌ | Label for the add child button (e.g. 'Add Scope'). Required if addChildType is set. |
| addChildType | string | ❌ | The type of child node to create. If omitted, no add button is shown for this node type. |
ScopeTreeNode
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | ✅ | Unique identifier for the node. |
| name | string | ✅ | Display name of the node. |
| type | 'GLOBAL' \| 'SCOPE' \| 'SUBSCOPE' \| 'Custom' | ✅ | Node type. |
| metrics | NodeMetrics | ✅ | Metric values used by section badges. |
| children | ScopeTreeNode[] | ✅ | Child nodes. Pass [] for leaf nodes. |
Type Definitions
export interface TreeSidebarProps {
data: ScopeTreeNode | null;
defaultExpandedIds?: string[];
onSectionSelect?: (nodeId: string, sectionId: string) => void;
onNodeOptionsClick?: (nodeId: string) => void;
onAddChild?: (parentId: string, childType: 'SCOPE' | 'SUBSCOPE', parentName?: string) => void;
onRenameChild?: (scopeId: string, initialName: string) => void;
onDeleteChild?: (scopeId: string, scopeName: string, learningsCount: number) => void;
searchQuery?: string;
onSearchCountChange?: (count: number) => void;
sectionsConfig?: TreeSectionConfig[];
nodeTypesConfig?: NodeTypeConfig[];
selectedRuleSetId?: string | null;
activeSectionId?: string;
showChildrenCount?: boolean;
showSectionBadge?: boolean;
}
export interface TreeSectionConfig {
id: string;
label: string;
icon: any;
badgeKey?: keyof NodeMetrics;
isVisible?: (node: ScopeTreeNode, depth: number) => boolean;
}
export interface NodeTypeConfig {
type: string;
label: string;
addChildLabel?: string;
addChildType?: string;
}
export interface ScopeTreeNode {
id: string;
name: string;
type: 'GLOBAL' | 'SCOPE' | 'SUBSCOPE' | 'Custom';
metrics: NodeMetrics;
children: ScopeTreeNode[];
}
export interface NodeMetrics {
learningsCount: number;
stationsCount?: number;
}Examples
With search
const [searchQuery, setSearchQuery] = useState('');
const [searchCount, setSearchCount] = useState(0);
<>
<input
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search scopes..."
/>
<span>{searchCount} results</span>
<TreeSidebar
data={treeData}
sectionsConfig={sectionsConfig}
nodeTypesConfig={nodeTypesConfig}
searchQuery={searchQuery}
onSearchCountChange={setSearchCount}
/>
</>With only rename and delete (no add)
<TreeSidebar
data={treeData}
sectionsConfig={sectionsConfig}
nodeTypesConfig={nodeTypesConfig}
onRenameChild={(scopeId, name) => handleRename(scopeId, name)}
onDeleteChild={(scopeId, name, count) => handleDelete(scopeId, name, count)}
/>With badges hidden
<TreeSidebar
data={treeData}
sectionsConfig={sectionsConfig}
nodeTypesConfig={nodeTypesConfig}
showChildrenCount={false}
showSectionBadge={false}
/>With auto-selected node
<TreeSidebar
data={treeData}
sectionsConfig={sectionsConfig}
nodeTypesConfig={nodeTypesConfig}
selectedRuleSetId="scope-1"
activeSectionId="learnings"
/>With multiple sections
import { BookOpen, RadioTower } from 'lucide-react';
<TreeSidebar
data={treeData}
sectionsConfig={[
{ id: 'learnings', label: 'Learnings', icon: BookOpen, badgeKey: 'learningsCount' },
{
id: 'stations',
label: 'Stations',
icon: RadioTower,
badgeKey: 'stationsCount',
isVisible: (node, depth) => depth > 0, // Only show for non-root nodes
},
]}
nodeTypesConfig={nodeTypesConfig}
/>Notes
TreeSidebaris display-only — no internal data fetching. All state and data management lives in the consuming app.- Action buttons (Add, Rename, Delete) are conditionally rendered — only shown when the corresponding handler prop is passed.
- The
nodeTypesConfigarray maps to tree depth — index0applies to the root node, index1to its children, and so on. If the tree is deeper than the config array, the last config entry is reused. - Delete is automatically disabled for nodes that have children, with a tooltip explaining why.
- The root node auto-expands on load and whenever
datachanges. - Search expansion is automatic — all ancestor nodes of matching results are expanded.
Assign Reminder & Approval Manager
A configurable workflow component for managing assignees, approval levels, reminders, and review status tracking.
Features
- Multi-tab interface (Assignees, Reminders, Review Status)
- Multi-level approval workflows
- Controlled and uncontrolled state support
- Async user search and assignment
- Due date management
- Final approval workflow support
- Visual approval progress tracking
- Customizable metadata, tabs, and priority badges
- Reusable UI components (Dropdowns, Date Pickers, Checkboxes)
Installation
npm install @e-llm-studio/creative-workspace-ui-componentimport AssignReminderWrapper from "@e-llm-studio/creative-workspace-ui-component";Basic Usage
<AssignReminderWrapper
fetchUsers={fetchUsers}
onClose={() => setOpen(false)}
onSave={(payload) => console.log(payload)}
metadata={{
wrapperTitle: "Contract Review",
wrapperPriority: "High",
}}
/>Props
| Prop | Type | Description |
|--------|--------|-------------|
| metadata | MetaData | Customizes modal title, priority badge, tabs, and display settings. |
| reviewData | Record<number, Record<string, boolean>> | Approval status mapping used by Review Status. |
| levels | Level[] | Approval workflow levels. |
| setLevels | Dispatch | Controlled state setter for levels. |
| requireFinalApproval | boolean | Enables final approval step. |
| setRequireFinalApproval | Dispatch | Controlled setter for final approval state. |
| approvalDueDate | Date \| null | Final approval due date. |
| setApprovalDueDate | Dispatch | Controlled setter for approval due date. |
| fetchUsers | FetchUsersFunction | Async user lookup function. |
| onClose | () => void | Called when modal closes. |
| onSave | (payload) => void | Returns configured workflow data. |
| onValidationError | (message) => void | Handles validation errors. |
Core Types
MetaData
interface MetaData {
wrapperTitle?: string;
wrapperSubTitle?: string;
wrapperPriority?: string;
wrapperPriorityStyles?: React.CSSProperties;
tabData?: TabData[];
showFinalApprovalDueDate?: boolean;
}Level
interface Level {
headerName: string;
title: string;
isMandatory: boolean;
hasDueDate: boolean;
dueDate: Date | null;
assignees: AssigneeUser[];
}AssigneeUser
interface AssigneeUser {
id: any;
name: string;
email: string;
accessLevel?: string;
}SavePayload
interface SavePayload {
levels: Level[];
requireFinalApproval: boolean;
finalApprovalDueDate: Date | null;
activeTab: "assignees" | "reminders" | "review";
}Included Components
AssignReminderLevels
Manages approval levels, assignees, due dates, and final approval configuration.
ReviewStatus
Displays approval progress and assignee review status using reviewData.
LevelData
Handles assignee search, selection, and access-level assignment.
UI Components
CustomDropdownCustomDatePickerAssignLevelDatePickerCustomCheckbox
Utilities
getRandomColorPair
function getRandomColorPair(): {
backgroundColor: string;
color: string;
}Returns a contrasting color pair for badge styling.
DEFAULT_ASSIGN_REMINDER_LEVELS
Default workflow configuration containing:
- First Level (Mandatory)
- Second Level (Optional)
- Third Level (Optional)
Notes
- Supports both controlled and uncontrolled usage patterns.
fetchUsersis required for assignee lookup functionality.- Review progress is driven by the
reviewDataprop. - Final approval workflow can be enabled or disabled.
- All workflow configuration is returned through the
onSavecallback.
