mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Merge pull request #140 from johndoe6345789/codex/identify-components-for-hooks-and-context
Add JSON wrappers for hook-dependent components and register them in the registry
This commit is contained in:
@@ -67,7 +67,20 @@
|
||||
"description": "ComponentBindingDialog component",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "ComponentBindingDialogWrapper"
|
||||
},
|
||||
{
|
||||
"type": "ComponentBindingDialogWrapper",
|
||||
"name": "ComponentBindingDialogWrapper",
|
||||
"category": "layout",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for component binding dialog with props-driven bindings",
|
||||
"status": "json-compatible",
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "ComponentBindingDialog"
|
||||
},
|
||||
{
|
||||
"type": "Container",
|
||||
@@ -96,7 +109,20 @@
|
||||
"description": "DataSourceEditorDialog component",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "DataSourceEditorDialogWrapper"
|
||||
},
|
||||
{
|
||||
"type": "DataSourceEditorDialogWrapper",
|
||||
"name": "DataSourceEditorDialogWrapper",
|
||||
"category": "layout",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for data source editor dialog with props-driven fields",
|
||||
"status": "json-compatible",
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "DataSourceEditorDialog"
|
||||
},
|
||||
{
|
||||
"type": "Dialog",
|
||||
@@ -893,7 +919,20 @@
|
||||
"description": "GitHubBuildStatus component",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": false
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "GitHubBuildStatusWrapper"
|
||||
},
|
||||
{
|
||||
"type": "GitHubBuildStatusWrapper",
|
||||
"name": "GitHubBuildStatusWrapper",
|
||||
"category": "feedback",
|
||||
"canHaveChildren": false,
|
||||
"description": "JSON wrapper for props-driven GitHub build status summary",
|
||||
"status": "json-compatible",
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "GitHubBuildStatus"
|
||||
},
|
||||
{
|
||||
"type": "InfoBox",
|
||||
@@ -1039,30 +1078,69 @@
|
||||
"name": "LazyBarChart",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "Lazy-loaded Recharts bar chart with runtime library loading",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "LazyBarChartWrapper"
|
||||
},
|
||||
{
|
||||
"type": "LazyBarChartWrapper",
|
||||
"name": "LazyBarChartWrapper",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for a props-driven bar chart (no lazy hooks)",
|
||||
"status": "json-compatible",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "LazyBarChart"
|
||||
},
|
||||
{
|
||||
"type": "LazyD3BarChart",
|
||||
"name": "LazyD3BarChart",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "Lazy-loaded D3 bar chart with runtime library loading",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "LazyD3BarChartWrapper"
|
||||
},
|
||||
{
|
||||
"type": "LazyD3BarChartWrapper",
|
||||
"name": "LazyD3BarChartWrapper",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for a simple SVG bar chart (no D3 hooks)",
|
||||
"status": "json-compatible",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "LazyD3BarChart"
|
||||
},
|
||||
{
|
||||
"type": "LazyLineChart",
|
||||
"name": "LazyLineChart",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "Lazy-loaded Recharts line chart with runtime library loading",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "LazyLineChartWrapper"
|
||||
},
|
||||
{
|
||||
"type": "LazyLineChartWrapper",
|
||||
"name": "LazyLineChartWrapper",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for a props-driven line chart (no lazy hooks)",
|
||||
"status": "json-compatible",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "LazyLineChart"
|
||||
},
|
||||
{
|
||||
"type": "List",
|
||||
@@ -1105,10 +1183,23 @@
|
||||
"name": "SeedDataManager",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "Seed data management with app-level hook state",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "SeedDataManagerWrapper"
|
||||
},
|
||||
{
|
||||
"type": "SeedDataManagerWrapper",
|
||||
"name": "SeedDataManagerWrapper",
|
||||
"category": "data",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for seed data management with props-driven state",
|
||||
"status": "json-compatible",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "SeedDataManager"
|
||||
},
|
||||
{
|
||||
"type": "StatCard",
|
||||
@@ -1337,7 +1428,20 @@
|
||||
"description": "ComponentTree component",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "ComponentTreeWrapper"
|
||||
},
|
||||
{
|
||||
"type": "ComponentTreeWrapper",
|
||||
"name": "ComponentTreeWrapper",
|
||||
"category": "custom",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for a props-driven component tree view",
|
||||
"status": "json-compatible",
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "ComponentTree"
|
||||
},
|
||||
{
|
||||
"type": "ComponentTreeNode",
|
||||
@@ -1564,10 +1668,23 @@
|
||||
"name": "SaveIndicator",
|
||||
"category": "custom",
|
||||
"canHaveChildren": true,
|
||||
"description": "Save status indicator with hook-driven state",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "SaveIndicatorWrapper"
|
||||
},
|
||||
{
|
||||
"type": "SaveIndicatorWrapper",
|
||||
"name": "SaveIndicatorWrapper",
|
||||
"category": "custom",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for save status indicator with pure-props API",
|
||||
"status": "json-compatible",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "SaveIndicator"
|
||||
},
|
||||
{
|
||||
"type": "SchemaEditorCanvas",
|
||||
@@ -1715,10 +1832,23 @@
|
||||
"name": "StorageSettings",
|
||||
"category": "custom",
|
||||
"canHaveChildren": true,
|
||||
"description": "Storage settings controls with hook-driven state",
|
||||
"status": "supported",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": false,
|
||||
"wrapperRequired": true,
|
||||
"wrapperComponent": "StorageSettingsWrapper"
|
||||
},
|
||||
{
|
||||
"type": "StorageSettingsWrapper",
|
||||
"name": "StorageSettingsWrapper",
|
||||
"category": "custom",
|
||||
"canHaveChildren": true,
|
||||
"description": "JSON wrapper for storage settings controls with props-driven state",
|
||||
"status": "json-compatible",
|
||||
"source": "molecules",
|
||||
"jsonCompatible": true
|
||||
"source": "wrappers",
|
||||
"jsonCompatible": true,
|
||||
"wrapperFor": "StorageSettings"
|
||||
},
|
||||
{
|
||||
"type": "Timestamp",
|
||||
|
||||
@@ -265,12 +265,20 @@ export function registerComponent(name: string, component: ComponentType<any>) {
|
||||
uiComponentRegistry[name] = component
|
||||
}
|
||||
|
||||
const resolveWrapperComponent = (type: string): ComponentType<any> | null => {
|
||||
const entry = registryEntryByType.get(type)
|
||||
if (entry?.wrapperRequired && entry.wrapperComponent) {
|
||||
return uiComponentRegistry[entry.wrapperComponent] || null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function getUIComponent(type: string): ComponentType<any> | string | null {
|
||||
return uiComponentRegistry[type] || null
|
||||
return resolveWrapperComponent(type) ?? uiComponentRegistry[type] ?? null
|
||||
}
|
||||
|
||||
export function hasComponent(type: string): boolean {
|
||||
return type in uiComponentRegistry
|
||||
return Boolean(resolveWrapperComponent(type) ?? uiComponentRegistry[type])
|
||||
}
|
||||
|
||||
export function getDeprecatedComponentInfo(type: string): DeprecatedComponentInfo | null {
|
||||
|
||||
@@ -205,6 +205,11 @@ export function ComponentRenderer({ component, data, context = {}, state, onEven
|
||||
return null
|
||||
}
|
||||
|
||||
const resolvedChildren = component.children ?? resolvedProps.children
|
||||
if (resolvedChildren !== undefined && resolvedChildren !== component.children) {
|
||||
delete resolvedProps.children
|
||||
}
|
||||
|
||||
if (component.loop) {
|
||||
const items = resolveDataBinding(component.loop.source, data, context, { state, bindings: context }) || []
|
||||
const loopChildren = items.map((item: unknown, index: number) => {
|
||||
@@ -232,7 +237,7 @@ export function ComponentRenderer({ component, data, context = {}, state, onEven
|
||||
|
||||
return (
|
||||
<Fragment key={`${component.id}-${index}`}>
|
||||
{renderChildren(component.children, loopContext)}
|
||||
{renderChildren(resolvedChildren as UIComponent[] | string | undefined, loopContext)}
|
||||
</Fragment>
|
||||
)
|
||||
})
|
||||
@@ -254,5 +259,9 @@ export function ComponentRenderer({ component, data, context = {}, state, onEven
|
||||
}
|
||||
}
|
||||
|
||||
return createElement(Component, resolvedProps, renderChildren(component.children, context))
|
||||
return createElement(
|
||||
Component,
|
||||
resolvedProps,
|
||||
renderChildren(resolvedChildren as UIComponent[] | string | undefined, context)
|
||||
)
|
||||
}
|
||||
|
||||
47
src/lib/json-ui/wrappers/ComponentBindingDialogWrapper.tsx
Normal file
47
src/lib/json-ui/wrappers/ComponentBindingDialogWrapper.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { ComponentRenderer } from '@/lib/json-ui/component-renderer'
|
||||
import { cn } from '@/lib/utils'
|
||||
import componentBindingDialogDefinition from './definitions/component-binding-dialog.json'
|
||||
import type { ComponentBindingDialogWrapperProps } from './interfaces'
|
||||
|
||||
export function ComponentBindingDialogWrapper({
|
||||
open = false,
|
||||
title = 'Component Bindings',
|
||||
description = 'Connect component props to data sources.',
|
||||
componentType,
|
||||
componentId,
|
||||
bindings = [],
|
||||
onBindingChange,
|
||||
onSave,
|
||||
onCancel,
|
||||
onOpenChange,
|
||||
className,
|
||||
}: ComponentBindingDialogWrapperProps) {
|
||||
const handleBindingFieldChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const fieldId = event.currentTarget?.dataset?.fieldId || event.target?.dataset?.fieldId
|
||||
if (!fieldId) return
|
||||
onBindingChange?.(fieldId, event.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<ComponentRenderer
|
||||
component={componentBindingDialogDefinition}
|
||||
data={{
|
||||
open,
|
||||
title,
|
||||
description,
|
||||
componentType,
|
||||
componentId,
|
||||
bindingFields: bindings,
|
||||
emptyMessage: 'No bindings configured.',
|
||||
contentClassName: cn('max-w-2xl', className),
|
||||
onBindingFieldChange: handleBindingFieldChange,
|
||||
onSave,
|
||||
onCancel,
|
||||
onOpenChange,
|
||||
cancelLabel: 'Cancel',
|
||||
saveLabel: 'Save',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
55
src/lib/json-ui/wrappers/ComponentTreeWrapper.tsx
Normal file
55
src/lib/json-ui/wrappers/ComponentTreeWrapper.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
import type { UIComponent } from '@/types/json-ui'
|
||||
import type { ComponentTreeWrapperProps } from './interfaces'
|
||||
|
||||
const renderTreeNodes = (
|
||||
components: UIComponent[],
|
||||
depth: number,
|
||||
selectedId: string | null,
|
||||
onSelect?: (id: string) => void
|
||||
) => {
|
||||
return components.map((component) => {
|
||||
const hasChildren = Array.isArray(component.children) && component.children.length > 0
|
||||
const isSelected = selectedId === component.id
|
||||
|
||||
return (
|
||||
<div key={component.id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSelect?.(component.id)}
|
||||
className={cn(
|
||||
'flex w-full items-center gap-2 rounded-md px-2 py-1 text-left text-sm transition-colors',
|
||||
isSelected ? 'bg-accent/40 text-foreground' : 'hover:bg-muted'
|
||||
)}
|
||||
style={{ paddingLeft: `${depth * 16 + 8}px` }}
|
||||
>
|
||||
<span className="font-medium">{component.type}</span>
|
||||
<span className="text-xs text-muted-foreground">{component.id}</span>
|
||||
</button>
|
||||
{hasChildren && (
|
||||
<div className="mt-1">
|
||||
{renderTreeNodes(component.children as UIComponent[], depth + 1, selectedId, onSelect)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function ComponentTreeWrapper({
|
||||
components = [],
|
||||
selectedId = null,
|
||||
emptyMessage = 'No components available.',
|
||||
onSelect,
|
||||
className,
|
||||
}: ComponentTreeWrapperProps) {
|
||||
return (
|
||||
<div className={cn('space-y-2', className)}>
|
||||
{components.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">{emptyMessage}</p>
|
||||
) : (
|
||||
<div className="space-y-1">{renderTreeNodes(components, 0, selectedId, onSelect)}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
43
src/lib/json-ui/wrappers/DataSourceEditorDialogWrapper.tsx
Normal file
43
src/lib/json-ui/wrappers/DataSourceEditorDialogWrapper.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { ComponentRenderer } from '@/lib/json-ui/component-renderer'
|
||||
import { cn } from '@/lib/utils'
|
||||
import dataSourceEditorDialogDefinition from './definitions/data-source-editor-dialog.json'
|
||||
import type { DataSourceEditorDialogWrapperProps } from './interfaces'
|
||||
|
||||
export function DataSourceEditorDialogWrapper({
|
||||
open = false,
|
||||
title = 'Data Source',
|
||||
description = 'Update data source details and fields.',
|
||||
fields = [],
|
||||
onFieldChange,
|
||||
onSave,
|
||||
onCancel,
|
||||
onOpenChange,
|
||||
className,
|
||||
}: DataSourceEditorDialogWrapperProps) {
|
||||
const handleFieldChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const fieldId = event.currentTarget?.dataset?.fieldId || event.target?.dataset?.fieldId
|
||||
if (!fieldId) return
|
||||
onFieldChange?.(fieldId, event.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<ComponentRenderer
|
||||
component={dataSourceEditorDialogDefinition}
|
||||
data={{
|
||||
open,
|
||||
title,
|
||||
description,
|
||||
fields,
|
||||
emptyMessage: 'No fields configured.',
|
||||
contentClassName: cn('max-w-2xl', className),
|
||||
onFieldChange: handleFieldChange,
|
||||
onSave,
|
||||
onCancel,
|
||||
onOpenChange,
|
||||
cancelLabel: 'Cancel',
|
||||
saveLabel: 'Save',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
64
src/lib/json-ui/wrappers/GitHubBuildStatusWrapper.tsx
Normal file
64
src/lib/json-ui/wrappers/GitHubBuildStatusWrapper.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ComponentRenderer } from '@/lib/json-ui/component-renderer'
|
||||
import { cn } from '@/lib/utils'
|
||||
import gitHubBuildStatusDefinition from './definitions/github-build-status.json'
|
||||
import type { GitHubBuildStatusWrapperProps, GitHubBuildStatusWorkflowItem } from './interfaces'
|
||||
|
||||
const getWorkflowStatus = (workflow: GitHubBuildStatusWorkflowItem) => {
|
||||
if (workflow.status === 'completed') {
|
||||
if (workflow.conclusion === 'success') {
|
||||
return {
|
||||
label: 'Success',
|
||||
className: 'bg-green-500/10 text-green-600 border-green-500/20',
|
||||
}
|
||||
}
|
||||
if (workflow.conclusion === 'failure') {
|
||||
return { label: 'Failed', className: 'bg-red-500/10 text-red-600 border-red-500/20' }
|
||||
}
|
||||
if (workflow.conclusion === 'cancelled') {
|
||||
return { label: 'Cancelled', className: 'bg-yellow-500/10 text-yellow-600 border-yellow-500/20' }
|
||||
}
|
||||
}
|
||||
|
||||
return { label: 'Running', className: 'border-blue-500/50 text-blue-500' }
|
||||
}
|
||||
|
||||
export function GitHubBuildStatusWrapper({
|
||||
title = 'GitHub Build Status',
|
||||
description = 'Latest workflow runs and status badges.',
|
||||
workflows = [],
|
||||
isLoading = false,
|
||||
errorMessage,
|
||||
emptyMessage = 'No workflows to display yet.',
|
||||
footerLinkLabel = 'View on GitHub',
|
||||
footerLinkUrl,
|
||||
className,
|
||||
}: GitHubBuildStatusWrapperProps) {
|
||||
const normalizedWorkflows = workflows.map((workflow) => {
|
||||
const status = getWorkflowStatus(workflow)
|
||||
return {
|
||||
...workflow,
|
||||
statusLabel: status.label,
|
||||
statusClass: status.className,
|
||||
summaryLine: [workflow.branch, workflow.updatedAt, workflow.event].filter(Boolean).join(' • '),
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<ComponentRenderer
|
||||
component={gitHubBuildStatusDefinition}
|
||||
data={{
|
||||
title,
|
||||
description,
|
||||
workflows: normalizedWorkflows,
|
||||
isLoading,
|
||||
errorMessage,
|
||||
emptyMessage,
|
||||
loadingMessage: 'Loading workflows…',
|
||||
hasWorkflows: normalizedWorkflows.length > 0,
|
||||
footerLinkLabel,
|
||||
footerLinkUrl,
|
||||
className: cn(className),
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
{
|
||||
"id": "component-binding-dialog",
|
||||
"type": "Dialog",
|
||||
"bindings": {
|
||||
"open": "open",
|
||||
"onOpenChange": "onOpenChange"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-content",
|
||||
"type": "DialogContent",
|
||||
"bindings": {
|
||||
"className": "contentClassName"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-header",
|
||||
"type": "DialogHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-title",
|
||||
"type": "DialogTitle",
|
||||
"bindings": {
|
||||
"children": "title"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-description",
|
||||
"type": "DialogDescription",
|
||||
"bindings": {
|
||||
"children": "description"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-info",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "rounded-md border border-border bg-muted/30 p-3 text-sm"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "componentType || componentId"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-type",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "componentType"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-type-label",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"className": "text-muted-foreground"
|
||||
},
|
||||
"children": "Component:"
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-type-value",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"className": "font-mono font-medium"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "componentType"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-id",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "componentId"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-id-label",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"className": "text-muted-foreground"
|
||||
},
|
||||
"children": "ID:"
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-id-value",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"className": "font-mono text-xs"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "componentId"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-body",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-empty",
|
||||
"type": "p",
|
||||
"props": {
|
||||
"className": "text-sm text-muted-foreground"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "emptyMessage"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "!bindingFields || bindingFields.length === 0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-fields",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-4"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "bindingFields && bindingFields.length > 0"
|
||||
},
|
||||
"loop": {
|
||||
"source": "bindingFields",
|
||||
"itemVar": "field"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-field",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-field-label",
|
||||
"type": "Label",
|
||||
"bindings": {
|
||||
"children": "field.label"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-field-input",
|
||||
"type": "Input",
|
||||
"bindings": {
|
||||
"value": "field.value",
|
||||
"placeholder": "field.placeholder",
|
||||
"onChange": "onBindingFieldChange",
|
||||
"data-field-id": "field.id"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-footer",
|
||||
"type": "DialogFooter",
|
||||
"children": [
|
||||
{
|
||||
"id": "component-binding-dialog-cancel",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"variant": "outline"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": "onCancel",
|
||||
"children": "cancelLabel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "component-binding-dialog-save",
|
||||
"type": "Button",
|
||||
"bindings": {
|
||||
"onClick": "onSave",
|
||||
"children": "saveLabel"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"id": "data-source-editor-dialog",
|
||||
"type": "Dialog",
|
||||
"bindings": {
|
||||
"open": "open",
|
||||
"onOpenChange": "onOpenChange"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "data-source-editor-dialog-content",
|
||||
"type": "DialogContent",
|
||||
"bindings": {
|
||||
"className": "contentClassName"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "data-source-editor-dialog-header",
|
||||
"type": "DialogHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "data-source-editor-dialog-title",
|
||||
"type": "DialogTitle",
|
||||
"bindings": {
|
||||
"children": "title"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "data-source-editor-dialog-description",
|
||||
"type": "DialogDescription",
|
||||
"bindings": {
|
||||
"children": "description"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "data-source-editor-dialog-body",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "data-source-editor-dialog-empty",
|
||||
"type": "p",
|
||||
"props": {
|
||||
"className": "text-sm text-muted-foreground"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "emptyMessage"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "!fields || fields.length === 0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "data-source-editor-dialog-fields",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-4"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "fields && fields.length > 0"
|
||||
},
|
||||
"loop": {
|
||||
"source": "fields",
|
||||
"itemVar": "field"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "data-source-editor-dialog-field",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "data-source-editor-dialog-field-label",
|
||||
"type": "Label",
|
||||
"bindings": {
|
||||
"children": "field.label"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "data-source-editor-dialog-field-input",
|
||||
"type": "Input",
|
||||
"bindings": {
|
||||
"value": "field.value",
|
||||
"placeholder": "field.placeholder",
|
||||
"onChange": "onFieldChange",
|
||||
"data-field-id": "field.id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "data-source-editor-dialog-field-helper",
|
||||
"type": "p",
|
||||
"props": {
|
||||
"className": "text-xs text-muted-foreground"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "field.helperText"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "field.helperText"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "data-source-editor-dialog-footer",
|
||||
"type": "DialogFooter",
|
||||
"children": [
|
||||
{
|
||||
"id": "data-source-editor-dialog-cancel",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"variant": "outline"
|
||||
},
|
||||
"bindings": {
|
||||
"onClick": "onCancel",
|
||||
"children": "cancelLabel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "data-source-editor-dialog-save",
|
||||
"type": "Button",
|
||||
"bindings": {
|
||||
"onClick": "onSave",
|
||||
"children": "saveLabel"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
210
src/lib/json-ui/wrappers/definitions/github-build-status.json
Normal file
210
src/lib/json-ui/wrappers/definitions/github-build-status.json
Normal file
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"id": "github-build-status-card",
|
||||
"type": "Card",
|
||||
"bindings": {
|
||||
"className": "className"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-title",
|
||||
"type": "CardTitle",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "title"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-description",
|
||||
"type": "CardDescription",
|
||||
"bindings": {
|
||||
"children": "description"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-content",
|
||||
"type": "CardContent",
|
||||
"props": {
|
||||
"className": "space-y-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-loading",
|
||||
"type": "p",
|
||||
"props": {
|
||||
"className": "text-sm text-muted-foreground"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "loadingMessage"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "isLoading"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-error",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2 text-sm text-red-500"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "errorMessage"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-error-text",
|
||||
"type": "span",
|
||||
"bindings": {
|
||||
"children": "errorMessage"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-empty",
|
||||
"type": "p",
|
||||
"props": {
|
||||
"className": "text-sm text-muted-foreground"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "emptyMessage"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "!isLoading && !errorMessage && !hasWorkflows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-list",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-3"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "hasWorkflows"
|
||||
},
|
||||
"loop": {
|
||||
"source": "workflows",
|
||||
"itemVar": "workflow"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-item",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center justify-between gap-3 rounded-lg border border-border p-3"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-item-info",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "min-w-0"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-item-row",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-item-name",
|
||||
"type": "p",
|
||||
"props": {
|
||||
"className": "text-sm font-medium truncate"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "workflow.name"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-item-badge",
|
||||
"type": "Badge",
|
||||
"bindings": {
|
||||
"className": "workflow.statusClass",
|
||||
"children": "workflow.statusLabel"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-item-meta",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "text-xs text-muted-foreground truncate"
|
||||
},
|
||||
"bindings": {
|
||||
"children": "workflow.summaryLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-item-link",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"variant": "ghost",
|
||||
"size": "sm",
|
||||
"asChild": true
|
||||
},
|
||||
"conditional": {
|
||||
"if": "workflow.url"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-item-anchor",
|
||||
"type": "a",
|
||||
"bindings": {
|
||||
"href": "workflow.url"
|
||||
},
|
||||
"props": {
|
||||
"target": "_blank",
|
||||
"rel": "noopener noreferrer"
|
||||
},
|
||||
"children": "View"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "github-build-status-footer",
|
||||
"type": "Button",
|
||||
"props": {
|
||||
"variant": "outline",
|
||||
"size": "sm",
|
||||
"asChild": true,
|
||||
"className": "w-full"
|
||||
},
|
||||
"conditional": {
|
||||
"if": "footerLinkUrl"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "github-build-status-footer-anchor",
|
||||
"type": "a",
|
||||
"bindings": {
|
||||
"href": "footerLinkUrl",
|
||||
"children": "footerLinkLabel"
|
||||
},
|
||||
"props": {
|
||||
"target": "_blank",
|
||||
"rel": "noopener noreferrer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,3 +5,7 @@ export { LazyLineChartWrapper } from './LazyLineChartWrapper'
|
||||
export { LazyD3BarChartWrapper } from './LazyD3BarChartWrapper'
|
||||
export { SeedDataManagerWrapper } from './SeedDataManagerWrapper'
|
||||
export { StorageSettingsWrapper } from './StorageSettingsWrapper'
|
||||
export { GitHubBuildStatusWrapper } from './GitHubBuildStatusWrapper'
|
||||
export { ComponentBindingDialogWrapper } from './ComponentBindingDialogWrapper'
|
||||
export { DataSourceEditorDialogWrapper } from './DataSourceEditorDialogWrapper'
|
||||
export { ComponentTreeWrapper } from './ComponentTreeWrapper'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { StorageBackendKey } from '@/components/storage/storageSettingsConfig'
|
||||
import type { UIComponent } from '@/types/json-ui'
|
||||
|
||||
export interface BreadcrumbItem {
|
||||
label: string
|
||||
@@ -89,3 +90,75 @@ export interface StorageSettingsWrapperProps {
|
||||
onExport?: () => void
|
||||
onImport?: () => void
|
||||
}
|
||||
|
||||
export interface GitHubBuildStatusWorkflowItem {
|
||||
id: string
|
||||
name: string
|
||||
status?: string
|
||||
conclusion?: string | null
|
||||
branch?: string
|
||||
updatedAt?: string
|
||||
event?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
export interface GitHubBuildStatusWrapperProps {
|
||||
title?: string
|
||||
description?: string
|
||||
workflows?: GitHubBuildStatusWorkflowItem[]
|
||||
isLoading?: boolean
|
||||
errorMessage?: string
|
||||
emptyMessage?: string
|
||||
footerLinkLabel?: string
|
||||
footerLinkUrl?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export interface ComponentBindingField {
|
||||
id: string
|
||||
label: string
|
||||
value?: string
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
export interface ComponentBindingDialogWrapperProps {
|
||||
open?: boolean
|
||||
title?: string
|
||||
description?: string
|
||||
componentType?: string
|
||||
componentId?: string
|
||||
bindings?: ComponentBindingField[]
|
||||
onBindingChange?: (id: string, value: string) => void
|
||||
onSave?: () => void
|
||||
onCancel?: () => void
|
||||
onOpenChange?: (open: boolean) => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
export interface DataSourceField {
|
||||
id: string
|
||||
label: string
|
||||
value?: string
|
||||
placeholder?: string
|
||||
helperText?: string
|
||||
}
|
||||
|
||||
export interface DataSourceEditorDialogWrapperProps {
|
||||
open?: boolean
|
||||
title?: string
|
||||
description?: string
|
||||
fields?: DataSourceField[]
|
||||
onFieldChange?: (id: string, value: string) => void
|
||||
onSave?: () => void
|
||||
onCancel?: () => void
|
||||
onOpenChange?: (open: boolean) => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
export interface ComponentTreeWrapperProps {
|
||||
components?: UIComponent[]
|
||||
selectedId?: string | null
|
||||
emptyMessage?: string
|
||||
onSelect?: (id: string) => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user