@alaarab/ogrid-vue 2.7.3 → 2.9.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.
@@ -10,7 +10,7 @@
10
10
  /* Cell selection highlighting.
11
11
  Qualify with .ogrid-outer-container (specificity 0,2,0) to beat row-level hover backgrounds. */
12
12
  .ogrid-outer-container .ogrid-cell-in-range {
13
- background: var(--ogrid-bg-range, rgba(33, 115, 70, 0.12));
13
+ background: var(--ogrid-range-bg, rgba(33, 115, 70, 0.12));
14
14
  }
15
15
 
16
16
  /* Cut range highlighting */
@@ -62,6 +62,10 @@
62
62
  font-size: 0.875rem;
63
63
  background-color: var(--ogrid-bg);
64
64
  color: var(--ogrid-fg);
65
+ font-family: var(--ogrid-font, inherit);
66
+ /* Tabular numerics + OpenType features so digits align in columns. */
67
+ font-variant-numeric: tabular-nums;
68
+ font-feature-settings: "tnum" 1, "ss01" 1, "cv11" 1;
65
69
  }
66
70
 
67
71
  /* No left border on first column — the grid container provides the left edge */
@@ -279,7 +283,7 @@
279
283
  }
280
284
 
281
285
  .ogrid-cell-content--active {
282
- outline: 2px solid var(--ogrid-selection, #217346);
286
+ outline: 2px solid var(--ogrid-selection-color, #217346);
283
287
  outline-offset: -1px;
284
288
  z-index: var(--ogrid-z-active-cell, 2);
285
289
  position: relative;
@@ -327,7 +331,7 @@
327
331
  bottom: -3px;
328
332
  width: 7px;
329
333
  height: 7px;
330
- background-color: var(--ogrid-selection, #217346);
334
+ background-color: var(--ogrid-selection-color, #217346);
331
335
  border: 1px solid var(--ogrid-bg);
332
336
  border-radius: 1px;
333
337
  cursor: crosshair;
@@ -1,119 +1,228 @@
1
- /* OGrid Shared Theme Variables consumed by all Vue UI packages.
1
+ /* OGrid Vue themegenerated from packages/core/src/styles/_ogrid-theme.scss
2
+ * Run `node scripts/sync-theme.mjs` to regenerate. Do not edit by hand.
2
3
  *
3
- * Uses :where() selectors for ZERO specificity — consumer overrides always win.
4
- * Dark mode: auto via prefers-color-scheme, explicit via [data-theme="dark"].
4
+ * Dark mode activates via:
5
+ * 1. System preference (prefers-color-scheme: dark) unless [data-theme="light"]
6
+ * or .light is set on root.
7
+ * 2. Explicit attribute: [data-theme="dark"] on any ancestor.
8
+ * 3. Tailwind/shadcn convention: .dark class on any ancestor.
9
+ *
10
+ * To opt OUT of auto-dark: set .light or [data-theme="light"] on :root.
5
11
  */
6
12
 
7
13
  /* ─── Light Theme (default) ─── */
8
14
  :where(:root) {
9
- /* Cell padding — override for row density:
10
- --ogrid-cell-padding : shorthand (default 6px 10px)
11
- --ogrid-cell-padding-vertical : vertical only (default 6px)
15
+ /* ── Cell padding — override for row density ──
16
+ --ogrid-cell-padding : shorthand (default 6px 10px)
17
+ --ogrid-cell-padding-vertical : vertical only (default 6px)
12
18
  --ogrid-cell-padding-horizontal: horizontal only (default 10px) */
13
19
  --ogrid-cell-padding: 6px 10px;
14
20
  --ogrid-cell-padding-vertical: 6px;
15
21
  --ogrid-cell-padding-horizontal: 10px;
22
+
23
+ /* ── Radius scale — override --ogrid-radius to scale all corners ──
24
+ --ogrid-radius : base (default 6px) — buttons, inputs, popovers
25
+ --ogrid-radius-sm : tighter (calc 0.6x) — checkboxes, tags
26
+ --ogrid-radius-lg : looser (calc 1.4x) — cards, dialogs
27
+ --ogrid-radius-xl : largest (calc 1.8x) — hero surfaces
28
+ --ogrid-radius-full: 9999px — pills, dots */
29
+ --ogrid-radius: 6px;
30
+ --ogrid-radius-sm: calc(var(--ogrid-radius) * 0.6);
31
+ --ogrid-radius-lg: calc(var(--ogrid-radius) * 1.4);
32
+ --ogrid-radius-xl: calc(var(--ogrid-radius) * 1.8);
33
+ --ogrid-radius-full: 9999px;
34
+
35
+ /* ── Typography — override --ogrid-font to re-skin the type stack ──
36
+ --ogrid-font : font-family for grid chrome (default: inherit from host)
37
+ --ogrid-font-size: base body font-size (default 13px) */
38
+ --ogrid-font: inherit;
39
+ --ogrid-font-size: 13px;
40
+
41
+ /* ── Focus ring — color used for focus outlines / rings ──
42
+ --ogrid-ring: focus ring color (defaults to accent) */
43
+ --ogrid-ring: var(--ogrid-accent, #0078d4);
44
+
45
+ /* ── Z-Index Stacking Order ──
46
+ --ogrid-z-resize-handle : 1 column resize drag handle
47
+ --ogrid-z-active-cell : 2 active/editing cell outline
48
+ --ogrid-z-fill-handle : 3 fill handle dot
49
+ --ogrid-z-row-number : 5 row number column
50
+ --ogrid-z-pinned : 6 sticky pinned body cells
51
+ --ogrid-z-selection-cell : 7 selection checkbox column in body
52
+ --ogrid-z-thead : 8 sticky thead row
53
+ --ogrid-z-pinned-header : 10 pinned header cells (sticky both axes)
54
+ --ogrid-z-header-focus : 11 focused header cell
55
+ --ogrid-z-selection-header-pinned: 12 checkbox column in sticky header (sticky both axes)
56
+ --ogrid-z-checkbox : 12 alias — checkbox column in sticky header
57
+ --ogrid-z-loading : 2 loading overlay within table
58
+ --ogrid-z-drop-indicator : 100 column reorder drop indicator
59
+ --ogrid-z-filter-popover : 1000 filter popovers
60
+ --ogrid-z-popover : 1000 alias — filter popovers
61
+ --ogrid-z-fullscreen : 9999 fullscreen grid container
62
+ --ogrid-z-context-menu : 10000 context menu (fixed, above everything) */
63
+ --ogrid-z-resize-handle: 1;
64
+ --ogrid-z-active-cell: 2;
65
+ --ogrid-z-fill-handle: 3;
66
+ --ogrid-z-row-number: 5;
67
+ --ogrid-z-pinned: 6;
68
+ --ogrid-z-selection-cell: 7;
69
+ --ogrid-z-thead: 8;
70
+ --ogrid-z-pinned-header: 10;
71
+ --ogrid-z-header-focus: 11;
72
+ --ogrid-z-selection-header-pinned: 12;
73
+ --ogrid-z-checkbox: 12;
74
+ --ogrid-z-loading: 2;
75
+ --ogrid-z-drop-indicator: 100;
76
+ --ogrid-z-filter-popover: 1000;
77
+ --ogrid-z-popover: 1000;
78
+ --ogrid-z-fullscreen: 9999;
79
+ --ogrid-z-context-menu: 10000;
80
+
81
+ /* Core */
16
82
  --ogrid-bg: #ffffff;
17
83
  --ogrid-fg: rgba(0, 0, 0, 0.87);
18
84
  --ogrid-fg-secondary: rgba(0, 0, 0, 0.6);
19
85
  --ogrid-fg-muted: rgba(0, 0, 0, 0.5);
86
+
87
+ /* Borders */
20
88
  --ogrid-border: rgba(0, 0, 0, 0.12);
21
89
  --ogrid-border-strong: rgba(0, 0, 0, 0.5);
22
90
  --ogrid-border-hover: rgba(0, 0, 0, 0.3);
91
+
92
+ /* Table */
23
93
  --ogrid-header-bg: #f5f5f5;
24
94
  --ogrid-hover-bg: rgba(0, 0, 0, 0.04);
25
95
  --ogrid-selected-row-bg: #e6f0fb;
26
96
  --ogrid-bg-selected-hover: #dae8f8;
27
97
  --ogrid-active-cell-bg: rgba(0, 0, 0, 0.02);
28
98
  --ogrid-range-bg: rgba(33, 115, 70, 0.12);
99
+
100
+ /* Accent & Selection */
29
101
  --ogrid-accent: #0078d4;
30
102
  --ogrid-accent-dark: #005a9e;
31
103
  --ogrid-selection-color: #217346;
32
- --ogrid-primary: #0078d4;
33
- --ogrid-primary-fg: #fff;
34
- --ogrid-primary-hover: #106ebe;
104
+
105
+ /* Primary (buttons, badges) — neutral by default; host theme should override */
106
+ --ogrid-primary: oklch(0.55 0 0);
107
+ --ogrid-primary-fg: oklch(1 0 0);
108
+ --ogrid-primary-hover: oklch(0.45 0 0);
109
+
110
+ /* Surfaces */
35
111
  --ogrid-bg-subtle: #f5f5f5;
36
112
  --ogrid-bg-hover: rgba(0, 0, 0, 0.04);
37
113
  --ogrid-active-bg: rgba(0, 0, 0, 0.06);
38
114
  --ogrid-muted: rgba(0, 0, 0, 0.5);
115
+
116
+ /* Shadows */
39
117
  --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
40
118
  --ogrid-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.08);
41
119
  --ogrid-pinned-shadow: rgba(0, 0, 0, 0.1);
120
+
121
+ /* Loading */
42
122
  --ogrid-loading-overlay: rgba(255, 255, 255, 0.7);
43
- --ogrid-selection: #217346;
44
- --ogrid-bg-range: rgba(33, 115, 70, 0.12);
45
- --ogrid-bg-selected: #e6f0fb;
46
- --ogrid-loading-bg: rgba(255, 255, 255, 0.7);
123
+
124
+ /* Formula errors */
125
+ --ogrid-formula-error-color: #d32f2f;
47
126
  }
48
127
 
49
- /* ─── Auto Dark (system preference) ─── */
128
+ /* ─── Auto Dark (system preference, unless explicitly set to light) ─── */
50
129
  @media (prefers-color-scheme: dark) {
51
- :where(:root:not([data-theme="light"])) {
130
+ :where(:root:not([data-theme="light"]):not(.light)) {
131
+ /* Core */
52
132
  --ogrid-bg: #1e1e1e;
53
133
  --ogrid-fg: rgba(255, 255, 255, 0.87);
54
134
  --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
55
135
  --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
136
+
137
+ /* Borders */
56
138
  --ogrid-border: rgba(255, 255, 255, 0.12);
57
139
  --ogrid-border-strong: rgba(255, 255, 255, 0.5);
58
140
  --ogrid-border-hover: rgba(255, 255, 255, 0.3);
141
+
142
+ /* Table */
59
143
  --ogrid-header-bg: #2c2c2c;
60
144
  --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
61
145
  --ogrid-selected-row-bg: #1a3a5c;
62
146
  --ogrid-bg-selected-hover: #1f3650;
63
147
  --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
64
148
  --ogrid-range-bg: rgba(46, 160, 67, 0.15);
149
+
150
+ /* Accent & Selection */
65
151
  --ogrid-accent: #4da6ff;
66
152
  --ogrid-accent-dark: #3390e0;
67
153
  --ogrid-selection-color: #2ea043;
68
- --ogrid-primary: #4da6ff;
69
- --ogrid-primary-fg: #fff;
70
- --ogrid-primary-hover: #66b3ff;
154
+
155
+ /* Primary — neutral by default; host theme should override */
156
+ --ogrid-primary: oklch(0.7 0 0);
157
+ --ogrid-primary-fg: oklch(0.1 0 0);
158
+ --ogrid-primary-hover: oklch(0.8 0 0);
159
+
160
+ /* Surfaces */
71
161
  --ogrid-bg-subtle: rgba(255, 255, 255, 0.04);
72
162
  --ogrid-bg-hover: rgba(255, 255, 255, 0.08);
73
163
  --ogrid-active-bg: rgba(255, 255, 255, 0.08);
74
164
  --ogrid-muted: rgba(255, 255, 255, 0.5);
165
+
166
+ /* Shadows */
75
167
  --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
76
168
  --ogrid-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.25);
77
169
  --ogrid-pinned-shadow: rgba(0, 0, 0, 0.3);
170
+
171
+ /* Loading */
78
172
  --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
79
- --ogrid-selection: #2ea043;
80
- --ogrid-bg-range: rgba(46, 160, 67, 0.15);
81
- --ogrid-bg-selected: #1a3a5c;
82
- --ogrid-loading-bg: rgba(0, 0, 0, 0.7);
173
+
174
+ /* Formula errors */
175
+ --ogrid-formula-error-color: #ef5350;
83
176
  }
84
177
  }
85
178
 
86
- /* ─── Explicit Dark ─── */
87
- :where([data-theme="dark"]) {
179
+ /* ─── Explicit Dark (data-theme="dark" or .dark — Tailwind/shadcn convention) ─── */
180
+ :where([data-theme="dark"], .dark) {
181
+ /* Core */
88
182
  --ogrid-bg: #1e1e1e;
89
183
  --ogrid-fg: rgba(255, 255, 255, 0.87);
90
184
  --ogrid-fg-secondary: rgba(255, 255, 255, 0.6);
91
185
  --ogrid-fg-muted: rgba(255, 255, 255, 0.5);
186
+
187
+ /* Borders */
92
188
  --ogrid-border: rgba(255, 255, 255, 0.12);
93
189
  --ogrid-border-strong: rgba(255, 255, 255, 0.5);
94
190
  --ogrid-border-hover: rgba(255, 255, 255, 0.3);
191
+
192
+ /* Table */
95
193
  --ogrid-header-bg: #2c2c2c;
96
194
  --ogrid-hover-bg: rgba(255, 255, 255, 0.08);
97
195
  --ogrid-selected-row-bg: #1a3a5c;
98
196
  --ogrid-bg-selected-hover: #1f3650;
99
197
  --ogrid-active-cell-bg: rgba(255, 255, 255, 0.06);
100
198
  --ogrid-range-bg: rgba(46, 160, 67, 0.15);
199
+
200
+ /* Accent & Selection */
101
201
  --ogrid-accent: #4da6ff;
102
202
  --ogrid-accent-dark: #3390e0;
103
203
  --ogrid-selection-color: #2ea043;
204
+
205
+ /* Primary */
104
206
  --ogrid-primary: #4da6ff;
105
207
  --ogrid-primary-fg: #fff;
106
208
  --ogrid-primary-hover: #66b3ff;
209
+
210
+ /* Surfaces */
107
211
  --ogrid-bg-subtle: rgba(255, 255, 255, 0.04);
108
212
  --ogrid-bg-hover: rgba(255, 255, 255, 0.08);
109
213
  --ogrid-active-bg: rgba(255, 255, 255, 0.08);
110
214
  --ogrid-muted: rgba(255, 255, 255, 0.5);
215
+
216
+ /* Shadows */
111
217
  --ogrid-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
112
218
  --ogrid-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.25);
113
219
  --ogrid-pinned-shadow: rgba(0, 0, 0, 0.3);
220
+
221
+ /* Loading */
114
222
  --ogrid-loading-overlay: rgba(0, 0, 0, 0.7);
115
- --ogrid-selection: #2ea043;
116
- --ogrid-bg-range: rgba(46, 160, 67, 0.15);
117
- --ogrid-bg-selected: #1a3a5c;
118
- --ogrid-loading-bg: rgba(0, 0, 0, 0.7);
223
+
224
+ /* Formula errors */
225
+ --ogrid-formula-error-color: #ef5350;
226
+
119
227
  }
228
+ /* @sync-end — do not move; sync-theme.mjs anchors here when splicing. */
@@ -1,5 +1,7 @@
1
1
  export { useOGrid } from './useOGrid';
2
2
  export type { UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, } from './useOGrid';
3
+ export { useHeadlessGrid } from './useHeadlessGrid';
4
+ export type { UseHeadlessGridParams, UseHeadlessGridResult, RowId as HeadlessGridRowId, SortState as HeadlessGridSortState, } from './useHeadlessGrid';
3
5
  export { useDataGridState } from './useDataGridState';
4
6
  export type { UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, } from './useDataGridState';
5
7
  export { useActiveCell } from './useActiveCell';
@@ -14,8 +16,18 @@ export { useRowSelection } from './useRowSelection';
14
16
  export type { UseRowSelectionParams, UseRowSelectionResult } from './useRowSelection';
15
17
  export { useKeyboardNavigation } from './useKeyboardNavigation';
16
18
  export type { UseKeyboardNavigationParams, UseKeyboardNavigationResult } from './useKeyboardNavigation';
19
+ export { useFillHandleInternal } from './useFillHandleInternal';
20
+ export type { UseFillHandleInternalParams, UseFillHandleInternalResult, } from './useFillHandleInternal';
17
21
  export { useFillHandle } from './useFillHandle';
18
22
  export type { UseFillHandleParams, UseFillHandleResult } from './useFillHandle';
23
+ export { useInlineEdit } from './useInlineEdit';
24
+ export type { UseInlineEditParams, UseInlineEditResult, InlineEditEvent, InlineEditorProps, } from './useInlineEdit';
25
+ export { useRangeSelection } from './useRangeSelection';
26
+ export type { UseRangeSelectionParams, UseRangeSelectionResult, CellCoord, } from './useRangeSelection';
27
+ export { useCellClipboard } from './useCellClipboard';
28
+ export type { UseCellClipboardParams, UseCellClipboardResult, } from './useCellClipboard';
29
+ export { useGridFocus } from './useGridFocus';
30
+ export type { UseGridFocusParams, UseGridFocusResult, } from './useGridFocus';
19
31
  export { useUndoRedo } from './useUndoRedo';
20
32
  export type { UseUndoRedoParams, UseUndoRedoResult } from './useUndoRedo';
21
33
  export { useContextMenu } from './useContextMenu';
@@ -0,0 +1,28 @@
1
+ /**
2
+ * useCellClipboard (Vue) — TSV copy/cut/paste for cell ranges.
3
+ *
4
+ * Mirrors the React API. Honors clipboardFormatter (copy) + valueParser (paste).
5
+ */
6
+ import { type ComputedRef, type Ref } from 'vue';
7
+ import type { IColumnDef as ICoreColumnDef, ISelectionRange, ICellValueChangedEvent } from '@alaarab/ogrid-core';
8
+ import type { UseRangeSelectionResult } from './useRangeSelection';
9
+ export interface UseCellClipboardParams<T> {
10
+ rangeSelection: UseRangeSelectionResult;
11
+ rows: T[];
12
+ columns: ICoreColumnDef<T>[];
13
+ onCellEdit: (events: ICellValueChangedEvent<T>[]) => void;
14
+ clipboard?: {
15
+ readText: () => Promise<string>;
16
+ writeText: (text: string) => Promise<void>;
17
+ };
18
+ }
19
+ export interface UseCellClipboardResult {
20
+ copyRange: () => Promise<void>;
21
+ cutRange: () => Promise<void>;
22
+ pasteRange: () => Promise<void>;
23
+ canPaste: ComputedRef<boolean>;
24
+ activeCutRange: Ref<ISelectionRange | null>;
25
+ activeCopyRange: Ref<ISelectionRange | null>;
26
+ clearClipboard: () => void;
27
+ }
28
+ export declare function useCellClipboard<T>(params: UseCellClipboardParams<T>): UseCellClipboardResult;
@@ -1,34 +1,26 @@
1
- import { type Ref, type ShallowRef } from 'vue';
2
- import type { ISelectionRange, IActiveCell, IColumnDef, ICellValueChangedEvent } from '../types';
3
- import type { IVisibleRange } from '@alaarab/ogrid-core';
1
+ /**
2
+ * useFillHandle (Vue) headless drag-to-fill.
3
+ *
4
+ * Mirrors React useFillHandle. Pairs with useRangeSelection. On commit,
5
+ * applies fill via core's applyFillValues and emits cell-change events.
6
+ */
7
+ import { type ComputedRef, type Ref } from 'vue';
8
+ import type { IColumnDef as ICoreColumnDef, ISelectionRange, ICellValueChangedEvent } from '@alaarab/ogrid-core';
9
+ import type { CellCoord, UseRangeSelectionResult } from './useRangeSelection';
4
10
  export interface UseFillHandleParams<T> {
5
- items: Ref<T[]>;
6
- visibleCols: Ref<IColumnDef<T>[]>;
7
- editable: Ref<boolean | undefined>;
8
- onCellValueChanged: Ref<((event: ICellValueChangedEvent<T>) => void) | undefined>;
9
- selectionRange: Ref<ISelectionRange | null> | ShallowRef<ISelectionRange | null>;
10
- setSelectionRange: (range: ISelectionRange | null) => void;
11
- setActiveCell: (cell: IActiveCell | null) => void;
12
- colOffset: Ref<number> | number;
13
- wrapperRef: Ref<HTMLElement | null> | ShallowRef<HTMLElement | null>;
14
- beginBatch?: () => void;
15
- endBatch?: () => void;
16
- visibleRange?: Ref<IVisibleRange | null>;
11
+ rangeSelection: UseRangeSelectionResult;
12
+ rows: T[];
13
+ columns: ICoreColumnDef<T>[];
14
+ onFillCells: (events: ICellValueChangedEvent<T>[]) => void;
17
15
  }
18
16
  export interface UseFillHandleResult {
19
- fillDrag: ShallowRef<{
20
- startRow: number;
21
- startCol: number;
22
- } | null>;
23
- setFillDrag: (value: {
24
- startRow: number;
25
- startCol: number;
26
- } | null) => void;
27
- handleFillHandleMouseDown: (e: PointerEvent) => void;
28
- /** Fill the current selection down from the top row (Ctrl+D). No-op if no selection or editable=false. */
29
- fillDown: () => void;
17
+ fillTarget: Ref<CellCoord | null>;
18
+ isFilling: ComputedRef<boolean>;
19
+ startFill: () => void;
20
+ updateFill: (row: number, col: number) => void;
21
+ commitFill: () => void;
22
+ cancelFill: () => void;
23
+ fillRange: ComputedRef<ISelectionRange | null>;
24
+ isInFillRange: (row: number, col: number) => boolean;
30
25
  }
31
- /**
32
- * Manages Excel-style fill handle drag-to-fill for cell ranges.
33
- */
34
26
  export declare function useFillHandle<T>(params: UseFillHandleParams<T>): UseFillHandleResult;
@@ -0,0 +1,34 @@
1
+ import { type Ref, type ShallowRef } from 'vue';
2
+ import type { ISelectionRange, IActiveCell, IColumnDef, ICellValueChangedEvent } from '../types';
3
+ import type { IVisibleRange } from '@alaarab/ogrid-core';
4
+ export interface UseFillHandleInternalParams<T> {
5
+ items: Ref<T[]>;
6
+ visibleCols: Ref<IColumnDef<T>[]>;
7
+ editable: Ref<boolean | undefined>;
8
+ onCellValueChanged: Ref<((event: ICellValueChangedEvent<T>) => void) | undefined>;
9
+ selectionRange: Ref<ISelectionRange | null> | ShallowRef<ISelectionRange | null>;
10
+ setSelectionRange: (range: ISelectionRange | null) => void;
11
+ setActiveCell: (cell: IActiveCell | null) => void;
12
+ colOffset: Ref<number> | number;
13
+ wrapperRef: Ref<HTMLElement | null> | ShallowRef<HTMLElement | null>;
14
+ beginBatch?: () => void;
15
+ endBatch?: () => void;
16
+ visibleRange?: Ref<IVisibleRange | null>;
17
+ }
18
+ export interface UseFillHandleInternalResult {
19
+ fillDrag: ShallowRef<{
20
+ startRow: number;
21
+ startCol: number;
22
+ } | null>;
23
+ setFillDrag: (value: {
24
+ startRow: number;
25
+ startCol: number;
26
+ } | null) => void;
27
+ handleFillHandleMouseDown: (e: PointerEvent) => void;
28
+ /** Fill the current selection down from the top row (Ctrl+D). No-op if no selection or editable=false. */
29
+ fillDown: () => void;
30
+ }
31
+ /**
32
+ * Manages Excel-style fill handle drag-to-fill for cell ranges.
33
+ */
34
+ export declare function useFillHandleInternal<T>(params: UseFillHandleInternalParams<T>): UseFillHandleInternalResult;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * useGridFocus (Vue) — headless arrow-key cell navigation.
3
+ *
4
+ * Mirrors React API with Vue refs.
5
+ */
6
+ import { type Ref } from 'vue';
7
+ import type { CellCoord, UseRangeSelectionResult } from './useRangeSelection';
8
+ export interface UseGridFocusParams {
9
+ rowCount: number;
10
+ colCount: number;
11
+ pageSize?: number;
12
+ rangeSelection?: UseRangeSelectionResult;
13
+ }
14
+ export interface UseGridFocusResult {
15
+ activeCell: Ref<CellCoord | null>;
16
+ setActiveCell: (cell: CellCoord | null) => void;
17
+ moveUp: (n?: number) => void;
18
+ moveDown: (n?: number) => void;
19
+ moveLeft: (n?: number) => void;
20
+ moveRight: (n?: number) => void;
21
+ moveToRowStart: () => void;
22
+ moveToRowEnd: () => void;
23
+ moveToStart: () => void;
24
+ moveToEnd: () => void;
25
+ getKeyDownHandler: () => (e: {
26
+ key: string;
27
+ shiftKey?: boolean;
28
+ ctrlKey?: boolean;
29
+ metaKey?: boolean;
30
+ preventDefault?: () => void;
31
+ }) => void;
32
+ }
33
+ export declare function useGridFocus(params: UseGridFocusParams): UseGridFocusResult;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * useHeadlessGrid — the v3 headless API for OGrid (Vue).
3
+ *
4
+ * Returns reactive sort/filter/paginate state and rows, without imposing
5
+ * any chrome. Render with your own table primitives.
6
+ *
7
+ * Mirrors the React `useHeadlessGrid` API (see `@alaarab/ogrid-react`)
8
+ * with Vue-idiomatic returns: refs for state, computed for derived
9
+ * values, plain functions for actions. Inputs accept refs, getters, or
10
+ * plain values via `toValue()`.
11
+ *
12
+ * Example:
13
+ *
14
+ * const grid = useHeadlessGrid({
15
+ * columns,
16
+ * data,
17
+ * getRowId: (r) => r.id,
18
+ * initialSort: { field: 'name', direction: 'asc' },
19
+ * });
20
+ *
21
+ * <template>
22
+ * <table>
23
+ * <thead>
24
+ * <tr>
25
+ * <th v-for="col in grid.columns.value" :key="col.columnId"
26
+ * @click="grid.toggleSort(col.columnId)">
27
+ * {{ col.name }} {{ grid.sortIndicator(col.columnId).value }}
28
+ * </th>
29
+ * </tr>
30
+ * </thead>
31
+ * <tbody>
32
+ * <tr v-for="row in grid.rows.value" :key="grid.getRowId(row)">
33
+ * <td v-for="col in grid.columns.value" :key="col.columnId">
34
+ * {{ grid.getCellValue(row, col.columnId) }}
35
+ * </td>
36
+ * </tr>
37
+ * </tbody>
38
+ * </table>
39
+ * </template>
40
+ */
41
+ import { type ComputedRef, type Ref, type MaybeRefOrGetter } from 'vue';
42
+ import type { IColumnDef, IFilters, FilterValue } from '@alaarab/ogrid-core';
43
+ export type RowId = string | number;
44
+ export interface SortState {
45
+ field: string;
46
+ direction: 'asc' | 'desc';
47
+ }
48
+ export interface UseHeadlessGridParams<T> {
49
+ columns: MaybeRefOrGetter<IColumnDef<T>[]>;
50
+ data: MaybeRefOrGetter<T[]>;
51
+ /** Stable row ID extractor — must return the same ID for the same row across renders. */
52
+ getRowId: (row: T) => RowId;
53
+ initialSort?: SortState;
54
+ initialFilters?: IFilters;
55
+ initialPage?: number;
56
+ initialPageSize?: number;
57
+ }
58
+ export interface UseHeadlessGridResult<T> {
59
+ /** Resolved column definitions (reactive). */
60
+ columns: ComputedRef<IColumnDef<T>[]>;
61
+ /** Rows on the current page after sort + filter (reactive). */
62
+ rows: ComputedRef<T[]>;
63
+ /** Post-filter total row count (reactive). */
64
+ totalCount: ComputedRef<number>;
65
+ /** Total number of pages at current page size (reactive). */
66
+ totalPages: ComputedRef<number>;
67
+ /** Filtered + sorted rows across all pages (reactive). */
68
+ allFilteredRows: ComputedRef<T[]>;
69
+ /** Current sort state (mutable ref). */
70
+ sort: Ref<SortState>;
71
+ setSort: (sort: SortState) => void;
72
+ /** Cycle a column's sort: asc → desc → reset. */
73
+ toggleSort: (columnId: string) => void;
74
+ /** Reactive sort indicator: returns ComputedRef<'▲' | '▼' | ''>. */
75
+ sortIndicator: (columnId: string) => ComputedRef<'▲' | '▼' | ''>;
76
+ /** Current filter state (mutable ref). */
77
+ filters: Ref<IFilters>;
78
+ setFilters: (filters: IFilters) => void;
79
+ setFilter: (key: string, value: FilterValue | undefined) => void;
80
+ hasActiveFilters: ComputedRef<boolean>;
81
+ /** Pagination (mutable refs). */
82
+ page: Ref<number>;
83
+ pageSize: Ref<number>;
84
+ setPage: (page: number) => void;
85
+ setPageSize: (size: number) => void;
86
+ /** Stable row identity. */
87
+ getRowId: (row: T) => RowId;
88
+ /** Read a cell value with full column-resolution (valueGetter, key, etc). */
89
+ getCellValue: (row: T, columnId: string) => unknown;
90
+ /** Row selection — minimal Set-based API. */
91
+ selectedRowIds: Ref<Set<RowId>>;
92
+ isRowSelected: (row: T) => boolean;
93
+ toggleRowSelection: (row: T) => void;
94
+ selectAllOnPage: () => void;
95
+ clearSelection: () => void;
96
+ }
97
+ /**
98
+ * Headless grid state + actions composable for Vue.
99
+ *
100
+ * Pure data layer — does not render anything. Use this when you want to
101
+ * compose OGrid's sort/filter/paginate logic with your own table chrome.
102
+ */
103
+ export declare function useHeadlessGrid<T>(params: UseHeadlessGridParams<T>): UseHeadlessGridResult<T>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * useInlineEdit (Vue) — headless inline-edit lifecycle for OGrid cells.
3
+ *
4
+ * Mirrors the React useInlineEdit API with Vue-native reactivity (refs,
5
+ * computeds). Pairs with `useHeadlessGrid` for shadcn-style table chrome.
6
+ */
7
+ import { type Ref } from 'vue';
8
+ import type { IColumnDef as ICoreColumnDef } from '@alaarab/ogrid-core';
9
+ export type RowId = string | number;
10
+ export interface InlineEditEvent<T> {
11
+ item: T;
12
+ columnId: string;
13
+ oldValue: unknown;
14
+ newValue: unknown;
15
+ }
16
+ export interface UseInlineEditParams<T> {
17
+ columns: ICoreColumnDef<T>[];
18
+ getRowId: (row: T) => RowId;
19
+ onCellEdit: (event: InlineEditEvent<T>) => void;
20
+ isCellEditable?: (row: T, columnId: string) => boolean;
21
+ }
22
+ export interface InlineEditorProps {
23
+ value: unknown;
24
+ onChange: (value: unknown) => void;
25
+ onCommit: () => void;
26
+ onCancel: () => void;
27
+ onKeyDown: (e: {
28
+ key: string;
29
+ preventDefault?: () => void;
30
+ stopPropagation?: () => void;
31
+ }) => void;
32
+ onBlur: () => void;
33
+ }
34
+ export interface UseInlineEditResult<T> {
35
+ editingCell: Ref<{
36
+ rowId: RowId;
37
+ columnId: string;
38
+ } | null>;
39
+ pendingValue: Ref<unknown>;
40
+ setPendingValue: (value: unknown) => void;
41
+ startEdit: (row: T, columnId: string) => void;
42
+ commitEdit: () => void;
43
+ cancelEdit: () => void;
44
+ isEditing: (row: T, columnId: string) => boolean;
45
+ canEdit: (row: T, columnId: string) => boolean;
46
+ getEditorProps: (row: T, columnId: string) => InlineEditorProps;
47
+ }
48
+ export declare function useInlineEdit<T>(params: UseInlineEditParams<T>): UseInlineEditResult<T>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * useRangeSelection (Vue) — headless cell-range selection for OGrid.
3
+ *
4
+ * Mirrors the React API with Vue refs/computeds. Anchor + focus model.
5
+ * Foundation for useFillHandle and useCellClipboard.
6
+ */
7
+ import { type ComputedRef, type Ref } from 'vue';
8
+ import type { ISelectionRange } from '@alaarab/ogrid-core';
9
+ export interface CellCoord {
10
+ row: number;
11
+ col: number;
12
+ }
13
+ export interface UseRangeSelectionParams {
14
+ rowCount: number;
15
+ colCount: number;
16
+ }
17
+ export interface UseRangeSelectionResult {
18
+ range: ComputedRef<ISelectionRange | null>;
19
+ anchor: Ref<CellCoord | null>;
20
+ focus: Ref<CellCoord | null>;
21
+ startRange: (row: number, col: number) => void;
22
+ extendRange: (row: number, col: number) => void;
23
+ setRange: (range: ISelectionRange | null) => void;
24
+ clearRange: () => void;
25
+ selectAll: () => void;
26
+ isInRange: (row: number, col: number) => boolean;
27
+ getRangeRows: () => number[];
28
+ getRangeCells: () => CellCoord[];
29
+ }
30
+ export declare function useRangeSelection(params: UseRangeSelectionParams): UseRangeSelectionResult;