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>
137 lines
3.8 KiB
TypeScript
137 lines
3.8 KiB
TypeScript
/**
|
|
* useStateWithHistory Hook
|
|
* State management with full undo/redo history tracking
|
|
*
|
|
* Features:
|
|
* - Track complete value history
|
|
* - Undo and redo operations with boundary checks
|
|
* - Query undo/redo availability
|
|
* - Access full history array
|
|
* - Efficient history management with configurable max size
|
|
* - Type-safe generic typing for any value
|
|
*
|
|
* @example
|
|
* const { value, setValue, undo, redo, canUndo, canRedo, history } = useStateWithHistory('initial')
|
|
*
|
|
* <div>
|
|
* <p>Current: {value}</p>
|
|
* <p>History: {history.length} states</p>
|
|
* <Button onClick={() => setValue('new value')}>Update</Button>
|
|
* <Button onClick={undo} disabled={!canUndo}>Undo</Button>
|
|
* <Button onClick={redo} disabled={!canRedo}>Redo</Button>
|
|
* </div>
|
|
*
|
|
* @example
|
|
* // With objects - form history
|
|
* const initialForm = { name: '', email: '', age: 0 }
|
|
* const { value: form, setValue: setForm, undo, canUndo } = useStateWithHistory(initialForm)
|
|
*
|
|
* <form>
|
|
* <input
|
|
* value={form.name}
|
|
* onChange={(e) => setForm({ ...form, name: e.target.value })}
|
|
* />
|
|
* <button type="button" onClick={undo} disabled={!canUndo}>
|
|
* Undo Changes
|
|
* </button>
|
|
* </form>
|
|
*
|
|
* @example
|
|
* // With canvas drawing history
|
|
* const { value: drawingState, setValue, undo, redo } = useStateWithHistory(
|
|
* initialDrawing,
|
|
* { maxHistory: 50 }
|
|
* )
|
|
*/
|
|
|
|
import { useState, useCallback } from 'react'
|
|
|
|
export interface UseStateWithHistoryOptions {
|
|
/** Maximum history size (default: 100) */
|
|
maxHistory?: number
|
|
}
|
|
|
|
export interface UseStateWithHistoryReturn<T> {
|
|
/** Current value */
|
|
value: T
|
|
/** Set new value and add to history */
|
|
setValue: (value: T | ((prev: T) => T)) => void
|
|
/** Undo to previous state */
|
|
undo: () => void
|
|
/** Redo to next state */
|
|
redo: () => void
|
|
/** Check if undo is available */
|
|
canUndo: boolean
|
|
/** Check if redo is available */
|
|
canRedo: boolean
|
|
/** Full history array (current and all previous states) */
|
|
history: T[]
|
|
}
|
|
|
|
/**
|
|
* Hook for managing state with undo/redo capability
|
|
* @template T - The type of the state value
|
|
* @param initialValue - Initial state value
|
|
* @param options - Configuration options
|
|
* @returns Object containing state and history operations
|
|
*/
|
|
export function useStateWithHistory<T>(
|
|
initialValue: T,
|
|
options: UseStateWithHistoryOptions = {}
|
|
): UseStateWithHistoryReturn<T> {
|
|
const { maxHistory = 100 } = options
|
|
|
|
// History is array of all states, currentIndex points to current state
|
|
const [history, setHistory] = useState<T[]>([initialValue])
|
|
const [currentIndex, setCurrentIndex] = useState<number>(0)
|
|
|
|
const value = history[currentIndex]
|
|
|
|
const setValue = useCallback((newValue: T | ((prev: T) => T)) => {
|
|
setHistory((prevHistory) => {
|
|
const valueToStore = newValue instanceof Function ? newValue(prevHistory[currentIndex]) : newValue
|
|
|
|
// If we're not at the end, remove "future" history
|
|
const newHistory = prevHistory.slice(0, currentIndex + 1)
|
|
|
|
// Add new state
|
|
newHistory.push(valueToStore)
|
|
|
|
// Limit history size
|
|
if (newHistory.length > maxHistory) {
|
|
newHistory.shift()
|
|
setCurrentIndex((prev) => Math.max(0, prev - 1))
|
|
} else {
|
|
setCurrentIndex(newHistory.length - 1)
|
|
}
|
|
|
|
return newHistory
|
|
})
|
|
}, [currentIndex, maxHistory])
|
|
|
|
const undo = useCallback(() => {
|
|
setCurrentIndex((prev) => Math.max(0, prev - 1))
|
|
}, [])
|
|
|
|
const redo = useCallback(() => {
|
|
setHistory((prevHistory) => {
|
|
const nextIndex = Math.min(currentIndex + 1, prevHistory.length - 1)
|
|
setCurrentIndex(nextIndex)
|
|
return prevHistory
|
|
})
|
|
}, [currentIndex])
|
|
|
|
const canUndo = currentIndex > 0
|
|
const canRedo = currentIndex < history.length - 1
|
|
|
|
return {
|
|
value,
|
|
setValue,
|
|
undo,
|
|
redo,
|
|
canUndo,
|
|
canRedo,
|
|
history,
|
|
}
|
|
}
|