mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-05-02 17:44:52 +00:00
Merge pull request #197 from johndoe6345789/codex/refactor-computed-json-configs
Remove legacy `computed` data sources; migrate to expression/valueTemplate and update UI
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { DataSource } from '@/types/json-ui'
|
||||
|
||||
export function useDataSourceEditor(
|
||||
dataSource: DataSource | null,
|
||||
allDataSources: DataSource[],
|
||||
) {
|
||||
const [editingSource, setEditingSource] = useState<DataSource | null>(dataSource)
|
||||
|
||||
@@ -15,44 +14,8 @@ export function useDataSourceEditor(
|
||||
setEditingSource(prev => (prev ? { ...prev, [field]: value } : prev))
|
||||
}, [])
|
||||
|
||||
const addDependency = useCallback((depId: string) => {
|
||||
setEditingSource(prev => {
|
||||
if (!prev || prev.type !== 'computed') return prev
|
||||
const deps = prev.dependencies || []
|
||||
if (deps.includes(depId)) return prev
|
||||
return { ...prev, dependencies: [...deps, depId] }
|
||||
})
|
||||
}, [])
|
||||
|
||||
const removeDependency = useCallback((depId: string) => {
|
||||
setEditingSource(prev => {
|
||||
if (!prev || prev.type !== 'computed') return prev
|
||||
const deps = prev.dependencies || []
|
||||
return { ...prev, dependencies: deps.filter(dep => dep !== depId) }
|
||||
})
|
||||
}, [])
|
||||
|
||||
const availableDeps = useMemo(() => {
|
||||
if (!editingSource) return []
|
||||
return allDataSources.filter(
|
||||
ds => ds.id !== editingSource.id && ds.type !== 'computed',
|
||||
)
|
||||
}, [allDataSources, editingSource])
|
||||
|
||||
const selectedDeps = useMemo(() => editingSource?.dependencies || [], [editingSource])
|
||||
|
||||
const unselectedDeps = useMemo(
|
||||
() => availableDeps.filter(ds => !selectedDeps.includes(ds.id)),
|
||||
[availableDeps, selectedDeps],
|
||||
)
|
||||
|
||||
return {
|
||||
editingSource,
|
||||
updateField,
|
||||
addDependency,
|
||||
removeDependency,
|
||||
availableDeps,
|
||||
selectedDeps,
|
||||
unselectedDeps,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ export function useDataSourceManager(initialSources: DataSource[] = []) {
|
||||
id: `ds-${Date.now()}`,
|
||||
type,
|
||||
...(type === 'kv' && { key: '', defaultValue: null }),
|
||||
...(type === 'computed' && { expression: '', dependencies: [] }),
|
||||
...(type === 'static' && { defaultValue: null }),
|
||||
}
|
||||
|
||||
@@ -33,7 +32,6 @@ export function useDataSourceManager(initialSources: DataSource[] = []) {
|
||||
|
||||
const getDependents = useCallback((sourceId: string) => {
|
||||
return dataSources.filter(ds =>
|
||||
ds.type === 'computed' &&
|
||||
ds.dependencies?.includes(sourceId)
|
||||
)
|
||||
}, [dataSources])
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { useKV } from '@/hooks/use-kv'
|
||||
|
||||
export type DataSourceType = 'kv' | 'static' | 'computed'
|
||||
export type DataSourceType = 'kv' | 'static'
|
||||
|
||||
export interface DataSourceConfig<T = any> {
|
||||
type: DataSourceType
|
||||
key?: string
|
||||
defaultValue?: T
|
||||
compute?: (allData: Record<string, any>) => T
|
||||
dependencies?: string[]
|
||||
}
|
||||
|
||||
export function useKVDataSource<T = any>(key: string, defaultValue?: T) {
|
||||
@@ -18,13 +16,6 @@ export function useStaticDataSource<T = any>(defaultValue: T) {
|
||||
return [defaultValue, () => {}, () => {}] as const
|
||||
}
|
||||
|
||||
export function useComputedDataSource<T = any>(
|
||||
compute: (allData: Record<string, any>) => T,
|
||||
dependencies: Record<string, any>
|
||||
) {
|
||||
return compute(dependencies)
|
||||
}
|
||||
|
||||
export function useMultipleDataSources(_sources: DataSourceConfig[]) {
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -41,20 +41,20 @@ export function useDataSources(dataSources: DataSource[]) {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const computedSources = dataSources.filter(ds => ds.type === 'computed')
|
||||
|
||||
computedSources.forEach(source => {
|
||||
const derivedSources = dataSources.filter(ds => ds.expression || ds.valueTemplate)
|
||||
|
||||
derivedSources.forEach(source => {
|
||||
const deps = source.dependencies || []
|
||||
const hasAllDeps = deps.every(dep => dep in data)
|
||||
|
||||
if (hasAllDeps) {
|
||||
const evaluationContext = { data }
|
||||
const computedValue = source.expression
|
||||
const derivedValue = source.expression
|
||||
? evaluateExpression(source.expression, evaluationContext)
|
||||
: source.valueTemplate
|
||||
? evaluateTemplate(source.valueTemplate, evaluationContext)
|
||||
: source.defaultValue
|
||||
setData(prev => ({ ...prev, [source.id]: computedValue }))
|
||||
setData(prev => ({ ...prev, [source.id]: derivedValue }))
|
||||
}
|
||||
})
|
||||
}, [data, dataSources])
|
||||
|
||||
@@ -13,8 +13,8 @@ export function useDataSources(dataSources: DataSource[]) {
|
||||
[dataSources]
|
||||
)
|
||||
|
||||
const computedSources = useMemo(
|
||||
() => dataSources.filter((ds) => ds.type === 'computed'),
|
||||
const derivedSources = useMemo(
|
||||
() => dataSources.filter((ds) => ds.expression || ds.valueTemplate),
|
||||
[dataSources]
|
||||
)
|
||||
|
||||
@@ -54,8 +54,8 @@ export function useDataSources(dataSources: DataSource[]) {
|
||||
const computedData = useMemo(() => {
|
||||
const result: Record<string, any> = {}
|
||||
|
||||
computedSources.forEach((ds) => {
|
||||
const evaluationContext = { data }
|
||||
derivedSources.forEach((ds) => {
|
||||
const evaluationContext = { data: { ...data, ...result } }
|
||||
if (ds.expression) {
|
||||
result[ds.id] = evaluateExpression(ds.expression, evaluationContext)
|
||||
return
|
||||
@@ -70,7 +70,7 @@ export function useDataSources(dataSources: DataSource[]) {
|
||||
})
|
||||
|
||||
return result
|
||||
}, [computedSources, data])
|
||||
}, [derivedSources, data])
|
||||
|
||||
const allData = useMemo(
|
||||
() => ({ ...data, ...computedData }),
|
||||
|
||||
@@ -45,22 +45,26 @@ export function usePage(schema: PageSchema) {
|
||||
useEffect(() => {
|
||||
if (schema.data) {
|
||||
const computed: Record<string, any> = {}
|
||||
|
||||
|
||||
schema.data.forEach(source => {
|
||||
if (source.type === 'computed') {
|
||||
if (source.expression) {
|
||||
computed[source.id] = evaluateBindingExpression(source.expression, dataContext, {
|
||||
fallback: undefined,
|
||||
label: `computed data (${source.id})`,
|
||||
})
|
||||
} else if (source.valueTemplate) {
|
||||
computed[source.id] = evaluateTemplate(source.valueTemplate, { data: dataContext })
|
||||
}
|
||||
} else if (source.type === 'static' && source.defaultValue !== undefined) {
|
||||
if (source.expression) {
|
||||
computed[source.id] = evaluateBindingExpression(source.expression, { ...dataContext, ...computed }, {
|
||||
fallback: undefined,
|
||||
label: `derived data (${source.id})`,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (source.valueTemplate) {
|
||||
computed[source.id] = evaluateTemplate(source.valueTemplate, { data: { ...dataContext, ...computed } })
|
||||
return
|
||||
}
|
||||
|
||||
if (source.type === 'static' && source.defaultValue !== undefined) {
|
||||
computed[source.id] = source.defaultValue
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
setComputedData(computed)
|
||||
}
|
||||
}, [schema.data, dataContext])
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { DataSource } from '@/types/json-ui'
|
||||
|
||||
interface UseDataSourceEditorParams {
|
||||
dataSource: DataSource | null
|
||||
allDataSources: DataSource[]
|
||||
onSave: (dataSource: DataSource) => void
|
||||
onOpenChange: (open: boolean) => void
|
||||
}
|
||||
|
||||
export function useDataSourceEditor({
|
||||
dataSource,
|
||||
allDataSources,
|
||||
onSave,
|
||||
onOpenChange,
|
||||
}: UseDataSourceEditorParams) {
|
||||
@@ -27,51 +25,15 @@ export function useDataSourceEditor({
|
||||
})
|
||||
}, [])
|
||||
|
||||
const addDependency = useCallback((depId: string) => {
|
||||
setEditingSource((prev) => {
|
||||
if (!prev || prev.type !== 'computed') return prev
|
||||
const deps = prev.dependencies || []
|
||||
if (deps.includes(depId)) return prev
|
||||
return { ...prev, dependencies: [...deps, depId] }
|
||||
})
|
||||
}, [])
|
||||
|
||||
const removeDependency = useCallback((depId: string) => {
|
||||
setEditingSource((prev) => {
|
||||
if (!prev || prev.type !== 'computed') return prev
|
||||
const deps = prev.dependencies || []
|
||||
return { ...prev, dependencies: deps.filter((id) => id !== depId) }
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
if (!editingSource) return
|
||||
onSave(editingSource)
|
||||
onOpenChange(false)
|
||||
}, [editingSource, onOpenChange, onSave])
|
||||
|
||||
const availableDeps = useMemo(() => {
|
||||
if (!editingSource) return []
|
||||
return allDataSources.filter(
|
||||
(ds) => ds.id !== editingSource.id && ds.type !== 'computed',
|
||||
)
|
||||
}, [allDataSources, editingSource])
|
||||
|
||||
const selectedDeps = useMemo(() => editingSource?.dependencies || [], [editingSource])
|
||||
|
||||
const unselectedDeps = useMemo(() => {
|
||||
if (!editingSource) return []
|
||||
return availableDeps.filter((ds) => !selectedDeps.includes(ds.id))
|
||||
}, [availableDeps, editingSource, selectedDeps])
|
||||
|
||||
return {
|
||||
editingSource,
|
||||
updateField,
|
||||
addDependency,
|
||||
removeDependency,
|
||||
handleSave,
|
||||
availableDeps,
|
||||
selectedDeps,
|
||||
unselectedDeps,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user