@eigenpal/docx-editor-react 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +204 -0
  2. package/README.md +70 -0
  3. package/dist/FindReplaceDialog-6D74QA4V.mjs +1 -0
  4. package/dist/FindReplaceDialog-RUFM7CNM.js +1 -0
  5. package/dist/FootnotePropertiesDialog-INLVLE26.mjs +1 -0
  6. package/dist/FootnotePropertiesDialog-WCGK6MV3.js +1 -0
  7. package/dist/HyperlinkDialog-G5FFKLWY.js +1 -0
  8. package/dist/HyperlinkDialog-PSJ6P3E3.mjs +1 -0
  9. package/dist/ImagePositionDialog-DTGYFFAR.js +1 -0
  10. package/dist/ImagePositionDialog-FKBG2MSL.mjs +1 -0
  11. package/dist/ImagePropertiesDialog-SO6RAOT6.js +1 -0
  12. package/dist/ImagePropertiesDialog-T77XMKJF.mjs +1 -0
  13. package/dist/KeyboardShortcutsDialog-B-h3NAat.d.mts +415 -0
  14. package/dist/KeyboardShortcutsDialog-B-h3NAat.d.ts +415 -0
  15. package/dist/PageSetupDialog-CCJAZEIN.mjs +1 -0
  16. package/dist/PageSetupDialog-UKXK6X35.js +1 -0
  17. package/dist/PrintPreview-DEhwRBC_.d.mts +93 -0
  18. package/dist/PrintPreview-DEhwRBC_.d.ts +93 -0
  19. package/dist/SplitCellDialog-NPGW4JGQ.mjs +1 -0
  20. package/dist/SplitCellDialog-RNP37SKZ.js +1 -0
  21. package/dist/TablePropertiesDialog-3QNCWNEG.mjs +1 -0
  22. package/dist/TablePropertiesDialog-AQVM7FGA.js +1 -0
  23. package/dist/chunk-5BSDRI3Z.js +1 -0
  24. package/dist/chunk-6AX4I76A.mjs +1 -0
  25. package/dist/chunk-AJ2BNSGR.mjs +1 -0
  26. package/dist/chunk-BJ5RZW6Y.js +1 -0
  27. package/dist/chunk-BMBP5UFA.mjs +1 -0
  28. package/dist/chunk-BZO4G5M6.js +1 -0
  29. package/dist/chunk-G3VEHNWI.js +2 -0
  30. package/dist/chunk-GM2S2WMT.mjs +1 -0
  31. package/dist/chunk-GNIO6SOS.js +1 -0
  32. package/dist/chunk-ICVBXT6V.mjs +2 -0
  33. package/dist/chunk-IL65NW44.mjs +2 -0
  34. package/dist/chunk-IVFYCMAM.mjs +1 -0
  35. package/dist/chunk-JGPOALUP.js +1 -0
  36. package/dist/chunk-JHSXIGTQ.js +1 -0
  37. package/dist/chunk-K5DD2LSK.mjs +1 -0
  38. package/dist/chunk-L4MDZW2J.js +1 -0
  39. package/dist/chunk-NIBCC7WQ.js +1 -0
  40. package/dist/chunk-OHNO4NRQ.mjs +1 -0
  41. package/dist/chunk-P3ADI7OT.mjs +1 -0
  42. package/dist/chunk-PGHNUFNT.js +2 -0
  43. package/dist/chunk-SW2JOSQG.mjs +2 -0
  44. package/dist/chunk-TKMHUFSP.js +1 -0
  45. package/dist/chunk-TN5C2ZYV.mjs +2 -0
  46. package/dist/chunk-UT6DJWGC.js +2 -0
  47. package/dist/chunk-YVYT3JWY.js +2 -0
  48. package/dist/chunk-ZFZJRL2R.mjs +1 -0
  49. package/dist/dialogs.d.mts +40 -0
  50. package/dist/dialogs.d.ts +40 -0
  51. package/dist/dialogs.js +1 -0
  52. package/dist/dialogs.mjs +1 -0
  53. package/dist/hooks.d.mts +497 -0
  54. package/dist/hooks.d.ts +497 -0
  55. package/dist/hooks.js +1 -0
  56. package/dist/hooks.mjs +1 -0
  57. package/dist/index.d.mts +524 -0
  58. package/dist/index.d.ts +524 -0
  59. package/dist/index.js +33 -0
  60. package/dist/index.mjs +33 -0
  61. package/dist/plugin-api.d.mts +90 -0
  62. package/dist/plugin-api.d.ts +90 -0
  63. package/dist/plugin-api.js +246 -0
  64. package/dist/plugin-api.mjs +246 -0
  65. package/dist/styles.css +1 -0
  66. package/dist/styles.d.mts +13 -0
  67. package/dist/styles.d.ts +13 -0
  68. package/dist/styles.js +1 -0
  69. package/dist/styles.mjs +1 -0
  70. package/dist/types-D35gNE-_.d.mts +106 -0
  71. package/dist/types-D35gNE-_.d.ts +106 -0
  72. package/dist/ui.d.mts +1565 -0
  73. package/dist/ui.d.ts +1565 -0
  74. package/dist/ui.js +111 -0
  75. package/dist/ui.mjs +111 -0
  76. package/dist/useFindReplace-Bc2ubEeV.d.mts +219 -0
  77. package/dist/useFindReplace-Bc2ubEeV.d.ts +219 -0
  78. package/package.json +138 -0
@@ -0,0 +1,497 @@
1
+ /**
2
+ * @eigenpal/docx-editor-react/hooks
3
+ *
4
+ * React hooks for editor history, table selection, find/replace, autosave,
5
+ * clipboard, and zoom. Use alongside the main `DocxEditor` component.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useAutoSave, useFindReplace } from '@eigenpal/docx-editor-react/hooks';
10
+ * ```
11
+ *
12
+ * @packageDocumentation
13
+ * @public
14
+ */
15
+ import { Table, Document } from '@eigenpal/docx-editor-core/types/document';
16
+ import { a as TableContext, c as TableAction, b as TableSplitConfig } from './useFindReplace-Bc2ubEeV.js';
17
+ export { F as FindReplaceOptions, d as FindReplaceState, U as UseFindReplaceReturn, u as useFindReplace } from './useFindReplace-Bc2ubEeV.js';
18
+ import * as React$1 from 'react';
19
+ import React__default, { CSSProperties, RefObject } from 'react';
20
+ import { HighlightRect, SelectionHighlightConfig, ParsedClipboardContent } from '@eigenpal/docx-editor-core/utils';
21
+ import { ClipboardSelection, Theme, SavedDocumentData, AutoSaveStatus } from '@eigenpal/docx-editor-core';
22
+ export { AutoSaveStatus, ClipboardSelection, SavedDocumentData, TABLE_DATA_ATTRIBUTES, createSelectionFromDOM, formatLastSaveTime, formatStorageSize, getAutoSaveStatusLabel, getAutoSaveStorageSize, getSelectionRuns, isAutoSaveSupported } from '@eigenpal/docx-editor-core';
23
+ import { EditorView } from 'prosemirror-view';
24
+ import '@eigenpal/docx-editor-core/utils/findReplace';
25
+
26
+ /**
27
+ * History hook for undo/redo functionality
28
+ *
29
+ * Maintains undo/redo stacks with support for:
30
+ * - undo() and redo() operations
31
+ * - canUndo and canRedo state
32
+ * - Keyboard shortcuts (Ctrl+Z, Ctrl+Y, Ctrl+Shift+Z)
33
+ * - Grouping rapid changes to avoid cluttering history
34
+ */
35
+ /**
36
+ * History entry containing state and metadata
37
+ */
38
+ interface HistoryEntry<T> {
39
+ /** The state at this point */
40
+ state: T;
41
+ /** Timestamp when this entry was created */
42
+ timestamp: number;
43
+ /** Optional description of what changed */
44
+ description?: string;
45
+ }
46
+ /**
47
+ * Options for the useHistory hook
48
+ */
49
+ interface UseHistoryOptions<T> {
50
+ /** Maximum number of entries in history (default: 100) */
51
+ maxEntries?: number;
52
+ /** Time in ms to group rapid changes (default: 500) */
53
+ groupingInterval?: number;
54
+ /** Whether to enable keyboard shortcuts (default: true) */
55
+ enableKeyboardShortcuts?: boolean;
56
+ /** Custom comparison function for detecting changes */
57
+ isEqual?: (a: T, b: T) => boolean;
58
+ /** Callback when undo is triggered */
59
+ onUndo?: (state: T) => void;
60
+ /** Callback when redo is triggered */
61
+ onRedo?: (state: T) => void;
62
+ /** Ref to the container element for keyboard events */
63
+ containerRef?: React.RefObject<HTMLElement>;
64
+ }
65
+ /**
66
+ * Return type of the useHistory hook
67
+ */
68
+ interface UseHistoryReturn<T> {
69
+ /** Current state */
70
+ state: T;
71
+ /** Whether undo is available */
72
+ canUndo: boolean;
73
+ /** Whether redo is available */
74
+ canRedo: boolean;
75
+ /** Number of entries in undo stack */
76
+ undoCount: number;
77
+ /** Number of entries in redo stack */
78
+ redoCount: number;
79
+ /** Push a new state to history */
80
+ push: (newState: T, description?: string) => void;
81
+ /** Undo to previous state */
82
+ undo: () => T | undefined;
83
+ /** Redo to next state */
84
+ redo: () => T | undefined;
85
+ /** Clear all history */
86
+ clear: () => void;
87
+ /** Reset to initial state and clear history */
88
+ reset: (newInitialState?: T) => void;
89
+ /** Get all undo entries (for debugging/display) */
90
+ getUndoStack: () => HistoryEntry<T>[];
91
+ /** Get all redo entries (for debugging/display) */
92
+ getRedoStack: () => HistoryEntry<T>[];
93
+ /** Transform all stored states (current + undo/redo stacks) */
94
+ transformAll: (fn: (state: T) => T) => void;
95
+ }
96
+ /**
97
+ * Custom hook for managing undo/redo history
98
+ */
99
+ declare function useHistory<T>(initialState: T, options?: UseHistoryOptions<T>): UseHistoryReturn<T>;
100
+ /**
101
+ * Simplified hook that just tracks state changes automatically
102
+ */
103
+ declare function useAutoHistory<T>(value: T, options?: UseHistoryOptions<T>): Omit<UseHistoryReturn<T>, 'push'>;
104
+ /**
105
+ * Hook for document history with specialized comparison
106
+ */
107
+ declare function useDocumentHistory<T extends {
108
+ package?: {
109
+ document?: unknown;
110
+ headers?: unknown;
111
+ footers?: unknown;
112
+ } | null;
113
+ } | null>(document: T, options?: Omit<UseHistoryOptions<T>, 'isEqual'>): UseHistoryReturn<T>;
114
+ /**
115
+ * Create a history manager for non-React usage
116
+ */
117
+ declare class HistoryManager<T> {
118
+ private undoStack;
119
+ private redoStack;
120
+ private currentState;
121
+ private maxEntries;
122
+ private groupingInterval;
123
+ private lastPushTime;
124
+ private isEqual;
125
+ constructor(initialState: T, options?: {
126
+ maxEntries?: number;
127
+ groupingInterval?: number;
128
+ isEqual?: (a: T, b: T) => boolean;
129
+ });
130
+ get state(): T;
131
+ get canUndo(): boolean;
132
+ get canRedo(): boolean;
133
+ push(newState: T, description?: string): void;
134
+ undo(): T | undefined;
135
+ redo(): T | undefined;
136
+ clear(): void;
137
+ reset(newInitialState?: T): void;
138
+ }
139
+
140
+ /**
141
+ * useTableSelection Hook
142
+ *
143
+ * Thin React wrapper around the framework-agnostic TableSelectionManager.
144
+ * Provides table selection tracking and table operation dispatch.
145
+ */
146
+
147
+ interface TableSelectionState {
148
+ context: TableContext | null;
149
+ table: Table | null;
150
+ tableIndex: number | null;
151
+ rowIndex: number | null;
152
+ columnIndex: number | null;
153
+ }
154
+ interface UseTableSelectionReturn {
155
+ state: TableSelectionState;
156
+ handleCellClick: (tableIndex: number, rowIndex: number, columnIndex: number) => void;
157
+ handleAction: (action: TableAction) => void;
158
+ getSplitCellConfig: () => TableSplitConfig | null;
159
+ applySplitCell: (rows: number, cols: number) => void;
160
+ clearSelection: () => void;
161
+ isCellSelected: (tableIndex: number, rowIndex: number, columnIndex: number) => boolean;
162
+ tableContext: TableContext | null;
163
+ }
164
+ interface UseTableSelectionOptions {
165
+ document: Document | null;
166
+ onChange?: (document: Document) => void;
167
+ onSelectionChange?: (context: TableContext | null) => void;
168
+ }
169
+ declare function useTableSelection({ document: doc, onChange, onSelectionChange, }: UseTableSelectionOptions): UseTableSelectionReturn;
170
+
171
+ /**
172
+ * Selection Highlight Hook
173
+ *
174
+ * A React hook that manages visual selection highlighting across multiple runs.
175
+ * Uses a combination of CSS ::selection pseudo-element styling and optional
176
+ * overlay rectangles for complex scenarios.
177
+ *
178
+ * Features:
179
+ * - Consistent selection highlighting across all text runs
180
+ * - Support for text with different backgrounds (highlighted, dark bg)
181
+ * - Optional overlay rectangles for custom highlight effects
182
+ * - Debounced updates for performance
183
+ */
184
+
185
+ /**
186
+ * Options for the useSelectionHighlight hook
187
+ */
188
+ interface UseSelectionHighlightOptions {
189
+ /** Reference to the container element */
190
+ containerRef: React__default.RefObject<HTMLElement>;
191
+ /** Whether to enable selection highlighting */
192
+ enabled?: boolean;
193
+ /** Custom highlight configuration */
194
+ config?: SelectionHighlightConfig;
195
+ /** Whether to use overlay rectangles (default: false, uses CSS) */
196
+ useOverlay?: boolean;
197
+ /** Debounce delay for rect updates in ms (default: 16) */
198
+ debounceMs?: number;
199
+ /** Callback when selection changes */
200
+ onSelectionChange?: (hasSelection: boolean, text: string) => void;
201
+ }
202
+ /**
203
+ * Return value from the useSelectionHighlight hook
204
+ */
205
+ interface UseSelectionHighlightReturn {
206
+ /** Whether there is an active selection */
207
+ hasSelection: boolean;
208
+ /** The selected text */
209
+ selectedText: string;
210
+ /** Highlight rectangles (only populated if useOverlay is true) */
211
+ highlightRects: HighlightRect[];
212
+ /** Whether selection is within the container */
213
+ isSelectionInContainer: boolean;
214
+ /** Refresh the highlight state */
215
+ refresh: () => void;
216
+ /** Get styles for a highlight rect overlay */
217
+ getOverlayStyle: (rect: HighlightRect) => CSSProperties;
218
+ }
219
+ /**
220
+ * Hook to manage selection highlighting in the editor
221
+ */
222
+ declare function useSelectionHighlight(options: UseSelectionHighlightOptions): UseSelectionHighlightReturn;
223
+ /**
224
+ * Props for selection overlay component
225
+ */
226
+ interface SelectionOverlayProps {
227
+ /** Highlight rectangles to render */
228
+ rects: HighlightRect[];
229
+ /** Style configuration */
230
+ config?: SelectionHighlightConfig;
231
+ /** Additional class name */
232
+ className?: string;
233
+ }
234
+ /**
235
+ * Generate selection overlay elements (for use in JSX)
236
+ *
237
+ * Usage:
238
+ * ```tsx
239
+ * const { highlightRects } = useSelectionHighlight({ ... });
240
+ * return (
241
+ * <div style={{ position: 'relative' }}>
242
+ * {generateOverlayElements(highlightRects)}
243
+ * <div>... content ...</div>
244
+ * </div>
245
+ * );
246
+ * ```
247
+ */
248
+ declare function generateOverlayElements(rects: HighlightRect[], config?: SelectionHighlightConfig): React__default.ReactNode[];
249
+
250
+ /**
251
+ * useClipboard Hook
252
+ *
253
+ * Thin React wrapper around the framework-agnostic ClipboardManager.
254
+ * Handles clipboard operations with formatting preservation.
255
+ */
256
+
257
+ interface UseClipboardOptions {
258
+ onCopy?: (selection: ClipboardSelection) => void;
259
+ onCut?: (selection: ClipboardSelection) => void;
260
+ onPaste?: (content: ParsedClipboardContent, asPlainText: boolean) => void;
261
+ cleanWordFormatting?: boolean;
262
+ editable?: boolean;
263
+ onError?: (error: Error) => void;
264
+ /** Document theme — used to resolve themed colors in the HTML clipboard payload. */
265
+ theme?: Theme | null;
266
+ }
267
+ interface UseClipboardReturn {
268
+ copy: (selection: ClipboardSelection) => Promise<boolean>;
269
+ cut: (selection: ClipboardSelection) => Promise<boolean>;
270
+ paste: (asPlainText?: boolean) => Promise<ParsedClipboardContent | null>;
271
+ handleCopy: (event: ClipboardEvent) => void;
272
+ handleCut: (event: ClipboardEvent) => void;
273
+ handlePaste: (event: ClipboardEvent) => void;
274
+ handleKeyDown: (event: KeyboardEvent) => void;
275
+ isProcessing: boolean;
276
+ lastPastedContent: ParsedClipboardContent | null;
277
+ }
278
+ declare function useClipboard(options?: UseClipboardOptions): UseClipboardReturn;
279
+
280
+ /**
281
+ * useAutoSave Hook
282
+ *
283
+ * Thin React wrapper around the framework-agnostic AutoSaveManager.
284
+ * Bridges AutoSaveManager's subscribe/getSnapshot pattern with React state.
285
+ */
286
+
287
+ /** Options for useAutoSave hook */
288
+ interface UseAutoSaveOptions {
289
+ /** Storage key for localStorage (default: 'docx-editor-autosave') */
290
+ storageKey?: string;
291
+ /** Save interval in milliseconds (default: 30000 - 30 seconds) */
292
+ interval?: number;
293
+ /** Whether auto-save is enabled (default: true) */
294
+ enabled?: boolean;
295
+ /** Maximum age of auto-save in milliseconds before it's considered stale (default: 24 hours) */
296
+ maxAge?: number;
297
+ /** Callback when save succeeds */
298
+ onSave?: (timestamp: Date) => void;
299
+ /** Callback when save fails */
300
+ onError?: (error: Error) => void;
301
+ /** Callback when recovery data is found */
302
+ onRecoveryAvailable?: (savedDocument: SavedDocumentData) => void;
303
+ /** Whether to save immediately when document changes (debounced) */
304
+ saveOnChange?: boolean;
305
+ /** Debounce delay for saveOnChange in milliseconds (default: 2000) */
306
+ debounceDelay?: number;
307
+ }
308
+ /** Return value of useAutoSave hook */
309
+ interface UseAutoSaveReturn {
310
+ status: AutoSaveStatus;
311
+ lastSaveTime: Date | null;
312
+ save: () => Promise<boolean>;
313
+ clearAutoSave: () => void;
314
+ hasRecoveryData: boolean;
315
+ getRecoveryData: () => SavedDocumentData | null;
316
+ acceptRecovery: () => Document | null;
317
+ dismissRecovery: () => void;
318
+ isEnabled: boolean;
319
+ enable: () => void;
320
+ disable: () => void;
321
+ }
322
+ declare function useAutoSave(document: Document | null | undefined, options?: UseAutoSaveOptions): UseAutoSaveReturn;
323
+
324
+ /**
325
+ * Drag Auto-Scroll Hook
326
+ *
327
+ * When the user is drag-selecting text and moves the mouse near the
328
+ * top or bottom edge of the scroll container, this hook auto-scrolls
329
+ * the container and continues extending the selection.
330
+ */
331
+ interface DragAutoScrollOptions {
332
+ /** Ref to the pages container (used to find the scroll parent). */
333
+ pagesContainerRef: React.RefObject<HTMLDivElement | null>;
334
+ /** Called during auto-scroll to extend the selection at the current mouse position. */
335
+ onScrollExtendSelection: (clientX: number, clientY: number) => void;
336
+ }
337
+ declare function useDragAutoScroll({ pagesContainerRef, onScrollExtendSelection, }: DragAutoScrollOptions): {
338
+ updateMousePosition: (clientX: number, clientY: number) => void;
339
+ stopAutoScroll: () => void;
340
+ };
341
+
342
+ /**
343
+ * Hook for toolbar dropdowns that need position:fixed to escape overflow:auto/hidden ancestors.
344
+ *
345
+ * Returns refs and styles for a dropdown that positions itself below its trigger
346
+ * using fixed coordinates (like MenuDropdown), so it isn't clipped by the toolbar's
347
+ * overflow-x-auto container.
348
+ */
349
+
350
+ interface UseFixedDropdownOptions {
351
+ isOpen: boolean;
352
+ onClose: () => void;
353
+ /** 'left' aligns dropdown left edge to trigger, 'right' aligns right edge */
354
+ align?: 'left' | 'right';
355
+ }
356
+ interface UseFixedDropdownReturn {
357
+ containerRef: RefObject<HTMLDivElement | null>;
358
+ dropdownRef: RefObject<HTMLDivElement | null>;
359
+ dropdownStyle: CSSProperties;
360
+ handleMouseDown: (e: React.MouseEvent) => void;
361
+ }
362
+ declare function useFixedDropdown({ isOpen, onClose, align, }: UseFixedDropdownOptions): UseFixedDropdownReturn;
363
+
364
+ /**
365
+ * Width/height inputs with an optional aspect-ratio lock. `width`/`height`
366
+ * are `number | ''` so a cleared field shows empty instead of 0.
367
+ */
368
+ interface UseAspectLockedSizeReturn {
369
+ width: number | '';
370
+ height: number | '';
371
+ lockAspect: boolean;
372
+ setLockAspect: (locked: boolean) => void;
373
+ /** Number-input onChange handlers. Empty string clears, otherwise clamps to >= 1. */
374
+ handleWidthChange: (raw: string) => void;
375
+ handleHeightChange: (raw: string) => void;
376
+ /** Seed both fields and re-lock. Null/undefined values clear the input. */
377
+ seed: (w: number | null | undefined, h: number | null | undefined) => void;
378
+ }
379
+ declare function useAspectLockedSize(): UseAspectLockedSizeReturn;
380
+
381
+ interface VisualLineNavigationOptions {
382
+ pagesContainerRef: React.RefObject<HTMLDivElement | null>;
383
+ }
384
+ declare function useVisualLineNavigation({ pagesContainerRef }: VisualLineNavigationOptions): {
385
+ stickyXRef: React$1.RefObject<number | null>;
386
+ lastVisualLineIndexRef: React$1.RefObject<number>;
387
+ getCaretClientX: (pmPos: number) => number | null;
388
+ findLineElementAtPosition: (pmPos: number) => HTMLElement | null;
389
+ findPositionOnLineAtClientX: (lineEl: HTMLElement, clientX: number) => number | null;
390
+ handlePMKeyDown: (view: EditorView, event: KeyboardEvent) => boolean;
391
+ };
392
+
393
+ /**
394
+ * useWheelZoom Hook
395
+ *
396
+ * Enables Ctrl+scroll (or Cmd+scroll on Mac) to zoom in/out.
397
+ * Features:
398
+ * - Configurable zoom range and step
399
+ * - Smooth zoom transitions
400
+ * - Pinch-to-zoom support on trackpads
401
+ * - Zoom reset (Ctrl+0)
402
+ * - Zoom in/out shortcuts (Ctrl++, Ctrl+-)
403
+ */
404
+ /**
405
+ * Options for useWheelZoom hook
406
+ */
407
+ interface UseWheelZoomOptions {
408
+ /** Initial zoom level (default: 1.0) */
409
+ initialZoom?: number;
410
+ /** Minimum zoom level (default: 0.25) */
411
+ minZoom?: number;
412
+ /** Maximum zoom level (default: 4.0) */
413
+ maxZoom?: number;
414
+ /** Zoom step for each scroll event (default: 0.1) */
415
+ zoomStep?: number;
416
+ /** Whether zoom is enabled (default: true) */
417
+ enabled?: boolean;
418
+ /** Container element ref to attach wheel listener */
419
+ containerRef?: React.RefObject<HTMLElement>;
420
+ /** Callback when zoom changes */
421
+ onZoomChange?: (zoom: number) => void;
422
+ /** Whether to enable keyboard shortcuts (Ctrl++, Ctrl+-, Ctrl+0) */
423
+ enableKeyboardShortcuts?: boolean;
424
+ /** Whether to prevent default browser zoom behavior */
425
+ preventDefault?: boolean;
426
+ }
427
+ /**
428
+ * Return value of useWheelZoom hook
429
+ */
430
+ interface UseWheelZoomReturn {
431
+ /** Current zoom level */
432
+ zoom: number;
433
+ /** Set zoom level directly */
434
+ setZoom: (zoom: number) => void;
435
+ /** Zoom in by step */
436
+ zoomIn: () => void;
437
+ /** Zoom out by step */
438
+ zoomOut: () => void;
439
+ /** Reset zoom to initial level */
440
+ resetZoom: () => void;
441
+ /** Reset zoom to 100% */
442
+ zoomTo100: () => void;
443
+ /** Zoom to fit width */
444
+ zoomToFit: (containerWidth: number, contentWidth: number) => void;
445
+ /** Whether currently at minimum zoom */
446
+ isMinZoom: boolean;
447
+ /** Whether currently at maximum zoom */
448
+ isMaxZoom: boolean;
449
+ /** Zoom percentage (e.g., 100 for zoom level 1.0) */
450
+ zoomPercent: number;
451
+ /** Wheel event handler (for manual attachment) */
452
+ handleWheel: (event: WheelEvent) => void;
453
+ /** Keyboard event handler (for manual attachment) */
454
+ handleKeyDown: (event: KeyboardEvent) => void;
455
+ }
456
+ /**
457
+ * Preset zoom levels for snapping
458
+ */
459
+ declare const ZOOM_PRESETS: number[];
460
+ /**
461
+ * React hook for Ctrl+scroll zoom functionality
462
+ */
463
+ declare function useWheelZoom(options?: UseWheelZoomOptions): UseWheelZoomReturn;
464
+ /**
465
+ * Get zoom presets
466
+ */
467
+ declare function getZoomPresets(): number[];
468
+ /**
469
+ * Find nearest zoom preset
470
+ */
471
+ declare function findNearestZoomPreset(zoom: number): number;
472
+ /**
473
+ * Get next zoom preset (for zoom in)
474
+ */
475
+ declare function getNextZoomPreset(zoom: number): number;
476
+ /**
477
+ * Get previous zoom preset (for zoom out)
478
+ */
479
+ declare function getPreviousZoomPreset(zoom: number): number;
480
+ /**
481
+ * Format zoom level for display
482
+ */
483
+ declare function formatZoom(zoom: number): string;
484
+ /**
485
+ * Parse zoom from percentage string
486
+ */
487
+ declare function parseZoom(zoomString: string): number | null;
488
+ /**
489
+ * Check if zoom level is at a preset
490
+ */
491
+ declare function isZoomPreset(zoom: number): boolean;
492
+ /**
493
+ * Clamp zoom to valid range
494
+ */
495
+ declare function clampZoom(zoom: number, minZoom?: number, maxZoom?: number): number;
496
+
497
+ export { type DragAutoScrollOptions, type HistoryEntry, HistoryManager, type SelectionOverlayProps, type TableSelectionState, type UseAspectLockedSizeReturn, type UseAutoSaveOptions, type UseAutoSaveReturn, type UseClipboardOptions, type UseClipboardReturn, type UseFixedDropdownOptions, type UseFixedDropdownReturn, type UseHistoryOptions, type UseHistoryReturn, type UseSelectionHighlightOptions, type UseSelectionHighlightReturn, type UseTableSelectionOptions, type UseTableSelectionReturn, type UseWheelZoomOptions, type UseWheelZoomReturn, type VisualLineNavigationOptions, ZOOM_PRESETS, clampZoom, findNearestZoomPreset, formatZoom, generateOverlayElements, getNextZoomPreset, getPreviousZoomPreset, getZoomPresets, isZoomPreset, parseZoom, useAspectLockedSize, useAutoHistory, useAutoSave, useClipboard, useDocumentHistory, useDragAutoScroll, useFixedDropdown, useHistory, useSelectionHighlight, useTableSelection, useVisualLineNavigation, useWheelZoom };
package/dist/hooks.js ADDED
@@ -0,0 +1 @@
1
+ 'use strict';var chunkJHSXIGTQ_js=require('./chunk-JHSXIGTQ.js'),chunkNIBCC7WQ_js=require('./chunk-NIBCC7WQ.js'),chunkYVYT3JWY_js=require('./chunk-YVYT3JWY.js'),chunkL4MDZW2J_js=require('./chunk-L4MDZW2J.js');require('./chunk-GNIO6SOS.js'),require('./chunk-BJ5RZW6Y.js');var oe=require('react'),utils=require('@eigenpal/docx-editor-core/utils'),docxEditorCore=require('@eigenpal/docx-editor-core');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var oe__default=/*#__PURE__*/_interopDefault(oe);function ue(n){let{containerRef:e,enabled:r=true,config:s=utils.DEFAULT_SELECTION_STYLE,useOverlay:c=false,debounceMs:u=16,onSelectionChange:f}=n,[S,p]=oe.useState(false),[x,v]=oe.useState(""),[a,m]=oe.useState([]),[d,y]=oe.useState(false),h=oe.useRef(null),C=oe.useRef(0),o=oe.useCallback(()=>{let t=e.current,b=utils.hasActiveSelection(),D=utils.getSelectedText(),A=t?utils.isSelectionWithin(t):false;if(p(b),v(D),y(A),c&&A){let $=utils.getMergedSelectionRects(t);m($);}else m([]);f&&f(b&&A,D);},[e,c,f]),i=oe.useCallback(()=>{let t=performance.now();if(t-C.current<u){h.current!==null&&clearTimeout(h.current),h.current=window.setTimeout(()=>{C.current=performance.now(),o(),h.current=null;},u);return}C.current=t,o();},[u,o]),l=oe.useCallback(()=>{o();},[o]),R=oe.useCallback(t=>({position:"absolute",left:`${t.left}px`,top:`${t.top}px`,width:`${t.width}px`,height:`${t.height}px`,backgroundColor:s.backgroundColor,borderRadius:s.borderRadius?`${s.borderRadius}px`:void 0,border:s.borderColor?`1px solid ${s.borderColor}`:void 0,zIndex:s.zIndex??0,opacity:s.opacity??1,mixBlendMode:s.mixBlendMode,pointerEvents:"none",userSelect:"none"}),[s]);return oe.useEffect(()=>(r&&!utils.areSelectionStylesInjected()&&utils.injectSelectionStyles(s),()=>{}),[r,s]),oe.useEffect(()=>{if(!r)return;let t=()=>{i();};return document.addEventListener("selectionchange",t),document.addEventListener("mouseup",t),o(),()=>{document.removeEventListener("selectionchange",t),document.removeEventListener("mouseup",t),h.current!==null&&clearTimeout(h.current);}},[r,i,o]),{hasSelection:S,selectedText:x,highlightRects:a,isSelectionInContainer:d,refresh:l,getOverlayStyle:R}}function ce(n,e=utils.DEFAULT_SELECTION_STYLE){return n.map((r,s)=>oe__default.default.createElement("div",{key:`selection-overlay-${s}`,style:{position:"absolute",left:`${r.left}px`,top:`${r.top}px`,width:`${r.width}px`,height:`${r.height}px`,backgroundColor:e.backgroundColor,borderRadius:e.borderRadius?`${e.borderRadius}px`:void 0,border:e.borderColor?`1px solid ${e.borderColor}`:void 0,zIndex:e.zIndex??0,opacity:e.opacity??1,mixBlendMode:e.mixBlendMode,pointerEvents:"none",userSelect:"none"}}))}function be(n={}){let{onCopy:e,onCut:r,onPaste:s,cleanWordFormatting:c=true,editable:u=true,onError:f,theme:S}=n,p=oe.useRef(false),x=oe.useRef(null),v=oe.useCallback(async o=>{if(p.current)return false;p.current=true;try{let i=await utils.copyRuns(o.runs,{onError:f,theme:S});return i&&e?.(o),i}finally{p.current=false;}},[e,f,S]),a=oe.useCallback(async o=>{if(p.current||!u)return false;p.current=true;try{let i=await utils.copyRuns(o.runs,{onError:f,theme:S});return i&&r?.(o),i}finally{p.current=false;}},[r,u,f,S]),m=oe.useCallback(async(o=false)=>{if(p.current||!u)return null;p.current=true;try{if(navigator.clipboard&&navigator.clipboard.read){let i=await navigator.clipboard.read(),l="",R="";for(let b of i)b.types.includes("text/html")&&(l=await(await b.getType("text/html")).text()),b.types.includes("text/plain")&&(R=await(await b.getType("text/plain")).text());o&&(l="");let t=utils.parseClipboardHtml(l,R,c);return x.current=t,s?.(t,o),t}return null}catch(i){return f?.(i),null}finally{p.current=false;}},[u,c,s,f]),d=oe.useCallback(o=>{let i=docxEditorCore.createSelectionFromDOM();if(!i)return;o.preventDefault();let l=utils.runsToClipboardContent(i.runs,true,S);o.clipboardData&&(o.clipboardData.setData("text/plain",l.plainText),o.clipboardData.setData("text/html",l.html),l.internal&&o.clipboardData.setData("application/x-docx-editor",l.internal)),e?.(i);},[e,S]),y=oe.useCallback(o=>{if(!u)return;let i=docxEditorCore.createSelectionFromDOM();if(!i)return;o.preventDefault();let l=utils.runsToClipboardContent(i.runs,true,S);o.clipboardData&&(o.clipboardData.setData("text/plain",l.plainText),o.clipboardData.setData("text/html",l.html),l.internal&&o.clipboardData.setData("application/x-docx-editor",l.internal)),r?.(i);},[u,r,S]),h=oe.useCallback(o=>{if(!u)return;o.preventDefault();let i=utils.handlePasteEvent(o,{cleanWordFormatting:c});if(i){x.current=i;let l=o.shiftKey??false;s?.(i,l);}},[u,c,s]),C=oe.useCallback(o=>{},[]);return {copy:v,cut:a,paste:m,handleCopy:d,handleCut:y,handlePaste:h,handleKeyDown:C,isProcessing:p.current,lastPastedContent:x.current}}function Re(n,e={}){let{storageKey:r,interval:s,enabled:c=true,maxAge:u,onSave:f,onError:S,onRecoveryAvailable:p,saveOnChange:x,debounceDelay:v}=e,a=oe.useMemo(()=>new docxEditorCore.AutoSaveManager({storageKey:r,interval:s,maxAge:u,saveOnChange:x,debounceDelay:v,onSave:f,onError:S,onRecoveryAvailable:p}),[r]);oe.useEffect(()=>{c?(a.enable(),a.startInterval()):a.disable();},[a,c]),oe.useEffect(()=>{a.onDocumentChanged(n??null);},[a,n]),oe.useEffect(()=>()=>{a.destroy();},[a]);let m=oe.useSyncExternalStore(a.subscribe,a.getSnapshot),d=oe.useCallback(()=>a.save(),[a]),y=oe.useCallback(()=>a.clear(),[a]),h=oe.useCallback(()=>a.getRecoveryData(),[a]),C=oe.useCallback(()=>a.acceptRecovery(),[a]),o=oe.useCallback(()=>a.dismissRecovery(),[a]),i=oe.useCallback(()=>a.enable(),[a]),l=oe.useCallback(()=>a.disable(),[a]);return {status:m.status,lastSaveTime:m.lastSaveTime,save:d,clearAutoSave:y,hasRecoveryData:m.hasRecoveryData,getRecoveryData:h,acceptRecovery:C,dismissRecovery:o,isEnabled:m.isEnabled,enable:i,disable:l}}var Oe=1,K=.25,W=4,Pe=.1,g=[.25,.5,.75,1,1.25,1.5,2,3,4];function _(n,e,r){return Math.max(e,Math.min(r,n))}function N(n){return Math.round(n*100)/100}function Ze(n){let e=g[0],r=Math.abs(n-e);for(let s of g){let c=Math.abs(n-s);c<r&&(r=c,e=s);}return e}function Te(n){for(let e of g)if(e>n+.01)return e;return g[g.length-1]}function Ae(n){for(let e=g.length-1;e>=0;e--)if(g[e]<n-.01)return g[e];return g[0]}function Ue(n={}){let{initialZoom:e=Oe,minZoom:r=K,maxZoom:s=W,zoomStep:c=Pe,enabled:u=true,containerRef:f,onZoomChange:S,enableKeyboardShortcuts:p=true,preventDefault:x=true}=n,[v,a]=oe.useState(e),m=oe.useRef(v);oe.useEffect(()=>{m.current=v;},[v]);let d=oe.useCallback(t=>{let b=N(_(t,r,s));b!==m.current&&(a(b),S?.(b));},[r,s,S]),y=oe.useCallback(()=>{d(m.current+c);},[c,d]),h=oe.useCallback(()=>{d(m.current-c);},[c,d]),C=oe.useCallback(()=>{d(e);},[e,d]),o=oe.useCallback(()=>{d(1);},[d]),i=oe.useCallback((t,b)=>{if(b>0){let D=t/b;d(D);}},[d]),l=oe.useCallback(t=>{if(!u||!(t.ctrlKey||t.metaKey))return;x&&t.preventDefault();let D=t.deltaY;D<0?d(m.current+c):D>0&&d(m.current-c);},[u,x,c,d]),R=oe.useCallback(t=>{if(!(!u||!p||!(t.ctrlKey||t.metaKey))){if(t.key==="0"){t.preventDefault(),o();return}if(t.key==="+"||t.key==="="){t.preventDefault(),y();return}if(t.key==="-"){t.preventDefault(),h();return}}},[u,p,y,h,o]);return oe.useEffect(()=>{if(!u)return;let t=f?.current;if(t)return t.addEventListener("wheel",l,{passive:false}),()=>{t.removeEventListener("wheel",l);}},[u,f,l]),oe.useEffect(()=>{if(!(!u||!p))return document.addEventListener("keydown",R),()=>{document.removeEventListener("keydown",R);}},[u,p,R]),{zoom:v,setZoom:d,zoomIn:y,zoomOut:h,resetZoom:C,zoomTo100:o,zoomToFit:i,isMinZoom:v<=r,isMaxZoom:v>=s,zoomPercent:Math.round(v*100),handleWheel:l,handleKeyDown:R}}function Me(){return [...g]}function He(n){return Ze(n)}function we(n){return Te(n)}function Le(n){return Ae(n)}function ze(n){return `${Math.round(n*100)}%`}function Fe(n){let e=n.match(/(\d+(\.\d+)?)/);if(e){let r=parseFloat(e[1]);if(!isNaN(r))return r/100}return null}function ke(n){return g.some(e=>Math.abs(e-n)<.01)}function Ie(n,e=K,r=W){return N(_(n,e,r))}Object.defineProperty(exports,"HistoryManager",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.h}});Object.defineProperty(exports,"TABLE_DATA_ATTRIBUTES",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.d}});Object.defineProperty(exports,"useAutoHistory",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.f}});Object.defineProperty(exports,"useDocumentHistory",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.g}});Object.defineProperty(exports,"useDragAutoScroll",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.b}});Object.defineProperty(exports,"useHistory",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.e}});Object.defineProperty(exports,"useTableSelection",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.c}});Object.defineProperty(exports,"useVisualLineNavigation",{enumerable:true,get:function(){return chunkJHSXIGTQ_js.a}});Object.defineProperty(exports,"useAspectLockedSize",{enumerable:true,get:function(){return chunkNIBCC7WQ_js.a}});Object.defineProperty(exports,"useFixedDropdown",{enumerable:true,get:function(){return chunkYVYT3JWY_js.c}});Object.defineProperty(exports,"useFindReplace",{enumerable:true,get:function(){return chunkL4MDZW2J_js.b}});Object.defineProperty(exports,"createSelectionFromDOM",{enumerable:true,get:function(){return docxEditorCore.createSelectionFromDOM}});Object.defineProperty(exports,"formatLastSaveTime",{enumerable:true,get:function(){return docxEditorCore.formatLastSaveTime}});Object.defineProperty(exports,"formatStorageSize",{enumerable:true,get:function(){return docxEditorCore.formatStorageSize}});Object.defineProperty(exports,"getAutoSaveStatusLabel",{enumerable:true,get:function(){return docxEditorCore.getAutoSaveStatusLabel}});Object.defineProperty(exports,"getAutoSaveStorageSize",{enumerable:true,get:function(){return docxEditorCore.getAutoSaveStorageSize}});Object.defineProperty(exports,"getSelectionRuns",{enumerable:true,get:function(){return docxEditorCore.getSelectionRuns}});Object.defineProperty(exports,"isAutoSaveSupported",{enumerable:true,get:function(){return docxEditorCore.isAutoSaveSupported}});exports.ZOOM_PRESETS=g;exports.clampZoom=Ie;exports.findNearestZoomPreset=He;exports.formatZoom=ze;exports.generateOverlayElements=ce;exports.getNextZoomPreset=we;exports.getPreviousZoomPreset=Le;exports.getZoomPresets=Me;exports.isZoomPreset=ke;exports.parseZoom=Fe;exports.useAutoSave=Re;exports.useClipboard=be;exports.useSelectionHighlight=ue;exports.useWheelZoom=Ue;
package/dist/hooks.mjs ADDED
@@ -0,0 +1 @@
1
+ export{h as HistoryManager,d as TABLE_DATA_ATTRIBUTES,f as useAutoHistory,g as useDocumentHistory,b as useDragAutoScroll,e as useHistory,c as useTableSelection,a as useVisualLineNavigation}from'./chunk-6AX4I76A.mjs';export{a as useAspectLockedSize}from'./chunk-GM2S2WMT.mjs';export{c as useFixedDropdown}from'./chunk-TN5C2ZYV.mjs';export{b as useFindReplace}from'./chunk-OHNO4NRQ.mjs';import'./chunk-ZFZJRL2R.mjs';import'./chunk-BMBP5UFA.mjs';import oe,{useState,useRef,useCallback,useEffect,useMemo,useSyncExternalStore}from'react';import {DEFAULT_SELECTION_STYLE,hasActiveSelection,getSelectedText,isSelectionWithin,getMergedSelectionRects,areSelectionStylesInjected,injectSelectionStyles,copyRuns,parseClipboardHtml,runsToClipboardContent,handlePasteEvent}from'@eigenpal/docx-editor-core/utils';import {createSelectionFromDOM,AutoSaveManager}from'@eigenpal/docx-editor-core';export{createSelectionFromDOM,formatLastSaveTime,formatStorageSize,getAutoSaveStatusLabel,getAutoSaveStorageSize,getSelectionRuns,isAutoSaveSupported}from'@eigenpal/docx-editor-core';function ue(n){let{containerRef:e,enabled:r=true,config:s=DEFAULT_SELECTION_STYLE,useOverlay:c=false,debounceMs:u=16,onSelectionChange:f}=n,[S,p]=useState(false),[x,v]=useState(""),[a,m]=useState([]),[d,y]=useState(false),h=useRef(null),C=useRef(0),o=useCallback(()=>{let t=e.current,b=hasActiveSelection(),D=getSelectedText(),A=t?isSelectionWithin(t):false;if(p(b),v(D),y(A),c&&A){let $=getMergedSelectionRects(t);m($);}else m([]);f&&f(b&&A,D);},[e,c,f]),i=useCallback(()=>{let t=performance.now();if(t-C.current<u){h.current!==null&&clearTimeout(h.current),h.current=window.setTimeout(()=>{C.current=performance.now(),o(),h.current=null;},u);return}C.current=t,o();},[u,o]),l=useCallback(()=>{o();},[o]),R=useCallback(t=>({position:"absolute",left:`${t.left}px`,top:`${t.top}px`,width:`${t.width}px`,height:`${t.height}px`,backgroundColor:s.backgroundColor,borderRadius:s.borderRadius?`${s.borderRadius}px`:void 0,border:s.borderColor?`1px solid ${s.borderColor}`:void 0,zIndex:s.zIndex??0,opacity:s.opacity??1,mixBlendMode:s.mixBlendMode,pointerEvents:"none",userSelect:"none"}),[s]);return useEffect(()=>(r&&!areSelectionStylesInjected()&&injectSelectionStyles(s),()=>{}),[r,s]),useEffect(()=>{if(!r)return;let t=()=>{i();};return document.addEventListener("selectionchange",t),document.addEventListener("mouseup",t),o(),()=>{document.removeEventListener("selectionchange",t),document.removeEventListener("mouseup",t),h.current!==null&&clearTimeout(h.current);}},[r,i,o]),{hasSelection:S,selectedText:x,highlightRects:a,isSelectionInContainer:d,refresh:l,getOverlayStyle:R}}function ce(n,e=DEFAULT_SELECTION_STYLE){return n.map((r,s)=>oe.createElement("div",{key:`selection-overlay-${s}`,style:{position:"absolute",left:`${r.left}px`,top:`${r.top}px`,width:`${r.width}px`,height:`${r.height}px`,backgroundColor:e.backgroundColor,borderRadius:e.borderRadius?`${e.borderRadius}px`:void 0,border:e.borderColor?`1px solid ${e.borderColor}`:void 0,zIndex:e.zIndex??0,opacity:e.opacity??1,mixBlendMode:e.mixBlendMode,pointerEvents:"none",userSelect:"none"}}))}function be(n={}){let{onCopy:e,onCut:r,onPaste:s,cleanWordFormatting:c=true,editable:u=true,onError:f,theme:S}=n,p=useRef(false),x=useRef(null),v=useCallback(async o=>{if(p.current)return false;p.current=true;try{let i=await copyRuns(o.runs,{onError:f,theme:S});return i&&e?.(o),i}finally{p.current=false;}},[e,f,S]),a=useCallback(async o=>{if(p.current||!u)return false;p.current=true;try{let i=await copyRuns(o.runs,{onError:f,theme:S});return i&&r?.(o),i}finally{p.current=false;}},[r,u,f,S]),m=useCallback(async(o=false)=>{if(p.current||!u)return null;p.current=true;try{if(navigator.clipboard&&navigator.clipboard.read){let i=await navigator.clipboard.read(),l="",R="";for(let b of i)b.types.includes("text/html")&&(l=await(await b.getType("text/html")).text()),b.types.includes("text/plain")&&(R=await(await b.getType("text/plain")).text());o&&(l="");let t=parseClipboardHtml(l,R,c);return x.current=t,s?.(t,o),t}return null}catch(i){return f?.(i),null}finally{p.current=false;}},[u,c,s,f]),d=useCallback(o=>{let i=createSelectionFromDOM();if(!i)return;o.preventDefault();let l=runsToClipboardContent(i.runs,true,S);o.clipboardData&&(o.clipboardData.setData("text/plain",l.plainText),o.clipboardData.setData("text/html",l.html),l.internal&&o.clipboardData.setData("application/x-docx-editor",l.internal)),e?.(i);},[e,S]),y=useCallback(o=>{if(!u)return;let i=createSelectionFromDOM();if(!i)return;o.preventDefault();let l=runsToClipboardContent(i.runs,true,S);o.clipboardData&&(o.clipboardData.setData("text/plain",l.plainText),o.clipboardData.setData("text/html",l.html),l.internal&&o.clipboardData.setData("application/x-docx-editor",l.internal)),r?.(i);},[u,r,S]),h=useCallback(o=>{if(!u)return;o.preventDefault();let i=handlePasteEvent(o,{cleanWordFormatting:c});if(i){x.current=i;let l=o.shiftKey??false;s?.(i,l);}},[u,c,s]),C=useCallback(o=>{},[]);return {copy:v,cut:a,paste:m,handleCopy:d,handleCut:y,handlePaste:h,handleKeyDown:C,isProcessing:p.current,lastPastedContent:x.current}}function Re(n,e={}){let{storageKey:r,interval:s,enabled:c=true,maxAge:u,onSave:f,onError:S,onRecoveryAvailable:p,saveOnChange:x,debounceDelay:v}=e,a=useMemo(()=>new AutoSaveManager({storageKey:r,interval:s,maxAge:u,saveOnChange:x,debounceDelay:v,onSave:f,onError:S,onRecoveryAvailable:p}),[r]);useEffect(()=>{c?(a.enable(),a.startInterval()):a.disable();},[a,c]),useEffect(()=>{a.onDocumentChanged(n??null);},[a,n]),useEffect(()=>()=>{a.destroy();},[a]);let m=useSyncExternalStore(a.subscribe,a.getSnapshot),d=useCallback(()=>a.save(),[a]),y=useCallback(()=>a.clear(),[a]),h=useCallback(()=>a.getRecoveryData(),[a]),C=useCallback(()=>a.acceptRecovery(),[a]),o=useCallback(()=>a.dismissRecovery(),[a]),i=useCallback(()=>a.enable(),[a]),l=useCallback(()=>a.disable(),[a]);return {status:m.status,lastSaveTime:m.lastSaveTime,save:d,clearAutoSave:y,hasRecoveryData:m.hasRecoveryData,getRecoveryData:h,acceptRecovery:C,dismissRecovery:o,isEnabled:m.isEnabled,enable:i,disable:l}}var Oe=1,K=.25,W=4,Pe=.1,g=[.25,.5,.75,1,1.25,1.5,2,3,4];function _(n,e,r){return Math.max(e,Math.min(r,n))}function N(n){return Math.round(n*100)/100}function Ze(n){let e=g[0],r=Math.abs(n-e);for(let s of g){let c=Math.abs(n-s);c<r&&(r=c,e=s);}return e}function Te(n){for(let e of g)if(e>n+.01)return e;return g[g.length-1]}function Ae(n){for(let e=g.length-1;e>=0;e--)if(g[e]<n-.01)return g[e];return g[0]}function Ue(n={}){let{initialZoom:e=Oe,minZoom:r=K,maxZoom:s=W,zoomStep:c=Pe,enabled:u=true,containerRef:f,onZoomChange:S,enableKeyboardShortcuts:p=true,preventDefault:x=true}=n,[v,a]=useState(e),m=useRef(v);useEffect(()=>{m.current=v;},[v]);let d=useCallback(t=>{let b=N(_(t,r,s));b!==m.current&&(a(b),S?.(b));},[r,s,S]),y=useCallback(()=>{d(m.current+c);},[c,d]),h=useCallback(()=>{d(m.current-c);},[c,d]),C=useCallback(()=>{d(e);},[e,d]),o=useCallback(()=>{d(1);},[d]),i=useCallback((t,b)=>{if(b>0){let D=t/b;d(D);}},[d]),l=useCallback(t=>{if(!u||!(t.ctrlKey||t.metaKey))return;x&&t.preventDefault();let D=t.deltaY;D<0?d(m.current+c):D>0&&d(m.current-c);},[u,x,c,d]),R=useCallback(t=>{if(!(!u||!p||!(t.ctrlKey||t.metaKey))){if(t.key==="0"){t.preventDefault(),o();return}if(t.key==="+"||t.key==="="){t.preventDefault(),y();return}if(t.key==="-"){t.preventDefault(),h();return}}},[u,p,y,h,o]);return useEffect(()=>{if(!u)return;let t=f?.current;if(t)return t.addEventListener("wheel",l,{passive:false}),()=>{t.removeEventListener("wheel",l);}},[u,f,l]),useEffect(()=>{if(!(!u||!p))return document.addEventListener("keydown",R),()=>{document.removeEventListener("keydown",R);}},[u,p,R]),{zoom:v,setZoom:d,zoomIn:y,zoomOut:h,resetZoom:C,zoomTo100:o,zoomToFit:i,isMinZoom:v<=r,isMaxZoom:v>=s,zoomPercent:Math.round(v*100),handleWheel:l,handleKeyDown:R}}function Me(){return [...g]}function He(n){return Ze(n)}function we(n){return Te(n)}function Le(n){return Ae(n)}function ze(n){return `${Math.round(n*100)}%`}function Fe(n){let e=n.match(/(\d+(\.\d+)?)/);if(e){let r=parseFloat(e[1]);if(!isNaN(r))return r/100}return null}function ke(n){return g.some(e=>Math.abs(e-n)<.01)}function Ie(n,e=K,r=W){return N(_(n,e,r))}export{g as ZOOM_PRESETS,Ie as clampZoom,He as findNearestZoomPreset,ze as formatZoom,ce as generateOverlayElements,we as getNextZoomPreset,Le as getPreviousZoomPreset,Me as getZoomPresets,ke as isZoomPreset,Fe as parseZoom,Re as useAutoSave,be as useClipboard,ue as useSelectionHighlight,Ue as useWheelZoom};