diff --git a/src/components/JSONDemoPage.tsx b/src/components/JSONDemoPage.tsx index 5a87a5c..38ec0c3 100644 --- a/src/components/JSONDemoPage.tsx +++ b/src/components/JSONDemoPage.tsx @@ -3,45 +3,50 @@ import { toast } from 'sonner' import { useKV } from '@/hooks/use-kv' import { useState } from 'react' import { buildDemoPageSchema, demoCopy, demoInitialTodos } from '@/components/json-demo/schema' +import { Action } from '@/lib/json-ui/schema' export function JSONDemoPage() { const [todos, setTodos] = useKV('json-demo-todos', demoInitialTodos) const [newTodo, setNewTodo] = useState('') - const handleAction = (handler: any, event?: any) => { - switch (handler.action) { - case 'add-todo': - if (newTodo.trim()) { - setTodos((current: any) => [ - ...current, - { id: Date.now(), text: newTodo, completed: false }, - ]) - setNewTodo('') - toast.success(demoCopy.toastAdded) - } - break + const handleAction = (actions: Action[], event?: any) => { + actions.forEach((action) => { + const actionKey = action.type === 'custom' ? action.id : action.type - case 'toggle-todo': - setTodos((current: any) => - current.map((todo: any) => - todo.id === handler.params?.id - ? { ...todo, completed: !todo.completed } - : todo + switch (actionKey) { + case 'add-todo': + if (newTodo.trim()) { + setTodos((current: any) => [ + ...current, + { id: Date.now(), text: newTodo, completed: false }, + ]) + setNewTodo('') + toast.success(demoCopy.toastAdded) + } + break + + case 'toggle-todo': + setTodos((current: any) => + current.map((todo: any) => + todo.id === action.params?.id + ? { ...todo, completed: !todo.completed } + : todo + ) ) - ) - break + break - case 'delete-todo': - setTodos((current: any) => - current.filter((todo: any) => todo.id !== handler.params?.id) - ) - toast.success(demoCopy.toastDeleted) - break + case 'delete-todo': + setTodos((current: any) => + current.filter((todo: any) => todo.id !== action.params?.id) + ) + toast.success(demoCopy.toastDeleted) + break - case 'update-input': - setNewTodo(event.target.value) - break - } + case 'update-input': + setNewTodo(event.target.value) + break + } + }) } const pageSchema = buildDemoPageSchema(todos, newTodo) diff --git a/src/components/JSONUIPage.tsx b/src/components/JSONUIPage.tsx index 0e87aeb..55f189c 100644 --- a/src/components/JSONUIPage.tsx +++ b/src/components/JSONUIPage.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' import { JSONUIRenderer } from '@/lib/json-ui/renderer' -import { UIComponent, EventHandler, Layout } from '@/lib/json-ui/schema' +import { Action, UIComponent, Layout } from '@/lib/json-ui/schema' import { toast } from 'sonner' interface JSONUIPageProps { @@ -34,88 +34,91 @@ export function JSONUIPage({ jsonConfig }: JSONUIPageProps) { })) } - const handleAction = (handler: EventHandler, event?: any) => { - console.log('Action triggered:', handler.action, handler.params, event) - - switch (handler.action) { - case 'refresh-data': - toast.success('Data refreshed') - break - case 'create-project': - toast.info('Create project clicked') - break - case 'deploy': - toast.info('Deploy clicked') - break - case 'view-logs': - toast.info('View logs clicked') - break - case 'settings': - toast.info('Settings clicked') - break - case 'add-project': - toast.info('Add project clicked') - break - case 'view-project': - toast.info(`View project: ${handler.params?.projectId}`) - break - case 'edit-project': - toast.info(`Edit project: ${handler.params?.projectId}`) - break - case 'delete-project': - toast.error(`Delete project: ${handler.params?.projectId}`) - break - case 'update-field': - if (event?.target) { - const { name, value } = event.target - updateDataField('formData', name, value) - } - break - case 'update-checkbox': - if (handler.params?.field) { - updateDataField('formData', handler.params.field, event) - } - break - case 'submit-form': - toast.success('Form submitted!') - console.log('Form data:', dataMap.formData) - break - case 'cancel-form': - toast.info('Form cancelled') - break - case 'toggle-dark-mode': - updateDataField('settings', 'darkMode', event) - toast.success(`Dark mode ${event ? 'enabled' : 'disabled'}`) - break - case 'toggle-auto-save': - updateDataField('settings', 'autoSave', event) - toast.success(`Auto-save ${event ? 'enabled' : 'disabled'}`) - break - case 'toggle-email-notifications': - updateDataField('notifications', 'email', event) - toast.success(`Email notifications ${event ? 'enabled' : 'disabled'}`) - break - case 'toggle-push-notifications': - updateDataField('notifications', 'push', event) - toast.success(`Push notifications ${event ? 'enabled' : 'disabled'}`) - break - case 'toggle-2fa': - updateDataField('security', 'twoFactor', event) - toast.success(`Two-factor auth ${event ? 'enabled' : 'disabled'}`) - break - case 'logout-all-sessions': - toast.success('All other sessions logged out') - break - case 'save-settings': - toast.success('Settings saved successfully') - console.log('Settings:', dataMap) - break - case 'reset-settings': - toast.info('Settings reset to defaults') - break - default: - console.log('Unhandled action:', handler.action) - } + const handleAction = (actions: Action[], event?: any) => { + actions.forEach((action) => { + const actionKey = action.type === 'custom' ? action.id : action.type + console.log('Action triggered:', actionKey, action.params, event) + + switch (actionKey) { + case 'refresh-data': + toast.success('Data refreshed') + break + case 'create-project': + toast.info('Create project clicked') + break + case 'deploy': + toast.info('Deploy clicked') + break + case 'view-logs': + toast.info('View logs clicked') + break + case 'settings': + toast.info('Settings clicked') + break + case 'add-project': + toast.info('Add project clicked') + break + case 'view-project': + toast.info(`View project: ${action.params?.projectId}`) + break + case 'edit-project': + toast.info(`Edit project: ${action.params?.projectId}`) + break + case 'delete-project': + toast.error(`Delete project: ${action.params?.projectId}`) + break + case 'update-field': + if (event?.target) { + const { name, value } = event.target + updateDataField('formData', name, value) + } + break + case 'update-checkbox': + if (action.params?.field) { + updateDataField('formData', action.params.field, event) + } + break + case 'submit-form': + toast.success('Form submitted!') + console.log('Form data:', dataMap.formData) + break + case 'cancel-form': + toast.info('Form cancelled') + break + case 'toggle-dark-mode': + updateDataField('settings', 'darkMode', event) + toast.success(`Dark mode ${event ? 'enabled' : 'disabled'}`) + break + case 'toggle-auto-save': + updateDataField('settings', 'autoSave', event) + toast.success(`Auto-save ${event ? 'enabled' : 'disabled'}`) + break + case 'toggle-email-notifications': + updateDataField('notifications', 'email', event) + toast.success(`Email notifications ${event ? 'enabled' : 'disabled'}`) + break + case 'toggle-push-notifications': + updateDataField('notifications', 'push', event) + toast.success(`Push notifications ${event ? 'enabled' : 'disabled'}`) + break + case 'toggle-2fa': + updateDataField('security', 'twoFactor', event) + toast.success(`Two-factor auth ${event ? 'enabled' : 'disabled'}`) + break + case 'logout-all-sessions': + toast.success('All other sessions logged out') + break + case 'save-settings': + toast.success('Settings saved successfully') + console.log('Settings:', dataMap) + break + case 'reset-settings': + toast.info('Settings reset to defaults') + break + default: + console.log('Unhandled action:', actionKey) + } + }) } if (!jsonConfig.layout) { diff --git a/src/components/json-demo/schema.ts b/src/components/json-demo/schema.ts index 0a6628e..66c95fb 100644 --- a/src/components/json-demo/schema.ts +++ b/src/components/json-demo/schema.ts @@ -49,12 +49,18 @@ const buildTodoItem = (todo: TodoItem, copy: DemoCopy): UIComponent => ({ props: { checked: todo.completed, }, - events: { - onCheckedChange: { - action: 'toggle-todo', - params: { id: todo.id }, + events: [ + { + event: 'checkedChange', + actions: [ + { + id: 'toggle-todo', + type: 'custom', + params: { id: todo.id }, + }, + ], }, - }, + ], }, { id: `text-${todo.id}`, @@ -72,12 +78,18 @@ const buildTodoItem = (todo: TodoItem, copy: DemoCopy): UIComponent => ({ size: 'sm', children: copy.deleteButtonLabel, }, - events: { - onClick: { - action: 'delete-todo', - params: { id: todo.id }, + events: [ + { + event: 'click', + actions: [ + { + id: 'delete-todo', + type: 'custom', + params: { id: todo.id }, + }, + ], }, - }, + ], }, ], }) diff --git a/src/lib/json-ui/page-renderer.tsx b/src/lib/json-ui/page-renderer.tsx index e998798..17b613c 100644 --- a/src/lib/json-ui/page-renderer.tsx +++ b/src/lib/json-ui/page-renderer.tsx @@ -20,17 +20,10 @@ export function PageRenderer({ schema, onCustomAction }: PageRendererProps) { const { executeActions } = useActionExecutor(context) - const handleEvent = useCallback((componentId: string, event: string, eventData: any) => { - const component = findComponentById(schema.components, componentId) - if (!component) return - - const handler = component.events?.find(h => h.event === event) - if (!handler) return - - if (handler.condition && !handler.condition(data)) return - + const handleEvent = useCallback((_componentId: string, handler: { actions: any[] }, eventData: any) => { + if (!handler?.actions?.length) return executeActions(handler.actions, eventData) - }, [schema.components, data, executeActions]) + }, [executeActions]) return (