mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
Directory Restructuring: - qml/qml-components/qml-components/* → qml/components/ (flattens nesting) - All 104 QML files moved with git history preserved - Eliminates redundant qml-components nesting Documentation Updates: - ARCHITECTURE.md: Updated qml/components references (2 locations) - GETTING_STARTED.md: Updated qml/components path (1 location, end of file) - README.md: Updated qml/components references (3 locations) - CODE_REVIEW.md: Updated qml/components file paths (4 locations) - docs/ARCHITECTURE.md: Complete refactor with qml/components paths Verification: - ✅ No remaining qml-components/ references in documentation - ✅ All 104 QML files present in flattened structure - ✅ Directory structure verified (12 component categories) - ✅ First-class directory naming convention Structure Post-Refactor: qml/ ├── components/ │ ├── atoms/ (16 files) │ ├── core/ (11 files) │ ├── data-display/ (10 files) │ ├── feedback/ (11 files) │ ├── form/ (19 files) │ ├── lab/ (11 files) │ ├── layout/ (12 files) │ ├── navigation/ (12 files) │ ├── surfaces/ (7 files) │ ├── theming/ (4 files) │ └── utils/ (13 files) ├── hybrid/ └── widgets/ Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
136 lines
3.6 KiB
TypeScript
136 lines
3.6 KiB
TypeScript
/**
|
|
* useUndo Hook
|
|
* Simplified undo/redo wrapper for any state value
|
|
*
|
|
* Features:
|
|
* - Generic typing for any value type
|
|
* - Simple undo/redo operations with boundary checks
|
|
* - Reset to initial or specific value
|
|
* - Query undo/redo availability
|
|
* - Lightweight and performant
|
|
* - Useful as alternative to useStateWithHistory for simpler use cases
|
|
*
|
|
* @example
|
|
* const { value, undo, redo, reset, canUndo, canRedo } = useUndo('initial value')
|
|
*
|
|
* const handleChange = (newValue: string) => {
|
|
* // Setting a new value automatically enables undo
|
|
* setValue(newValue)
|
|
* }
|
|
*
|
|
* <div>
|
|
* <p>Current: {value}</p>
|
|
* <Button onClick={() => handleChange('changed')}>Change</Button>
|
|
* <Button onClick={undo} disabled={!canUndo}>Undo</Button>
|
|
* <Button onClick={redo} disabled={!canRedo}>Redo</Button>
|
|
* <Button onClick={() => reset()}>Reset</Button>
|
|
* </div>
|
|
*
|
|
* @example
|
|
* // With text editor
|
|
* const { value: text, undo, redo, canUndo, canRedo } = useUndo('')
|
|
*
|
|
* return (
|
|
* <div>
|
|
* <Editor
|
|
* value={text}
|
|
* onChange={(newText) => setValue(newText)}
|
|
* />
|
|
* <Toolbar>
|
|
* <Button onClick={undo} disabled={!canUndo} icon="undo" />
|
|
* <Button onClick={redo} disabled={!canRedo} icon="redo" />
|
|
* </Toolbar>
|
|
* </div>
|
|
* )
|
|
*
|
|
* @example
|
|
* // Track both past and future for undo/redo
|
|
* const { value, undo, redo } = useUndo({ x: 0, y: 0 })
|
|
* // Modify canUndo/canRedo based on history tracking
|
|
*/
|
|
|
|
import { useState, useCallback } from 'react'
|
|
|
|
export interface UseUndoReturn<T> {
|
|
/** Current value */
|
|
value: T
|
|
/** Set new value (stores current value for undo) */
|
|
setValue: (value: T | ((prev: T) => T)) => void
|
|
/** Undo to previous value */
|
|
undo: () => void
|
|
/** Redo to next value */
|
|
redo: () => void
|
|
/** Reset to initial value */
|
|
reset: (value?: T) => void
|
|
/** Check if undo is available */
|
|
canUndo: boolean
|
|
/** Check if redo is available */
|
|
canRedo: boolean
|
|
}
|
|
|
|
/**
|
|
* Hook for managing undo/redo operations on any value
|
|
* @template T - The type of the value
|
|
* @param initialValue - Initial value
|
|
* @returns Object containing value and undo/redo operations
|
|
*/
|
|
export function useUndo<T>(initialValue: T): UseUndoReturn<T> {
|
|
// past[past.length - 1] is the value before current
|
|
// future[0] is the value after current
|
|
const [past, setPast] = useState<T[]>([])
|
|
const [present, setPresent] = useState<T>(initialValue)
|
|
const [future, setFuture] = useState<T[]>([])
|
|
|
|
const setValue = useCallback((newValue: T | ((prev: T) => T)) => {
|
|
const valueToStore = newValue instanceof Function ? newValue(present) : newValue
|
|
|
|
setPast((prevPast) => [...prevPast, present])
|
|
setPresent(valueToStore)
|
|
setFuture([])
|
|
}, [present])
|
|
|
|
const undo = useCallback(() => {
|
|
if (past.length === 0) return
|
|
|
|
setPast((prevPast) => {
|
|
const newPast = [...prevPast]
|
|
const previousValue = newPast.pop()
|
|
|
|
if (previousValue !== undefined) {
|
|
setFuture((prevFuture) => [present, ...prevFuture])
|
|
setPresent(previousValue)
|
|
}
|
|
|
|
return newPast
|
|
})
|
|
}, [present])
|
|
|
|
const redo = useCallback(() => {
|
|
if (future.length === 0) return
|
|
|
|
const nextValue = future[0]
|
|
setPast((prevPast) => [...prevPast, present])
|
|
setFuture((prevFuture) => prevFuture.slice(1))
|
|
setPresent(nextValue)
|
|
}, [present, future])
|
|
|
|
const reset = useCallback((value: T = initialValue) => {
|
|
setPast([])
|
|
setPresent(value)
|
|
setFuture([])
|
|
}, [initialValue])
|
|
|
|
const canUndo = past.length > 0
|
|
const canRedo = future.length > 0
|
|
|
|
return {
|
|
value: present,
|
|
setValue,
|
|
undo,
|
|
redo,
|
|
reset,
|
|
canUndo,
|
|
canRedo,
|
|
}
|
|
}
|