mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-05-03 01:54:50 +00:00
Generated by Spark: Too risky making changes without refactoring now. Create hook library, All components <150LOC. Consider orchestrating pages using json. JSON can describe actions, hooks, component tree, seed data you name it.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
export { useFiles } from './use-files'
|
||||
export { useModels } from './use-models'
|
||||
export { useComponents } from './use-components'
|
||||
export { useWorkflows } from './use-workflows'
|
||||
export { useLambdas } from './use-lambdas'
|
||||
export * from './use-array'
|
||||
export * from './use-crud'
|
||||
export * from './use-search'
|
||||
export * from './use-debounce'
|
||||
export * from './use-sort'
|
||||
export * from './use-pagination'
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { useKV } from '@github/spark/hooks'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export function useArray<T>(key: string, defaultValue: T[] = []) {
|
||||
const [items, setItems] = useKV<T[]>(key, defaultValue)
|
||||
const safeItems = items || []
|
||||
|
||||
const add = useCallback((item: T) => {
|
||||
setItems((current) => [...(current || []), item])
|
||||
}, [setItems])
|
||||
|
||||
const addMany = useCallback((newItems: T[]) => {
|
||||
setItems((current) => [...(current || []), ...newItems])
|
||||
}, [setItems])
|
||||
|
||||
const remove = useCallback((predicate: (item: T) => boolean) => {
|
||||
setItems((current) => (current || []).filter((item) => !predicate(item)))
|
||||
}, [setItems])
|
||||
|
||||
const update = useCallback(
|
||||
(predicate: (item: T) => boolean, updater: (item: T) => T) => {
|
||||
setItems((current) =>
|
||||
(current || []).map((item) => (predicate(item) ? updater(item) : item))
|
||||
)
|
||||
},
|
||||
[setItems]
|
||||
)
|
||||
|
||||
const replace = useCallback((newItems: T[]) => {
|
||||
setItems(newItems)
|
||||
}, [setItems])
|
||||
|
||||
const clear = useCallback(() => {
|
||||
setItems([])
|
||||
}, [setItems])
|
||||
|
||||
const find = useCallback(
|
||||
(predicate: (item: T) => boolean) => {
|
||||
return safeItems.find(predicate)
|
||||
},
|
||||
[safeItems]
|
||||
)
|
||||
|
||||
const filter = useCallback(
|
||||
(predicate: (item: T) => boolean) => {
|
||||
return safeItems.filter(predicate)
|
||||
},
|
||||
[safeItems]
|
||||
)
|
||||
|
||||
return {
|
||||
items: safeItems,
|
||||
add,
|
||||
addMany,
|
||||
remove,
|
||||
update,
|
||||
replace,
|
||||
clear,
|
||||
find,
|
||||
filter,
|
||||
count: safeItems.length,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useState, useCallback } from 'react'
|
||||
|
||||
export interface Entity {
|
||||
id: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export function useCRUD<T extends Entity>(
|
||||
items: T[],
|
||||
setItems: (items: T[] | ((prev: T[]) => T[])) => void
|
||||
) {
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
const create = useCallback(
|
||||
(item: T) => {
|
||||
setItems((current) => [...(current || []), item])
|
||||
return item.id
|
||||
},
|
||||
[setItems]
|
||||
)
|
||||
|
||||
const read = useCallback(
|
||||
(id: string) => {
|
||||
return items?.find((item) => item.id === id)
|
||||
},
|
||||
[items]
|
||||
)
|
||||
|
||||
const update = useCallback(
|
||||
(id: string, updates: Partial<T>) => {
|
||||
setItems((current) =>
|
||||
(current || []).map((item) =>
|
||||
item.id === id ? { ...item, ...updates } : item
|
||||
)
|
||||
)
|
||||
},
|
||||
[setItems]
|
||||
)
|
||||
|
||||
const remove = useCallback(
|
||||
(id: string) => {
|
||||
setItems((current) => (current || []).filter((item) => item.id !== id))
|
||||
if (selectedId === id) {
|
||||
setSelectedId(null)
|
||||
}
|
||||
},
|
||||
[setItems, selectedId]
|
||||
)
|
||||
|
||||
const duplicate = useCallback(
|
||||
(id: string, newId: string) => {
|
||||
const item = read(id)
|
||||
if (!item) return null
|
||||
|
||||
const duplicated = { ...item, id: newId }
|
||||
create(duplicated)
|
||||
return newId
|
||||
},
|
||||
[read, create]
|
||||
)
|
||||
|
||||
const selected = selectedId ? read(selectedId) : null
|
||||
|
||||
return {
|
||||
items: items || [],
|
||||
create,
|
||||
read,
|
||||
update,
|
||||
remove,
|
||||
duplicate,
|
||||
selectedId,
|
||||
setSelectedId,
|
||||
selected,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export function useDebounce<T>(value: T, delay: number = 500): T {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setDebouncedValue(value)
|
||||
}, delay)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [value, delay])
|
||||
|
||||
return debouncedValue
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useState, useCallback, useMemo } from 'react'
|
||||
|
||||
export interface PaginationConfig {
|
||||
page: number
|
||||
pageSize: number
|
||||
total: number
|
||||
}
|
||||
|
||||
export function usePagination<T>(items: T[], initialPageSize: number = 10) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(initialPageSize)
|
||||
|
||||
const total = items.length
|
||||
const totalPages = Math.ceil(total / pageSize)
|
||||
|
||||
const paginatedItems = useMemo(() => {
|
||||
const start = (page - 1) * pageSize
|
||||
const end = start + pageSize
|
||||
return items.slice(start, end)
|
||||
}, [items, page, pageSize])
|
||||
|
||||
const goToPage = useCallback((newPage: number) => {
|
||||
setPage(Math.max(1, Math.min(newPage, totalPages)))
|
||||
}, [totalPages])
|
||||
|
||||
const nextPage = useCallback(() => {
|
||||
goToPage(page + 1)
|
||||
}, [page, goToPage])
|
||||
|
||||
const prevPage = useCallback(() => {
|
||||
goToPage(page - 1)
|
||||
}, [page, goToPage])
|
||||
|
||||
const changePageSize = useCallback((newSize: number) => {
|
||||
setPageSize(newSize)
|
||||
setPage(1)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
items: paginatedItems,
|
||||
page,
|
||||
pageSize,
|
||||
total,
|
||||
totalPages,
|
||||
goToPage,
|
||||
nextPage,
|
||||
prevPage,
|
||||
changePageSize,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 1,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useDebounce } from './use-debounce'
|
||||
|
||||
export function useSearch<T>(
|
||||
items: T[],
|
||||
searchKeys: (keyof T)[],
|
||||
debounceMs: number = 300
|
||||
) {
|
||||
const [query, setQuery] = useState('')
|
||||
const debouncedQuery = useDebounce(query, debounceMs)
|
||||
const [results, setResults] = useState<T[]>(items)
|
||||
|
||||
useEffect(() => {
|
||||
if (!debouncedQuery.trim()) {
|
||||
setResults(items)
|
||||
return
|
||||
}
|
||||
|
||||
const lowerQuery = debouncedQuery.toLowerCase()
|
||||
const filtered = items.filter((item) =>
|
||||
searchKeys.some((key) => {
|
||||
const value = item[key]
|
||||
if (typeof value === 'string') {
|
||||
return value.toLowerCase().includes(lowerQuery)
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return value.toString().includes(lowerQuery)
|
||||
}
|
||||
return false
|
||||
})
|
||||
)
|
||||
|
||||
setResults(filtered)
|
||||
}, [debouncedQuery, items, searchKeys])
|
||||
|
||||
return {
|
||||
query,
|
||||
setQuery,
|
||||
results,
|
||||
isSearching: query.length > 0,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useState, useCallback } from 'react'
|
||||
|
||||
export type SortDirection = 'asc' | 'desc'
|
||||
|
||||
export function useSort<T>(items: T[], defaultKey?: keyof T) {
|
||||
const [sortKey, setSortKey] = useState<keyof T | null>(defaultKey || null)
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>('asc')
|
||||
|
||||
const toggleSort = useCallback(
|
||||
(key: keyof T) => {
|
||||
if (sortKey === key) {
|
||||
setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc'))
|
||||
} else {
|
||||
setSortKey(key)
|
||||
setSortDirection('asc')
|
||||
}
|
||||
},
|
||||
[sortKey]
|
||||
)
|
||||
|
||||
const sortedItems = [...items].sort((a, b) => {
|
||||
if (!sortKey) return 0
|
||||
|
||||
const aVal = a[sortKey]
|
||||
const bVal = b[sortKey]
|
||||
|
||||
if (aVal === bVal) return 0
|
||||
|
||||
let comparison = 0
|
||||
if (typeof aVal === 'string' && typeof bVal === 'string') {
|
||||
comparison = aVal.localeCompare(bVal)
|
||||
} else if (typeof aVal === 'number' && typeof bVal === 'number') {
|
||||
comparison = aVal - bVal
|
||||
} else {
|
||||
comparison = String(aVal).localeCompare(String(bVal))
|
||||
}
|
||||
|
||||
return sortDirection === 'asc' ? comparison : -comparison
|
||||
})
|
||||
|
||||
return {
|
||||
sortedItems,
|
||||
sortKey,
|
||||
sortDirection,
|
||||
toggleSort,
|
||||
setSortKey,
|
||||
setSortDirection,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user