diff --git a/src/components/AtomicComponentDemo.tsx b/src/components/AtomicComponentDemo.tsx index 6e328ff..2297fb9 100644 --- a/src/components/AtomicComponentDemo.tsx +++ b/src/components/AtomicComponentDemo.tsx @@ -29,19 +29,15 @@ export function AtomicComponentDemo() { searchFields: ['title'], }) - const { filtered: filteredByPriority, filters, addFilter, clearFilters } = useFilter({ - items: filtered, - }) - const showCompleted = useToggle({ initial: true }) const addDialog = useDialog() const displayedTasks = showCompleted.value - ? filteredByPriority - : filteredByPriority.filter(t => t.status !== 'success') + ? filtered + : filtered.filter(t => t.status !== 'success') const handleAddTask = () => { - create({ + crud.create({ id: Date.now(), title: 'New Task', status: 'pending', @@ -97,17 +93,6 @@ export function AtomicComponentDemo() { placeholder="Search tasks..." /> - {filters.length > 0 && ( -
- - {filters.length} filter(s) active - - -
- )} -
{displayedTasks.map(task => ( @@ -119,7 +104,7 @@ export function AtomicComponentDemo() { diff --git a/src/components/ComprehensiveDemoPage.tsx b/src/components/ComprehensiveDemoPage.tsx index 90d6a66..1723694 100644 --- a/src/components/ComprehensiveDemoPage.tsx +++ b/src/components/ComprehensiveDemoPage.tsx @@ -7,6 +7,7 @@ import { Separator } from '@/components/ui/separator' import { Progress } from '@/components/ui/progress' import { useCRUD, useSearch } from '@/hooks/data' import { useDialog } from '@/hooks/ui' +import { useKV } from '@github/spark/hooks' import { SearchBar } from '@/components/molecules/SearchBar' import { DataList, ActionButton, IconButton } from '@/components/atoms' import { Plus, Trash, Check, Clock } from '@phosphor-icons/react' @@ -22,10 +23,11 @@ interface Todo { } export function ComprehensiveDemoPage() { - const { items: todos, create, update, remove } = useCRUD({ - key: 'json-demo-todos', - defaultValue: [], - persist: true, + const [todos, setTodos] = useKV('json-demo-todos', []) + + const crud = useCRUD({ + items: todos, + setItems: (updater) => setTodos(updater), }) const { query, setQuery, filtered } = useSearch({ @@ -46,7 +48,7 @@ export function ComprehensiveDemoPage() { const handleAddTodo = () => { if (newTodoText.trim()) { - create({ + crud.create({ id: Date.now(), text: newTodoText, completed: false, @@ -63,13 +65,13 @@ export function ComprehensiveDemoPage() { const handleToggleTodo = (id: number) => { const todo = todos.find(t => t.id === id) if (todo) { - update(id, { completed: !todo.completed }) + crud.update(id, { completed: !todo.completed }) toast.success(todo.completed ? 'Task marked as pending' : 'Task completed!') } } const handleDeleteTodo = (id: number) => { - remove(id) + crud.delete(id) toast.success('Task deleted') } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 1e8e06f..0c8054c 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -56,4 +56,9 @@ function Button({ ) } +export type ButtonProps = ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + } + export { Button, buttonVariants } diff --git a/src/hooks/data/index.ts b/src/hooks/data/index.ts index 5814ebe..6c0caf5 100644 --- a/src/hooks/data/index.ts +++ b/src/hooks/data/index.ts @@ -5,6 +5,7 @@ export { useSort } from './use-sort' export { usePagination } from './use-pagination' export { useSelection } from './use-selection' export { useSeedData } from './use-seed-data' +export { useSearch } from './use-search' export type { DataSourceConfig, DataSourceType } from './use-data-source' export type { CRUDOperations, CRUDConfig } from './use-crud' @@ -12,3 +13,4 @@ export type { SearchFilterConfig } from './use-search-filter' export type { SortConfig, SortDirection } from './use-sort' export type { PaginationConfig } from './use-pagination' export type { SelectionConfig } from './use-selection' +export type { UseSearchOptions } from './use-search' diff --git a/src/hooks/json-ui/use-data-sources.ts b/src/hooks/json-ui/use-data-sources.ts index 950e108..67d1df1 100644 --- a/src/hooks/json-ui/use-data-sources.ts +++ b/src/hooks/json-ui/use-data-sources.ts @@ -22,7 +22,7 @@ export function useDataSources(dataSources: DataSource[]) { for (const ds of dataSources) { if (ds.type === 'kv' && ds.key) { try { - const value = await spark.kv.get(ds.key) + const value = await window.spark.kv.get(ds.key) initialData[ds.id] = value !== undefined ? value : ds.defaultValue } catch { initialData[ds.id] = ds.defaultValue @@ -44,7 +44,7 @@ export function useDataSources(dataSources: DataSource[]) { const kvSource = dataSources.find((ds) => ds.id === id && ds.type === 'kv') if (kvSource && kvSource.key) { - await spark.kv.set(kvSource.key, value) + await window.spark.kv.set(kvSource.key, value) } }, [dataSources]) diff --git a/src/hooks/ui/index.ts b/src/hooks/ui/index.ts index 2e34ed1..3b715ed 100644 --- a/src/hooks/ui/index.ts +++ b/src/hooks/ui/index.ts @@ -4,3 +4,8 @@ export { useSchemaLoader } from './use-schema-loader' export { useComponentRegistry } from './use-component-registry' export { useDashboardMetrics } from './use-dashboard-metrics' export { useDashboardTips } from './use-dashboard-tips' +export { useToggle } from './use-toggle' +export { useDialog } from './use-dialog' + +export type { UseToggleOptions } from './use-toggle' +export type { UseDialogReturn } from './use-dialog' diff --git a/src/hooks/ui/use-component-registry.ts b/src/hooks/ui/use-component-registry.ts index edae7ca..365f57f 100644 --- a/src/hooks/ui/use-component-registry.ts +++ b/src/hooks/ui/use-component-registry.ts @@ -35,10 +35,10 @@ export function useComponentRegistry({ customComponents = {} }: ComponentRegistr return registry[type as keyof typeof registry] || null } - const getIcon = (iconName: string, props?: any) => { + const getIcon = (iconName: string, props?: any): React.ReactElement | null => { const IconComponent = (Icons as any)[iconName] if (!IconComponent) return null - return + return IconComponent({ size: 24, weight: "duotone", ...props }) } return { diff --git a/src/hooks/ui/use-form-state.ts b/src/hooks/ui/use-form-state.ts index 9b51fec..3ded5a1 100644 --- a/src/hooks/ui/use-form-state.ts +++ b/src/hooks/ui/use-form-state.ts @@ -19,10 +19,10 @@ export function useFormState>( fields: FormFieldConfig[], initialValues?: Partial ) { - const defaultValues = fields.reduce((acc, field) => { + const defaultValues: any = fields.reduce((acc: any, field) => { acc[field.name] = initialValues?.[field.name] ?? field.defaultValue return acc - }, {} as T) + }, {}) const [state, setState] = useState>({ values: defaultValues, diff --git a/src/schemas/ui-schema.ts b/src/schemas/ui-schema.ts index f3f9a21..490bbe4 100644 --- a/src/schemas/ui-schema.ts +++ b/src/schemas/ui-schema.ts @@ -10,7 +10,7 @@ export const ComponentSchema: z.ZodType = z.lazy(() => z.object({ id: z.string(), type: z.string(), - props: z.record(z.any()).optional(), + props: z.record(z.string(), z.any()).optional(), children: z.array(ComponentSchema).optional(), binding: z.string().optional(), condition: z.string().optional(), @@ -21,7 +21,7 @@ export const ComponentSchema: z.ZodType = z.lazy(() => indexVar: z.string().optional(), }) .optional(), - events: z.record(z.string()).optional(), + events: z.record(z.string(), z.string()).optional(), }) ) @@ -48,7 +48,7 @@ export const PageSchema = z.object({ layout: LayoutSchema, components: z.array(ComponentSchema), dataBindings: z.array(z.string()).optional(), - functions: z.record(z.string()).optional(), + functions: z.record(z.string(), z.string()).optional(), }) export type Binding = z.infer diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index 50e652d..f2dd350 100644 --- a/src/types/json-ui.ts +++ b/src/types/json-ui.ts @@ -86,3 +86,5 @@ export interface JSONUIContext { updateData: (sourceId: string, value: any) => void executeAction: (action: Action, event?: any) => Promise } + +export type ComponentSchema = UIComponent