Files
low-code-react-app-b/src/lib/json-ui/hooks.ts

146 lines
3.5 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react'
import { useKV } from '@/hooks/use-kv'
import type { DataSourceConfig } from './schema'
export function useJSONDataSource<T = unknown>(
id: string,
config: DataSourceConfig<T>
) {
const kvConfig = config.type === 'kv' ? config.config : undefined
const apiConfig = config.type === 'api' ? config.config : undefined
const computedConfig = config.type === 'computed' ? config.config : undefined
const defaultValue =
config.type === 'static' ? config.config : config.config?.defaultValue
const [kvValue, setKVValue] = useKV<T>(
kvConfig?.key || id,
(kvConfig?.defaultValue ?? defaultValue) as T
)
const [apiValue, setApiValue] = useState<T | undefined>(defaultValue)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const fetchAPI = useCallback(async () => {
if (config.type !== 'api' || !apiConfig?.url) return
setLoading(true)
setError(null)
try {
const response = await fetch(apiConfig.url)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
let data = await response.json()
if (apiConfig.transform) {
data = apiConfig.transform(data)
}
setApiValue(data)
} catch (err) {
setError(err as Error)
} finally {
setLoading(false)
}
}, [apiConfig, config.type])
useEffect(() => {
if (config.type === 'api') {
fetchAPI()
}
}, [config.type, fetchAPI])
const getValue = () => {
switch (config.type) {
case 'kv':
return kvValue
case 'api':
return apiValue
case 'static':
return config.config
case 'computed':
return computedConfig?.defaultValue
default:
return null
}
}
const setValue = (newValue: any) => {
switch (config.type) {
case 'kv':
setKVValue(newValue)
break
case 'api':
setApiValue(newValue)
break
default:
break
}
}
return {
value: getValue(),
setValue,
loading,
error,
refetch: fetchAPI,
}
}
export function useJSONDataSources<T = unknown>(
sources: Record<string, DataSourceConfig<T>>
) {
const [dataMap, setDataMap] = useState<Record<string, any>>({})
const [loadingMap, setLoadingMap] = useState<Record<string, boolean>>({})
const [errorMap, setErrorMap] = useState<Record<string, Error | null>>({})
const sourceIds = Object.keys(sources)
const updateData = useCallback((id: string, value: any) => {
setDataMap((prev) => ({ ...prev, [id]: value }))
}, [])
const getData = useCallback((id: string) => {
return dataMap[id]
}, [dataMap])
useEffect(() => {
sourceIds.forEach((id) => {
const config = sources[id]
if (config.type === 'static') {
updateData(id, config.config)
}
})
}, [sourceIds])
return {
dataMap,
loadingMap,
errorMap,
updateData,
getData,
}
}
export function useJSONActions() {
const [actionHandlers, setActionHandlers] = useState<Record<string, (...args: any[]) => void>>({})
const registerAction = useCallback((id: string, handler: (...args: any[]) => void) => {
setActionHandlers((prev) => ({ ...prev, [id]: handler }))
}, [])
const executeAction = useCallback((id: string, ...args: any[]) => {
const handler = actionHandlers[id]
if (handler) {
handler(...args)
} else {
console.warn(`Action handler not found: ${id}`)
}
}, [actionHandlers])
return {
registerAction,
executeAction,
}
}