gp-grid-vue 0.1.6 → 0.2.3

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.
package/dist/index.js CHANGED
@@ -1,1819 +1 @@
1
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, h, normalizeClass, normalizeStyle, onMounted, onUnmounted, openBlock, reactive, ref, renderList, resolveDynamicComponent, shallowRef, toDisplayString, unref, vModelText, watch, withDirectives } from "vue";
2
- import { GridCore, buildCellClasses, buildCellClasses as buildCellClasses$1, calculateColumnPositions, calculateColumnPositions as calculateColumnPositions$1, createClientDataSource, createClientDataSource as createClientDataSource$1, createDataSourceFromArray, createDataSourceFromArray as createDataSourceFromArray$1, createMutableClientDataSource, createServerDataSource, findColumnAtX, getTotalWidth, getTotalWidth as getTotalWidth$1, gridStyles, injectStyles, injectStyles as injectStyles$1, isCellActive, isCellActive as isCellActive$1, isCellEditing, isCellEditing as isCellEditing$1, isCellInFillPreview, isCellInFillPreview as isCellInFillPreview$1, isCellSelected, isCellSelected as isCellSelected$1, isRowVisible } from "gp-grid-core";
3
-
4
- //#region src/gridState/useGridState.ts
5
- function createInitialState() {
6
- return {
7
- slots: /* @__PURE__ */ new Map(),
8
- activeCell: null,
9
- selectionRange: null,
10
- editingCell: null,
11
- contentWidth: 0,
12
- contentHeight: 0,
13
- headers: /* @__PURE__ */ new Map(),
14
- filterPopup: null,
15
- isLoading: false,
16
- error: null,
17
- totalRows: 0,
18
- visibleRowRange: null
19
- };
20
- }
21
- /**
22
- * Apply a single instruction to mutable state
23
- */
24
- function applyInstruction(instruction, state) {
25
- switch (instruction.type) {
26
- case "CREATE_SLOT":
27
- state.slots.set(instruction.slotId, {
28
- slotId: instruction.slotId,
29
- rowIndex: -1,
30
- rowData: {},
31
- translateY: 0
32
- });
33
- break;
34
- case "DESTROY_SLOT":
35
- state.slots.delete(instruction.slotId);
36
- break;
37
- case "ASSIGN_SLOT": {
38
- const existing = state.slots.get(instruction.slotId);
39
- if (existing) state.slots.set(instruction.slotId, {
40
- ...existing,
41
- rowIndex: instruction.rowIndex,
42
- rowData: instruction.rowData
43
- });
44
- break;
45
- }
46
- case "MOVE_SLOT": {
47
- const existing = state.slots.get(instruction.slotId);
48
- if (existing) state.slots.set(instruction.slotId, {
49
- ...existing,
50
- translateY: instruction.translateY
51
- });
52
- break;
53
- }
54
- case "SET_ACTIVE_CELL":
55
- state.activeCell = instruction.position;
56
- break;
57
- case "SET_SELECTION_RANGE":
58
- state.selectionRange = instruction.range;
59
- break;
60
- case "UPDATE_VISIBLE_RANGE":
61
- state.visibleRowRange = {
62
- start: instruction.start,
63
- end: instruction.end
64
- };
65
- break;
66
- case "START_EDIT":
67
- state.editingCell = {
68
- row: instruction.row,
69
- col: instruction.col,
70
- initialValue: instruction.initialValue
71
- };
72
- break;
73
- case "STOP_EDIT":
74
- state.editingCell = null;
75
- break;
76
- case "SET_CONTENT_SIZE":
77
- state.contentWidth = instruction.width;
78
- state.contentHeight = instruction.height;
79
- break;
80
- case "UPDATE_HEADER":
81
- state.headers.set(instruction.colIndex, {
82
- column: instruction.column,
83
- sortDirection: instruction.sortDirection,
84
- sortIndex: instruction.sortIndex,
85
- sortable: instruction.sortable,
86
- filterable: instruction.filterable,
87
- hasFilter: instruction.hasFilter
88
- });
89
- break;
90
- case "OPEN_FILTER_POPUP":
91
- state.filterPopup = {
92
- isOpen: true,
93
- colIndex: instruction.colIndex,
94
- column: instruction.column,
95
- anchorRect: instruction.anchorRect,
96
- distinctValues: instruction.distinctValues,
97
- currentFilter: instruction.currentFilter
98
- };
99
- break;
100
- case "CLOSE_FILTER_POPUP":
101
- state.filterPopup = null;
102
- break;
103
- case "DATA_LOADING":
104
- state.isLoading = true;
105
- state.error = null;
106
- break;
107
- case "DATA_LOADED":
108
- state.isLoading = false;
109
- state.totalRows = instruction.totalRows;
110
- break;
111
- case "DATA_ERROR":
112
- state.isLoading = false;
113
- state.error = instruction.error;
114
- break;
115
- case "ROWS_ADDED":
116
- case "ROWS_REMOVED":
117
- state.totalRows = instruction.totalRows;
118
- break;
119
- case "ROWS_UPDATED":
120
- case "TRANSACTION_PROCESSED": break;
121
- }
122
- }
123
- /**
124
- * Vue composable for managing grid state
125
- */
126
- function useGridState() {
127
- const state = reactive(createInitialState());
128
- /**
129
- * Apply a batch of instructions to the state
130
- */
131
- function applyInstructions(instructions) {
132
- for (const instruction of instructions) applyInstruction(instruction, state);
133
- }
134
- /**
135
- * Reset state to initial values
136
- */
137
- function reset() {
138
- const initial = createInitialState();
139
- state.slots = initial.slots;
140
- state.activeCell = initial.activeCell;
141
- state.selectionRange = initial.selectionRange;
142
- state.editingCell = initial.editingCell;
143
- state.contentWidth = initial.contentWidth;
144
- state.contentHeight = initial.contentHeight;
145
- state.headers = initial.headers;
146
- state.filterPopup = initial.filterPopup;
147
- state.isLoading = initial.isLoading;
148
- state.error = initial.error;
149
- state.totalRows = initial.totalRows;
150
- state.visibleRowRange = initial.visibleRowRange;
151
- }
152
- return {
153
- state,
154
- applyInstructions,
155
- reset
156
- };
157
- }
158
-
159
- //#endregion
160
- //#region src/composables/useAutoScroll.ts
161
- const AUTO_SCROLL_INTERVAL = 16;
162
- /**
163
- * Vue composable for auto-scrolling during drag operations
164
- */
165
- function useAutoScroll(containerRef) {
166
- const autoScrollInterval = ref(null);
167
- /**
168
- * Start auto-scrolling in the given direction
169
- */
170
- function startAutoScroll(dx, dy) {
171
- if (autoScrollInterval.value) clearInterval(autoScrollInterval.value);
172
- autoScrollInterval.value = setInterval(() => {
173
- const container = containerRef.value;
174
- if (container) {
175
- container.scrollTop += dy;
176
- container.scrollLeft += dx;
177
- }
178
- }, AUTO_SCROLL_INTERVAL);
179
- }
180
- /**
181
- * Stop auto-scrolling
182
- */
183
- function stopAutoScroll() {
184
- if (autoScrollInterval.value) {
185
- clearInterval(autoScrollInterval.value);
186
- autoScrollInterval.value = null;
187
- }
188
- }
189
- onUnmounted(() => {
190
- stopAutoScroll();
191
- });
192
- return {
193
- startAutoScroll,
194
- stopAutoScroll
195
- };
196
- }
197
-
198
- //#endregion
199
- //#region src/composables/useInputHandler.ts
200
- /**
201
- * Find the slot for a given row index
202
- */
203
- function findSlotForRow(slots, rowIndex) {
204
- for (const slot of slots.values()) if (slot.rowIndex === rowIndex) return slot;
205
- return null;
206
- }
207
- /**
208
- * Scroll a cell into view if needed
209
- */
210
- function scrollCellIntoView(core, container, row, rowHeight, headerHeight, slots) {
211
- const slot = findSlotForRow(slots, row);
212
- const cellViewportTop = (slot ? slot.translateY : headerHeight + row * rowHeight) - container.scrollTop;
213
- const cellViewportBottom = cellViewportTop + rowHeight;
214
- const visibleTop = headerHeight;
215
- const visibleBottom = container.clientHeight;
216
- if (cellViewportTop < visibleTop) container.scrollTop = core.getScrollTopForRow(row);
217
- else if (cellViewportBottom > visibleBottom) {
218
- const visibleDataHeight = container.clientHeight - headerHeight;
219
- const rowsInView = Math.floor(visibleDataHeight / rowHeight);
220
- const targetRow = Math.max(0, row - rowsInView + 1);
221
- container.scrollTop = core.getScrollTopForRow(targetRow);
222
- }
223
- }
224
- /**
225
- * Vue composable for handling all input interactions
226
- */
227
- function useInputHandler(coreRef, containerRef, columns, options) {
228
- const { activeCell, selectionRange, editingCell, filterPopupOpen, rowHeight, headerHeight, columnPositions, slots } = options;
229
- const { startAutoScroll, stopAutoScroll } = useAutoScroll(containerRef);
230
- const dragState = ref({
231
- isDragging: false,
232
- dragType: null,
233
- fillSourceRange: null,
234
- fillTarget: null
235
- });
236
- watch([
237
- () => headerHeight,
238
- () => rowHeight,
239
- columnPositions,
240
- () => columns.value.length
241
- ], () => {
242
- const core = coreRef.value;
243
- if (core?.input) core.input.updateDeps({
244
- getHeaderHeight: () => headerHeight,
245
- getRowHeight: () => rowHeight,
246
- getColumnPositions: () => columnPositions.value,
247
- getColumnCount: () => columns.value.length
248
- });
249
- }, { immediate: true });
250
- function getContainerBounds() {
251
- const container = containerRef.value;
252
- if (!container) return null;
253
- const rect = container.getBoundingClientRect();
254
- return {
255
- top: rect.top,
256
- left: rect.left,
257
- width: rect.width,
258
- height: rect.height,
259
- scrollTop: container.scrollTop,
260
- scrollLeft: container.scrollLeft
261
- };
262
- }
263
- function toPointerEventData(e) {
264
- return {
265
- clientX: e.clientX,
266
- clientY: e.clientY,
267
- button: e.button,
268
- shiftKey: e.shiftKey,
269
- ctrlKey: e.ctrlKey,
270
- metaKey: e.metaKey
271
- };
272
- }
273
- function startGlobalDragListeners() {
274
- const handleMouseMove = (e) => {
275
- const core = coreRef.value;
276
- const bounds = getContainerBounds();
277
- if (!core?.input || !bounds) return;
278
- const result = core.input.handleDragMove(toPointerEventData(e), bounds);
279
- if (result) {
280
- if (result.autoScroll) startAutoScroll(result.autoScroll.dx, result.autoScroll.dy);
281
- else stopAutoScroll();
282
- dragState.value = core.input.getDragState();
283
- }
284
- };
285
- const handleMouseUp = () => {
286
- const core = coreRef.value;
287
- if (core?.input) {
288
- core.input.handleDragEnd();
289
- dragState.value = core.input.getDragState();
290
- }
291
- stopAutoScroll();
292
- document.removeEventListener("mousemove", handleMouseMove);
293
- document.removeEventListener("mouseup", handleMouseUp);
294
- };
295
- document.addEventListener("mousemove", handleMouseMove);
296
- document.addEventListener("mouseup", handleMouseUp);
297
- }
298
- function handleCellMouseDown(rowIndex, colIndex, e) {
299
- const core = coreRef.value;
300
- if (!core?.input) return;
301
- const result = core.input.handleCellMouseDown(rowIndex, colIndex, toPointerEventData(e));
302
- if (result.focusContainer) containerRef.value?.focus();
303
- if (result.startDrag === "selection") {
304
- core.input.startSelectionDrag();
305
- dragState.value = core.input.getDragState();
306
- startGlobalDragListeners();
307
- }
308
- }
309
- function handleCellDoubleClick(rowIndex, colIndex) {
310
- const core = coreRef.value;
311
- if (!core?.input) return;
312
- core.input.handleCellDoubleClick(rowIndex, colIndex);
313
- }
314
- function handleFillHandleMouseDown(e) {
315
- const core = coreRef.value;
316
- if (!core?.input) return;
317
- const result = core.input.handleFillHandleMouseDown(activeCell.value, selectionRange.value, toPointerEventData(e));
318
- if (result.preventDefault) e.preventDefault();
319
- if (result.stopPropagation) e.stopPropagation();
320
- if (result.startDrag === "fill") {
321
- dragState.value = core.input.getDragState();
322
- startGlobalDragListeners();
323
- }
324
- }
325
- function handleHeaderClick(colIndex, e) {
326
- const core = coreRef.value;
327
- if (!core?.input) return;
328
- const column = columns.value[colIndex];
329
- if (!column) return;
330
- const colId = column.colId ?? column.field;
331
- core.input.handleHeaderClick(colId, e.shiftKey);
332
- }
333
- function handleKeyDown(e) {
334
- const core = coreRef.value;
335
- const container = containerRef.value;
336
- if (!core?.input) return;
337
- const result = core.input.handleKeyDown({
338
- key: e.key,
339
- shiftKey: e.shiftKey,
340
- ctrlKey: e.ctrlKey,
341
- metaKey: e.metaKey
342
- }, activeCell.value, editingCell.value, filterPopupOpen.value);
343
- if (result.preventDefault) e.preventDefault();
344
- if (result.scrollToCell && container) scrollCellIntoView(core, container, result.scrollToCell.row, rowHeight, headerHeight, slots.value);
345
- }
346
- function handleWheel(e, wheelDampening) {
347
- const core = coreRef.value;
348
- const container = containerRef.value;
349
- if (!core?.input || !container) return;
350
- const dampened = core.input.handleWheel(e.deltaY, e.deltaX, wheelDampening);
351
- if (dampened) {
352
- e.preventDefault();
353
- container.scrollTop += dampened.dy;
354
- container.scrollLeft += dampened.dx;
355
- }
356
- }
357
- onUnmounted(() => {
358
- stopAutoScroll();
359
- });
360
- return {
361
- handleCellMouseDown,
362
- handleCellDoubleClick,
363
- handleFillHandleMouseDown,
364
- handleHeaderClick,
365
- handleKeyDown,
366
- handleWheel,
367
- dragState
368
- };
369
- }
370
-
371
- //#endregion
372
- //#region src/composables/useFillHandle.ts
373
- /**
374
- * Composable for calculating the fill handle position.
375
- * The fill handle appears at the bottom-right corner of the selection
376
- * when all selected columns are editable.
377
- */
378
- function useFillHandle(options) {
379
- const { activeCell, selectionRange, slots, columns, columnPositions, rowHeight } = options;
380
- return { fillHandlePosition: computed(() => {
381
- const active = activeCell.value;
382
- const selection = selectionRange.value;
383
- const slotsMap = slots.value;
384
- if (!active && !selection) return null;
385
- let row, col;
386
- let minCol, maxCol;
387
- if (selection) {
388
- row = Math.max(selection.startRow, selection.endRow);
389
- col = Math.max(selection.startCol, selection.endCol);
390
- minCol = Math.min(selection.startCol, selection.endCol);
391
- maxCol = Math.max(selection.startCol, selection.endCol);
392
- } else if (active) {
393
- row = active.row;
394
- col = active.col;
395
- minCol = col;
396
- maxCol = col;
397
- } else return null;
398
- const cols = columns.value;
399
- for (let c = minCol; c <= maxCol; c++) {
400
- const column = cols[c];
401
- if (!column || column.editable !== true) return null;
402
- }
403
- let cellTop = null;
404
- for (const slot of slotsMap.values()) if (slot.rowIndex === row) {
405
- cellTop = slot.translateY;
406
- break;
407
- }
408
- if (cellTop === null) return null;
409
- const cellLeft = columnPositions.value[col] ?? 0;
410
- const cellWidth = cols[col]?.width ?? 0;
411
- return {
412
- top: cellTop + rowHeight - 5,
413
- left: cellLeft + cellWidth - 20
414
- };
415
- }) };
416
- }
417
-
418
- //#endregion
419
- //#region src/renderers/cellRenderer.ts
420
- /**
421
- * Ensure we always return a VNode, never a plain string
422
- */
423
- function toVNode$2(value) {
424
- if (value == null || value === "") return createTextVNode("");
425
- if (typeof value === "string") return createTextVNode(value);
426
- return value;
427
- }
428
- /**
429
- * Get cell value from row data, supporting dot-notation for nested fields
430
- */
431
- function getCellValue(rowData, field) {
432
- const parts = field.split(".");
433
- let value = rowData;
434
- for (const part of parts) {
435
- if (value == null || typeof value !== "object") return null;
436
- value = value[part];
437
- }
438
- return value ?? null;
439
- }
440
- /**
441
- * Render cell content based on column configuration and renderer registries
442
- */
443
- function renderCell(options) {
444
- const { column, rowData, rowIndex, colIndex, isActive, isSelected, isEditing, cellRenderers, globalCellRenderer } = options;
445
- const value = getCellValue(rowData, column.field);
446
- const params = {
447
- value,
448
- rowData,
449
- column,
450
- rowIndex,
451
- colIndex,
452
- isActive,
453
- isSelected,
454
- isEditing
455
- };
456
- if (column.cellRenderer && typeof column.cellRenderer === "string") {
457
- const renderer = cellRenderers[column.cellRenderer];
458
- if (renderer) return toVNode$2(renderer(params));
459
- }
460
- if (globalCellRenderer) return toVNode$2(globalCellRenderer(params));
461
- return createTextVNode(value == null ? "" : String(value));
462
- }
463
-
464
- //#endregion
465
- //#region src/renderers/editRenderer.ts
466
- /**
467
- * Ensure we always return a VNode, never a plain string
468
- */
469
- function toVNode$1(value) {
470
- if (value == null || value === "") return createTextVNode("");
471
- if (typeof value === "string") return createTextVNode(value);
472
- return value;
473
- }
474
- /**
475
- * Render edit cell content based on column configuration and renderer registries
476
- */
477
- function renderEditCell(options) {
478
- const { column, rowData, rowIndex, colIndex, initialValue, core, editRenderers, globalEditRenderer } = options;
479
- if (!core) return createTextVNode("");
480
- const params = {
481
- value: getCellValue(rowData, column.field),
482
- rowData,
483
- column,
484
- rowIndex,
485
- colIndex,
486
- isActive: true,
487
- isSelected: true,
488
- isEditing: true,
489
- initialValue,
490
- onValueChange: (newValue) => core.updateEditValue(newValue),
491
- onCommit: () => core.commitEdit(),
492
- onCancel: () => core.cancelEdit()
493
- };
494
- if (column.editRenderer && typeof column.editRenderer === "string") {
495
- const renderer = editRenderers[column.editRenderer];
496
- if (renderer) return toVNode$1(renderer(params));
497
- }
498
- if (globalEditRenderer) return toVNode$1(globalEditRenderer(params));
499
- return h("input", {
500
- class: "gp-grid-edit-input",
501
- type: "text",
502
- value: initialValue == null ? "" : String(initialValue),
503
- autofocus: true,
504
- onFocus: (e) => e.target.select(),
505
- onInput: (e) => core.updateEditValue(e.target.value),
506
- onKeydown: (e) => {
507
- e.stopPropagation();
508
- if (e.key === "Enter") core.commitEdit();
509
- else if (e.key === "Escape") core.cancelEdit();
510
- else if (e.key === "Tab") {
511
- e.preventDefault();
512
- core.commitEdit();
513
- core.selection.moveFocus(e.shiftKey ? "left" : "right", false);
514
- }
515
- },
516
- onBlur: () => core.commitEdit()
517
- });
518
- }
519
-
520
- //#endregion
521
- //#region src/renderers/headerRenderer.ts
522
- /**
523
- * Ensure we always return a VNode, never a plain string
524
- */
525
- function toVNode(value) {
526
- if (value == null || value === "") return createTextVNode("");
527
- if (typeof value === "string") return createTextVNode(value);
528
- return value;
529
- }
530
- /**
531
- * Render header content based on column configuration and renderer registries
532
- */
533
- function renderHeader(options) {
534
- const { column, colIndex, sortDirection, sortIndex, sortable, filterable, hasFilter, core, container, headerRenderers, globalHeaderRenderer } = options;
535
- const params = {
536
- column,
537
- colIndex,
538
- sortDirection,
539
- sortIndex,
540
- sortable,
541
- filterable,
542
- hasFilter,
543
- onSort: (direction, addToExisting) => {
544
- if (core && sortable) core.setSort(column.colId ?? column.field, direction, addToExisting);
545
- },
546
- onFilterClick: () => {
547
- if (core && filterable) {
548
- const headerCell = container?.querySelector(`[data-col-index="${colIndex}"]`);
549
- if (headerCell) {
550
- const rect = headerCell.getBoundingClientRect();
551
- core.openFilterPopup(colIndex, {
552
- top: rect.top,
553
- left: rect.left,
554
- width: rect.width,
555
- height: rect.height
556
- });
557
- }
558
- }
559
- }
560
- };
561
- if (column.headerRenderer && typeof column.headerRenderer === "string") {
562
- const renderer = headerRenderers[column.headerRenderer];
563
- if (renderer) return toVNode(renderer(params));
564
- }
565
- if (globalHeaderRenderer) return toVNode(globalHeaderRenderer(params));
566
- const children = [h("span", { class: "gp-grid-header-text" }, column.headerName ?? column.field)];
567
- const iconsChildren = [];
568
- if (sortable) {
569
- const arrowsChildren = [h("span", { class: "gp-grid-sort-arrows-stack" }, [h("svg", {
570
- class: `gp-grid-sort-arrow-up${sortDirection === "asc" ? " active" : ""}`,
571
- width: "8",
572
- height: "6",
573
- viewBox: "0 0 8 6"
574
- }, [h("path", {
575
- d: "M4 0L8 6H0L4 0Z",
576
- fill: "currentColor"
577
- })]), h("svg", {
578
- class: `gp-grid-sort-arrow-down${sortDirection === "desc" ? " active" : ""}`,
579
- width: "8",
580
- height: "6",
581
- viewBox: "0 0 8 6"
582
- }, [h("path", {
583
- d: "M4 6L0 0H8L4 6Z",
584
- fill: "currentColor"
585
- })])])];
586
- if (sortIndex !== void 0 && sortIndex > 0) arrowsChildren.push(h("span", { class: "gp-grid-sort-index" }, String(sortIndex)));
587
- iconsChildren.push(h("span", { class: "gp-grid-sort-arrows" }, arrowsChildren));
588
- }
589
- if (filterable) iconsChildren.push(h("span", {
590
- class: `gp-grid-filter-icon${hasFilter ? " active" : ""}`,
591
- onMousedown: (e) => {
592
- e.stopPropagation();
593
- e.preventDefault();
594
- params.onFilterClick();
595
- },
596
- onClick: (e) => {
597
- e.stopPropagation();
598
- }
599
- }, [h("svg", {
600
- width: "16",
601
- height: "16",
602
- viewBox: "0 0 24 24",
603
- fill: "currentColor"
604
- }, [h("path", { d: "M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" })])]));
605
- if (iconsChildren.length > 0) children.push(h("span", { class: "gp-grid-header-icons" }, iconsChildren));
606
- return h(Fragment, children);
607
- }
608
-
609
- //#endregion
610
- //#region src/composables/useFilterPopup.ts
611
- /**
612
- * Composable for filter popup behavior.
613
- * Handles click-outside detection and escape key to close the popup.
614
- */
615
- function useFilterPopup(popupRef, options) {
616
- const { onClose, ignoreSelector = ".gp-grid-filter-icon" } = options;
617
- let handleClickOutside = null;
618
- let handleKeyDown = null;
619
- onMounted(() => {
620
- handleClickOutside = (e) => {
621
- const target = e.target;
622
- if (ignoreSelector && target.closest(ignoreSelector)) return;
623
- if (popupRef.value && !popupRef.value.contains(target)) onClose();
624
- };
625
- handleKeyDown = (e) => {
626
- if (e.key === "Escape") onClose();
627
- };
628
- requestAnimationFrame(() => {
629
- if (handleClickOutside) document.addEventListener("mousedown", handleClickOutside);
630
- if (handleKeyDown) document.addEventListener("keydown", handleKeyDown);
631
- });
632
- });
633
- onUnmounted(() => {
634
- if (handleClickOutside) document.removeEventListener("mousedown", handleClickOutside);
635
- if (handleKeyDown) document.removeEventListener("keydown", handleKeyDown);
636
- });
637
- }
638
-
639
- //#endregion
640
- //#region src/composables/useFilterConditions.ts
641
- /**
642
- * Composable for managing filter conditions.
643
- * Used by NumberFilterContent, DateFilterContent, and TextFilterContent (condition mode).
644
- */
645
- function useFilterConditions(initialConditions, initialCombination = "and") {
646
- const conditions = ref([...initialConditions]);
647
- const combination = ref(initialCombination);
648
- const updateCondition = (index, updates) => {
649
- const next = [...conditions.value];
650
- next[index] = {
651
- ...next[index],
652
- ...updates
653
- };
654
- conditions.value = next;
655
- };
656
- const addCondition = (defaultOperator) => {
657
- conditions.value = [...conditions.value, {
658
- operator: defaultOperator,
659
- value: "",
660
- valueTo: "",
661
- nextOperator: "and"
662
- }];
663
- };
664
- const removeCondition = (index) => {
665
- conditions.value = conditions.value.filter((_, i) => i !== index);
666
- };
667
- return {
668
- conditions,
669
- combination,
670
- updateCondition,
671
- addCondition,
672
- removeCondition
673
- };
674
- }
675
-
676
- //#endregion
677
- //#region src/components/TextFilterContent.vue
678
- const _hoisted_1$4 = { class: "gp-grid-filter-content gp-grid-filter-text" };
679
- const _hoisted_2$3 = {
680
- key: 0,
681
- class: "gp-grid-filter-mode-toggle"
682
- };
683
- const _hoisted_3$3 = {
684
- key: 1,
685
- class: "gp-grid-filter-info"
686
- };
687
- const _hoisted_4$3 = { class: "gp-grid-filter-actions" };
688
- const _hoisted_5$3 = ["disabled"];
689
- const _hoisted_6$2 = { class: "gp-grid-filter-list" };
690
- const _hoisted_7$2 = {
691
- key: 0,
692
- class: "gp-grid-filter-option"
693
- };
694
- const _hoisted_8$2 = ["checked"];
695
- const _hoisted_9$2 = ["checked", "onChange"];
696
- const _hoisted_10$2 = {
697
- key: 0,
698
- class: "gp-grid-filter-combination"
699
- };
700
- const _hoisted_11 = ["onClick"];
701
- const _hoisted_12 = ["onClick"];
702
- const _hoisted_13 = { class: "gp-grid-filter-row" };
703
- const _hoisted_14 = [
704
- "value",
705
- "autofocus",
706
- "onChange"
707
- ];
708
- const _hoisted_15 = ["value"];
709
- const _hoisted_16 = ["value", "onInput"];
710
- const _hoisted_17 = ["onClick"];
711
- const MAX_VALUES_FOR_LIST = 100;
712
- const _sfc_main$4 = /* @__PURE__ */ defineComponent({
713
- __name: "TextFilterContent",
714
- props: {
715
- distinctValues: {},
716
- currentFilter: {}
717
- },
718
- emits: ["apply", "close"],
719
- setup(__props, { emit: __emit }) {
720
- const OPERATORS = [
721
- {
722
- value: "contains",
723
- label: "Contains"
724
- },
725
- {
726
- value: "notContains",
727
- label: "Does not contain"
728
- },
729
- {
730
- value: "equals",
731
- label: "Equals"
732
- },
733
- {
734
- value: "notEquals",
735
- label: "Does not equal"
736
- },
737
- {
738
- value: "startsWith",
739
- label: "Starts with"
740
- },
741
- {
742
- value: "endsWith",
743
- label: "Ends with"
744
- },
745
- {
746
- value: "blank",
747
- label: "Is blank"
748
- },
749
- {
750
- value: "notBlank",
751
- label: "Is not blank"
752
- }
753
- ];
754
- const props = __props;
755
- const emit = __emit;
756
- function valueToString(v) {
757
- if (Array.isArray(v)) return v.join(", ");
758
- return String(v ?? "");
759
- }
760
- const uniqueValues = computed(() => {
761
- const values = props.distinctValues.filter((v) => v != null && v !== "" && !(Array.isArray(v) && v.length === 0)).map((v) => valueToString(v));
762
- return Array.from(new Set(values)).sort((a, b) => {
763
- const numA = parseFloat(a);
764
- const numB = parseFloat(b);
765
- if (!isNaN(numA) && !isNaN(numB)) return numA - numB;
766
- return a.localeCompare(b, void 0, {
767
- numeric: true,
768
- sensitivity: "base"
769
- });
770
- });
771
- });
772
- const hasTooManyValues = computed(() => uniqueValues.value.length > MAX_VALUES_FOR_LIST);
773
- const mode = ref(computed(() => {
774
- if (!props.currentFilter?.conditions[0]) return hasTooManyValues.value ? "condition" : "values";
775
- const cond = props.currentFilter.conditions[0];
776
- if (cond.selectedValues && cond.selectedValues.size > 0) return "values";
777
- return "condition";
778
- }).value);
779
- const initialSelected = computed(() => {
780
- if (!props.currentFilter?.conditions[0]) return /* @__PURE__ */ new Set();
781
- return props.currentFilter.conditions[0].selectedValues ?? /* @__PURE__ */ new Set();
782
- });
783
- const initialIncludeBlanks = computed(() => {
784
- if (!props.currentFilter?.conditions[0]) return true;
785
- return props.currentFilter.conditions[0].includeBlank ?? true;
786
- });
787
- const searchText = ref("");
788
- const selectedValues = ref(new Set(initialSelected.value));
789
- const includeBlanks = ref(initialIncludeBlanks.value);
790
- const { conditions, combination, updateCondition, addCondition, removeCondition } = useFilterConditions(computed(() => {
791
- if (!props.currentFilter?.conditions.length) return [{
792
- operator: "contains",
793
- value: "",
794
- valueTo: "",
795
- nextOperator: "and"
796
- }];
797
- const cond = props.currentFilter.conditions[0];
798
- if (cond.selectedValues && cond.selectedValues.size > 0) return [{
799
- operator: "contains",
800
- value: "",
801
- valueTo: "",
802
- nextOperator: "and"
803
- }];
804
- const defaultCombination = props.currentFilter.combination ?? "and";
805
- return props.currentFilter.conditions.map((c) => {
806
- const tc = c;
807
- return {
808
- operator: tc.operator,
809
- value: tc.value ?? "",
810
- valueTo: "",
811
- nextOperator: tc.nextOperator ?? defaultCombination
812
- };
813
- });
814
- }).value, props.currentFilter?.combination ?? "and");
815
- const displayValues = computed(() => {
816
- if (!searchText.value) return uniqueValues.value;
817
- const lower = searchText.value.toLowerCase();
818
- return uniqueValues.value.filter((v) => v.toLowerCase().includes(lower));
819
- });
820
- const hasBlanks = computed(() => {
821
- return props.distinctValues.some((v) => v == null || v === "");
822
- });
823
- const allSelected = computed(() => {
824
- return displayValues.value.every((v) => selectedValues.value.has(v)) && (!hasBlanks.value || includeBlanks.value);
825
- });
826
- function handleSelectAll() {
827
- selectedValues.value = new Set(displayValues.value);
828
- if (hasBlanks.value) includeBlanks.value = true;
829
- }
830
- function handleDeselectAll() {
831
- selectedValues.value = /* @__PURE__ */ new Set();
832
- includeBlanks.value = false;
833
- }
834
- function handleValueToggle(value) {
835
- const next = new Set(selectedValues.value);
836
- if (next.has(value)) next.delete(value);
837
- else next.add(value);
838
- selectedValues.value = next;
839
- }
840
- function handleApply() {
841
- if (mode.value === "values") {
842
- if (uniqueValues.value.every((v) => selectedValues.value.has(v)) && (!hasBlanks.value || includeBlanks.value)) {
843
- emit("apply", null);
844
- return;
845
- }
846
- emit("apply", {
847
- conditions: [{
848
- type: "text",
849
- operator: "equals",
850
- selectedValues: selectedValues.value,
851
- includeBlank: includeBlanks.value
852
- }],
853
- combination: "and"
854
- });
855
- } else {
856
- const validConditions = conditions.value.filter((c) => {
857
- if (c.operator === "blank" || c.operator === "notBlank") return true;
858
- return c.value.trim() !== "";
859
- });
860
- if (validConditions.length === 0) {
861
- emit("apply", null);
862
- return;
863
- }
864
- emit("apply", {
865
- conditions: validConditions.map((c) => ({
866
- type: "text",
867
- operator: c.operator,
868
- value: c.value,
869
- nextOperator: c.nextOperator
870
- })),
871
- combination: "and"
872
- });
873
- }
874
- }
875
- function handleClear() {
876
- emit("apply", null);
877
- }
878
- return (_ctx, _cache) => {
879
- return openBlock(), createElementBlock("div", _hoisted_1$4, [
880
- createCommentVNode(" Mode toggle - only show if not too many values "),
881
- !hasTooManyValues.value ? (openBlock(), createElementBlock("div", _hoisted_2$3, [createElementVNode("button", {
882
- type: "button",
883
- class: normalizeClass({ active: mode.value === "values" }),
884
- onClick: _cache[0] || (_cache[0] = ($event) => mode.value = "values")
885
- }, " Values ", 2), createElementVNode("button", {
886
- type: "button",
887
- class: normalizeClass({ active: mode.value === "condition" }),
888
- onClick: _cache[1] || (_cache[1] = ($event) => mode.value = "condition")
889
- }, " Condition ", 2)])) : createCommentVNode("v-if", true),
890
- createCommentVNode(" Too many values message "),
891
- hasTooManyValues.value && mode.value === "condition" ? (openBlock(), createElementBlock("div", _hoisted_3$3, " Too many unique values (" + toDisplayString(uniqueValues.value.length) + "). Use conditions to filter. ", 1)) : createCommentVNode("v-if", true),
892
- createCommentVNode(" VALUES MODE "),
893
- mode.value === "values" ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [
894
- createCommentVNode(" Search input "),
895
- withDirectives(createElementVNode("input", {
896
- "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => searchText.value = $event),
897
- class: "gp-grid-filter-search",
898
- type: "text",
899
- placeholder: "Search...",
900
- autofocus: ""
901
- }, null, 512), [[vModelText, searchText.value]]),
902
- createCommentVNode(" Select all / Deselect all "),
903
- createElementVNode("div", _hoisted_4$3, [createElementVNode("button", {
904
- type: "button",
905
- disabled: allSelected.value,
906
- onClick: handleSelectAll
907
- }, " Select All ", 8, _hoisted_5$3), createElementVNode("button", {
908
- type: "button",
909
- onClick: handleDeselectAll
910
- }, " Deselect All ")]),
911
- createCommentVNode(" Checkbox list "),
912
- createElementVNode("div", _hoisted_6$2, [
913
- createCommentVNode(" Blanks option "),
914
- hasBlanks.value ? (openBlock(), createElementBlock("label", _hoisted_7$2, [createElementVNode("input", {
915
- type: "checkbox",
916
- checked: includeBlanks.value,
917
- onChange: _cache[3] || (_cache[3] = ($event) => includeBlanks.value = !includeBlanks.value)
918
- }, null, 40, _hoisted_8$2), _cache[5] || (_cache[5] = createElementVNode("span", { class: "gp-grid-filter-blank" }, "(Blanks)", -1))])) : createCommentVNode("v-if", true),
919
- createCommentVNode(" Values "),
920
- (openBlock(true), createElementBlock(Fragment, null, renderList(displayValues.value, (value) => {
921
- return openBlock(), createElementBlock("label", {
922
- key: value,
923
- class: "gp-grid-filter-option"
924
- }, [createElementVNode("input", {
925
- type: "checkbox",
926
- checked: selectedValues.value.has(value),
927
- onChange: ($event) => handleValueToggle(value)
928
- }, null, 40, _hoisted_9$2), createElementVNode("span", null, toDisplayString(value), 1)]);
929
- }), 128))
930
- ])
931
- ], 64)) : createCommentVNode("v-if", true),
932
- createCommentVNode(" CONDITION MODE "),
933
- mode.value === "condition" ? (openBlock(), createElementBlock(Fragment, { key: 3 }, [
934
- (openBlock(true), createElementBlock(Fragment, null, renderList(unref(conditions), (cond, index) => {
935
- return openBlock(), createElementBlock("div", {
936
- key: index,
937
- class: "gp-grid-filter-condition"
938
- }, [
939
- createCommentVNode(" Combination toggle (AND/OR) for conditions after the first "),
940
- index > 0 ? (openBlock(), createElementBlock("div", _hoisted_10$2, [createElementVNode("button", {
941
- type: "button",
942
- class: normalizeClass({ active: unref(conditions)[index - 1]?.nextOperator === "and" }),
943
- onClick: ($event) => unref(updateCondition)(index - 1, { nextOperator: "and" })
944
- }, " AND ", 10, _hoisted_11), createElementVNode("button", {
945
- type: "button",
946
- class: normalizeClass({ active: unref(conditions)[index - 1]?.nextOperator === "or" }),
947
- onClick: ($event) => unref(updateCondition)(index - 1, { nextOperator: "or" })
948
- }, " OR ", 10, _hoisted_12)])) : createCommentVNode("v-if", true),
949
- createElementVNode("div", _hoisted_13, [
950
- createCommentVNode(" Operator select "),
951
- createElementVNode("select", {
952
- value: cond.operator,
953
- autofocus: index === 0,
954
- onChange: ($event) => unref(updateCondition)(index, { operator: $event.target.value })
955
- }, [(openBlock(), createElementBlock(Fragment, null, renderList(OPERATORS, (op) => {
956
- return createElementVNode("option", {
957
- key: op.value,
958
- value: op.value
959
- }, toDisplayString(op.label), 9, _hoisted_15);
960
- }), 64))], 40, _hoisted_14),
961
- createCommentVNode(" Text input (hidden for blank/notBlank) "),
962
- cond.operator !== "blank" && cond.operator !== "notBlank" ? (openBlock(), createElementBlock("input", {
963
- key: 0,
964
- type: "text",
965
- value: cond.value,
966
- placeholder: "Value",
967
- class: "gp-grid-filter-text-input",
968
- onInput: ($event) => unref(updateCondition)(index, { value: $event.target.value })
969
- }, null, 40, _hoisted_16)) : createCommentVNode("v-if", true),
970
- createCommentVNode(" Remove button (only if more than one condition) "),
971
- unref(conditions).length > 1 ? (openBlock(), createElementBlock("button", {
972
- key: 1,
973
- type: "button",
974
- class: "gp-grid-filter-remove",
975
- onClick: ($event) => unref(removeCondition)(index)
976
- }, " × ", 8, _hoisted_17)) : createCommentVNode("v-if", true)
977
- ])
978
- ]);
979
- }), 128)),
980
- createCommentVNode(" Add condition button "),
981
- createElementVNode("button", {
982
- type: "button",
983
- class: "gp-grid-filter-add",
984
- onClick: _cache[4] || (_cache[4] = ($event) => unref(addCondition)("contains"))
985
- }, " + Add condition ")
986
- ], 64)) : createCommentVNode("v-if", true),
987
- createCommentVNode(" Apply/Clear buttons "),
988
- createElementVNode("div", { class: "gp-grid-filter-buttons" }, [createElementVNode("button", {
989
- type: "button",
990
- class: "gp-grid-filter-btn-clear",
991
- onClick: handleClear
992
- }, " Clear "), createElementVNode("button", {
993
- type: "button",
994
- class: "gp-grid-filter-btn-apply",
995
- onClick: handleApply
996
- }, " Apply ")])
997
- ]);
998
- };
999
- }
1000
- });
1001
- var TextFilterContent_default = _sfc_main$4;
1002
-
1003
- //#endregion
1004
- //#region src/components/NumberFilterContent.vue
1005
- const _hoisted_1$3 = { class: "gp-grid-filter-content gp-grid-filter-number" };
1006
- const _hoisted_2$2 = {
1007
- key: 0,
1008
- class: "gp-grid-filter-combination"
1009
- };
1010
- const _hoisted_3$2 = ["onClick"];
1011
- const _hoisted_4$2 = ["onClick"];
1012
- const _hoisted_5$2 = { class: "gp-grid-filter-row" };
1013
- const _hoisted_6$1 = ["value", "onChange"];
1014
- const _hoisted_7$1 = ["value"];
1015
- const _hoisted_8$1 = ["value", "onInput"];
1016
- const _hoisted_9$1 = ["value", "onInput"];
1017
- const _hoisted_10$1 = ["onClick"];
1018
- const _sfc_main$3 = /* @__PURE__ */ defineComponent({
1019
- __name: "NumberFilterContent",
1020
- props: { currentFilter: {} },
1021
- emits: ["apply", "close"],
1022
- setup(__props, { emit: __emit }) {
1023
- const OPERATORS = [
1024
- {
1025
- value: "=",
1026
- label: "="
1027
- },
1028
- {
1029
- value: "!=",
1030
- label: "≠"
1031
- },
1032
- {
1033
- value: ">",
1034
- label: ">"
1035
- },
1036
- {
1037
- value: "<",
1038
- label: "<"
1039
- },
1040
- {
1041
- value: ">=",
1042
- label: "≥"
1043
- },
1044
- {
1045
- value: "<=",
1046
- label: "≤"
1047
- },
1048
- {
1049
- value: "between",
1050
- label: "↔"
1051
- },
1052
- {
1053
- value: "blank",
1054
- label: "Is blank"
1055
- },
1056
- {
1057
- value: "notBlank",
1058
- label: "Not blank"
1059
- }
1060
- ];
1061
- const props = __props;
1062
- const emit = __emit;
1063
- const { conditions, combination, updateCondition, addCondition, removeCondition } = useFilterConditions(computed(() => {
1064
- if (!props.currentFilter?.conditions.length) return [{
1065
- operator: "=",
1066
- value: "",
1067
- valueTo: "",
1068
- nextOperator: "and"
1069
- }];
1070
- const defaultCombination = props.currentFilter.combination ?? "and";
1071
- return props.currentFilter.conditions.map((c) => {
1072
- const cond = c;
1073
- return {
1074
- operator: cond.operator,
1075
- value: cond.value != null ? String(cond.value) : "",
1076
- valueTo: cond.valueTo != null ? String(cond.valueTo) : "",
1077
- nextOperator: cond.nextOperator ?? defaultCombination
1078
- };
1079
- });
1080
- }).value, props.currentFilter?.combination ?? "and");
1081
- function handleApply() {
1082
- const validConditions = conditions.value.filter((c) => {
1083
- if (c.operator === "blank" || c.operator === "notBlank") return true;
1084
- if (c.operator === "between") return c.value !== "" && c.valueTo !== "";
1085
- return c.value !== "";
1086
- });
1087
- if (validConditions.length === 0) {
1088
- emit("apply", null);
1089
- return;
1090
- }
1091
- emit("apply", {
1092
- conditions: validConditions.map((c) => ({
1093
- type: "number",
1094
- operator: c.operator,
1095
- value: c.value ? parseFloat(c.value) : void 0,
1096
- valueTo: c.valueTo ? parseFloat(c.valueTo) : void 0,
1097
- nextOperator: c.nextOperator
1098
- })),
1099
- combination: "and"
1100
- });
1101
- }
1102
- function handleClear() {
1103
- emit("apply", null);
1104
- }
1105
- return (_ctx, _cache) => {
1106
- return openBlock(), createElementBlock("div", _hoisted_1$3, [
1107
- (openBlock(true), createElementBlock(Fragment, null, renderList(unref(conditions), (cond, index) => {
1108
- return openBlock(), createElementBlock("div", {
1109
- key: index,
1110
- class: "gp-grid-filter-condition"
1111
- }, [
1112
- createCommentVNode(" Combination toggle (AND/OR) for conditions after the first "),
1113
- index > 0 ? (openBlock(), createElementBlock("div", _hoisted_2$2, [createElementVNode("button", {
1114
- type: "button",
1115
- class: normalizeClass({ active: unref(conditions)[index - 1]?.nextOperator === "and" }),
1116
- onClick: ($event) => unref(updateCondition)(index - 1, { nextOperator: "and" })
1117
- }, " AND ", 10, _hoisted_3$2), createElementVNode("button", {
1118
- type: "button",
1119
- class: normalizeClass({ active: unref(conditions)[index - 1]?.nextOperator === "or" }),
1120
- onClick: ($event) => unref(updateCondition)(index - 1, { nextOperator: "or" })
1121
- }, " OR ", 10, _hoisted_4$2)])) : createCommentVNode("v-if", true),
1122
- createElementVNode("div", _hoisted_5$2, [
1123
- createCommentVNode(" Operator select "),
1124
- createElementVNode("select", {
1125
- value: cond.operator,
1126
- onChange: ($event) => unref(updateCondition)(index, { operator: $event.target.value })
1127
- }, [(openBlock(), createElementBlock(Fragment, null, renderList(OPERATORS, (op) => {
1128
- return createElementVNode("option", {
1129
- key: op.value,
1130
- value: op.value
1131
- }, toDisplayString(op.label), 9, _hoisted_7$1);
1132
- }), 64))], 40, _hoisted_6$1),
1133
- createCommentVNode(" Number input (hidden for blank/notBlank) "),
1134
- cond.operator !== "blank" && cond.operator !== "notBlank" ? (openBlock(), createElementBlock("input", {
1135
- key: 0,
1136
- type: "number",
1137
- value: cond.value,
1138
- placeholder: "Value",
1139
- onInput: ($event) => unref(updateCondition)(index, { value: $event.target.value })
1140
- }, null, 40, _hoisted_8$1)) : createCommentVNode("v-if", true),
1141
- createCommentVNode(" Second number input for \"between\" "),
1142
- cond.operator === "between" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [_cache[1] || (_cache[1] = createElementVNode("span", { class: "gp-grid-filter-to" }, "to", -1)), createElementVNode("input", {
1143
- type: "number",
1144
- value: cond.valueTo,
1145
- placeholder: "Value",
1146
- onInput: ($event) => unref(updateCondition)(index, { valueTo: $event.target.value })
1147
- }, null, 40, _hoisted_9$1)], 64)) : createCommentVNode("v-if", true),
1148
- createCommentVNode(" Remove button (only if more than one condition) "),
1149
- unref(conditions).length > 1 ? (openBlock(), createElementBlock("button", {
1150
- key: 2,
1151
- type: "button",
1152
- class: "gp-grid-filter-remove",
1153
- onClick: ($event) => unref(removeCondition)(index)
1154
- }, " × ", 8, _hoisted_10$1)) : createCommentVNode("v-if", true)
1155
- ])
1156
- ]);
1157
- }), 128)),
1158
- createCommentVNode(" Add condition button "),
1159
- createElementVNode("button", {
1160
- type: "button",
1161
- class: "gp-grid-filter-add",
1162
- onClick: _cache[0] || (_cache[0] = ($event) => unref(addCondition)("="))
1163
- }, " + Add condition "),
1164
- createCommentVNode(" Apply/Clear buttons "),
1165
- createElementVNode("div", { class: "gp-grid-filter-buttons" }, [createElementVNode("button", {
1166
- type: "button",
1167
- class: "gp-grid-filter-btn-clear",
1168
- onClick: handleClear
1169
- }, " Clear "), createElementVNode("button", {
1170
- type: "button",
1171
- class: "gp-grid-filter-btn-apply",
1172
- onClick: handleApply
1173
- }, " Apply ")])
1174
- ]);
1175
- };
1176
- }
1177
- });
1178
- var NumberFilterContent_default = _sfc_main$3;
1179
-
1180
- //#endregion
1181
- //#region src/components/DateFilterContent.vue
1182
- const _hoisted_1$2 = { class: "gp-grid-filter-content gp-grid-filter-date" };
1183
- const _hoisted_2$1 = {
1184
- key: 0,
1185
- class: "gp-grid-filter-combination"
1186
- };
1187
- const _hoisted_3$1 = ["onClick"];
1188
- const _hoisted_4$1 = ["onClick"];
1189
- const _hoisted_5$1 = { class: "gp-grid-filter-row" };
1190
- const _hoisted_6 = ["value", "onChange"];
1191
- const _hoisted_7 = ["value"];
1192
- const _hoisted_8 = ["value", "onInput"];
1193
- const _hoisted_9 = ["value", "onInput"];
1194
- const _hoisted_10 = ["onClick"];
1195
- const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1196
- __name: "DateFilterContent",
1197
- props: { currentFilter: {} },
1198
- emits: ["apply", "close"],
1199
- setup(__props, { emit: __emit }) {
1200
- const OPERATORS = [
1201
- {
1202
- value: "=",
1203
- label: "="
1204
- },
1205
- {
1206
- value: "!=",
1207
- label: "≠"
1208
- },
1209
- {
1210
- value: ">",
1211
- label: ">"
1212
- },
1213
- {
1214
- value: "<",
1215
- label: "<"
1216
- },
1217
- {
1218
- value: "between",
1219
- label: "↔"
1220
- },
1221
- {
1222
- value: "blank",
1223
- label: "Is blank"
1224
- },
1225
- {
1226
- value: "notBlank",
1227
- label: "Not blank"
1228
- }
1229
- ];
1230
- const props = __props;
1231
- const emit = __emit;
1232
- function formatDateForInput(date) {
1233
- if (!date) return "";
1234
- const d = typeof date === "string" ? new Date(date) : date;
1235
- if (isNaN(d.getTime())) return "";
1236
- return d.toISOString().split("T")[0];
1237
- }
1238
- const { conditions, combination, updateCondition, addCondition, removeCondition } = useFilterConditions(computed(() => {
1239
- if (!props.currentFilter?.conditions.length) return [{
1240
- operator: "=",
1241
- value: "",
1242
- valueTo: "",
1243
- nextOperator: "and"
1244
- }];
1245
- const defaultCombination = props.currentFilter.combination ?? "and";
1246
- return props.currentFilter.conditions.map((c) => {
1247
- const cond = c;
1248
- return {
1249
- operator: cond.operator,
1250
- value: formatDateForInput(cond.value),
1251
- valueTo: formatDateForInput(cond.valueTo),
1252
- nextOperator: cond.nextOperator ?? defaultCombination
1253
- };
1254
- });
1255
- }).value, props.currentFilter?.combination ?? "and");
1256
- function handleApply() {
1257
- const validConditions = conditions.value.filter((c) => {
1258
- if (c.operator === "blank" || c.operator === "notBlank") return true;
1259
- if (c.operator === "between") return c.value !== "" && c.valueTo !== "";
1260
- return c.value !== "";
1261
- });
1262
- if (validConditions.length === 0) {
1263
- emit("apply", null);
1264
- return;
1265
- }
1266
- emit("apply", {
1267
- conditions: validConditions.map((c) => ({
1268
- type: "date",
1269
- operator: c.operator,
1270
- value: c.value || void 0,
1271
- valueTo: c.valueTo || void 0,
1272
- nextOperator: c.nextOperator
1273
- })),
1274
- combination: "and"
1275
- });
1276
- }
1277
- function handleClear() {
1278
- emit("apply", null);
1279
- }
1280
- return (_ctx, _cache) => {
1281
- return openBlock(), createElementBlock("div", _hoisted_1$2, [
1282
- (openBlock(true), createElementBlock(Fragment, null, renderList(unref(conditions), (cond, index) => {
1283
- return openBlock(), createElementBlock("div", {
1284
- key: index,
1285
- class: "gp-grid-filter-condition"
1286
- }, [
1287
- createCommentVNode(" Combination toggle (AND/OR) for conditions after the first "),
1288
- index > 0 ? (openBlock(), createElementBlock("div", _hoisted_2$1, [createElementVNode("button", {
1289
- type: "button",
1290
- class: normalizeClass({ active: unref(conditions)[index - 1]?.nextOperator === "and" }),
1291
- onClick: ($event) => unref(updateCondition)(index - 1, { nextOperator: "and" })
1292
- }, " AND ", 10, _hoisted_3$1), createElementVNode("button", {
1293
- type: "button",
1294
- class: normalizeClass({ active: unref(conditions)[index - 1]?.nextOperator === "or" }),
1295
- onClick: ($event) => unref(updateCondition)(index - 1, { nextOperator: "or" })
1296
- }, " OR ", 10, _hoisted_4$1)])) : createCommentVNode("v-if", true),
1297
- createElementVNode("div", _hoisted_5$1, [
1298
- createCommentVNode(" Operator select "),
1299
- createElementVNode("select", {
1300
- value: cond.operator,
1301
- onChange: ($event) => unref(updateCondition)(index, { operator: $event.target.value })
1302
- }, [(openBlock(), createElementBlock(Fragment, null, renderList(OPERATORS, (op) => {
1303
- return createElementVNode("option", {
1304
- key: op.value,
1305
- value: op.value
1306
- }, toDisplayString(op.label), 9, _hoisted_7);
1307
- }), 64))], 40, _hoisted_6),
1308
- createCommentVNode(" Date input (hidden for blank/notBlank) "),
1309
- cond.operator !== "blank" && cond.operator !== "notBlank" ? (openBlock(), createElementBlock("input", {
1310
- key: 0,
1311
- type: "date",
1312
- value: cond.value,
1313
- onInput: ($event) => unref(updateCondition)(index, { value: $event.target.value })
1314
- }, null, 40, _hoisted_8)) : createCommentVNode("v-if", true),
1315
- createCommentVNode(" Second date input for \"between\" "),
1316
- cond.operator === "between" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [_cache[1] || (_cache[1] = createElementVNode("span", { class: "gp-grid-filter-to" }, "to", -1)), createElementVNode("input", {
1317
- type: "date",
1318
- value: cond.valueTo,
1319
- onInput: ($event) => unref(updateCondition)(index, { valueTo: $event.target.value })
1320
- }, null, 40, _hoisted_9)], 64)) : createCommentVNode("v-if", true),
1321
- createCommentVNode(" Remove button (only if more than one condition) "),
1322
- unref(conditions).length > 1 ? (openBlock(), createElementBlock("button", {
1323
- key: 2,
1324
- type: "button",
1325
- class: "gp-grid-filter-remove",
1326
- onClick: ($event) => unref(removeCondition)(index)
1327
- }, " × ", 8, _hoisted_10)) : createCommentVNode("v-if", true)
1328
- ])
1329
- ]);
1330
- }), 128)),
1331
- createCommentVNode(" Add condition button "),
1332
- createElementVNode("button", {
1333
- type: "button",
1334
- class: "gp-grid-filter-add",
1335
- onClick: _cache[0] || (_cache[0] = ($event) => unref(addCondition)("="))
1336
- }, " + Add condition "),
1337
- createCommentVNode(" Apply/Clear buttons "),
1338
- createElementVNode("div", { class: "gp-grid-filter-buttons" }, [createElementVNode("button", {
1339
- type: "button",
1340
- class: "gp-grid-filter-btn-clear",
1341
- onClick: handleClear
1342
- }, " Clear "), createElementVNode("button", {
1343
- type: "button",
1344
- class: "gp-grid-filter-btn-apply",
1345
- onClick: handleApply
1346
- }, " Apply ")])
1347
- ]);
1348
- };
1349
- }
1350
- });
1351
- var DateFilterContent_default = _sfc_main$2;
1352
-
1353
- //#endregion
1354
- //#region src/components/FilterPopup.vue
1355
- const _hoisted_1$1 = { class: "gp-grid-filter-header" };
1356
- const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1357
- __name: "FilterPopup",
1358
- props: {
1359
- column: {},
1360
- colIndex: {},
1361
- anchorRect: {},
1362
- distinctValues: {},
1363
- currentFilter: {}
1364
- },
1365
- emits: ["apply", "close"],
1366
- setup(__props, { emit: __emit }) {
1367
- const props = __props;
1368
- const emit = __emit;
1369
- const popupRef = ref(null);
1370
- useFilterPopup(popupRef, {
1371
- onClose: () => emit("close"),
1372
- ignoreSelector: ".gp-grid-filter-icon"
1373
- });
1374
- const colId = computed(() => props.column.colId ?? props.column.field);
1375
- function handleApply(filter) {
1376
- emit("apply", colId.value, filter);
1377
- emit("close");
1378
- }
1379
- function handleClose() {
1380
- emit("close");
1381
- }
1382
- const dataType = computed(() => props.column.cellDataType);
1383
- computed(() => dataType.value === "text" || dataType.value === "object");
1384
- const isNumberType = computed(() => dataType.value === "number");
1385
- const isDateType = computed(() => dataType.value === "date" || dataType.value === "dateString" || dataType.value === "dateTime" || dataType.value === "dateTimeString");
1386
- const popupStyle = computed(() => ({
1387
- position: "fixed",
1388
- top: `${props.anchorRect.top + props.anchorRect.height + 4}px`,
1389
- left: `${props.anchorRect.left}px`,
1390
- minWidth: `${Math.max(200, props.anchorRect.width)}px`,
1391
- zIndex: 1e4
1392
- }));
1393
- return (_ctx, _cache) => {
1394
- return openBlock(), createElementBlock("div", {
1395
- ref_key: "popupRef",
1396
- ref: popupRef,
1397
- class: "gp-grid-filter-popup",
1398
- style: normalizeStyle(popupStyle.value)
1399
- }, [
1400
- createElementVNode("div", _hoisted_1$1, " Filter: " + toDisplayString(__props.column.headerName ?? __props.column.field), 1),
1401
- createCommentVNode(" Number filter "),
1402
- isNumberType.value ? (openBlock(), createBlock(NumberFilterContent_default, {
1403
- key: 0,
1404
- "current-filter": __props.currentFilter,
1405
- onApply: handleApply,
1406
- onClose: handleClose
1407
- }, null, 8, ["current-filter"])) : isDateType.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" Date filter "), createVNode(DateFilterContent_default, {
1408
- "current-filter": __props.currentFilter,
1409
- onApply: handleApply,
1410
- onClose: handleClose
1411
- }, null, 8, ["current-filter"])], 2112)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [createCommentVNode(" Text filter (default) "), createVNode(TextFilterContent_default, {
1412
- "distinct-values": __props.distinctValues,
1413
- "current-filter": __props.currentFilter,
1414
- onApply: handleApply,
1415
- onClose: handleClose
1416
- }, null, 8, ["distinct-values", "current-filter"])], 2112))
1417
- ], 4);
1418
- };
1419
- }
1420
- });
1421
- var FilterPopup_default = _sfc_main$1;
1422
-
1423
- //#endregion
1424
- //#region src/GpGrid.vue
1425
- const _hoisted_1 = ["data-col-index", "onClick"];
1426
- const _hoisted_2 = ["onMousedown", "onDblclick"];
1427
- const _hoisted_3 = {
1428
- key: 1,
1429
- class: "gp-grid-loading"
1430
- };
1431
- const _hoisted_4 = {
1432
- key: 2,
1433
- class: "gp-grid-error"
1434
- };
1435
- const _hoisted_5 = {
1436
- key: 3,
1437
- class: "gp-grid-empty"
1438
- };
1439
- const _sfc_main = /* @__PURE__ */ defineComponent({
1440
- __name: "GpGrid",
1441
- props: {
1442
- columns: {},
1443
- dataSource: {},
1444
- rowData: {},
1445
- rowHeight: {},
1446
- headerHeight: {},
1447
- overscan: { default: 3 },
1448
- sortingEnabled: {
1449
- type: Boolean,
1450
- default: true
1451
- },
1452
- darkMode: {
1453
- type: Boolean,
1454
- default: false
1455
- },
1456
- wheelDampening: { default: .1 },
1457
- cellRenderers: { default: () => ({}) },
1458
- editRenderers: { default: () => ({}) },
1459
- headerRenderers: { default: () => ({}) },
1460
- cellRenderer: {},
1461
- editRenderer: {},
1462
- headerRenderer: {}
1463
- },
1464
- setup(__props) {
1465
- injectStyles$1();
1466
- const props = __props;
1467
- const containerRef = ref(null);
1468
- const coreRef = shallowRef(null);
1469
- const { state, applyInstructions } = useGridState();
1470
- const totalHeaderHeight = computed(() => props.headerHeight ?? props.rowHeight);
1471
- const columnPositions = computed(() => calculateColumnPositions$1(props.columns));
1472
- const totalWidth = computed(() => getTotalWidth$1(columnPositions.value));
1473
- const slotsArray = computed(() => Array.from(state.slots.values()));
1474
- const { handleCellMouseDown, handleCellDoubleClick, handleFillHandleMouseDown, handleHeaderClick, handleKeyDown, handleWheel, dragState } = useInputHandler(coreRef, containerRef, computed(() => props.columns), {
1475
- activeCell: computed(() => state.activeCell),
1476
- selectionRange: computed(() => state.selectionRange),
1477
- editingCell: computed(() => state.editingCell),
1478
- filterPopupOpen: computed(() => state.filterPopup?.isOpen ?? false),
1479
- rowHeight: props.rowHeight,
1480
- headerHeight: totalHeaderHeight.value,
1481
- columnPositions,
1482
- slots: computed(() => state.slots)
1483
- });
1484
- const { fillHandlePosition } = useFillHandle({
1485
- activeCell: computed(() => state.activeCell),
1486
- selectionRange: computed(() => state.selectionRange),
1487
- slots: computed(() => state.slots),
1488
- columns: computed(() => props.columns),
1489
- columnPositions,
1490
- rowHeight: props.rowHeight
1491
- });
1492
- function handleScroll() {
1493
- const container = containerRef.value;
1494
- const core = coreRef.value;
1495
- if (!container || !core) return;
1496
- core.setViewport(container.scrollTop, container.scrollLeft, container.clientWidth, container.clientHeight);
1497
- }
1498
- function handleFilterApply(colId, filter) {
1499
- const core = coreRef.value;
1500
- if (core) core.setFilter(colId, filter);
1501
- }
1502
- function handleFilterPopupClose() {
1503
- const core = coreRef.value;
1504
- if (core) core.closeFilterPopup();
1505
- }
1506
- function getCellClasses(rowIndex, colIndex) {
1507
- const isEditing = isCellEditing$1(rowIndex, colIndex, state.editingCell);
1508
- return buildCellClasses$1(isCellActive$1(rowIndex, colIndex, state.activeCell), isCellSelected$1(rowIndex, colIndex, state.selectionRange), isEditing, isCellInFillPreview$1(rowIndex, colIndex, dragState.value.dragType === "fill", dragState.value.fillSourceRange, dragState.value.fillTarget));
1509
- }
1510
- onMounted(() => {
1511
- const dataSource = props.dataSource ?? (props.rowData ? createDataSourceFromArray$1(props.rowData) : createClientDataSource$1([]));
1512
- const core = new GridCore({
1513
- columns: props.columns,
1514
- dataSource,
1515
- rowHeight: props.rowHeight,
1516
- headerHeight: totalHeaderHeight.value,
1517
- overscan: props.overscan,
1518
- sortingEnabled: props.sortingEnabled
1519
- });
1520
- coreRef.value = core;
1521
- const unsubscribe = core.onBatchInstruction((instructions) => {
1522
- applyInstructions(instructions);
1523
- });
1524
- core.initialize();
1525
- const container = containerRef.value;
1526
- if (container) {
1527
- core.setViewport(container.scrollTop, container.scrollLeft, container.clientWidth, container.clientHeight);
1528
- const resizeObserver = new ResizeObserver(() => {
1529
- core.setViewport(container.scrollTop, container.scrollLeft, container.clientWidth, container.clientHeight);
1530
- });
1531
- resizeObserver.observe(container);
1532
- onUnmounted(() => {
1533
- resizeObserver.disconnect();
1534
- unsubscribe();
1535
- coreRef.value = null;
1536
- });
1537
- }
1538
- });
1539
- watch(() => props.dataSource, (dataSource) => {
1540
- if (dataSource) {
1541
- const mutableDataSource = dataSource;
1542
- if (mutableDataSource.subscribe) {
1543
- const unsubscribe = mutableDataSource.subscribe(() => {
1544
- coreRef.value?.refresh();
1545
- });
1546
- onUnmounted(() => unsubscribe());
1547
- }
1548
- }
1549
- }, { immediate: true });
1550
- return (_ctx, _cache) => {
1551
- return openBlock(), createElementBlock("div", {
1552
- ref_key: "containerRef",
1553
- ref: containerRef,
1554
- class: normalizeClass(["gp-grid-container", { "gp-grid-container--dark": __props.darkMode }]),
1555
- style: {
1556
- "width": "100%",
1557
- "height": "100%",
1558
- "overflow": "auto",
1559
- "position": "relative"
1560
- },
1561
- tabindex: "0",
1562
- onScroll: handleScroll,
1563
- onWheel: _cache[1] || (_cache[1] = (e) => unref(handleWheel)(e, __props.wheelDampening)),
1564
- onKeydown: _cache[2] || (_cache[2] = (...args) => unref(handleKeyDown) && unref(handleKeyDown)(...args))
1565
- }, [
1566
- createCommentVNode(" Content sizer "),
1567
- createElementVNode("div", { style: normalizeStyle({
1568
- width: `${Math.max(unref(state).contentWidth, totalWidth.value)}px`,
1569
- height: `${Math.max(unref(state).contentHeight, totalHeaderHeight.value)}px`,
1570
- position: "relative",
1571
- minWidth: "100%"
1572
- }) }, [
1573
- createCommentVNode(" Headers "),
1574
- createElementVNode("div", {
1575
- class: "gp-grid-header",
1576
- style: normalizeStyle({
1577
- position: "sticky",
1578
- top: 0,
1579
- left: 0,
1580
- height: `${totalHeaderHeight.value}px`,
1581
- width: `${Math.max(unref(state).contentWidth, totalWidth.value)}px`,
1582
- minWidth: "100%",
1583
- zIndex: 100
1584
- })
1585
- }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns, (column, colIndex) => {
1586
- return openBlock(), createElementBlock("div", {
1587
- key: column.colId ?? column.field,
1588
- class: "gp-grid-header-cell",
1589
- "data-col-index": colIndex,
1590
- style: normalizeStyle({
1591
- position: "absolute",
1592
- left: `${columnPositions.value[colIndex]}px`,
1593
- top: 0,
1594
- width: `${column.width}px`,
1595
- height: `${totalHeaderHeight.value}px`,
1596
- background: "transparent"
1597
- }),
1598
- onClick: (e) => unref(handleHeaderClick)(colIndex, e)
1599
- }, [(openBlock(), createBlock(resolveDynamicComponent(unref(renderHeader)({
1600
- column,
1601
- colIndex,
1602
- sortDirection: unref(state).headers.get(colIndex)?.sortDirection,
1603
- sortIndex: unref(state).headers.get(colIndex)?.sortIndex,
1604
- sortable: unref(state).headers.get(colIndex)?.sortable ?? true,
1605
- filterable: unref(state).headers.get(colIndex)?.filterable ?? true,
1606
- hasFilter: unref(state).headers.get(colIndex)?.hasFilter ?? false,
1607
- core: coreRef.value,
1608
- container: containerRef.value,
1609
- headerRenderers: __props.headerRenderers ?? {},
1610
- globalHeaderRenderer: __props.headerRenderer
1611
- }))))], 12, _hoisted_1);
1612
- }), 128))], 4),
1613
- createCommentVNode(" Row slots "),
1614
- (openBlock(true), createElementBlock(Fragment, null, renderList(slotsArray.value.filter((s) => s.rowIndex >= 0), (slot) => {
1615
- return openBlock(), createElementBlock("div", {
1616
- key: slot.slotId,
1617
- class: normalizeClass(["gp-grid-row", { "gp-grid-row--even": slot.rowIndex % 2 === 0 }]),
1618
- style: normalizeStyle({
1619
- position: "absolute",
1620
- top: 0,
1621
- left: 0,
1622
- transform: `translateY(${slot.translateY}px)`,
1623
- width: `${Math.max(unref(state).contentWidth, totalWidth.value)}px`,
1624
- height: `${__props.rowHeight}px`
1625
- })
1626
- }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns, (column, colIndex) => {
1627
- return openBlock(), createElementBlock("div", {
1628
- key: `${slot.slotId}-${colIndex}`,
1629
- class: normalizeClass(getCellClasses(slot.rowIndex, colIndex)),
1630
- style: normalizeStyle({
1631
- position: "absolute",
1632
- left: `${columnPositions.value[colIndex]}px`,
1633
- top: 0,
1634
- width: `${column.width}px`,
1635
- height: `${__props.rowHeight}px`
1636
- }),
1637
- onMousedown: (e) => unref(handleCellMouseDown)(slot.rowIndex, colIndex, e),
1638
- onDblclick: () => unref(handleCellDoubleClick)(slot.rowIndex, colIndex)
1639
- }, [createCommentVNode(" Edit mode "), unref(isCellEditing$1)(slot.rowIndex, colIndex, unref(state).editingCell) && unref(state).editingCell ? (openBlock(), createBlock(resolveDynamicComponent(unref(renderEditCell)({
1640
- column,
1641
- rowData: slot.rowData,
1642
- rowIndex: slot.rowIndex,
1643
- colIndex,
1644
- initialValue: unref(state).editingCell.initialValue,
1645
- core: coreRef.value,
1646
- editRenderers: __props.editRenderers ?? {},
1647
- globalEditRenderer: __props.editRenderer
1648
- })), { key: 0 })) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" View mode "), (openBlock(), createBlock(resolveDynamicComponent(unref(renderCell)({
1649
- column,
1650
- rowData: slot.rowData,
1651
- rowIndex: slot.rowIndex,
1652
- colIndex,
1653
- isActive: unref(isCellActive$1)(slot.rowIndex, colIndex, unref(state).activeCell),
1654
- isSelected: unref(isCellSelected$1)(slot.rowIndex, colIndex, unref(state).selectionRange),
1655
- isEditing: false,
1656
- cellRenderers: __props.cellRenderers ?? {},
1657
- globalCellRenderer: __props.cellRenderer
1658
- }))))], 64))], 46, _hoisted_2);
1659
- }), 128))], 6);
1660
- }), 128)),
1661
- createCommentVNode(" Fill handle "),
1662
- unref(fillHandlePosition) && !unref(state).editingCell ? (openBlock(), createElementBlock("div", {
1663
- key: 0,
1664
- class: "gp-grid-fill-handle",
1665
- style: normalizeStyle({
1666
- position: "absolute",
1667
- top: `${unref(fillHandlePosition).top}px`,
1668
- left: `${unref(fillHandlePosition).left}px`,
1669
- zIndex: 200
1670
- }),
1671
- onMousedown: _cache[0] || (_cache[0] = (...args) => unref(handleFillHandleMouseDown) && unref(handleFillHandleMouseDown)(...args))
1672
- }, null, 36)) : createCommentVNode("v-if", true),
1673
- createCommentVNode(" Loading indicator "),
1674
- unref(state).isLoading ? (openBlock(), createElementBlock("div", _hoisted_3, [..._cache[3] || (_cache[3] = [createElementVNode("div", { class: "gp-grid-loading-spinner" }, null, -1), createTextVNode(" Loading... ", -1)])])) : createCommentVNode("v-if", true),
1675
- createCommentVNode(" Error message "),
1676
- unref(state).error ? (openBlock(), createElementBlock("div", _hoisted_4, " Error: " + toDisplayString(unref(state).error), 1)) : createCommentVNode("v-if", true),
1677
- createCommentVNode(" Empty state "),
1678
- !unref(state).isLoading && !unref(state).error && unref(state).totalRows === 0 ? (openBlock(), createElementBlock("div", _hoisted_5, " No data to display ")) : createCommentVNode("v-if", true)
1679
- ], 4),
1680
- createCommentVNode(" Filter Popup "),
1681
- unref(state).filterPopup?.isOpen && unref(state).filterPopup.column && unref(state).filterPopup.anchorRect ? (openBlock(), createBlock(FilterPopup_default, {
1682
- key: 0,
1683
- column: unref(state).filterPopup.column,
1684
- "col-index": unref(state).filterPopup.colIndex,
1685
- "anchor-rect": unref(state).filterPopup.anchorRect,
1686
- "distinct-values": unref(state).filterPopup.distinctValues,
1687
- "current-filter": unref(state).filterPopup.currentFilter,
1688
- onApply: handleFilterApply,
1689
- onClose: handleFilterPopupClose
1690
- }, null, 8, [
1691
- "column",
1692
- "col-index",
1693
- "anchor-rect",
1694
- "distinct-values",
1695
- "current-filter"
1696
- ])) : createCommentVNode("v-if", true)
1697
- ], 34);
1698
- };
1699
- }
1700
- });
1701
- var GpGrid_default = _sfc_main;
1702
-
1703
- //#endregion
1704
- //#region src/composables/useGpGrid.ts
1705
- /**
1706
- * Nuxt-friendly composable for using gp-grid.
1707
- * Returns all the pieces needed to build a custom grid component.
1708
- */
1709
- function useGpGrid(options) {
1710
- injectStyles$1();
1711
- const containerRef = ref(null);
1712
- const coreRef = ref(null);
1713
- const { state, applyInstructions } = useGridState();
1714
- const totalHeaderHeight = computed(() => options.headerHeight ?? options.rowHeight);
1715
- const columnPositions = computed(() => calculateColumnPositions$1(options.columns));
1716
- const totalWidth = computed(() => getTotalWidth$1(columnPositions.value));
1717
- const slotsArray = computed(() => Array.from(state.slots.values()));
1718
- const { handleCellMouseDown, handleCellDoubleClick, handleFillHandleMouseDown, handleHeaderClick, handleKeyDown, handleWheel, dragState } = useInputHandler(coreRef, containerRef, computed(() => options.columns), {
1719
- activeCell: computed(() => state.activeCell),
1720
- selectionRange: computed(() => state.selectionRange),
1721
- editingCell: computed(() => state.editingCell),
1722
- filterPopupOpen: computed(() => state.filterPopup?.isOpen ?? false),
1723
- rowHeight: options.rowHeight,
1724
- headerHeight: totalHeaderHeight.value,
1725
- columnPositions,
1726
- slots: computed(() => state.slots)
1727
- });
1728
- const handleScroll = () => {
1729
- const container = containerRef.value;
1730
- const core = coreRef.value;
1731
- if (!container || !core) return;
1732
- core.setViewport(container.scrollTop, container.scrollLeft, container.clientWidth, container.clientHeight);
1733
- };
1734
- const handleFilterApply = (colId, filter) => {
1735
- const core = coreRef.value;
1736
- if (core) core.setFilter(colId, filter);
1737
- };
1738
- const handleFilterPopupClose = () => {
1739
- const core = coreRef.value;
1740
- if (core) core.closeFilterPopup();
1741
- };
1742
- onMounted(() => {
1743
- const dataSource = options.dataSource ?? (options.rowData ? createDataSourceFromArray$1(options.rowData) : createClientDataSource$1([]));
1744
- const core = new GridCore({
1745
- columns: options.columns,
1746
- dataSource,
1747
- rowHeight: options.rowHeight,
1748
- headerHeight: totalHeaderHeight.value,
1749
- overscan: options.overscan ?? 3,
1750
- sortingEnabled: options.sortingEnabled ?? true
1751
- });
1752
- coreRef.value = core;
1753
- const unsubscribe = core.onBatchInstruction((instructions) => {
1754
- applyInstructions(instructions);
1755
- });
1756
- core.initialize();
1757
- const container = containerRef.value;
1758
- if (container) {
1759
- core.setViewport(container.scrollTop, container.scrollLeft, container.clientWidth, container.clientHeight);
1760
- const resizeObserver = new ResizeObserver(() => {
1761
- core.setViewport(container.scrollTop, container.scrollLeft, container.clientWidth, container.clientHeight);
1762
- });
1763
- resizeObserver.observe(container);
1764
- onUnmounted(() => {
1765
- resizeObserver.disconnect();
1766
- unsubscribe();
1767
- coreRef.value = null;
1768
- });
1769
- }
1770
- });
1771
- watch(() => options.dataSource, (dataSource) => {
1772
- if (dataSource) {
1773
- const mutableDataSource = dataSource;
1774
- if (mutableDataSource.subscribe) {
1775
- const unsubscribe = mutableDataSource.subscribe(() => {
1776
- coreRef.value?.refresh();
1777
- });
1778
- onUnmounted(() => unsubscribe());
1779
- }
1780
- }
1781
- }, { immediate: true });
1782
- const { fillHandlePosition } = useFillHandle({
1783
- activeCell: computed(() => state.activeCell),
1784
- selectionRange: computed(() => state.selectionRange),
1785
- slots: computed(() => state.slots),
1786
- columns: computed(() => options.columns),
1787
- columnPositions,
1788
- rowHeight: options.rowHeight
1789
- });
1790
- return {
1791
- containerRef,
1792
- coreRef,
1793
- state,
1794
- slotsArray,
1795
- totalHeaderHeight,
1796
- columnPositions,
1797
- totalWidth,
1798
- fillHandlePosition,
1799
- handleScroll,
1800
- handleCellMouseDown,
1801
- handleCellDoubleClick,
1802
- handleFillHandleMouseDown,
1803
- handleHeaderClick,
1804
- handleKeyDown,
1805
- handleWheel,
1806
- handleFilterApply,
1807
- handleFilterPopupClose,
1808
- dragState,
1809
- isCellSelected: isCellSelected$1,
1810
- isCellActive: isCellActive$1,
1811
- isCellEditing: isCellEditing$1,
1812
- isCellInFillPreview: isCellInFillPreview$1,
1813
- buildCellClasses: buildCellClasses$1
1814
- };
1815
- }
1816
-
1817
- //#endregion
1818
- export { GpGrid_default as GpGrid, GpGrid_default as default, buildCellClasses, calculateColumnPositions, createClientDataSource, createDataSourceFromArray, createInitialState, createMutableClientDataSource, createServerDataSource, findColumnAtX, getCellValue, getTotalWidth, gridStyles, injectStyles, isCellActive, isCellEditing, isCellInFillPreview, isCellSelected, isRowVisible, renderCell, renderEditCell, renderHeader, useAutoScroll, useFillHandle, useFilterConditions, useFilterPopup, useGpGrid, useGridState, useInputHandler };
1819
- //# sourceMappingURL=index.js.map
1
+ import{Fragment as e,computed as t,createBlock as n,createCommentVNode as r,createElementBlock as i,createElementVNode as a,createTextVNode as o,createVNode as s,defineComponent as c,h as l,normalizeClass as u,normalizeStyle as d,onMounted as f,onUnmounted as p,openBlock as m,reactive as h,ref as g,renderList as _,resolveDynamicComponent as v,shallowRef as y,toDisplayString as b,unref as x,vModelText as S,watch as C,withDirectives as w}from"vue";import{GridCore as T,buildCellClasses as E,buildCellClasses as D,calculateColumnPositions as O,calculateColumnPositions as k,createClientDataSource as A,createClientDataSource as j,createDataSourceFromArray as M,createDataSourceFromArray as N,createMutableClientDataSource as P,createServerDataSource as F,findColumnAtX as I,getTotalWidth as L,getTotalWidth as R,gridStyles as z,injectStyles as ee,injectStyles as te,isCellActive as B,isCellActive as V,isCellEditing as H,isCellEditing as U,isCellInFillPreview as W,isCellInFillPreview as ne,isCellSelected as re,isCellSelected as G,isRowVisible as ie}from"gp-grid-core";function K(){return{slots:new Map,activeCell:null,selectionRange:null,editingCell:null,contentWidth:0,contentHeight:0,headers:new Map,filterPopup:null,isLoading:!1,error:null,totalRows:0,visibleRowRange:null}}function ae(e,t){switch(e.type){case`CREATE_SLOT`:t.slots.set(e.slotId,{slotId:e.slotId,rowIndex:-1,rowData:{},translateY:0});break;case`DESTROY_SLOT`:t.slots.delete(e.slotId);break;case`ASSIGN_SLOT`:{let n=t.slots.get(e.slotId);n&&t.slots.set(e.slotId,{...n,rowIndex:e.rowIndex,rowData:e.rowData});break}case`MOVE_SLOT`:{let n=t.slots.get(e.slotId);n&&t.slots.set(e.slotId,{...n,translateY:e.translateY});break}case`SET_ACTIVE_CELL`:t.activeCell=e.position;break;case`SET_SELECTION_RANGE`:t.selectionRange=e.range;break;case`UPDATE_VISIBLE_RANGE`:t.visibleRowRange={start:e.start,end:e.end};break;case`START_EDIT`:t.editingCell={row:e.row,col:e.col,initialValue:e.initialValue};break;case`STOP_EDIT`:t.editingCell=null;break;case`SET_CONTENT_SIZE`:t.contentWidth=e.width,t.contentHeight=e.height;break;case`UPDATE_HEADER`:t.headers.set(e.colIndex,{column:e.column,sortDirection:e.sortDirection,sortIndex:e.sortIndex,sortable:e.sortable,filterable:e.filterable,hasFilter:e.hasFilter});break;case`OPEN_FILTER_POPUP`:t.filterPopup={isOpen:!0,colIndex:e.colIndex,column:e.column,anchorRect:e.anchorRect,distinctValues:e.distinctValues,currentFilter:e.currentFilter};break;case`CLOSE_FILTER_POPUP`:t.filterPopup=null;break;case`DATA_LOADING`:t.isLoading=!0,t.error=null;break;case`DATA_LOADED`:t.isLoading=!1,t.totalRows=e.totalRows;break;case`DATA_ERROR`:t.isLoading=!1,t.error=e.error;break;case`ROWS_ADDED`:case`ROWS_REMOVED`:t.totalRows=e.totalRows;break;case`ROWS_UPDATED`:case`TRANSACTION_PROCESSED`:break}}function q(){let e=h(K());function t(t){for(let n of t)ae(n,e)}function n(){let t=K();e.slots=t.slots,e.activeCell=t.activeCell,e.selectionRange=t.selectionRange,e.editingCell=t.editingCell,e.contentWidth=t.contentWidth,e.contentHeight=t.contentHeight,e.headers=t.headers,e.filterPopup=t.filterPopup,e.isLoading=t.isLoading,e.error=t.error,e.totalRows=t.totalRows,e.visibleRowRange=t.visibleRowRange}return{state:e,applyInstructions:t,reset:n}}function J(e){let t=g(null);function n(n,r){t.value&&clearInterval(t.value),t.value=setInterval(()=>{let t=e.value;t&&(t.scrollTop+=r,t.scrollLeft+=n)},16)}function r(){t.value&&=(clearInterval(t.value),null)}return p(()=>{r()}),{startAutoScroll:n,stopAutoScroll:r}}function oe(e,t){for(let n of e.values())if(n.rowIndex===t)return n;return null}function se(e,t,n,r,i,a){let o=oe(a,n),s=(o?o.translateY:i+n*r)-t.scrollTop,c=s+r,l=i,u=t.clientHeight;if(s<l)t.scrollTop=e.getScrollTopForRow(n);else if(c>u){let a=t.clientHeight-i,o=Math.floor(a/r),s=Math.max(0,n-o+1);t.scrollTop=e.getScrollTopForRow(s)}}function Y(e,t,n,r){let{activeCell:i,selectionRange:a,editingCell:o,filterPopupOpen:s,rowHeight:c,headerHeight:l,columnPositions:u,slots:d}=r,{startAutoScroll:f,stopAutoScroll:m}=J(t),h=g({isDragging:!1,dragType:null,fillSourceRange:null,fillTarget:null});C([()=>l,()=>c,u,()=>n.value.length],()=>{let t=e.value;t?.input&&t.input.updateDeps({getHeaderHeight:()=>l,getRowHeight:()=>c,getColumnPositions:()=>u.value,getColumnCount:()=>n.value.length})},{immediate:!0});function _(){let e=t.value;if(!e)return null;let n=e.getBoundingClientRect();return{top:n.top,left:n.left,width:n.width,height:n.height,scrollTop:e.scrollTop,scrollLeft:e.scrollLeft}}function v(e){return{clientX:e.clientX,clientY:e.clientY,button:e.button,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey}}function y(){let t=t=>{let n=e.value,r=_();if(!n?.input||!r)return;let i=n.input.handleDragMove(v(t),r);i&&(i.autoScroll?f(i.autoScroll.dx,i.autoScroll.dy):m(),h.value=n.input.getDragState())},n=()=>{let r=e.value;r?.input&&(r.input.handleDragEnd(),h.value=r.input.getDragState()),m(),document.removeEventListener(`mousemove`,t),document.removeEventListener(`mouseup`,n)};document.addEventListener(`mousemove`,t),document.addEventListener(`mouseup`,n)}function b(n,r,i){let a=e.value;if(!a?.input)return;let o=a.input.handleCellMouseDown(n,r,v(i));o.focusContainer&&t.value?.focus(),o.startDrag===`selection`&&(a.input.startSelectionDrag(),h.value=a.input.getDragState(),y())}function x(t,n){let r=e.value;r?.input&&r.input.handleCellDoubleClick(t,n)}function S(t){let n=e.value;if(!n?.input)return;let r=n.input.handleFillHandleMouseDown(i.value,a.value,v(t));r.preventDefault&&t.preventDefault(),r.stopPropagation&&t.stopPropagation(),r.startDrag===`fill`&&(h.value=n.input.getDragState(),y())}function w(t,r){let i=e.value;if(!i?.input)return;let a=n.value[t];if(!a)return;let o=a.colId??a.field;i.input.handleHeaderClick(o,r.shiftKey)}function T(n){let r=e.value,a=t.value;if(!r?.input)return;let u=r.input.handleKeyDown({key:n.key,shiftKey:n.shiftKey,ctrlKey:n.ctrlKey,metaKey:n.metaKey},i.value,o.value,s.value);u.preventDefault&&n.preventDefault(),u.scrollToCell&&a&&se(r,a,u.scrollToCell.row,c,l,d.value)}function E(n,r){let i=e.value,a=t.value;if(!i?.input||!a)return;let o=i.input.handleWheel(n.deltaY,n.deltaX,r);o&&(n.preventDefault(),a.scrollTop+=o.dy,a.scrollLeft+=o.dx)}return p(()=>{m()}),{handleCellMouseDown:b,handleCellDoubleClick:x,handleFillHandleMouseDown:S,handleHeaderClick:w,handleKeyDown:T,handleWheel:E,dragState:h}}function X(e){let{activeCell:n,selectionRange:r,slots:i,columns:a,columnPositions:o,rowHeight:s}=e;return{fillHandlePosition:t(()=>{let e=n.value,t=r.value,c=i.value;if(!e&&!t)return null;let l,u,d,f;if(t)l=Math.max(t.startRow,t.endRow),u=Math.max(t.startCol,t.endCol),d=Math.min(t.startCol,t.endCol),f=Math.max(t.startCol,t.endCol);else if(e)l=e.row,u=e.col,d=u,f=u;else return null;let p=a.value;for(let e=d;e<=f;e++){let t=p[e];if(!t||t.editable!==!0)return null}let m=null;for(let e of c.values())if(e.rowIndex===l){m=e.translateY;break}if(m===null)return null;let h=o.value[u]??0,g=p[u]?.width??0;return{top:m+s-5,left:h+g-20}})}}function ce(e){return e==null||e===``?o(``):typeof e==`string`?o(e):e}function Z(e,t){let n=t.split(`.`),r=e;for(let e of n){if(typeof r!=`object`||!r)return null;r=r[e]}return r??null}function le(e){let{column:t,rowData:n,rowIndex:r,colIndex:i,isActive:a,isSelected:s,isEditing:c,cellRenderers:l,globalCellRenderer:u}=e,d=Z(n,t.field),f={value:d,rowData:n,column:t,rowIndex:r,colIndex:i,isActive:a,isSelected:s,isEditing:c};if(t.cellRenderer&&typeof t.cellRenderer==`string`){let e=l[t.cellRenderer];if(e)return ce(e(f))}return u?ce(u(f)):o(d==null?``:String(d))}function ue(e){return e==null||e===``?o(``):typeof e==`string`?o(e):e}function Q(e){let{column:t,rowData:n,rowIndex:r,colIndex:i,initialValue:a,core:s,editRenderers:c,globalEditRenderer:u}=e;if(!s)return o(``);let d={value:Z(n,t.field),rowData:n,column:t,rowIndex:r,colIndex:i,isActive:!0,isSelected:!0,isEditing:!0,initialValue:a,onValueChange:e=>s.updateEditValue(e),onCommit:()=>s.commitEdit(),onCancel:()=>s.cancelEdit()};if(t.editRenderer&&typeof t.editRenderer==`string`){let e=c[t.editRenderer];if(e)return ue(e(d))}return u?ue(u(d)):l(`input`,{class:`gp-grid-edit-input`,type:`text`,value:a==null?``:String(a),autofocus:!0,onFocus:e=>e.target.select(),onInput:e=>s.updateEditValue(e.target.value),onKeydown:e=>{e.stopPropagation(),e.key===`Enter`?s.commitEdit():e.key===`Escape`?s.cancelEdit():e.key===`Tab`&&(e.preventDefault(),s.commitEdit(),s.selection.moveFocus(e.shiftKey?`left`:`right`,!1))},onBlur:()=>s.commitEdit()})}function de(e){return e==null||e===``?o(``):typeof e==`string`?o(e):e}function fe(t){let{column:n,colIndex:r,sortDirection:i,sortIndex:a,sortable:o,filterable:s,hasFilter:c,core:u,container:d,headerRenderers:f,globalHeaderRenderer:p}=t,m={column:n,colIndex:r,sortDirection:i,sortIndex:a,sortable:o,filterable:s,hasFilter:c,onSort:(e,t)=>{u&&o&&u.setSort(n.colId??n.field,e,t)},onFilterClick:()=>{if(u&&s){let e=d?.querySelector(`[data-col-index="${r}"]`);if(e){let t=e.getBoundingClientRect();u.openFilterPopup(r,{top:t.top,left:t.left,width:t.width,height:t.height})}}}};if(n.headerRenderer&&typeof n.headerRenderer==`string`){let e=f[n.headerRenderer];if(e)return de(e(m))}if(p)return de(p(m));let h=[l(`span`,{class:`gp-grid-header-text`},n.headerName??n.field)],g=[];if(o){let e=[l(`span`,{class:`gp-grid-sort-arrows-stack`},[l(`svg`,{class:`gp-grid-sort-arrow-up${i===`asc`?` active`:``}`,width:`8`,height:`6`,viewBox:`0 0 8 6`},[l(`path`,{d:`M4 0L8 6H0L4 0Z`,fill:`currentColor`})]),l(`svg`,{class:`gp-grid-sort-arrow-down${i===`desc`?` active`:``}`,width:`8`,height:`6`,viewBox:`0 0 8 6`},[l(`path`,{d:`M4 6L0 0H8L4 6Z`,fill:`currentColor`})])])];a!==void 0&&a>0&&e.push(l(`span`,{class:`gp-grid-sort-index`},String(a))),g.push(l(`span`,{class:`gp-grid-sort-arrows`},e))}return s&&g.push(l(`span`,{class:`gp-grid-filter-icon${c?` active`:``}`,onMousedown:e=>{e.stopPropagation(),e.preventDefault(),m.onFilterClick()},onClick:e=>{e.stopPropagation()}},[l(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`currentColor`},[l(`path`,{d:`M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z`})])])),g.length>0&&h.push(l(`span`,{class:`gp-grid-header-icons`},g)),l(e,h)}function pe(e,t){let{onClose:n,ignoreSelector:r=`.gp-grid-filter-icon`}=t,i=null,a=null;f(()=>{i=t=>{let i=t.target;r&&i.closest(r)||e.value&&!e.value.contains(i)&&n()},a=e=>{e.key===`Escape`&&n()},requestAnimationFrame(()=>{i&&document.addEventListener(`mousedown`,i),a&&document.addEventListener(`keydown`,a)})}),p(()=>{i&&document.removeEventListener(`mousedown`,i),a&&document.removeEventListener(`keydown`,a)})}function $(e,t=`and`){let n=g([...e]);return{conditions:n,combination:g(t),updateCondition:(e,t)=>{let r=[...n.value];r[e]={...r[e],...t},n.value=r},addCondition:e=>{n.value=[...n.value,{operator:e,value:``,valueTo:``,nextOperator:`and`}]},removeCondition:e=>{n.value=n.value.filter((t,n)=>n!==e)}}}const me={class:`gp-grid-filter-content gp-grid-filter-text`},he={key:0,class:`gp-grid-filter-mode-toggle`},ge={key:1,class:`gp-grid-filter-info`},_e={class:`gp-grid-filter-actions`},ve=[`disabled`],ye={class:`gp-grid-filter-list`},be={key:0,class:`gp-grid-filter-option`},xe=[`checked`],Se=[`checked`,`onChange`],Ce={key:0,class:`gp-grid-filter-combination`},we=[`onClick`],Te=[`onClick`],Ee={class:`gp-grid-filter-row`},De=[`value`,`autofocus`,`onChange`],Oe=[`value`],ke=[`value`,`onInput`],Ae=[`onClick`];var je=c({__name:`TextFilterContent`,props:{distinctValues:{},currentFilter:{}},emits:[`apply`,`close`],setup(n,{emit:o}){let s=[{value:`contains`,label:`Contains`},{value:`notContains`,label:`Does not contain`},{value:`equals`,label:`Equals`},{value:`notEquals`,label:`Does not equal`},{value:`startsWith`,label:`Starts with`},{value:`endsWith`,label:`Ends with`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Is not blank`}],c=n,l=o;function d(e){return Array.isArray(e)?e.join(`, `):String(e??``)}let f=t(()=>{let e=c.distinctValues.filter(e=>e!=null&&e!==``&&!(Array.isArray(e)&&e.length===0)).map(e=>d(e));return Array.from(new Set(e)).sort((e,t)=>{let n=parseFloat(e),r=parseFloat(t);return!isNaN(n)&&!isNaN(r)?n-r:e.localeCompare(t,void 0,{numeric:!0,sensitivity:`base`})})}),p=t(()=>f.value.length>100),h=g(t(()=>{if(!c.currentFilter?.conditions[0])return p.value?`condition`:`values`;let e=c.currentFilter.conditions[0];return e.selectedValues&&e.selectedValues.size>0?`values`:`condition`}).value),v=t(()=>c.currentFilter?.conditions[0]?c.currentFilter.conditions[0].selectedValues??new Set:new Set),y=t(()=>c.currentFilter?.conditions[0]?c.currentFilter.conditions[0].includeBlank??!0:!0),C=g(``),T=g(new Set(v.value)),E=g(y.value),{conditions:D,combination:O,updateCondition:k,addCondition:A,removeCondition:j}=$(t(()=>{if(!c.currentFilter?.conditions.length)return[{operator:`contains`,value:``,valueTo:``,nextOperator:`and`}];let e=c.currentFilter.conditions[0];if(e.selectedValues&&e.selectedValues.size>0)return[{operator:`contains`,value:``,valueTo:``,nextOperator:`and`}];let t=c.currentFilter.combination??`and`;return c.currentFilter.conditions.map(e=>{let n=e;return{operator:n.operator,value:n.value??``,valueTo:``,nextOperator:n.nextOperator??t}})}).value,c.currentFilter?.combination??`and`),M=t(()=>{if(!C.value)return f.value;let e=C.value.toLowerCase();return f.value.filter(t=>t.toLowerCase().includes(e))}),N=t(()=>c.distinctValues.some(e=>e==null||e===``)),P=t(()=>M.value.every(e=>T.value.has(e))&&(!N.value||E.value));function F(){T.value=new Set(M.value),N.value&&(E.value=!0)}function I(){T.value=new Set,E.value=!1}function L(e){let t=new Set(T.value);t.has(e)?t.delete(e):t.add(e),T.value=t}function R(){if(h.value===`values`){if(f.value.every(e=>T.value.has(e))&&(!N.value||E.value)){l(`apply`,null);return}l(`apply`,{conditions:[{type:`text`,operator:`equals`,selectedValues:T.value,includeBlank:E.value}],combination:`and`})}else{let e=D.value.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.value.trim()!==``);if(e.length===0){l(`apply`,null);return}l(`apply`,{conditions:e.map(e=>({type:`text`,operator:e.operator,value:e.value,nextOperator:e.nextOperator})),combination:`and`})}}function z(){l(`apply`,null)}return(t,n)=>(m(),i(`div`,me,[r(` Mode toggle - only show if not too many values `),p.value?r(`v-if`,!0):(m(),i(`div`,he,[a(`button`,{type:`button`,class:u({active:h.value===`values`}),onClick:n[0]||=e=>h.value=`values`},` Values `,2),a(`button`,{type:`button`,class:u({active:h.value===`condition`}),onClick:n[1]||=e=>h.value=`condition`},` Condition `,2)])),r(` Too many values message `),p.value&&h.value===`condition`?(m(),i(`div`,ge,` Too many unique values (`+b(f.value.length)+`). Use conditions to filter. `,1)):r(`v-if`,!0),r(` VALUES MODE `),h.value===`values`?(m(),i(e,{key:2},[r(` Search input `),w(a(`input`,{"onUpdate:modelValue":n[2]||=e=>C.value=e,class:`gp-grid-filter-search`,type:`text`,placeholder:`Search...`,autofocus:``},null,512),[[S,C.value]]),r(` Select all / Deselect all `),a(`div`,_e,[a(`button`,{type:`button`,disabled:P.value,onClick:F},` Select All `,8,ve),a(`button`,{type:`button`,onClick:I},` Deselect All `)]),r(` Checkbox list `),a(`div`,ye,[r(` Blanks option `),N.value?(m(),i(`label`,be,[a(`input`,{type:`checkbox`,checked:E.value,onChange:n[3]||=e=>E.value=!E.value},null,40,xe),n[5]||=a(`span`,{class:`gp-grid-filter-blank`},`(Blanks)`,-1)])):r(`v-if`,!0),r(` Values `),(m(!0),i(e,null,_(M.value,e=>(m(),i(`label`,{key:e,class:`gp-grid-filter-option`},[a(`input`,{type:`checkbox`,checked:T.value.has(e),onChange:t=>L(e)},null,40,Se),a(`span`,null,b(e),1)]))),128))])],64)):r(`v-if`,!0),r(` CONDITION MODE `),h.value===`condition`?(m(),i(e,{key:3},[(m(!0),i(e,null,_(x(D),(t,n)=>(m(),i(`div`,{key:n,class:`gp-grid-filter-condition`},[r(` Combination toggle (AND/OR) for conditions after the first `),n>0?(m(),i(`div`,Ce,[a(`button`,{type:`button`,class:u({active:x(D)[n-1]?.nextOperator===`and`}),onClick:e=>x(k)(n-1,{nextOperator:`and`})},` AND `,10,we),a(`button`,{type:`button`,class:u({active:x(D)[n-1]?.nextOperator===`or`}),onClick:e=>x(k)(n-1,{nextOperator:`or`})},` OR `,10,Te)])):r(`v-if`,!0),a(`div`,Ee,[r(` Operator select `),a(`select`,{value:t.operator,autofocus:n===0,onChange:e=>x(k)(n,{operator:e.target.value})},[(m(),i(e,null,_(s,e=>a(`option`,{key:e.value,value:e.value},b(e.label),9,Oe)),64))],40,De),r(` Text input (hidden for blank/notBlank) `),t.operator!==`blank`&&t.operator!==`notBlank`?(m(),i(`input`,{key:0,type:`text`,value:t.value,placeholder:`Value`,class:`gp-grid-filter-text-input`,onInput:e=>x(k)(n,{value:e.target.value})},null,40,ke)):r(`v-if`,!0),r(` Remove button (only if more than one condition) `),x(D).length>1?(m(),i(`button`,{key:1,type:`button`,class:`gp-grid-filter-remove`,onClick:e=>x(j)(n)},` × `,8,Ae)):r(`v-if`,!0)])]))),128)),r(` Add condition button `),a(`button`,{type:`button`,class:`gp-grid-filter-add`,onClick:n[4]||=e=>x(A)(`contains`)},` + Add condition `)],64)):r(`v-if`,!0),r(` Apply/Clear buttons `),a(`div`,{class:`gp-grid-filter-buttons`},[a(`button`,{type:`button`,class:`gp-grid-filter-btn-clear`,onClick:z},` Clear `),a(`button`,{type:`button`,class:`gp-grid-filter-btn-apply`,onClick:R},` Apply `)])]))}});const Me={class:`gp-grid-filter-content gp-grid-filter-number`},Ne={key:0,class:`gp-grid-filter-combination`},Pe=[`onClick`],Fe=[`onClick`],Ie={class:`gp-grid-filter-row`},Le=[`value`,`onChange`],Re=[`value`],ze=[`value`,`onInput`],Be=[`value`,`onInput`],Ve=[`onClick`];var He=c({__name:`NumberFilterContent`,props:{currentFilter:{}},emits:[`apply`,`close`],setup(n,{emit:o}){let s=[{value:`=`,label:`=`},{value:`!=`,label:`≠`},{value:`>`,label:`>`},{value:`<`,label:`<`},{value:`>=`,label:`≥`},{value:`<=`,label:`≤`},{value:`between`,label:`↔`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Not blank`}],c=n,l=o,{conditions:d,combination:f,updateCondition:p,addCondition:h,removeCondition:g}=$(t(()=>{if(!c.currentFilter?.conditions.length)return[{operator:`=`,value:``,valueTo:``,nextOperator:`and`}];let e=c.currentFilter.combination??`and`;return c.currentFilter.conditions.map(t=>{let n=t;return{operator:n.operator,value:n.value==null?``:String(n.value),valueTo:n.valueTo==null?``:String(n.valueTo),nextOperator:n.nextOperator??e}})}).value,c.currentFilter?.combination??`and`);function v(){let e=d.value.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.operator===`between`?e.value!==``&&e.valueTo!==``:e.value!==``);if(e.length===0){l(`apply`,null);return}l(`apply`,{conditions:e.map(e=>({type:`number`,operator:e.operator,value:e.value?parseFloat(e.value):void 0,valueTo:e.valueTo?parseFloat(e.valueTo):void 0,nextOperator:e.nextOperator})),combination:`and`})}function y(){l(`apply`,null)}return(t,n)=>(m(),i(`div`,Me,[(m(!0),i(e,null,_(x(d),(t,o)=>(m(),i(`div`,{key:o,class:`gp-grid-filter-condition`},[r(` Combination toggle (AND/OR) for conditions after the first `),o>0?(m(),i(`div`,Ne,[a(`button`,{type:`button`,class:u({active:x(d)[o-1]?.nextOperator===`and`}),onClick:e=>x(p)(o-1,{nextOperator:`and`})},` AND `,10,Pe),a(`button`,{type:`button`,class:u({active:x(d)[o-1]?.nextOperator===`or`}),onClick:e=>x(p)(o-1,{nextOperator:`or`})},` OR `,10,Fe)])):r(`v-if`,!0),a(`div`,Ie,[r(` Operator select `),a(`select`,{value:t.operator,onChange:e=>x(p)(o,{operator:e.target.value})},[(m(),i(e,null,_(s,e=>a(`option`,{key:e.value,value:e.value},b(e.label),9,Re)),64))],40,Le),r(` Number input (hidden for blank/notBlank) `),t.operator!==`blank`&&t.operator!==`notBlank`?(m(),i(`input`,{key:0,type:`number`,value:t.value,placeholder:`Value`,onInput:e=>x(p)(o,{value:e.target.value})},null,40,ze)):r(`v-if`,!0),r(` Second number input for "between" `),t.operator===`between`?(m(),i(e,{key:1},[n[1]||=a(`span`,{class:`gp-grid-filter-to`},`to`,-1),a(`input`,{type:`number`,value:t.valueTo,placeholder:`Value`,onInput:e=>x(p)(o,{valueTo:e.target.value})},null,40,Be)],64)):r(`v-if`,!0),r(` Remove button (only if more than one condition) `),x(d).length>1?(m(),i(`button`,{key:2,type:`button`,class:`gp-grid-filter-remove`,onClick:e=>x(g)(o)},` × `,8,Ve)):r(`v-if`,!0)])]))),128)),r(` Add condition button `),a(`button`,{type:`button`,class:`gp-grid-filter-add`,onClick:n[0]||=e=>x(h)(`=`)},` + Add condition `),r(` Apply/Clear buttons `),a(`div`,{class:`gp-grid-filter-buttons`},[a(`button`,{type:`button`,class:`gp-grid-filter-btn-clear`,onClick:y},` Clear `),a(`button`,{type:`button`,class:`gp-grid-filter-btn-apply`,onClick:v},` Apply `)])]))}});const Ue={class:`gp-grid-filter-content gp-grid-filter-date`},We={key:0,class:`gp-grid-filter-combination`},Ge=[`onClick`],Ke=[`onClick`],qe={class:`gp-grid-filter-row`},Je=[`value`,`onChange`],Ye=[`value`],Xe=[`value`,`onInput`],Ze=[`value`,`onInput`],Qe=[`onClick`];var $e=c({__name:`DateFilterContent`,props:{currentFilter:{}},emits:[`apply`,`close`],setup(n,{emit:o}){let s=[{value:`=`,label:`=`},{value:`!=`,label:`≠`},{value:`>`,label:`>`},{value:`<`,label:`<`},{value:`between`,label:`↔`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Not blank`}],c=n,l=o;function d(e){if(!e)return``;let t=typeof e==`string`?new Date(e):e;return isNaN(t.getTime())?``:t.toISOString().split(`T`)[0]}let{conditions:f,combination:p,updateCondition:h,addCondition:g,removeCondition:v}=$(t(()=>{if(!c.currentFilter?.conditions.length)return[{operator:`=`,value:``,valueTo:``,nextOperator:`and`}];let e=c.currentFilter.combination??`and`;return c.currentFilter.conditions.map(t=>{let n=t;return{operator:n.operator,value:d(n.value),valueTo:d(n.valueTo),nextOperator:n.nextOperator??e}})}).value,c.currentFilter?.combination??`and`);function y(){let e=f.value.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.operator===`between`?e.value!==``&&e.valueTo!==``:e.value!==``);if(e.length===0){l(`apply`,null);return}l(`apply`,{conditions:e.map(e=>({type:`date`,operator:e.operator,value:e.value||void 0,valueTo:e.valueTo||void 0,nextOperator:e.nextOperator})),combination:`and`})}function S(){l(`apply`,null)}return(t,n)=>(m(),i(`div`,Ue,[(m(!0),i(e,null,_(x(f),(t,o)=>(m(),i(`div`,{key:o,class:`gp-grid-filter-condition`},[r(` Combination toggle (AND/OR) for conditions after the first `),o>0?(m(),i(`div`,We,[a(`button`,{type:`button`,class:u({active:x(f)[o-1]?.nextOperator===`and`}),onClick:e=>x(h)(o-1,{nextOperator:`and`})},` AND `,10,Ge),a(`button`,{type:`button`,class:u({active:x(f)[o-1]?.nextOperator===`or`}),onClick:e=>x(h)(o-1,{nextOperator:`or`})},` OR `,10,Ke)])):r(`v-if`,!0),a(`div`,qe,[r(` Operator select `),a(`select`,{value:t.operator,onChange:e=>x(h)(o,{operator:e.target.value})},[(m(),i(e,null,_(s,e=>a(`option`,{key:e.value,value:e.value},b(e.label),9,Ye)),64))],40,Je),r(` Date input (hidden for blank/notBlank) `),t.operator!==`blank`&&t.operator!==`notBlank`?(m(),i(`input`,{key:0,type:`date`,value:t.value,onInput:e=>x(h)(o,{value:e.target.value})},null,40,Xe)):r(`v-if`,!0),r(` Second date input for "between" `),t.operator===`between`?(m(),i(e,{key:1},[n[1]||=a(`span`,{class:`gp-grid-filter-to`},`to`,-1),a(`input`,{type:`date`,value:t.valueTo,onInput:e=>x(h)(o,{valueTo:e.target.value})},null,40,Ze)],64)):r(`v-if`,!0),r(` Remove button (only if more than one condition) `),x(f).length>1?(m(),i(`button`,{key:2,type:`button`,class:`gp-grid-filter-remove`,onClick:e=>x(v)(o)},` × `,8,Qe)):r(`v-if`,!0)])]))),128)),r(` Add condition button `),a(`button`,{type:`button`,class:`gp-grid-filter-add`,onClick:n[0]||=e=>x(g)(`=`)},` + Add condition `),r(` Apply/Clear buttons `),a(`div`,{class:`gp-grid-filter-buttons`},[a(`button`,{type:`button`,class:`gp-grid-filter-btn-clear`,onClick:S},` Clear `),a(`button`,{type:`button`,class:`gp-grid-filter-btn-apply`,onClick:y},` Apply `)])]))}});const et={class:`gp-grid-filter-header`};var tt=c({__name:`FilterPopup`,props:{column:{},colIndex:{},anchorRect:{},distinctValues:{},currentFilter:{}},emits:[`apply`,`close`],setup(o,{emit:c}){let l=o,u=c,f=g(null);pe(f,{onClose:()=>u(`close`),ignoreSelector:`.gp-grid-filter-icon`});let p=t(()=>l.column.colId??l.column.field);function h(e){u(`apply`,p.value,e),u(`close`)}function _(){u(`close`)}let v=t(()=>l.column.cellDataType);t(()=>v.value===`text`||v.value===`object`);let y=t(()=>v.value===`number`),x=t(()=>v.value===`date`||v.value===`dateString`||v.value===`dateTime`||v.value===`dateTimeString`),S=t(()=>({position:`fixed`,top:`${l.anchorRect.top+l.anchorRect.height+4}px`,left:`${l.anchorRect.left}px`,minWidth:`${Math.max(200,l.anchorRect.width)}px`,zIndex:1e4}));return(t,c)=>(m(),i(`div`,{ref_key:`popupRef`,ref:f,class:`gp-grid-filter-popup`,style:d(S.value)},[a(`div`,et,` Filter: `+b(o.column.headerName??o.column.field),1),r(` Number filter `),y.value?(m(),n(He,{key:0,"current-filter":o.currentFilter,onApply:h,onClose:_},null,8,[`current-filter`])):x.value?(m(),i(e,{key:1},[r(` Date filter `),s($e,{"current-filter":o.currentFilter,onApply:h,onClose:_},null,8,[`current-filter`])],2112)):(m(),i(e,{key:2},[r(` Text filter (default) `),s(je,{"distinct-values":o.distinctValues,"current-filter":o.currentFilter,onApply:h,onClose:_},null,8,[`distinct-values`,`current-filter`])],2112))],4))}});const nt=[`data-col-index`,`onClick`],rt=[`onMousedown`,`onDblclick`],it={key:1,class:`gp-grid-loading`},at={key:2,class:`gp-grid-error`},ot={key:3,class:`gp-grid-empty`};var st=c({__name:`GpGrid`,props:{columns:{},dataSource:{},rowData:{},rowHeight:{},headerHeight:{},overscan:{default:3},sortingEnabled:{type:Boolean,default:!0},darkMode:{type:Boolean,default:!1},wheelDampening:{default:.1},cellRenderers:{default:()=>({})},editRenderers:{default:()=>({})},headerRenderers:{default:()=>({})},cellRenderer:{},editRenderer:{},headerRenderer:{}},setup(s){te();let c=s,l=g(null),h=y(null),{state:S,applyInstructions:w}=q(),E=t(()=>c.headerHeight??c.rowHeight),O=t(()=>k(c.columns)),A=t(()=>R(O.value)),M=t(()=>Array.from(S.slots.values())),{handleCellMouseDown:P,handleCellDoubleClick:F,handleFillHandleMouseDown:I,handleHeaderClick:L,handleKeyDown:z,handleWheel:ee,dragState:B}=Y(h,l,t(()=>c.columns),{activeCell:t(()=>S.activeCell),selectionRange:t(()=>S.selectionRange),editingCell:t(()=>S.editingCell),filterPopupOpen:t(()=>S.filterPopup?.isOpen??!1),rowHeight:c.rowHeight,headerHeight:E.value,columnPositions:O,slots:t(()=>S.slots)}),{fillHandlePosition:H}=X({activeCell:t(()=>S.activeCell),selectionRange:t(()=>S.selectionRange),slots:t(()=>S.slots),columns:t(()=>c.columns),columnPositions:O,rowHeight:c.rowHeight});function W(){let e=l.value,t=h.value;!e||!t||t.setViewport(e.scrollTop,e.scrollLeft,e.clientWidth,e.clientHeight)}function re(e,t){let n=h.value;n&&n.setFilter(e,t)}function ie(){let e=h.value;e&&e.closeFilterPopup()}function K(e,t){let n=U(e,t,S.editingCell);return D(V(e,t,S.activeCell),G(e,t,S.selectionRange),n,ne(e,t,B.value.dragType===`fill`,B.value.fillSourceRange,B.value.fillTarget))}return f(()=>{let e=c.dataSource??(c.rowData?N(c.rowData):j([])),t=new T({columns:c.columns,dataSource:e,rowHeight:c.rowHeight,headerHeight:E.value,overscan:c.overscan,sortingEnabled:c.sortingEnabled});h.value=t;let n=t.onBatchInstruction(e=>{w(e)});t.initialize();let r=l.value;if(r){t.setViewport(r.scrollTop,r.scrollLeft,r.clientWidth,r.clientHeight);let e=new ResizeObserver(()=>{t.setViewport(r.scrollTop,r.scrollLeft,r.clientWidth,r.clientHeight)});e.observe(r),p(()=>{e.disconnect(),n(),h.value=null})}}),C(()=>c.dataSource,e=>{if(e){let t=e;if(t.subscribe){let e=t.subscribe(()=>{h.value?.refresh()});p(()=>e())}}},{immediate:!0}),(t,c)=>(m(),i(`div`,{ref_key:`containerRef`,ref:l,class:u([`gp-grid-container`,{"gp-grid-container--dark":s.darkMode}]),style:{width:`100%`,height:`100%`,overflow:`auto`,position:`relative`},tabindex:`0`,onScroll:W,onWheel:c[1]||=e=>x(ee)(e,s.wheelDampening),onKeydown:c[2]||=(...e)=>x(z)&&x(z)(...e)},[r(` Content sizer `),a(`div`,{style:d({width:`${Math.max(x(S).contentWidth,A.value)}px`,height:`${Math.max(x(S).contentHeight,E.value)}px`,position:`relative`,minWidth:`100%`})},[r(` Headers `),a(`div`,{class:`gp-grid-header`,style:d({position:`sticky`,top:0,left:0,height:`${E.value}px`,width:`${Math.max(x(S).contentWidth,A.value)}px`,minWidth:`100%`,zIndex:100})},[(m(!0),i(e,null,_(s.columns,(e,t)=>(m(),i(`div`,{key:e.colId??e.field,class:`gp-grid-header-cell`,"data-col-index":t,style:d({position:`absolute`,left:`${O.value[t]}px`,top:0,width:`${e.width}px`,height:`${E.value}px`,background:`transparent`}),onClick:e=>x(L)(t,e)},[(m(),n(v(x(fe)({column:e,colIndex:t,sortDirection:x(S).headers.get(t)?.sortDirection,sortIndex:x(S).headers.get(t)?.sortIndex,sortable:x(S).headers.get(t)?.sortable??!0,filterable:x(S).headers.get(t)?.filterable??!0,hasFilter:x(S).headers.get(t)?.hasFilter??!1,core:h.value,container:l.value,headerRenderers:s.headerRenderers??{},globalHeaderRenderer:s.headerRenderer}))))],12,nt))),128))],4),r(` Row slots `),(m(!0),i(e,null,_(M.value.filter(e=>e.rowIndex>=0),t=>(m(),i(`div`,{key:t.slotId,class:u([`gp-grid-row`,{"gp-grid-row--even":t.rowIndex%2==0}]),style:d({position:`absolute`,top:0,left:0,transform:`translateY(${t.translateY}px)`,width:`${Math.max(x(S).contentWidth,A.value)}px`,height:`${s.rowHeight}px`})},[(m(!0),i(e,null,_(s.columns,(a,o)=>(m(),i(`div`,{key:`${t.slotId}-${o}`,class:u(K(t.rowIndex,o)),style:d({position:`absolute`,left:`${O.value[o]}px`,top:0,width:`${a.width}px`,height:`${s.rowHeight}px`}),onMousedown:e=>x(P)(t.rowIndex,o,e),onDblclick:()=>x(F)(t.rowIndex,o)},[r(` Edit mode `),x(U)(t.rowIndex,o,x(S).editingCell)&&x(S).editingCell?(m(),n(v(x(Q)({column:a,rowData:t.rowData,rowIndex:t.rowIndex,colIndex:o,initialValue:x(S).editingCell.initialValue,core:h.value,editRenderers:s.editRenderers??{},globalEditRenderer:s.editRenderer})),{key:0})):(m(),i(e,{key:1},[r(` View mode `),(m(),n(v(x(le)({column:a,rowData:t.rowData,rowIndex:t.rowIndex,colIndex:o,isActive:x(V)(t.rowIndex,o,x(S).activeCell),isSelected:x(G)(t.rowIndex,o,x(S).selectionRange),isEditing:!1,cellRenderers:s.cellRenderers??{},globalCellRenderer:s.cellRenderer}))))],64))],46,rt))),128))],6))),128)),r(` Fill handle `),x(H)&&!x(S).editingCell?(m(),i(`div`,{key:0,class:`gp-grid-fill-handle`,style:d({position:`absolute`,top:`${x(H).top}px`,left:`${x(H).left}px`,zIndex:200}),onMousedown:c[0]||=(...e)=>x(I)&&x(I)(...e)},null,36)):r(`v-if`,!0),r(` Loading indicator `),x(S).isLoading?(m(),i(`div`,it,[...c[3]||=[a(`div`,{class:`gp-grid-loading-spinner`},null,-1),o(` Loading... `,-1)]])):r(`v-if`,!0),r(` Error message `),x(S).error?(m(),i(`div`,at,` Error: `+b(x(S).error),1)):r(`v-if`,!0),r(` Empty state `),!x(S).isLoading&&!x(S).error&&x(S).totalRows===0?(m(),i(`div`,ot,` No data to display `)):r(`v-if`,!0)],4),r(` Filter Popup `),x(S).filterPopup?.isOpen&&x(S).filterPopup.column&&x(S).filterPopup.anchorRect?(m(),n(tt,{key:0,column:x(S).filterPopup.column,"col-index":x(S).filterPopup.colIndex,"anchor-rect":x(S).filterPopup.anchorRect,"distinct-values":x(S).filterPopup.distinctValues,"current-filter":x(S).filterPopup.currentFilter,onApply:re,onClose:ie},null,8,[`column`,`col-index`,`anchor-rect`,`distinct-values`,`current-filter`])):r(`v-if`,!0)],34))}});function ct(e){te();let n=g(null),r=g(null),{state:i,applyInstructions:a}=q(),o=t(()=>e.headerHeight??e.rowHeight),s=t(()=>k(e.columns)),c=t(()=>R(s.value)),l=t(()=>Array.from(i.slots.values())),{handleCellMouseDown:u,handleCellDoubleClick:d,handleFillHandleMouseDown:m,handleHeaderClick:h,handleKeyDown:_,handleWheel:v,dragState:y}=Y(r,n,t(()=>e.columns),{activeCell:t(()=>i.activeCell),selectionRange:t(()=>i.selectionRange),editingCell:t(()=>i.editingCell),filterPopupOpen:t(()=>i.filterPopup?.isOpen??!1),rowHeight:e.rowHeight,headerHeight:o.value,columnPositions:s,slots:t(()=>i.slots)}),b=()=>{let e=n.value,t=r.value;!e||!t||t.setViewport(e.scrollTop,e.scrollLeft,e.clientWidth,e.clientHeight)},x=(e,t)=>{let n=r.value;n&&n.setFilter(e,t)},S=()=>{let e=r.value;e&&e.closeFilterPopup()};f(()=>{let t=e.dataSource??(e.rowData?N(e.rowData):j([])),i=new T({columns:e.columns,dataSource:t,rowHeight:e.rowHeight,headerHeight:o.value,overscan:e.overscan??3,sortingEnabled:e.sortingEnabled??!0});r.value=i;let s=i.onBatchInstruction(e=>{a(e)});i.initialize();let c=n.value;if(c){i.setViewport(c.scrollTop,c.scrollLeft,c.clientWidth,c.clientHeight);let e=new ResizeObserver(()=>{i.setViewport(c.scrollTop,c.scrollLeft,c.clientWidth,c.clientHeight)});e.observe(c),p(()=>{e.disconnect(),s(),r.value=null})}}),C(()=>e.dataSource,e=>{if(e){let t=e;if(t.subscribe){let e=t.subscribe(()=>{r.value?.refresh()});p(()=>e())}}},{immediate:!0});let{fillHandlePosition:w}=X({activeCell:t(()=>i.activeCell),selectionRange:t(()=>i.selectionRange),slots:t(()=>i.slots),columns:t(()=>e.columns),columnPositions:s,rowHeight:e.rowHeight});return{containerRef:n,coreRef:r,state:i,slotsArray:l,totalHeaderHeight:o,columnPositions:s,totalWidth:c,fillHandlePosition:w,handleScroll:b,handleCellMouseDown:u,handleCellDoubleClick:d,handleFillHandleMouseDown:m,handleHeaderClick:h,handleKeyDown:_,handleWheel:v,handleFilterApply:x,handleFilterPopupClose:S,dragState:y,isCellSelected:G,isCellActive:V,isCellEditing:U,isCellInFillPreview:ne,buildCellClasses:D}}export{st as GpGrid,st as default,E as buildCellClasses,O as calculateColumnPositions,A as createClientDataSource,M as createDataSourceFromArray,K as createInitialState,P as createMutableClientDataSource,F as createServerDataSource,I as findColumnAtX,Z as getCellValue,L as getTotalWidth,z as gridStyles,ee as injectStyles,B as isCellActive,H as isCellEditing,W as isCellInFillPreview,re as isCellSelected,ie as isRowVisible,le as renderCell,Q as renderEditCell,fe as renderHeader,J as useAutoScroll,X as useFillHandle,$ as useFilterConditions,pe as useFilterPopup,ct as useGpGrid,q as useGridState,Y as useInputHandler};