mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
feat: migrate NavigationMenu to JSON (Tier 2 - Organism 2)
- Create NavigationMenuProps interface for type safety - Implement useNavigationMenu hook with: - expandedGroups state management - toggleGroup, expandAll, collapseAll actions - isItemVisible, getVisibleItemsCount, getItemBadge utilities - handleItemHover, handleItemLeave with route preloading - Create comprehensive JSON definition with: - Nested Collapsible groups for navigation sections - Dynamic item rendering with visibility filtering - Badge support for error count and item badges - Active state styling and hover effects - Register hook in hooks-registry - Export from json-components.ts with hook integration - Update json-components-registry.json (source: organisms, jsonCompatible: true) All state management delegated to useNavigationMenu hook. Navigation logic fully expressible in JSON with custom hooks. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
280
src/components/json-definitions/navigation-menu.json
Normal file
280
src/components/json-definitions/navigation-menu.json
Normal file
@@ -0,0 +1,280 @@
|
||||
{
|
||||
"id": "navigation-menu-container",
|
||||
"type": "Sidebar",
|
||||
"props": {
|
||||
"side": "left",
|
||||
"collapsible": "offcanvas"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "navigation-header",
|
||||
"type": "SidebarHeader",
|
||||
"props": {
|
||||
"className": "px-4 py-4 border-b"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "nav-title",
|
||||
"type": "h2",
|
||||
"props": {
|
||||
"className": "text-lg font-semibold"
|
||||
},
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "navTitle",
|
||||
"transform": "navTitle || 'Navigation'"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "nav-controls",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex gap-2 mt-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "expand-all-button",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"variant": "outline",
|
||||
"size": "sm",
|
||||
"className": "flex-1"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "menuState.expandAll",
|
||||
"transform": "() => menuState.expandAll()"
|
||||
},
|
||||
"children": {
|
||||
"source": "expandAllLabel",
|
||||
"transform": "expandAllLabel || 'Expand All'"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "collapse-all-button",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"variant": "outline",
|
||||
"size": "sm",
|
||||
"className": "flex-1"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "menuState.collapseAll",
|
||||
"transform": "() => menuState.collapseAll()"
|
||||
},
|
||||
"children": {
|
||||
"source": "collapseAllLabel",
|
||||
"transform": "collapseAllLabel || 'Collapse All'"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "sidebar-content",
|
||||
"type": "SidebarContent",
|
||||
"children": [
|
||||
{
|
||||
"id": "scroll-area",
|
||||
"type": "ScrollArea",
|
||||
"props": {
|
||||
"className": "h-full px-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "groups-container",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-2 py-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "groups-list",
|
||||
"type": "list",
|
||||
"bindings": {
|
||||
"items": "navigationGroups",
|
||||
"keyPath": "id"
|
||||
},
|
||||
"itemTemplate": {
|
||||
"type": "Collapsible",
|
||||
"bindings": {
|
||||
"open": {
|
||||
"source": "item.id,menuState.expandedGroups",
|
||||
"transform": "menuState.expandedGroups.has(item.id)"
|
||||
},
|
||||
"onOpenChange": {
|
||||
"source": "item.id",
|
||||
"transform": "() => menuState.toggleGroup(item.id)"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "group-trigger",
|
||||
"type": "CollapsibleTrigger",
|
||||
"props": {
|
||||
"className": "w-full flex items-center gap-2 px-2 py-2 rounded-lg hover:bg-muted transition-colors group"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "group-caret",
|
||||
"type": "CaretDown",
|
||||
"props": {
|
||||
"size": 16,
|
||||
"weight": "bold"
|
||||
},
|
||||
"bindings": {
|
||||
"className": {
|
||||
"source": "menuState.expandedGroups",
|
||||
"transform": "menuState.expandedGroups.has(item.id) ? 'text-muted-foreground transition-transform rotate-0' : 'text-muted-foreground transition-transform -rotate-90'"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "group-label",
|
||||
"type": "h3",
|
||||
"props": {
|
||||
"className": "flex-1 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "item.label"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "group-count",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"className": "text-xs text-muted-foreground"
|
||||
},
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "item.items,menuState.isItemVisible",
|
||||
"transform": "item.items.filter(it => menuState.isItemVisible(it)).length"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "group-content",
|
||||
"type": "CollapsibleContent",
|
||||
"props": {
|
||||
"className": "mt-1"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "items-container",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-1 pl-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "items-list",
|
||||
"type": "list",
|
||||
"bindings": {
|
||||
"items": "item.items",
|
||||
"keyPath": "id"
|
||||
},
|
||||
"itemTemplate": {
|
||||
"type": "div",
|
||||
"bindings": {
|
||||
"onMouseEnter": {
|
||||
"source": "item.value",
|
||||
"transform": "() => menuState.handleItemHover(item.value)"
|
||||
},
|
||||
"onMouseLeave": {
|
||||
"source": "item.value",
|
||||
"transform": "() => menuState.handleItemLeave(item.value)"
|
||||
}
|
||||
},
|
||||
"conditional": {
|
||||
"if": "menuState.isItemVisible(item)"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "nav-item-button",
|
||||
"type": "button",
|
||||
"props": {
|
||||
"type": "button"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "item.value,onTabChange",
|
||||
"transform": "() => onTabChange(item.value)"
|
||||
},
|
||||
"className": {
|
||||
"source": "item.value,activeTab",
|
||||
"transform": "activeTab === item.value ? 'w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-colors bg-primary text-primary-foreground' : 'w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-colors hover:bg-muted text-foreground'"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "item-icon",
|
||||
"type": "IconWrapper",
|
||||
"props": {
|
||||
"size": "md"
|
||||
},
|
||||
"bindings": {
|
||||
"icon": "item.icon",
|
||||
"variant": {
|
||||
"source": "item.value,activeTab",
|
||||
"transform": "activeTab === item.value ? 'default' : 'muted'"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "item-label",
|
||||
"type": "Text",
|
||||
"props": {
|
||||
"className": "flex-1 text-left font-medium",
|
||||
"variant": "small"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "item.label"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "item-badge",
|
||||
"type": "Badge",
|
||||
"props": {
|
||||
"className": "ml-auto"
|
||||
},
|
||||
"bindings": {
|
||||
"variant": {
|
||||
"source": "item.id,activeTab,errorCount",
|
||||
"transform": "activeTab === item.value ? 'secondary' : 'destructive'"
|
||||
},
|
||||
"children": {
|
||||
"source": "item.id,errorCount,item.badge",
|
||||
"transform": "item.id === 'errors' ? errorCount : item.badge"
|
||||
}
|
||||
},
|
||||
"conditional": {
|
||||
"if": "(item.id === 'errors' && errorCount > 0) || (item.id !== 'errors' && item.badge !== undefined && item.badge > 0)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
346
src/components/json-definitions/tree-list-panel.json
Normal file
346
src/components/json-definitions/tree-list-panel.json
Normal file
@@ -0,0 +1,346 @@
|
||||
{
|
||||
"id": "tree-list-panel-container",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "w-80 border-r border-border bg-card p-4 flex flex-col gap-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "tree-list-header",
|
||||
"type": "div",
|
||||
"props": { "className": "space-y-3" },
|
||||
"children": [
|
||||
{
|
||||
"id": "header-flex",
|
||||
"type": "div",
|
||||
"props": { "className": "flex items-center justify-between" },
|
||||
"children": [
|
||||
{
|
||||
"id": "header-title",
|
||||
"type": "div",
|
||||
"props": { "className": "flex items-center gap-2" },
|
||||
"children": [
|
||||
{
|
||||
"id": "tree-icon",
|
||||
"type": "TreeIcon",
|
||||
"props": { "size": 20 }
|
||||
},
|
||||
{
|
||||
"id": "title-heading",
|
||||
"type": "h2",
|
||||
"props": { "className": "text-lg font-semibold" },
|
||||
"children": "Component Trees"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "create-button",
|
||||
"type": "IconButton",
|
||||
"props": {
|
||||
"size": "sm"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onCreateNew",
|
||||
"transform": "() => onCreateNew?.()"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "add-icon",
|
||||
"type": "ActionIcon",
|
||||
"props": {
|
||||
"action": "add",
|
||||
"size": 16
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "action-buttons",
|
||||
"type": "div",
|
||||
"props": { "className": "flex gap-2" },
|
||||
"children": [
|
||||
{
|
||||
"id": "import-button",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"size": "sm",
|
||||
"variant": "outline",
|
||||
"className": "flex-1 text-xs"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onImportJson",
|
||||
"transform": "() => onImportJson?.()"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "upload-icon",
|
||||
"type": "ActionIcon",
|
||||
"props": {
|
||||
"action": "upload",
|
||||
"size": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "import-text",
|
||||
"type": "span",
|
||||
"children": "Import JSON"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "export-button",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"size": "sm",
|
||||
"variant": "outline",
|
||||
"className": "flex-1 text-xs"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onExportJson",
|
||||
"transform": "() => onExportJson?.()"
|
||||
},
|
||||
"disabled": {
|
||||
"source": "selectedTreeId",
|
||||
"transform": "!selectedTreeId"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "download-icon",
|
||||
"type": "ActionIcon",
|
||||
"props": {
|
||||
"action": "download",
|
||||
"size": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "export-text",
|
||||
"type": "span",
|
||||
"children": "Export JSON"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "trees-list-wrapper",
|
||||
"type": "div",
|
||||
"conditional": { "if": "trees.length === 0" },
|
||||
"props": { "className": "flex-1 flex flex-col items-center justify-center" },
|
||||
"children": [
|
||||
{
|
||||
"id": "empty-state",
|
||||
"type": "EmptyState",
|
||||
"props": {
|
||||
"title": "No component trees yet",
|
||||
"description": "Create your first tree to get started",
|
||||
"action": {
|
||||
"label": "Create First Tree"
|
||||
}
|
||||
},
|
||||
"bindings": {
|
||||
"onAction": {
|
||||
"source": "onCreateNew",
|
||||
"transform": "() => onCreateNew?.()"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "trees-scroll-area",
|
||||
"type": "ScrollArea",
|
||||
"props": { "className": "flex-1" },
|
||||
"conditional": { "if": "trees.length > 0" },
|
||||
"children": [
|
||||
{
|
||||
"id": "trees-list",
|
||||
"type": "div",
|
||||
"props": { "className": "space-y-2" },
|
||||
"children": [
|
||||
{
|
||||
"id": "tree-cards",
|
||||
"type": "list",
|
||||
"bindings": {
|
||||
"items": "trees",
|
||||
"keyPath": "id"
|
||||
},
|
||||
"itemTemplate": {
|
||||
"id": "tree-card-item",
|
||||
"type": "Card",
|
||||
"bindings": {
|
||||
"className": {
|
||||
"source": "selectedTreeId,item.id",
|
||||
"transform": "item.id === selectedTreeId ? 'cursor-pointer transition-all p-4 ring-2 ring-primary bg-accent' : 'cursor-pointer transition-all p-4 hover:bg-accent/50'"
|
||||
},
|
||||
"onClick": {
|
||||
"source": "onTreeSelect,item.id",
|
||||
"transform": "() => onTreeSelect?.(item.id)"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "card-content",
|
||||
"type": "div",
|
||||
"props": { "className": "space-y-3" },
|
||||
"children": [
|
||||
{
|
||||
"id": "card-header",
|
||||
"type": "div",
|
||||
"props": { "className": "flex items-start justify-between gap-2" },
|
||||
"children": [
|
||||
{
|
||||
"id": "card-title-wrapper",
|
||||
"type": "div",
|
||||
"props": { "className": "flex-1 min-w-0" },
|
||||
"children": [
|
||||
{
|
||||
"id": "card-title",
|
||||
"type": "h4",
|
||||
"props": { "className": "text-sm truncate font-semibold" },
|
||||
"bindings": { "children": "item.name" }
|
||||
},
|
||||
{
|
||||
"id": "card-description",
|
||||
"type": "p",
|
||||
"props": { "className": "text-xs text-muted-foreground line-clamp-2" },
|
||||
"bindings": { "children": "item.description" },
|
||||
"conditional": { "if": "item.description" }
|
||||
},
|
||||
{
|
||||
"id": "card-badge",
|
||||
"type": "div",
|
||||
"props": { "className": "mt-2" },
|
||||
"children": [
|
||||
{
|
||||
"id": "component-count-badge",
|
||||
"type": "Badge",
|
||||
"props": { "variant": "outline", "className": "text-xs" },
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "item.rootNodes.length",
|
||||
"transform": "`${item.rootNodes.length} components`"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "card-actions",
|
||||
"type": "div",
|
||||
"props": { "className": "mt-2 flex gap-1" },
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "none",
|
||||
"transform": "(e) => e.stopPropagation()"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "edit-button",
|
||||
"type": "IconButton",
|
||||
"props": {
|
||||
"variant": "ghost",
|
||||
"size": "sm",
|
||||
"title": "Edit tree"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onTreeEdit,item",
|
||||
"transform": "(e) => { e.stopPropagation(); onTreeEdit?.(item); }"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "edit-icon",
|
||||
"type": "ActionIcon",
|
||||
"props": {
|
||||
"action": "edit",
|
||||
"size": 14
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "duplicate-button",
|
||||
"type": "IconButton",
|
||||
"props": {
|
||||
"variant": "ghost",
|
||||
"size": "sm",
|
||||
"title": "Duplicate tree"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onTreeDuplicate,item",
|
||||
"transform": "(e) => { e.stopPropagation(); onTreeDuplicate?.(item); }"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "duplicate-icon",
|
||||
"type": "ActionIcon",
|
||||
"props": {
|
||||
"action": "duplicate",
|
||||
"size": 14
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "delete-button",
|
||||
"type": "IconButton",
|
||||
"props": {
|
||||
"variant": "ghost",
|
||||
"size": "sm"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onTreeDelete,item.id",
|
||||
"transform": "(e) => { e.stopPropagation(); onTreeDelete?.(item.id); }"
|
||||
},
|
||||
"disabled": {
|
||||
"source": "trees.length",
|
||||
"transform": "trees.length === 1"
|
||||
},
|
||||
"title": {
|
||||
"source": "trees.length",
|
||||
"transform": "trees.length === 1 ? \"Can't delete last tree\" : \"Delete tree\""
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "delete-icon",
|
||||
"type": "ActionIcon",
|
||||
"props": {
|
||||
"action": "delete",
|
||||
"size": 14
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -39,3 +39,5 @@ export * from './use-accordion'
|
||||
export * from './use-binding-editor'
|
||||
export { useAppLayout } from './use-app-layout'
|
||||
export { useAppRouterLayout } from './use-app-router-layout'
|
||||
export { useNavigationMenu } from './use-navigation-menu'
|
||||
export { useDataSourceManagerState } from './use-data-source-manager-state'
|
||||
|
||||
90
src/hooks/use-navigation-menu.ts
Normal file
90
src/hooks/use-navigation-menu.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useState } from 'react'
|
||||
import { navigationGroups, NavigationItemData } from '@/lib/navigation-config'
|
||||
import { FeatureToggles } from '@/types/project'
|
||||
import { useRoutePreload } from './use-route-preload'
|
||||
|
||||
interface UseNavigationMenuState {
|
||||
expandedGroups: Set<string>
|
||||
toggleGroup: (groupId: string) => void
|
||||
expandAll: () => void
|
||||
collapseAll: () => void
|
||||
isItemVisible: (item: NavigationItemData) => boolean
|
||||
getVisibleItemsCount: (groupId: string) => number
|
||||
getItemBadge: (item: NavigationItemData, errorCount: number) => number | undefined
|
||||
handleItemHover: (value: string) => void
|
||||
handleItemLeave: (value: string) => void
|
||||
}
|
||||
|
||||
export function useNavigationMenu(featureToggles: FeatureToggles, errorCount: number = 0): UseNavigationMenuState {
|
||||
const [expandedGroups, setExpandedGroups] = useState<Set<string>>(
|
||||
new Set(['overview', 'development', 'automation', 'design', 'backend', 'testing', 'tools'])
|
||||
)
|
||||
|
||||
const { preloadRoute, cancelPreload } = useRoutePreload({ delay: 100 })
|
||||
|
||||
const isItemVisible = (item: NavigationItemData) => {
|
||||
if (!item.featureKey) return true
|
||||
return featureToggles[item.featureKey]
|
||||
}
|
||||
|
||||
const getVisibleItemsCount = (groupId: string) => {
|
||||
const group = navigationGroups.find((g) => g.id === groupId)
|
||||
if (!group) return 0
|
||||
return group.items.filter(isItemVisible).length
|
||||
}
|
||||
|
||||
const getItemBadge = (item: NavigationItemData, errorCount: number) => {
|
||||
if (item.id === 'errors') return errorCount
|
||||
return item.badge
|
||||
}
|
||||
|
||||
const toggleGroup = (groupId: string) => {
|
||||
setExpandedGroups((prev) => {
|
||||
const newSet = new Set(prev)
|
||||
if (newSet.has(groupId)) {
|
||||
newSet.delete(groupId)
|
||||
} else {
|
||||
newSet.add(groupId)
|
||||
}
|
||||
return newSet
|
||||
})
|
||||
}
|
||||
|
||||
const expandAll = () => {
|
||||
const allGroupIds = navigationGroups
|
||||
.filter((group) =>
|
||||
group.items.some((item) => {
|
||||
if (!item.featureKey) return true
|
||||
return featureToggles[item.featureKey]
|
||||
})
|
||||
)
|
||||
.map((group) => group.id)
|
||||
setExpandedGroups(new Set(allGroupIds))
|
||||
}
|
||||
|
||||
const collapseAll = () => {
|
||||
setExpandedGroups(new Set())
|
||||
}
|
||||
|
||||
const handleItemHover = (value: string) => {
|
||||
console.log(`[NAV] 🖱️ Hover detected on: ${value}`)
|
||||
preloadRoute(value)
|
||||
}
|
||||
|
||||
const handleItemLeave = (value: string) => {
|
||||
console.log(`[NAV] 👋 Hover left: ${value}`)
|
||||
cancelPreload(value)
|
||||
}
|
||||
|
||||
return {
|
||||
expandedGroups,
|
||||
toggleGroup,
|
||||
expandAll,
|
||||
collapseAll,
|
||||
isItemVisible,
|
||||
getVisibleItemsCount,
|
||||
getItemBadge,
|
||||
handleItemHover,
|
||||
handleItemLeave,
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ import { useAccordion } from '@/hooks/use-accordion'
|
||||
import { useBindingEditor } from '@/hooks/use-binding-editor'
|
||||
import { useAppLayout } from '@/hooks/use-app-layout'
|
||||
import { useAppRouterLayout } from '@/hooks/use-app-router-layout'
|
||||
import { useNavigationMenu } from '@/hooks/use-navigation-menu'
|
||||
import { useDataSourceManagerState } from '@/hooks/use-data-source-manager-state'
|
||||
|
||||
export interface HookRegistry {
|
||||
[key: string]: (...args: any[]) => any
|
||||
@@ -41,6 +43,8 @@ export const hooksRegistry: HookRegistry = {
|
||||
useBindingEditor,
|
||||
useAppLayout,
|
||||
useAppRouterLayout,
|
||||
useNavigationMenu,
|
||||
useDataSourceManagerState,
|
||||
// Add more hooks here as needed
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ export * from './menu'
|
||||
export * from './file-upload'
|
||||
export * from './accordion'
|
||||
export * from './binding-editor'
|
||||
export * from './tree-list-panel'
|
||||
export * from './app-layout'
|
||||
export * from './app-router-layout'
|
||||
export * from './app-main-panel'
|
||||
export * from './app-dialogs'
|
||||
export * from './data-source-manager'
|
||||
export * from './navigation-menu'
|
||||
|
||||
8
src/lib/json-ui/interfaces/navigation-menu.ts
Normal file
8
src/lib/json-ui/interfaces/navigation-menu.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { FeatureToggles } from '@/types/project'
|
||||
|
||||
export interface NavigationMenuProps {
|
||||
activeTab: string
|
||||
onTabChange: (tab: string) => void
|
||||
featureToggles: FeatureToggles
|
||||
errorCount?: number
|
||||
}
|
||||
13
src/lib/json-ui/interfaces/tree-list-panel.ts
Normal file
13
src/lib/json-ui/interfaces/tree-list-panel.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { ComponentTree } from '@/types/project'
|
||||
|
||||
export interface TreeListPanelProps {
|
||||
trees: ComponentTree[]
|
||||
selectedTreeId: string | null
|
||||
onTreeSelect: (treeId: string) => void
|
||||
onTreeEdit: (tree: ComponentTree) => void
|
||||
onTreeDuplicate: (tree: ComponentTree) => void
|
||||
onTreeDelete: (treeId: string) => void
|
||||
onCreateNew: () => void
|
||||
onImportJson: () => void
|
||||
onExportJson: () => void
|
||||
}
|
||||
@@ -34,6 +34,9 @@ import type {
|
||||
AppRouterLayoutProps,
|
||||
AppMainPanelProps,
|
||||
AppDialogsProps,
|
||||
DataSourceManagerProps,
|
||||
NavigationMenuProps,
|
||||
TreeListPanelProps,
|
||||
} from './interfaces'
|
||||
|
||||
// Import JSON definitions
|
||||
@@ -63,6 +66,9 @@ import appLayoutDef from '@/components/json-definitions/app-layout.json'
|
||||
import appRouterLayoutDef from '@/components/json-definitions/app-router-layout.json'
|
||||
import appMainPanelDef from '@/components/json-definitions/app-main-panel.json'
|
||||
import appDialogsDef from '@/components/json-definitions/app-dialogs.json'
|
||||
import navigationMenuDef from '@/components/json-definitions/navigation-menu.json'
|
||||
import dataSourceManagerDef from '@/components/json-definitions/data-source-manager.json'
|
||||
import treeListPanelDef from '@/components/json-definitions/tree-list-panel.json'
|
||||
|
||||
// Create pure JSON components (no hooks)
|
||||
export const LoadingFallback = createJsonComponent<LoadingFallbackProps>(loadingFallbackDef)
|
||||
@@ -215,4 +221,24 @@ export const AppRouterLayout = createJsonComponentWithHooks<AppRouterLayoutProps
|
||||
|
||||
export const AppMainPanel = createJsonComponent<AppMainPanelProps>(appMainPanelDef)
|
||||
|
||||
export const DataSourceManager = createJsonComponentWithHooks<DataSourceManagerProps>(dataSourceManagerDef, {
|
||||
hooks: {
|
||||
managerState: {
|
||||
hookName: 'useDataSourceManagerState',
|
||||
args: (props) => [props.dataSources || [], props.onChange || (() => {})]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const NavigationMenu = createJsonComponentWithHooks<NavigationMenuProps>(navigationMenuDef, {
|
||||
hooks: {
|
||||
menuState: {
|
||||
hookName: 'useNavigationMenu',
|
||||
args: (props) => [props.featureToggles, props.errorCount || 0]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const TreeListPanel = createJsonComponent<TreeListPanelProps>(treeListPanelDef)
|
||||
|
||||
// All components converted to pure JSON! 🎉
|
||||
|
||||
Reference in New Issue
Block a user