From 087ab7e389fcacb3b47942b9d9c3d236de73f6f6 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 00:45:23 +0000 Subject: [PATCH] Refactor data binding designer copy --- src/components/DataBindingDesigner.tsx | 220 ++++-------------- .../ComponentBindingsCard.tsx | 115 +++++++++ .../DataBindingHeader.tsx | 17 ++ .../data-binding-designer/HowItWorksCard.tsx | 28 +++ .../molecules/DataSourceEditorDialog.tsx | 192 ++++----------- .../ComputedSourceFields.tsx | 110 +++++++++ .../data-source-editor/DataSourceIdField.tsx | 29 +++ .../data-source-editor/KvSourceFields.tsx | 54 +++++ .../data-source-editor/StaticSourceFields.tsx | 36 +++ .../organisms/DataSourceManager.tsx | 169 ++++---------- .../DataSourceGroupSection.tsx | 48 ++++ .../DataSourceManagerHeader.tsx | 64 +++++ src/data/data-binding-designer.json | 84 +++++++ src/data/data-source-editor-dialog.json | 33 +++ src/data/data-source-manager.json | 29 +++ 15 files changed, 786 insertions(+), 442 deletions(-) create mode 100644 src/components/data-binding-designer/ComponentBindingsCard.tsx create mode 100644 src/components/data-binding-designer/DataBindingHeader.tsx create mode 100644 src/components/data-binding-designer/HowItWorksCard.tsx create mode 100644 src/components/molecules/data-source-editor/ComputedSourceFields.tsx create mode 100644 src/components/molecules/data-source-editor/DataSourceIdField.tsx create mode 100644 src/components/molecules/data-source-editor/KvSourceFields.tsx create mode 100644 src/components/molecules/data-source-editor/StaticSourceFields.tsx create mode 100644 src/components/organisms/data-source-manager/DataSourceGroupSection.tsx create mode 100644 src/components/organisms/data-source-manager/DataSourceManagerHeader.tsx create mode 100644 src/data/data-binding-designer.json create mode 100644 src/data/data-source-editor-dialog.json create mode 100644 src/data/data-source-manager.json diff --git a/src/components/DataBindingDesigner.tsx b/src/components/DataBindingDesigner.tsx index aef1e29..1c32c81 100644 --- a/src/components/DataBindingDesigner.tsx +++ b/src/components/DataBindingDesigner.tsx @@ -1,61 +1,39 @@ import { useState } from 'react' import { DataSourceManager } from '@/components/organisms/DataSourceManager' import { ComponentBindingDialog } from '@/components/molecules/ComponentBindingDialog' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { Button } from '@/components/ui/button' -import { Badge } from '@/components/ui/badge' import { DataSource, UIComponent } from '@/types/json-ui' -import { Link, Code } from '@phosphor-icons/react' -import { ScrollArea } from '@/components/ui/scroll-area' +import { DataBindingHeader } from '@/components/data-binding-designer/DataBindingHeader' +import { ComponentBindingsCard } from '@/components/data-binding-designer/ComponentBindingsCard' +import { HowItWorksCard } from '@/components/data-binding-designer/HowItWorksCard' +import dataBindingCopy from '@/data/data-binding-designer.json' + +interface SeedDataSource extends Omit { + computeId?: string +} + +const computeRegistry: Record) => any> = { + displayName: (data) => `Welcome, ${data.userProfile?.name || 'Guest'}!`, +} + +const buildSeedDataSources = (sources: SeedDataSource[]): DataSource[] => { + return sources.map((source) => { + if (source.type === 'computed' && source.computeId) { + return { + ...source, + compute: computeRegistry[source.computeId], + } + } + + return source + }) +} export function DataBindingDesigner() { - const [dataSources, setDataSources] = useState([ - { - id: 'userProfile', - type: 'kv', - key: 'user-profile', - defaultValue: { name: 'John Doe', email: 'john@example.com' }, - }, - { - id: 'counter', - type: 'kv', - key: 'app-counter', - defaultValue: 0, - }, - { - id: 'displayName', - type: 'computed', - compute: (data) => `Welcome, ${data.userProfile?.name || 'Guest'}!`, - dependencies: ['userProfile'], - }, - ]) + const [dataSources, setDataSources] = useState( + buildSeedDataSources(dataBindingCopy.seed.dataSources as SeedDataSource[]), + ) - const [mockComponents] = useState([ - { - id: 'title', - type: 'Heading', - props: { className: 'text-2xl font-bold' }, - bindings: { - children: { source: 'displayName' }, - }, - }, - { - id: 'counter-display', - type: 'Text', - props: { className: 'text-lg' }, - bindings: { - children: { source: 'counter' }, - }, - }, - { - id: 'email-input', - type: 'Input', - props: { placeholder: 'Enter email' }, - bindings: { - value: { source: 'userProfile', path: 'email' }, - }, - }, - ]) + const [mockComponents] = useState(dataBindingCopy.seed.components) const [selectedComponent, setSelectedComponent] = useState(null) const [bindingDialogOpen, setBindingDialogOpen] = useState(false) @@ -69,21 +47,13 @@ export function DataBindingDesigner() { console.log('Updated component bindings:', updatedComponent) } - const getSourceById = (sourceId: string) => { - return dataSources.find(ds => ds.id === sourceId) - } - return (
-
-

- Data Binding Designer -

-

- Connect UI components to KV storage and computed values -

-
+
@@ -94,121 +64,17 @@ export function DataBindingDesigner() {
- - - - - Component Bindings - - - Example components with data bindings - - - - -
- {mockComponents.map(component => { - const bindingCount = Object.keys(component.bindings || {}).length - - return ( - - -
-
-
- - {component.type} - - - #{component.id} - -
+ - {bindingCount > 0 ? ( -
- {Object.entries(component.bindings || {}).map(([prop, binding]) => { - const source = getSourceById(binding.source) - return ( -
- - {prop}: - - - {binding.source} - {binding.path && `.${binding.path}`} - - {source && ( - - {source.type} - - )} -
- ) - })} -
- ) : ( -

- No bindings configured -

- )} -
- - -
-
-
- ) - })} -
-
-
-
- - - - How It Works - - -
-
- 1 -
-

- Create data sources (KV store for persistence, static for constants) -

-
-
-
- 2 -
-

- Add computed sources to derive values from other sources -

-
-
-
- 3 -
-

- Bind component properties to data sources for reactive updates -

-
-
-
+
diff --git a/src/components/data-binding-designer/ComponentBindingsCard.tsx b/src/components/data-binding-designer/ComponentBindingsCard.tsx new file mode 100644 index 0000000..6cf09b9 --- /dev/null +++ b/src/components/data-binding-designer/ComponentBindingsCard.tsx @@ -0,0 +1,115 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Badge } from '@/components/ui/badge' +import { ScrollArea } from '@/components/ui/scroll-area' +import { DataSource, UIComponent } from '@/types/json-ui' +import { Link, Code } from '@phosphor-icons/react' + +interface ComponentBindingsCardCopy { + title: string + description: string + emptyState: string + actionLabel: string +} + +interface ComponentBindingsCardProps { + components: UIComponent[] + dataSources: DataSource[] + copy: ComponentBindingsCardCopy + onEditBinding: (component: UIComponent) => void +} + +export function ComponentBindingsCard({ + components, + dataSources, + copy, + onEditBinding, +}: ComponentBindingsCardProps) { + const getSourceById = (sourceId: string) => dataSources.find(ds => ds.id === sourceId) + + return ( + + + + + {copy.title} + + + {copy.description} + + + + +
+ {components.map(component => { + const bindingCount = Object.keys(component.bindings || {}).length + + return ( + + +
+
+
+ + {component.type} + + + #{component.id} + +
+ + {bindingCount > 0 ? ( +
+ {Object.entries(component.bindings || {}).map(([prop, binding]) => { + const source = getSourceById(binding.source) + return ( +
+ + {prop}: + + + {binding.source} + {binding.path && `.${binding.path}`} + + {source && ( + + {source.type} + + )} +
+ ) + })} +
+ ) : ( +

+ {copy.emptyState} +

+ )} +
+ + +
+
+
+ ) + })} +
+
+
+
+ ) +} diff --git a/src/components/data-binding-designer/DataBindingHeader.tsx b/src/components/data-binding-designer/DataBindingHeader.tsx new file mode 100644 index 0000000..73958cc --- /dev/null +++ b/src/components/data-binding-designer/DataBindingHeader.tsx @@ -0,0 +1,17 @@ +interface DataBindingHeaderProps { + title: string + description: string +} + +export function DataBindingHeader({ title, description }: DataBindingHeaderProps) { + return ( +
+

+ {title} +

+

+ {description} +

+
+ ) +} diff --git a/src/components/data-binding-designer/HowItWorksCard.tsx b/src/components/data-binding-designer/HowItWorksCard.tsx new file mode 100644 index 0000000..285196d --- /dev/null +++ b/src/components/data-binding-designer/HowItWorksCard.tsx @@ -0,0 +1,28 @@ +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' + +interface HowItWorksCardProps { + title: string + steps: string[] +} + +export function HowItWorksCard({ title, steps }: HowItWorksCardProps) { + return ( + + + {title} + + + {steps.map((step, index) => ( +
+
+ {index + 1} +
+

+ {step} +

+
+ ))} +
+
+ ) +} diff --git a/src/components/molecules/DataSourceEditorDialog.tsx b/src/components/molecules/DataSourceEditorDialog.tsx index fbac361..5b13f15 100644 --- a/src/components/molecules/DataSourceEditorDialog.tsx +++ b/src/components/molecules/DataSourceEditorDialog.tsx @@ -1,14 +1,13 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { Textarea } from '@/components/ui/textarea' -import { DataSource, DataSourceType } from '@/types/json-ui' +import { DataSource } from '@/types/json-ui' import { DataSourceBadge } from '@/components/atoms/DataSourceBadge' -import { Badge } from '@/components/ui/badge' -import { X } from '@phosphor-icons/react' +import { DataSourceIdField } from '@/components/molecules/data-source-editor/DataSourceIdField' +import { KvSourceFields } from '@/components/molecules/data-source-editor/KvSourceFields' +import { StaticSourceFields } from '@/components/molecules/data-source-editor/StaticSourceFields' +import { ComputedSourceFields } from '@/components/molecules/data-source-editor/ComputedSourceFields' +import dataSourceEditorCopy from '@/data/data-source-editor-dialog.json' interface DataSourceEditorDialogProps { open: boolean @@ -18,15 +17,19 @@ interface DataSourceEditorDialogProps { onSave: (dataSource: DataSource) => void } -export function DataSourceEditorDialog({ - open, - dataSource, +export function DataSourceEditorDialog({ + open, + dataSource, allDataSources, - onOpenChange, - onSave + onOpenChange, + onSave, }: DataSourceEditorDialogProps) { const [editingSource, setEditingSource] = useState(dataSource) + useEffect(() => { + setEditingSource(dataSource) + }, [dataSource]) + const handleSave = () => { if (!editingSource) return onSave(editingSource) @@ -55,7 +58,7 @@ export function DataSourceEditorDialog({ if (!editingSource) return null const availableDeps = allDataSources.filter( - ds => ds.id !== editingSource.id && ds.type !== 'computed' + ds => ds.id !== editingSource.id && ds.type !== 'computed', ) const selectedDeps = editingSource.dependencies || [] @@ -66,158 +69,59 @@ export function DataSourceEditorDialog({ - Edit Data Source + {dataSourceEditorCopy.title} - Configure the data source settings and dependencies + {dataSourceEditorCopy.description}
-
- - updateField('id', e.target.value)} - placeholder="unique-id" - className="font-mono" - /> -
+ updateField('id', value)} + /> {editingSource.type === 'kv' && ( - <> -
- - updateField('key', e.target.value)} - placeholder="storage-key" - className="font-mono" - /> -

- Key used for persistent storage in the KV store -

-
- -
- -