@alaarab/ogrid-vue 2.0.7 → 2.0.8

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.
@@ -0,0 +1,164 @@
1
+ /**
2
+ * MarchingAntsOverlay — Renders range overlays on top of the grid:
3
+ *
4
+ * 1. **Selection range**: solid green border around the current selection
5
+ * 2. **Copy/Cut range**: animated dashed border (marching ants) like Excel
6
+ *
7
+ * Uses SVG rects positioned via cell data-attribute measurements.
8
+ */
9
+ import { defineComponent, ref, computed, watch, onMounted, onUnmounted, h } from 'vue';
10
+ import { Z_INDEX } from '@alaarab/ogrid-core';
11
+ // Inject the @keyframes rule once into <head> (deduplicates across multiple OGrid instances)
12
+ function ensureKeyframes() {
13
+ if (typeof document === 'undefined')
14
+ return;
15
+ if (document.getElementById('ogrid-marching-ants-keyframes'))
16
+ return;
17
+ const style = document.createElement('style');
18
+ style.id = 'ogrid-marching-ants-keyframes';
19
+ style.textContent =
20
+ '@keyframes ogrid-marching-ants{to{stroke-dashoffset:-8}}';
21
+ document.head.appendChild(style);
22
+ }
23
+ /** Measure the bounding rect of a range within a container. */
24
+ function measureRange(container, range, colOffset) {
25
+ const startGlobalCol = range.startCol + colOffset;
26
+ const endGlobalCol = range.endCol + colOffset;
27
+ const topLeft = container.querySelector(`[data-row-index="${range.startRow}"][data-col-index="${startGlobalCol}"]`);
28
+ const bottomRight = container.querySelector(`[data-row-index="${range.endRow}"][data-col-index="${endGlobalCol}"]`);
29
+ if (!topLeft || !bottomRight)
30
+ return null;
31
+ const cRect = container.getBoundingClientRect();
32
+ const tlRect = topLeft.getBoundingClientRect();
33
+ const brRect = bottomRight.getBoundingClientRect();
34
+ return {
35
+ top: tlRect.top - cRect.top,
36
+ left: tlRect.left - cRect.left,
37
+ width: brRect.right - tlRect.left,
38
+ height: brRect.bottom - tlRect.top,
39
+ };
40
+ }
41
+ export const MarchingAntsOverlay = defineComponent({
42
+ name: 'MarchingAntsOverlay',
43
+ props: {
44
+ /** Ref to the positioned container that wraps the table (must have position: relative) */
45
+ containerRef: { type: Object, required: true },
46
+ /** Current selection range — solid green border */
47
+ selectionRange: { type: Object, default: null },
48
+ /** Copy range — animated dashed border */
49
+ copyRange: { type: Object, default: null },
50
+ /** Cut range — animated dashed border */
51
+ cutRange: { type: Object, default: null },
52
+ /** Column offset — 1 when checkbox column is present, else 0 */
53
+ colOffset: { type: Number, required: true },
54
+ },
55
+ setup(props) {
56
+ const selRect = ref(null);
57
+ const clipRect = ref(null);
58
+ let rafId = 0;
59
+ let ro;
60
+ const clipRange = computed(() => props.copyRange ?? props.cutRange);
61
+ const measureAll = () => {
62
+ const container = props.containerRef.value;
63
+ if (!container) {
64
+ selRect.value = null;
65
+ clipRect.value = null;
66
+ return;
67
+ }
68
+ selRect.value = props.selectionRange ? measureRange(container, props.selectionRange, props.colOffset) : null;
69
+ clipRect.value = clipRange.value ? measureRange(container, clipRange.value, props.colOffset) : null;
70
+ };
71
+ // Inject keyframes on mount
72
+ onMounted(() => {
73
+ ensureKeyframes();
74
+ });
75
+ // Measure when any range changes; re-measure on resize
76
+ watch([() => props.selectionRange, clipRange, () => props.containerRef.value], () => {
77
+ if (!props.selectionRange && !clipRange.value) {
78
+ selRect.value = null;
79
+ clipRect.value = null;
80
+ return;
81
+ }
82
+ // Delay one frame so cells are rendered
83
+ rafId = requestAnimationFrame(measureAll);
84
+ const container = props.containerRef.value;
85
+ if (container) {
86
+ ro?.disconnect();
87
+ ro = new ResizeObserver(measureAll);
88
+ ro.observe(container);
89
+ }
90
+ }, { immediate: true });
91
+ onUnmounted(() => {
92
+ cancelAnimationFrame(rafId);
93
+ ro?.disconnect();
94
+ });
95
+ const clipRangeMatchesSel = computed(() => {
96
+ const sel = props.selectionRange;
97
+ const clip = clipRange.value;
98
+ return (sel != null &&
99
+ clip != null &&
100
+ sel.startRow === clip.startRow &&
101
+ sel.startCol === clip.startCol &&
102
+ sel.endRow === clip.endRow &&
103
+ sel.endCol === clip.endCol);
104
+ });
105
+ return () => {
106
+ if (!selRect.value && !clipRect.value)
107
+ return null;
108
+ return h('div', { style: { position: 'relative' } }, [
109
+ // Selection range: solid green border (hidden when clipboard range overlaps)
110
+ selRect.value && !clipRangeMatchesSel.value ? h('svg', {
111
+ style: {
112
+ position: 'absolute',
113
+ top: `${selRect.value.top}px`,
114
+ left: `${selRect.value.left}px`,
115
+ width: `${selRect.value.width}px`,
116
+ height: `${selRect.value.height}px`,
117
+ pointerEvents: 'none',
118
+ zIndex: Z_INDEX.SELECTION_OVERLAY,
119
+ overflow: 'visible',
120
+ },
121
+ 'aria-hidden': 'true',
122
+ }, [
123
+ h('rect', {
124
+ x: 1,
125
+ y: 1,
126
+ width: Math.max(0, selRect.value.width - 2),
127
+ height: Math.max(0, selRect.value.height - 2),
128
+ fill: 'none',
129
+ stroke: 'var(--ogrid-selection, #217346)',
130
+ 'stroke-width': 2,
131
+ }),
132
+ ]) : null,
133
+ // Copy/Cut range: animated marching ants
134
+ clipRect.value ? h('svg', {
135
+ style: {
136
+ position: 'absolute',
137
+ top: `${clipRect.value.top}px`,
138
+ left: `${clipRect.value.left}px`,
139
+ width: `${clipRect.value.width}px`,
140
+ height: `${clipRect.value.height}px`,
141
+ pointerEvents: 'none',
142
+ zIndex: Z_INDEX.CLIPBOARD_OVERLAY,
143
+ overflow: 'visible',
144
+ },
145
+ 'aria-hidden': 'true',
146
+ }, [
147
+ h('rect', {
148
+ x: 1,
149
+ y: 1,
150
+ width: Math.max(0, clipRect.value.width - 2),
151
+ height: Math.max(0, clipRect.value.height - 2),
152
+ fill: 'none',
153
+ stroke: 'var(--ogrid-selection, #217346)',
154
+ 'stroke-width': 2,
155
+ 'stroke-dasharray': '4 4',
156
+ style: {
157
+ animation: 'ogrid-marching-ants 0.5s linear infinite',
158
+ },
159
+ }),
160
+ ]) : null,
161
+ ]);
162
+ };
163
+ },
164
+ });
@@ -0,0 +1,49 @@
1
+ /**
2
+ * StatusBar component — Shows row counts and aggregations at the bottom of the data grid.
3
+ * Displays filteredCount, totalCount, and optional aggregations (sum, avg, min, max, count).
4
+ */
5
+ import { defineComponent, h } from 'vue';
6
+ import { getStatusBarParts } from '@alaarab/ogrid-core';
7
+ export const StatusBar = defineComponent({
8
+ name: 'StatusBar',
9
+ props: {
10
+ totalCount: { type: Number, required: true },
11
+ filteredCount: { type: Number, default: undefined },
12
+ selectedCount: { type: Number, default: undefined },
13
+ selectedCellCount: { type: Number, default: undefined },
14
+ aggregation: { type: Object, default: undefined },
15
+ suppressRowCount: { type: Boolean, default: false },
16
+ },
17
+ setup(props) {
18
+ return () => {
19
+ const parts = getStatusBarParts(props);
20
+ return h('div', {
21
+ role: 'status',
22
+ 'aria-live': 'polite',
23
+ style: {
24
+ marginTop: 'auto',
25
+ padding: '6px 12px',
26
+ borderTop: '1px solid rgba(0,0,0,0.12)',
27
+ backgroundColor: 'rgba(0,0,0,0.04)',
28
+ display: 'flex',
29
+ alignItems: 'center',
30
+ gap: '16px',
31
+ fontSize: '0.875rem',
32
+ },
33
+ }, parts.map((p, i) => h('span', {
34
+ key: p.key,
35
+ style: {
36
+ display: 'inline-flex',
37
+ alignItems: 'center',
38
+ gap: '4px',
39
+ ...(i < parts.length - 1
40
+ ? { marginRight: '16px', borderRight: '1px solid rgba(0,0,0,0.12)', paddingRight: '16px' }
41
+ : {}),
42
+ },
43
+ }, [
44
+ h('span', { style: { color: 'rgba(0,0,0,0.6)' } }, p.label),
45
+ h('span', { style: { fontWeight: '600' } }, p.value.toLocaleString()),
46
+ ])));
47
+ };
48
+ },
49
+ });
package/dist/esm/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  // Re-export core types + utils
2
2
  export * from '@alaarab/ogrid-core';
3
3
  export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
4
+ // Shared components
5
+ export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
6
+ export { StatusBar } from './components/StatusBar';
4
7
  // Composables
5
8
  export { useOGrid, useDataGridState, useActiveCell, useCellEditing, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useFillHandle, useUndoRedo, useContextMenu, useColumnResize, useColumnReorder, useVirtualScroll, useFilterOptions, useDebounce, useDebouncedCallback, useTableLayout, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useRichSelectState, useSideBarState, useColumnPinning, useColumnHeaderMenuState, useDataGridTableSetup, } from './composables';
6
9
  // View model utilities (for UI packages)
@@ -1,182 +1,10 @@
1
- import { getCellValue, isInSelectionRange } from '@alaarab/ogrid-core';
2
- export function getHeaderFilterConfig(col, input) {
3
- const filterable = col.filterable && typeof col.filterable === 'object' ? col.filterable : null;
4
- const filterType = (filterable?.type ?? 'none');
5
- const filterField = filterable?.filterField ?? col.columnId;
6
- const sortable = col.sortable !== false;
7
- const filterValue = input.filters[filterField];
8
- const base = {
9
- columnKey: col.columnId,
10
- columnName: col.name,
11
- filterType,
12
- isSorted: input.sortBy === col.columnId,
13
- isSortedDescending: input.sortBy === col.columnId && input.sortDirection === 'desc',
14
- onSort: sortable ? () => input.onColumnSort(col.columnId) : undefined,
15
- };
16
- if (filterType === 'text') {
17
- return {
18
- ...base,
19
- textValue: filterValue?.type === 'text' ? filterValue.value : '',
20
- onTextChange: (v) => input.onFilterChange(filterField, v.trim() ? { type: 'text', value: v } : undefined),
21
- };
22
- }
23
- if (filterType === 'people') {
24
- return {
25
- ...base,
26
- selectedUser: filterValue?.type === 'people' ? filterValue.value : undefined,
27
- onUserChange: (u) => input.onFilterChange(filterField, u ? { type: 'people', value: u } : undefined),
28
- peopleSearch: input.peopleSearch,
29
- };
30
- }
31
- if (filterType === 'multiSelect') {
32
- return {
33
- ...base,
34
- options: input.filterOptions[filterField] ?? [],
35
- isLoadingOptions: input.loadingFilterOptions[filterField] ?? false,
36
- selectedValues: filterValue?.type === 'multiSelect' ? filterValue.value : [],
37
- onFilterChange: (values) => input.onFilterChange(filterField, values.length ? { type: 'multiSelect', value: values } : undefined),
38
- };
39
- }
40
- if (filterType === 'date') {
41
- return {
42
- ...base,
43
- dateValue: filterValue?.type === 'date' ? filterValue.value : undefined,
44
- onDateChange: (v) => input.onFilterChange(filterField, v ? { type: 'date', value: v } : undefined),
45
- };
46
- }
47
- return base;
48
- }
49
- export function getCellRenderDescriptor(item, col, rowIndex, colIdx, input) {
50
- const rowId = input.getRowId(item);
51
- const globalColIndex = colIdx + input.colOffset;
52
- const colEditable = col.editable === true ||
53
- (typeof col.editable === 'function' && col.editable(item));
54
- const canEditInline = input.editable !== false &&
55
- !!colEditable &&
56
- !!input.onCellValueChanged &&
57
- typeof col.cellEditor !== 'function';
58
- const canEditPopup = input.editable !== false &&
59
- !!colEditable &&
60
- !!input.onCellValueChanged &&
61
- typeof col.cellEditor === 'function' &&
62
- col.cellEditorPopup !== false;
63
- const canEditAny = canEditInline || canEditPopup;
64
- const isEditing = input.editingCell?.rowId === rowId &&
65
- input.editingCell?.columnId === col.columnId;
66
- const isActive = input.activeCell?.rowIndex === rowIndex &&
67
- input.activeCell?.columnIndex === globalColIndex;
68
- const isInRange = input.selectionRange != null &&
69
- isInSelectionRange(input.selectionRange, rowIndex, colIdx);
70
- const isInCutRange = input.cutRange != null &&
71
- isInSelectionRange(input.cutRange, rowIndex, colIdx);
72
- const isInCopyRange = input.copyRange != null &&
73
- isInSelectionRange(input.copyRange, rowIndex, colIdx);
74
- const isSelectionEndCell = !input.isDragging &&
75
- input.copyRange == null &&
76
- input.cutRange == null &&
77
- input.selectionRange != null &&
78
- rowIndex === input.selectionRange.endRow &&
79
- colIdx === input.selectionRange.endCol;
80
- const isPinned = col.pinned != null;
81
- const pinnedSide = col.pinned ?? undefined;
82
- let mode = 'display';
83
- let editorType;
84
- let value;
85
- if (isEditing && canEditInline) {
86
- mode = 'editing-inline';
87
- if (col.cellEditor === 'text' ||
88
- col.cellEditor === 'select' ||
89
- col.cellEditor === 'checkbox' ||
90
- col.cellEditor === 'richSelect' ||
91
- col.cellEditor === 'date') {
92
- editorType = col.cellEditor;
93
- }
94
- else if (col.type === 'date') {
95
- editorType = 'date';
96
- }
97
- else if (col.type === 'boolean') {
98
- editorType = 'checkbox';
99
- }
100
- else {
101
- editorType = 'text';
102
- }
103
- value = getCellValue(item, col);
104
- }
105
- else if (isEditing && canEditPopup && typeof col.cellEditor === 'function') {
106
- mode = 'editing-popover';
107
- value = getCellValue(item, col);
108
- }
109
- else {
110
- value = getCellValue(item, col);
111
- }
112
- return {
113
- mode,
114
- editorType,
115
- value,
116
- isActive,
117
- isInRange,
118
- isInCutRange,
119
- isInCopyRange,
120
- isSelectionEndCell,
121
- canEditAny,
122
- isPinned,
123
- pinnedSide,
124
- globalColIndex,
125
- rowId,
126
- rowIndex,
127
- displayValue: value,
128
- };
129
- }
130
- // --- Cell rendering helpers ---
131
- export function resolveCellDisplayContent(col, item, displayValue) {
132
- if (col.renderCell)
133
- return col.renderCell(item);
134
- if (col.valueFormatter)
135
- return col.valueFormatter(displayValue, item);
136
- if (displayValue == null)
137
- return null;
138
- if (col.type === 'date') {
139
- const d = new Date(String(displayValue));
140
- if (!Number.isNaN(d.getTime()))
141
- return d.toLocaleDateString();
142
- }
143
- if (col.type === 'boolean') {
144
- return displayValue ? 'True' : 'False';
145
- }
146
- return String(displayValue);
147
- }
148
- export function resolveCellStyle(col, item) {
149
- if (!col.cellStyle)
150
- return undefined;
151
- return typeof col.cellStyle === 'function' ? col.cellStyle(item) : col.cellStyle;
152
- }
153
- export function buildInlineEditorProps(item, col, descriptor, callbacks) {
154
- return {
155
- value: descriptor.value,
156
- item,
157
- column: col,
158
- rowIndex: descriptor.rowIndex,
159
- editorType: (descriptor.editorType ?? 'text'),
160
- onCommit: (newValue) => callbacks.commitCellEdit(item, col.columnId, descriptor.value, newValue, descriptor.rowIndex, descriptor.globalColIndex),
161
- onCancel: () => callbacks.setEditingCell(null),
162
- };
163
- }
164
- export function buildPopoverEditorProps(item, col, descriptor, pendingEditorValue, callbacks) {
165
- const oldValue = descriptor.value;
166
- const displayValue = pendingEditorValue !== undefined ? pendingEditorValue : oldValue;
167
- return {
168
- value: displayValue,
169
- onValueChange: callbacks.setPendingEditorValue,
170
- onCommit: () => {
171
- const newValue = pendingEditorValue !== undefined ? pendingEditorValue : oldValue;
172
- callbacks.commitCellEdit(item, col.columnId, oldValue, newValue, descriptor.rowIndex, descriptor.globalColIndex);
173
- },
174
- onCancel: callbacks.cancelPopoverEdit,
175
- item,
176
- column: col,
177
- cellEditorParams: col.cellEditorParams,
178
- };
179
- }
1
+ /**
2
+ * View model helpers for Vue DataGridTable.
3
+ * Pure functions live in @alaarab/ogrid-core. This file re-exports them
4
+ * and adds Vue-specific helpers (getCellInteractionProps uses Vue event naming).
5
+ */
6
+ // Re-export everything from core's dataGridViewModel
7
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from '@alaarab/ogrid-core';
180
8
  export function getCellInteractionProps(descriptor, columnId, handlers) {
181
9
  const base = {
182
10
  'data-row-index': descriptor.rowIndex,
@@ -0,0 +1,69 @@
1
+ /**
2
+ * MarchingAntsOverlay — Renders range overlays on top of the grid:
3
+ *
4
+ * 1. **Selection range**: solid green border around the current selection
5
+ * 2. **Copy/Cut range**: animated dashed border (marching ants) like Excel
6
+ *
7
+ * Uses SVG rects positioned via cell data-attribute measurements.
8
+ */
9
+ import { type PropType, type Ref } from 'vue';
10
+ import { type ISelectionRange } from '@alaarab/ogrid-core';
11
+ export declare const MarchingAntsOverlay: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
12
+ /** Ref to the positioned container that wraps the table (must have position: relative) */
13
+ containerRef: {
14
+ type: PropType<Ref<HTMLElement | null>>;
15
+ required: true;
16
+ };
17
+ /** Current selection range — solid green border */
18
+ selectionRange: {
19
+ type: PropType<ISelectionRange | null>;
20
+ default: null;
21
+ };
22
+ /** Copy range — animated dashed border */
23
+ copyRange: {
24
+ type: PropType<ISelectionRange | null>;
25
+ default: null;
26
+ };
27
+ /** Cut range — animated dashed border */
28
+ cutRange: {
29
+ type: PropType<ISelectionRange | null>;
30
+ default: null;
31
+ };
32
+ /** Column offset — 1 when checkbox column is present, else 0 */
33
+ colOffset: {
34
+ type: NumberConstructor;
35
+ required: true;
36
+ };
37
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
38
+ [key: string]: any;
39
+ }> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
40
+ /** Ref to the positioned container that wraps the table (must have position: relative) */
41
+ containerRef: {
42
+ type: PropType<Ref<HTMLElement | null>>;
43
+ required: true;
44
+ };
45
+ /** Current selection range — solid green border */
46
+ selectionRange: {
47
+ type: PropType<ISelectionRange | null>;
48
+ default: null;
49
+ };
50
+ /** Copy range — animated dashed border */
51
+ copyRange: {
52
+ type: PropType<ISelectionRange | null>;
53
+ default: null;
54
+ };
55
+ /** Cut range — animated dashed border */
56
+ cutRange: {
57
+ type: PropType<ISelectionRange | null>;
58
+ default: null;
59
+ };
60
+ /** Column offset — 1 when checkbox column is present, else 0 */
61
+ colOffset: {
62
+ type: NumberConstructor;
63
+ required: true;
64
+ };
65
+ }>> & Readonly<{}>, {
66
+ selectionRange: ISelectionRange | null;
67
+ copyRange: ISelectionRange | null;
68
+ cutRange: ISelectionRange | null;
69
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * StatusBar component — Shows row counts and aggregations at the bottom of the data grid.
3
+ * Displays filteredCount, totalCount, and optional aggregations (sum, avg, min, max, count).
4
+ */
5
+ import { type PropType } from 'vue';
6
+ export interface StatusBarProps {
7
+ totalCount: number;
8
+ filteredCount?: number;
9
+ selectedCount?: number;
10
+ selectedCellCount?: number;
11
+ aggregation?: {
12
+ sum: number;
13
+ avg: number;
14
+ min: number;
15
+ max: number;
16
+ count: number;
17
+ } | null;
18
+ suppressRowCount?: boolean;
19
+ }
20
+ export declare const StatusBar: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
21
+ totalCount: {
22
+ type: NumberConstructor;
23
+ required: true;
24
+ };
25
+ filteredCount: {
26
+ type: NumberConstructor;
27
+ default: undefined;
28
+ };
29
+ selectedCount: {
30
+ type: NumberConstructor;
31
+ default: undefined;
32
+ };
33
+ selectedCellCount: {
34
+ type: NumberConstructor;
35
+ default: undefined;
36
+ };
37
+ aggregation: {
38
+ type: PropType<StatusBarProps["aggregation"]>;
39
+ default: undefined;
40
+ };
41
+ suppressRowCount: {
42
+ type: BooleanConstructor;
43
+ default: boolean;
44
+ };
45
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
46
+ [key: string]: any;
47
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
48
+ totalCount: {
49
+ type: NumberConstructor;
50
+ required: true;
51
+ };
52
+ filteredCount: {
53
+ type: NumberConstructor;
54
+ default: undefined;
55
+ };
56
+ selectedCount: {
57
+ type: NumberConstructor;
58
+ default: undefined;
59
+ };
60
+ selectedCellCount: {
61
+ type: NumberConstructor;
62
+ default: undefined;
63
+ };
64
+ aggregation: {
65
+ type: PropType<StatusBarProps["aggregation"]>;
66
+ default: undefined;
67
+ };
68
+ suppressRowCount: {
69
+ type: BooleanConstructor;
70
+ default: boolean;
71
+ };
72
+ }>> & Readonly<{}>, {
73
+ aggregation: {
74
+ sum: number;
75
+ avg: number;
76
+ min: number;
77
+ max: number;
78
+ count: number;
79
+ } | null | undefined;
80
+ filteredCount: number;
81
+ selectedCount: number;
82
+ selectedCellCount: number;
83
+ suppressRowCount: boolean;
84
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -2,6 +2,8 @@ export * from '@alaarab/ogrid-core';
2
2
  export type { IColumnDef, ICellEditorProps, IOGridProps, IOGridClientProps, IOGridServerProps, IOGridDataGridProps, } from './types';
3
3
  export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, CellEditorParams, IValueParserParams, IDateFilterValue, HeaderCell, HeaderRow, RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IVirtualScrollConfig, } from './types';
4
4
  export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
5
+ export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
6
+ export { StatusBar, type StatusBarProps } from './components/StatusBar';
5
7
  export { useOGrid, useDataGridState, useActiveCell, useCellEditing, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useFillHandle, useUndoRedo, useContextMenu, useColumnResize, useColumnReorder, useVirtualScroll, useFilterOptions, useDebounce, useDebouncedCallback, useTableLayout, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useRichSelectState, useSideBarState, useColumnPinning, useColumnHeaderMenuState, useDataGridTableSetup, } from './composables';
6
8
  export type { UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, UseActiveCellResult, EditingCell, UseCellEditingParams, UseCellEditingResult, UseCellSelectionParams, UseCellSelectionResult, UseClipboardParams, UseClipboardResult, UseRowSelectionParams, UseRowSelectionResult, UseKeyboardNavigationParams, UseKeyboardNavigationResult, UseFillHandleParams, UseFillHandleResult, UseUndoRedoParams, UseUndoRedoResult, ContextMenuPosition, UseContextMenuResult, UseColumnResizeParams, UseColumnResizeResult, UseColumnReorderParams, UseColumnReorderResult, UseVirtualScrollParams, UseVirtualScrollResult, UseFilterOptionsResult, UseTableLayoutParams, UseTableLayoutResult, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, InlineCellEditorType, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, DebouncedFn, UseColumnPinningParams, UseColumnPinningResult, UseColumnHeaderMenuStateParams, UseColumnHeaderMenuStateResult, UseDataGridTableSetupParams, UseDataGridTableSetupResult, } from './composables';
7
9
  export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from './utils';
@@ -1,112 +1,12 @@
1
1
  /**
2
2
  * View model helpers for Vue DataGridTable.
3
- * These are Vue equivalents of the React-specific utils in @alaarab/ogrid-react.
3
+ * Pure functions live in @alaarab/ogrid-core. This file re-exports them
4
+ * and adds Vue-specific helpers (getCellInteractionProps uses Vue event naming).
4
5
  */
5
- import type { ColumnFilterType, IDateFilterValue, ICellEditorProps } from '../types/columnTypes';
6
- import type { IColumnDef } from '../types/columnTypes';
7
- import type { RowId, UserLike, IFilters, FilterValue, ICellValueChangedEvent } from '../types';
8
- export interface HeaderFilterConfigInput {
9
- sortBy?: string;
10
- sortDirection: 'asc' | 'desc';
11
- onColumnSort: (columnKey: string) => void;
12
- filters: IFilters;
13
- onFilterChange: (key: string, value: FilterValue | undefined) => void;
14
- filterOptions: Record<string, string[]>;
15
- loadingFilterOptions: Record<string, boolean>;
16
- peopleSearch?: (query: string) => Promise<UserLike[]>;
17
- }
18
- export interface HeaderFilterConfig {
19
- columnKey: string;
20
- columnName: string;
21
- filterType: ColumnFilterType;
22
- isSorted?: boolean;
23
- isSortedDescending?: boolean;
24
- onSort?: () => void;
25
- selectedValues?: string[];
26
- onFilterChange?: (values: string[]) => void;
27
- options?: string[];
28
- isLoadingOptions?: boolean;
29
- textValue?: string;
30
- onTextChange?: (value: string) => void;
31
- selectedUser?: UserLike;
32
- onUserChange?: (user: UserLike | undefined) => void;
33
- peopleSearch?: (query: string) => Promise<UserLike[]>;
34
- dateValue?: IDateFilterValue;
35
- onDateChange?: (value: IDateFilterValue | undefined) => void;
36
- }
37
- export declare function getHeaderFilterConfig<T>(col: IColumnDef<T>, input: HeaderFilterConfigInput): HeaderFilterConfig;
38
- export type CellRenderMode = 'editing-inline' | 'editing-popover' | 'display';
39
- export interface CellRenderDescriptorInput<T> {
40
- editingCell: {
41
- rowId: RowId;
42
- columnId: string;
43
- } | null;
44
- activeCell: {
45
- rowIndex: number;
46
- columnIndex: number;
47
- } | null;
48
- selectionRange: {
49
- startRow: number;
50
- startCol: number;
51
- endRow: number;
52
- endCol: number;
53
- } | null;
54
- cutRange: {
55
- startRow: number;
56
- startCol: number;
57
- endRow: number;
58
- endCol: number;
59
- } | null;
60
- copyRange: {
61
- startRow: number;
62
- startCol: number;
63
- endRow: number;
64
- endCol: number;
65
- } | null;
66
- colOffset: number;
67
- itemsLength: number;
68
- getRowId: (item: T) => RowId;
69
- editable?: boolean;
70
- onCellValueChanged?: (event: ICellValueChangedEvent<T>) => void;
71
- isDragging?: boolean;
72
- }
73
- export interface CellRenderDescriptor {
74
- mode: CellRenderMode;
75
- editorType?: 'text' | 'select' | 'checkbox' | 'richSelect' | 'date';
76
- value?: unknown;
77
- isActive: boolean;
78
- isInRange: boolean;
79
- isInCutRange: boolean;
80
- isInCopyRange: boolean;
81
- isSelectionEndCell: boolean;
82
- canEditAny: boolean;
83
- isPinned: boolean;
84
- pinnedSide?: 'left' | 'right';
85
- globalColIndex: number;
86
- rowId: RowId;
87
- rowIndex: number;
88
- displayValue?: unknown;
89
- }
90
- export declare function getCellRenderDescriptor<T>(item: T, col: IColumnDef<T>, rowIndex: number, colIdx: number, input: CellRenderDescriptorInput<T>): CellRenderDescriptor;
91
- export declare function resolveCellDisplayContent<T>(col: IColumnDef<T>, item: T, displayValue: unknown): unknown;
92
- export declare function resolveCellStyle<T>(col: IColumnDef<T>, item: T): Record<string, string> | undefined;
93
- export declare function buildInlineEditorProps<T>(item: T, col: IColumnDef<T>, descriptor: CellRenderDescriptor, callbacks: {
94
- commitCellEdit: (item: T, columnId: string, oldValue: unknown, newValue: unknown, rowIndex: number, globalColIndex: number) => void;
95
- setEditingCell: (cell: null) => void;
96
- }): {
97
- value: unknown;
98
- item: T;
99
- column: IColumnDef<T>;
100
- rowIndex: number;
101
- editorType: "text" | "select" | "checkbox" | "richSelect" | "date";
102
- onCommit: (newValue: unknown) => void;
103
- onCancel: () => void;
104
- };
105
- export declare function buildPopoverEditorProps<T>(item: T, col: IColumnDef<T>, descriptor: CellRenderDescriptor, pendingEditorValue: unknown, callbacks: {
106
- setPendingEditorValue: (value: unknown) => void;
107
- commitCellEdit: (item: T, columnId: string, oldValue: unknown, newValue: unknown, rowIndex: number, globalColIndex: number) => void;
108
- cancelPopoverEdit: () => void;
109
- }): ICellEditorProps<T>;
6
+ import type { RowId } from '@alaarab/ogrid-core';
7
+ export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, } from '@alaarab/ogrid-core';
8
+ export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, } from '@alaarab/ogrid-core';
9
+ import type { CellRenderDescriptor } from '@alaarab/ogrid-core';
110
10
  export interface CellInteractionHandlers {
111
11
  handleCellMouseDown: (e: MouseEvent, rowIndex: number, colIndex: number) => void;
112
12
  setActiveCell: (cell: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-vue",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "OGrid Vue – Vue 3 composables, headless components, and utilities for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -16,11 +16,24 @@
16
16
  "build": "rimraf dist && tsc -p tsconfig.build.json",
17
17
  "test": "jest --passWithNoTests"
18
18
  },
19
- "keywords": ["ogrid", "vue", "datatable", "typescript", "grid", "composables"],
19
+ "keywords": [
20
+ "ogrid",
21
+ "vue",
22
+ "datatable",
23
+ "typescript",
24
+ "grid",
25
+ "composables"
26
+ ],
20
27
  "author": "Ala Arab",
21
28
  "license": "MIT",
22
- "files": ["dist", "README.md", "LICENSE"],
23
- "engines": { "node": ">=18" },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
24
37
  "dependencies": {
25
38
  "@alaarab/ogrid-core": "2.0.7"
26
39
  },
@@ -35,5 +48,7 @@
35
48
  "typescript": "^5.9.3"
36
49
  },
37
50
  "sideEffects": false,
38
- "publishConfig": { "access": "public" }
51
+ "publishConfig": {
52
+ "access": "public"
53
+ }
39
54
  }