mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
Merge pull request #154 from johndoe6345789/codex/update-json-ui-context-and-path-handling
Support dotted action targets for nested data updates
This commit is contained in:
@@ -96,6 +96,20 @@ Update a data source with a new value.
|
||||
}
|
||||
```
|
||||
|
||||
**Target-path convention:**
|
||||
To update nested values inside a data source, use a dotted `target` where the prefix is the data source ID and the remainder is the nested path:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "set-city",
|
||||
"type": "set-value",
|
||||
"target": "profile.address.city",
|
||||
"expression": "event.target.value"
|
||||
}
|
||||
```
|
||||
|
||||
This dotted `target` format works with `set-value`, `update`, `toggle-value`, `increment`, and `decrement`.
|
||||
|
||||
### create
|
||||
Add a new item to an array data source.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { useKV } from '@/hooks/use-kv'
|
||||
import { DataSource } from '@/types/json-ui'
|
||||
import { setNestedValue } from '@/lib/json-ui/utils'
|
||||
|
||||
export function useDataSources(dataSources: DataSource[]) {
|
||||
const [data, setData] = useState<Record<string, any>>({})
|
||||
@@ -86,18 +87,8 @@ export function useDataSources(dataSources: DataSource[]) {
|
||||
return prev
|
||||
}
|
||||
|
||||
const pathParts = path.split('.')
|
||||
const newData = { ...sourceData }
|
||||
let current: any = newData
|
||||
|
||||
for (let i = 0; i < pathParts.length - 1; i++) {
|
||||
if (!(pathParts[i] in current)) {
|
||||
current[pathParts[i]] = {}
|
||||
}
|
||||
current = current[pathParts[i]]
|
||||
}
|
||||
|
||||
current[pathParts[pathParts.length - 1]] = value
|
||||
const newData = Array.isArray(sourceData) ? [...sourceData] : { ...sourceData }
|
||||
setNestedValue(newData, path, value)
|
||||
|
||||
if (source.type === 'kv') {
|
||||
const kvIndex = kvSources.indexOf(source)
|
||||
|
||||
@@ -2,9 +2,17 @@ import { useCallback } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { Action, JSONUIContext } from '@/types/json-ui'
|
||||
import { evaluateExpression, evaluateTemplate } from '@/lib/json-ui/expression-evaluator'
|
||||
import { getNestedValue } from '@/lib/json-ui/utils'
|
||||
|
||||
export function useActionExecutor(context: JSONUIContext) {
|
||||
const { data, updateData, executeAction: contextExecute } = context
|
||||
const { data, updateData, updatePath, executeAction: contextExecute } = context
|
||||
|
||||
const getTargetParts = (target?: string) => {
|
||||
if (!target) return null
|
||||
const [sourceId, ...pathParts] = target.split('.')
|
||||
const path = pathParts.join('.')
|
||||
return { sourceId, path: path || undefined }
|
||||
}
|
||||
|
||||
const executeAction = useCallback(async (action: Action, event?: any) => {
|
||||
try {
|
||||
@@ -35,7 +43,8 @@ export function useActionExecutor(context: JSONUIContext) {
|
||||
}
|
||||
|
||||
case 'update': {
|
||||
if (!action.target) return
|
||||
const targetParts = getTargetParts(action.target)
|
||||
if (!targetParts) return
|
||||
|
||||
let newValue
|
||||
if (action.compute) {
|
||||
@@ -47,8 +56,12 @@ export function useActionExecutor(context: JSONUIContext) {
|
||||
} else {
|
||||
newValue = action.value
|
||||
}
|
||||
|
||||
updateData(action.target, newValue)
|
||||
|
||||
if (targetParts.path) {
|
||||
updatePath(targetParts.sourceId, targetParts.path, newValue)
|
||||
} else {
|
||||
updateData(targetParts.sourceId, newValue)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
@@ -66,7 +79,8 @@ export function useActionExecutor(context: JSONUIContext) {
|
||||
}
|
||||
|
||||
case 'set-value': {
|
||||
if (!action.target) return
|
||||
const targetParts = getTargetParts(action.target)
|
||||
if (!targetParts) return
|
||||
|
||||
let newValue
|
||||
if (action.compute) {
|
||||
@@ -78,31 +92,65 @@ export function useActionExecutor(context: JSONUIContext) {
|
||||
} else {
|
||||
newValue = action.value
|
||||
}
|
||||
|
||||
updateData(action.target, newValue)
|
||||
|
||||
if (targetParts.path) {
|
||||
updatePath(targetParts.sourceId, targetParts.path, newValue)
|
||||
} else {
|
||||
updateData(targetParts.sourceId, newValue)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'toggle-value': {
|
||||
if (!action.target) return
|
||||
const currentValue = data[action.target]
|
||||
updateData(action.target, !currentValue)
|
||||
const targetParts = getTargetParts(action.target)
|
||||
if (!targetParts) return
|
||||
|
||||
const currentValue = targetParts.path
|
||||
? getNestedValue(data[targetParts.sourceId], targetParts.path)
|
||||
: data[targetParts.sourceId]
|
||||
const nextValue = !currentValue
|
||||
|
||||
if (targetParts.path) {
|
||||
updatePath(targetParts.sourceId, targetParts.path, nextValue)
|
||||
} else {
|
||||
updateData(targetParts.sourceId, nextValue)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'increment': {
|
||||
if (!action.target) return
|
||||
const currentValue = data[action.target] || 0
|
||||
const targetParts = getTargetParts(action.target)
|
||||
if (!targetParts) return
|
||||
|
||||
const currentValue = targetParts.path
|
||||
? getNestedValue(data[targetParts.sourceId], targetParts.path)
|
||||
: data[targetParts.sourceId]
|
||||
const amount = action.value || 1
|
||||
updateData(action.target, currentValue + amount)
|
||||
const nextValue = (currentValue || 0) + amount
|
||||
|
||||
if (targetParts.path) {
|
||||
updatePath(targetParts.sourceId, targetParts.path, nextValue)
|
||||
} else {
|
||||
updateData(targetParts.sourceId, nextValue)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'decrement': {
|
||||
if (!action.target) return
|
||||
const currentValue = data[action.target] || 0
|
||||
const targetParts = getTargetParts(action.target)
|
||||
if (!targetParts) return
|
||||
|
||||
const currentValue = targetParts.path
|
||||
? getNestedValue(data[targetParts.sourceId], targetParts.path)
|
||||
: data[targetParts.sourceId]
|
||||
const amount = action.value || 1
|
||||
updateData(action.target, currentValue - amount)
|
||||
const nextValue = (currentValue || 0) - amount
|
||||
|
||||
if (targetParts.path) {
|
||||
updatePath(targetParts.sourceId, targetParts.path, nextValue)
|
||||
} else {
|
||||
updateData(targetParts.sourceId, nextValue)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
@@ -145,7 +193,7 @@ export function useActionExecutor(context: JSONUIContext) {
|
||||
console.error('Action execution failed:', error)
|
||||
toast.error('Action failed')
|
||||
}
|
||||
}, [data, updateData, contextExecute])
|
||||
}, [data, updateData, updatePath, contextExecute])
|
||||
|
||||
const executeActions = useCallback(async (actions: Action[], event?: any) => {
|
||||
for (const action of actions) {
|
||||
|
||||
@@ -17,6 +17,7 @@ export function PageRenderer({ schema, onCustomAction }: PageRendererProps) {
|
||||
const context = {
|
||||
data,
|
||||
updateData,
|
||||
updatePath,
|
||||
executeAction: onCustomAction || (async () => {}),
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ export interface PageSchema {
|
||||
export interface JSONUIContext {
|
||||
data: Record<string, any>
|
||||
updateData: (sourceId: string, value: any) => void
|
||||
updatePath: (sourceId: string, path: string, value: any) => void
|
||||
executeAction: (action: Action, event?: any) => Promise<void>
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user