UNPKG

vuetify-editable-data-grid

Version:

A highly customizable and interactive editable data table component built with Vue 3 and Vuetify 3. This component supports inline editing, undo/redo functionality, column visibility filters, drag-to-fill (like Excel), and more.

116 lines (97 loc) 3.25 kB
/** * useRefHistory - A Vue composable for tracking the history of a reactive ref value with undo/redo support. * * This utility allows you to store snapshots of a reactive `ref` (primitive or object), enabling undo/redo functionality. * Useful for form state, drawing apps, editors, etc. * * @param {Ref} source - The reactive ref to track history for. * @param {Object} options - Configuration options. * @param {boolean} [options.deep=false] - Whether to watch deeply nested changes. * @param {number} [options.capacity=Infinity] - Maximum number of history entries to keep. * @param {Function} [options.clone] - Function to clone a value (default: JSON deep clone). * * @returns {Object} { * history: Ref<Array>, // List of snapshots * pointer: Ref<number>, // Current position in the history * canUndo: Ref<boolean>, // Whether undo is possible * canRedo: Ref<boolean>, // Whether redo is possible * undo: Function, // Undo the last change * redo: Function // Redo a previously undone change * } * @author Rahul Rawat * Notes: * - Uses `v-watch` to track changes, with optional deep watch. * - Automatically trims history to respect `capacity`. * - Skips tracking during undo/redo to prevent recursive updates. */ import { ref, watch } from 'vue' interface UseRefHistoryOptions { deep?: boolean; capacity?: number; clone?: (value: unknown) => unknown; } import { Ref } from 'vue'; export function useRefHistory<T>(source: Ref<T>, options: UseRefHistoryOptions = {}) { const { deep = false, capacity = Infinity, clone = (v) => JSON.parse(JSON.stringify(v)) } = options const history = ref<unknown[]>([]) const pointer = ref(-1) const pauseTracking = ref(false) const canUndo = ref(false) const canRedo = ref(false) const updateMeta = () => { canUndo.value = pointer.value > 0 canRedo.value = pointer.value < history.value.length - 1 } const pushSnapshot = (value: unknown) => { const snapshot = clone(value) // Clear redo history if changed mid-way if (pointer.value < history.value.length - 1) history.value.splice(pointer.value + 1) history.value.push(snapshot) pointer.value++ // Trim oldest if over capacity if (history.value.length > capacity) { history.value.shift() pointer.value-- } updateMeta() } const undo = () => { if (!canUndo.value) return source.value = clone(history.value[pointer.value]) as T pointer.value-- source.value = clone(history.value[pointer.value]) pauseTracking.value = false updateMeta() } const redo = () => { if (!canRedo.value) return pauseTracking.value = true pointer.value++ source.value = clone(history.value[pointer.value]) pauseTracking.value = false updateMeta() } // Track changes automatically watch( source, (newVal) => { if (pauseTracking.value) return const newStr = JSON.stringify(newVal) const oldStr = JSON.stringify(history.value[pointer.value]) if (newStr !== oldStr) pushSnapshot(newVal) }, { deep, immediate: true } ) return { history, pointer, canUndo, canRedo, undo, redo } }