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