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/README.md +598 -0
- package/dist/index.d.ts +5 -6
- package/dist/index.js +1 -1819
- package/package.json +63 -55
- package/dist/index.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,55 +1,63 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "gp-grid-vue",
|
|
3
|
-
"description": "A high-performance Vue 3 data grid component with virtual scrolling, cell selection, sorting, filtering, and Excel-like editing",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"license": "Apache-2.0",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "dist/index.js",
|
|
8
|
-
"types": "dist/index.d.ts",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"grid"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "gp-grid-vue",
|
|
3
|
+
"description": "A high-performance Vue 3 data grid component with virtual scrolling, cell selection, sorting, filtering, and Excel-like editing",
|
|
4
|
+
"version": "0.2.3",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/GioPat/gp-grid.git",
|
|
12
|
+
"directory": "packages/vue"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/GioPat/gp-grid/issues"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"vue",
|
|
22
|
+
"vue3",
|
|
23
|
+
"grid",
|
|
24
|
+
"data-grid",
|
|
25
|
+
"datagrid",
|
|
26
|
+
"vue-grid",
|
|
27
|
+
"vue-table",
|
|
28
|
+
"table",
|
|
29
|
+
"data-table",
|
|
30
|
+
"datatable",
|
|
31
|
+
"virtual-scroll",
|
|
32
|
+
"virtualization",
|
|
33
|
+
"typescript",
|
|
34
|
+
"high-performance",
|
|
35
|
+
"spreadsheet",
|
|
36
|
+
"excel",
|
|
37
|
+
"lightweight",
|
|
38
|
+
"cell-selection",
|
|
39
|
+
"sorting",
|
|
40
|
+
"filtering",
|
|
41
|
+
"editable",
|
|
42
|
+
"nuxt",
|
|
43
|
+
"nuxt3"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "tsdown --watch",
|
|
47
|
+
"build": "tsdown --clean --sourcemap",
|
|
48
|
+
"build:production": "tsdown --clean --minify --treeshake"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"gp-grid-core": "workspace:*"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"vue": "^3.4.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"tsdown": "^0.15.9",
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"unplugin-vue": "^7.1.0",
|
|
60
|
+
"vue": "^3.5.13",
|
|
61
|
+
"vue-tsc": "~3.1.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["row: number","col: number","minCol: number","maxCol: number","cellTop: number | null","toVNode","value: unknown","params: CellRendererParams","toVNode","params: EditRendererParams","params: HeaderRendererParams","children: VNode[]","iconsChildren: VNode[]","arrowsChildren: VNode[]","handleClickOutside: ((e: MouseEvent) => void) | null","handleKeyDown: ((e: KeyboardEvent) => void) | null","OPERATORS: { value: TextFilterOperator; label: string }[]","filter: ColumnFilterModel","OPERATORS: { value: NumberFilterOperator; label: string }[]","filter: ColumnFilterModel","OPERATORS: { value: DateFilterOperator; label: string }[]","filter: ColumnFilterModel","calculateColumnPositions","getTotalWidth","createDataSourceFromArray","createClientDataSource"],"sources":["../src/gridState/useGridState.ts","../src/composables/useAutoScroll.ts","../src/composables/useInputHandler.ts","../src/composables/useFillHandle.ts","../src/renderers/cellRenderer.ts","../src/renderers/editRenderer.ts","../src/renderers/headerRenderer.ts","../src/composables/useFilterPopup.ts","../src/composables/useFilterConditions.ts","../src/components/TextFilterContent.vue","../src/components/TextFilterContent.vue","../src/components/NumberFilterContent.vue","../src/components/NumberFilterContent.vue","../src/components/DateFilterContent.vue","../src/components/DateFilterContent.vue","../src/components/FilterPopup.vue","../src/components/FilterPopup.vue","../src/GpGrid.vue","../src/GpGrid.vue","../src/composables/useGpGrid.ts"],"sourcesContent":["// packages/vue/src/gridState/useGridState.ts\r\n\r\nimport { reactive, readonly } from \"vue\";\r\nimport type {\r\n GridInstruction,\r\n GridState,\r\n SlotData,\r\n HeaderData,\r\n} from \"gp-grid-core\";\r\n\r\n// =============================================================================\r\n// Initial State\r\n// =============================================================================\r\n\r\nfunction createInitialState(): GridState {\r\n return {\r\n slots: new Map(),\r\n activeCell: null,\r\n selectionRange: null,\r\n editingCell: null,\r\n contentWidth: 0,\r\n contentHeight: 0,\r\n headers: new Map(),\r\n filterPopup: null,\r\n isLoading: false,\r\n error: null,\r\n totalRows: 0,\r\n visibleRowRange: null,\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Instruction Handler\r\n// =============================================================================\r\n\r\n/**\r\n * Apply a single instruction to mutable state\r\n */\r\nfunction applyInstruction(\r\n instruction: GridInstruction,\r\n state: GridState,\r\n): void {\r\n switch (instruction.type) {\r\n case \"CREATE_SLOT\":\r\n state.slots.set(instruction.slotId, {\r\n slotId: instruction.slotId,\r\n rowIndex: -1,\r\n rowData: {},\r\n translateY: 0,\r\n });\r\n break;\r\n\r\n case \"DESTROY_SLOT\":\r\n state.slots.delete(instruction.slotId);\r\n break;\r\n\r\n case \"ASSIGN_SLOT\": {\r\n const existing = state.slots.get(instruction.slotId);\r\n if (existing) {\r\n state.slots.set(instruction.slotId, {\r\n ...existing,\r\n rowIndex: instruction.rowIndex,\r\n rowData: instruction.rowData,\r\n });\r\n }\r\n break;\r\n }\r\n\r\n case \"MOVE_SLOT\": {\r\n const existing = state.slots.get(instruction.slotId);\r\n if (existing) {\r\n state.slots.set(instruction.slotId, {\r\n ...existing,\r\n translateY: instruction.translateY,\r\n });\r\n }\r\n break;\r\n }\r\n\r\n case \"SET_ACTIVE_CELL\":\r\n state.activeCell = instruction.position;\r\n break;\r\n\r\n case \"SET_SELECTION_RANGE\":\r\n state.selectionRange = instruction.range;\r\n break;\r\n\r\n case \"UPDATE_VISIBLE_RANGE\":\r\n state.visibleRowRange = { start: instruction.start, end: instruction.end };\r\n break;\r\n\r\n case \"START_EDIT\":\r\n state.editingCell = {\r\n row: instruction.row,\r\n col: instruction.col,\r\n initialValue: instruction.initialValue,\r\n };\r\n break;\r\n\r\n case \"STOP_EDIT\":\r\n state.editingCell = null;\r\n break;\r\n\r\n case \"SET_CONTENT_SIZE\":\r\n state.contentWidth = instruction.width;\r\n state.contentHeight = instruction.height;\r\n break;\r\n\r\n case \"UPDATE_HEADER\":\r\n state.headers.set(instruction.colIndex, {\r\n column: instruction.column,\r\n sortDirection: instruction.sortDirection,\r\n sortIndex: instruction.sortIndex,\r\n sortable: instruction.sortable,\r\n filterable: instruction.filterable,\r\n hasFilter: instruction.hasFilter,\r\n });\r\n break;\r\n\r\n case \"OPEN_FILTER_POPUP\":\r\n state.filterPopup = {\r\n isOpen: true,\r\n colIndex: instruction.colIndex,\r\n column: instruction.column,\r\n anchorRect: instruction.anchorRect,\r\n distinctValues: instruction.distinctValues,\r\n currentFilter: instruction.currentFilter,\r\n };\r\n break;\r\n\r\n case \"CLOSE_FILTER_POPUP\":\r\n state.filterPopup = null;\r\n break;\r\n\r\n case \"DATA_LOADING\":\r\n state.isLoading = true;\r\n state.error = null;\r\n break;\r\n\r\n case \"DATA_LOADED\":\r\n state.isLoading = false;\r\n state.totalRows = instruction.totalRows;\r\n break;\r\n\r\n case \"DATA_ERROR\":\r\n state.isLoading = false;\r\n state.error = instruction.error;\r\n break;\r\n\r\n // Transaction instructions\r\n case \"ROWS_ADDED\":\r\n case \"ROWS_REMOVED\":\r\n state.totalRows = instruction.totalRows;\r\n break;\r\n\r\n case \"ROWS_UPDATED\":\r\n case \"TRANSACTION_PROCESSED\":\r\n // These don't change state directly - slot updates come via ASSIGN_SLOT\r\n break;\r\n }\r\n}\r\n\r\n// =============================================================================\r\n// Composable\r\n// =============================================================================\r\n\r\n/**\r\n * Vue composable for managing grid state\r\n */\r\nexport function useGridState() {\r\n const state = reactive<GridState>(createInitialState());\r\n\r\n /**\r\n * Apply a batch of instructions to the state\r\n */\r\n function applyInstructions(instructions: GridInstruction[]): void {\r\n for (const instruction of instructions) {\r\n applyInstruction(instruction, state);\r\n }\r\n }\r\n\r\n /**\r\n * Reset state to initial values\r\n */\r\n function reset(): void {\r\n const initial = createInitialState();\r\n state.slots = initial.slots;\r\n state.activeCell = initial.activeCell;\r\n state.selectionRange = initial.selectionRange;\r\n state.editingCell = initial.editingCell;\r\n state.contentWidth = initial.contentWidth;\r\n state.contentHeight = initial.contentHeight;\r\n state.headers = initial.headers;\r\n state.filterPopup = initial.filterPopup;\r\n state.isLoading = initial.isLoading;\r\n state.error = initial.error;\r\n state.totalRows = initial.totalRows;\r\n state.visibleRowRange = initial.visibleRowRange;\r\n }\r\n\r\n return {\r\n state,\r\n applyInstructions,\r\n reset,\r\n };\r\n}\r\n\r\n// Re-export for backwards compatibility\r\nexport { createInitialState };\r\n","// packages/vue/src/composables/useAutoScroll.ts\r\n\r\nimport { ref, onUnmounted, type Ref } from \"vue\";\r\n\r\nconst AUTO_SCROLL_INTERVAL = 16; // ~60fps\r\n\r\n/**\r\n * Vue composable for auto-scrolling during drag operations\r\n */\r\nexport function useAutoScroll(containerRef: Ref<HTMLDivElement | null>) {\r\n const autoScrollInterval = ref<ReturnType<typeof setInterval> | null>(null);\r\n\r\n /**\r\n * Start auto-scrolling in the given direction\r\n */\r\n function startAutoScroll(dx: number, dy: number): void {\r\n if (autoScrollInterval.value) {\r\n clearInterval(autoScrollInterval.value);\r\n }\r\n autoScrollInterval.value = setInterval(() => {\r\n const container = containerRef.value;\r\n if (container) {\r\n container.scrollTop += dy;\r\n container.scrollLeft += dx;\r\n }\r\n }, AUTO_SCROLL_INTERVAL);\r\n }\r\n\r\n /**\r\n * Stop auto-scrolling\r\n */\r\n function stopAutoScroll(): void {\r\n if (autoScrollInterval.value) {\r\n clearInterval(autoScrollInterval.value);\r\n autoScrollInterval.value = null;\r\n }\r\n }\r\n\r\n // Cleanup on unmount\r\n onUnmounted(() => {\r\n stopAutoScroll();\r\n });\r\n\r\n return {\r\n startAutoScroll,\r\n stopAutoScroll,\r\n };\r\n}\r\n","// packages/vue/src/composables/useInputHandler.ts\r\n\r\nimport { ref, watch, onUnmounted, type Ref, type ShallowRef, type ComputedRef } from \"vue\";\r\nimport type {\r\n GridCore,\r\n Row,\r\n CellPosition,\r\n CellRange,\r\n PointerEventData,\r\n ContainerBounds,\r\n DragState,\r\n ColumnDefinition,\r\n SlotData,\r\n} from \"gp-grid-core\";\r\nimport { useAutoScroll } from \"./useAutoScroll\";\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface UseInputHandlerOptions {\r\n activeCell: ComputedRef<CellPosition | null>;\r\n selectionRange: ComputedRef<CellRange | null>;\r\n editingCell: ComputedRef<{ row: number; col: number } | null>;\r\n filterPopupOpen: ComputedRef<boolean>;\r\n rowHeight: number;\r\n headerHeight: number;\r\n columnPositions: ComputedRef<number[]>;\r\n slots: ComputedRef<Map<string, SlotData>>;\r\n}\r\n\r\nexport interface UseInputHandlerResult {\r\n handleCellMouseDown: (rowIndex: number, colIndex: number, e: MouseEvent) => void;\r\n handleCellDoubleClick: (rowIndex: number, colIndex: number) => void;\r\n handleFillHandleMouseDown: (e: MouseEvent) => void;\r\n handleHeaderClick: (colIndex: number, e: MouseEvent) => void;\r\n handleKeyDown: (e: KeyboardEvent) => void;\r\n handleWheel: (e: WheelEvent, wheelDampening: number) => void;\r\n dragState: Ref<DragState>;\r\n}\r\n\r\n// =============================================================================\r\n// Helper Functions\r\n// =============================================================================\r\n\r\n/**\r\n * Find the slot for a given row index\r\n */\r\nfunction findSlotForRow(slots: Map<string, SlotData>, rowIndex: number): SlotData | null {\r\n for (const slot of slots.values()) {\r\n if (slot.rowIndex === rowIndex) {\r\n return slot;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Scroll a cell into view if needed\r\n */\r\nfunction scrollCellIntoView(\r\n core: GridCore,\r\n container: HTMLDivElement,\r\n row: number,\r\n rowHeight: number,\r\n headerHeight: number,\r\n slots: Map<string, SlotData>,\r\n): void {\r\n const slot = findSlotForRow(slots, row);\r\n const cellTranslateY = slot ? slot.translateY : headerHeight + row * rowHeight;\r\n const cellViewportTop = cellTranslateY - container.scrollTop;\r\n const cellViewportBottom = cellViewportTop + rowHeight;\r\n const visibleTop = headerHeight;\r\n const visibleBottom = container.clientHeight;\r\n\r\n if (cellViewportTop < visibleTop) {\r\n container.scrollTop = core.getScrollTopForRow(row);\r\n } else if (cellViewportBottom > visibleBottom) {\r\n const visibleDataHeight = container.clientHeight - headerHeight;\r\n const rowsInView = Math.floor(visibleDataHeight / rowHeight);\r\n const targetRow = Math.max(0, row - rowsInView + 1);\r\n container.scrollTop = core.getScrollTopForRow(targetRow);\r\n }\r\n}\r\n\r\n// =============================================================================\r\n// Composable\r\n// =============================================================================\r\n\r\n/**\r\n * Vue composable for handling all input interactions\r\n */\r\nexport function useInputHandler(\r\n coreRef: ShallowRef<GridCore | null>,\r\n containerRef: Ref<HTMLDivElement | null>,\r\n columns: ComputedRef<ColumnDefinition[]>,\r\n options: UseInputHandlerOptions,\r\n): UseInputHandlerResult {\r\n const {\r\n activeCell,\r\n selectionRange,\r\n editingCell,\r\n filterPopupOpen,\r\n rowHeight,\r\n headerHeight,\r\n columnPositions,\r\n slots,\r\n } = options;\r\n\r\n // Auto-scroll helpers\r\n const { startAutoScroll, stopAutoScroll } = useAutoScroll(containerRef);\r\n\r\n // Drag state for UI (mirrors core's InputHandler state)\r\n const dragState = ref<DragState>({\r\n isDragging: false,\r\n dragType: null,\r\n fillSourceRange: null,\r\n fillTarget: null,\r\n });\r\n\r\n // Update InputHandler deps when options change\r\n watch(\r\n [() => headerHeight, () => rowHeight, columnPositions, () => columns.value.length],\r\n () => {\r\n const core = coreRef.value;\r\n if (core?.input) {\r\n core.input.updateDeps({\r\n getHeaderHeight: () => headerHeight,\r\n getRowHeight: () => rowHeight,\r\n getColumnPositions: () => columnPositions.value,\r\n getColumnCount: () => columns.value.length,\r\n });\r\n }\r\n },\r\n { immediate: true },\r\n );\r\n\r\n // Get container bounds\r\n function getContainerBounds(): ContainerBounds | null {\r\n const container = containerRef.value;\r\n if (!container) return null;\r\n const rect = container.getBoundingClientRect();\r\n return {\r\n top: rect.top,\r\n left: rect.left,\r\n width: rect.width,\r\n height: rect.height,\r\n scrollTop: container.scrollTop,\r\n scrollLeft: container.scrollLeft,\r\n };\r\n }\r\n\r\n // Convert mouse event to PointerEventData\r\n function toPointerEventData(e: MouseEvent): PointerEventData {\r\n return {\r\n clientX: e.clientX,\r\n clientY: e.clientY,\r\n button: e.button,\r\n shiftKey: e.shiftKey,\r\n ctrlKey: e.ctrlKey,\r\n metaKey: e.metaKey,\r\n };\r\n }\r\n\r\n // ===========================================================================\r\n // Global Drag Listeners\r\n // ===========================================================================\r\n\r\n function startGlobalDragListeners(): void {\r\n const handleMouseMove = (e: MouseEvent): void => {\r\n const core = coreRef.value;\r\n const bounds = getContainerBounds();\r\n if (!core?.input || !bounds) return;\r\n\r\n const result = core.input.handleDragMove(toPointerEventData(e), bounds);\r\n if (result) {\r\n if (result.autoScroll) {\r\n startAutoScroll(result.autoScroll.dx, result.autoScroll.dy);\r\n } else {\r\n stopAutoScroll();\r\n }\r\n // Update UI drag state\r\n dragState.value = core.input.getDragState();\r\n }\r\n };\r\n\r\n const handleMouseUp = (): void => {\r\n const core = coreRef.value;\r\n if (core?.input) {\r\n core.input.handleDragEnd();\r\n dragState.value = core.input.getDragState();\r\n }\r\n stopAutoScroll();\r\n document.removeEventListener(\"mousemove\", handleMouseMove);\r\n document.removeEventListener(\"mouseup\", handleMouseUp);\r\n };\r\n\r\n document.addEventListener(\"mousemove\", handleMouseMove);\r\n document.addEventListener(\"mouseup\", handleMouseUp);\r\n }\r\n\r\n // ===========================================================================\r\n // Event Handlers\r\n // ===========================================================================\r\n\r\n function handleCellMouseDown(rowIndex: number, colIndex: number, e: MouseEvent): void {\r\n const core = coreRef.value;\r\n if (!core?.input) return;\r\n\r\n const result = core.input.handleCellMouseDown(\r\n rowIndex,\r\n colIndex,\r\n toPointerEventData(e),\r\n );\r\n\r\n if (result.focusContainer) {\r\n containerRef.value?.focus();\r\n }\r\n if (result.startDrag === \"selection\") {\r\n core.input.startSelectionDrag();\r\n dragState.value = core.input.getDragState();\r\n startGlobalDragListeners();\r\n }\r\n }\r\n\r\n function handleCellDoubleClick(rowIndex: number, colIndex: number): void {\r\n const core = coreRef.value;\r\n if (!core?.input) return;\r\n core.input.handleCellDoubleClick(rowIndex, colIndex);\r\n }\r\n\r\n function handleFillHandleMouseDown(e: MouseEvent): void {\r\n const core = coreRef.value;\r\n if (!core?.input) return;\r\n\r\n const result = core.input.handleFillHandleMouseDown(\r\n activeCell.value,\r\n selectionRange.value,\r\n toPointerEventData(e),\r\n );\r\n\r\n if (result.preventDefault) e.preventDefault();\r\n if (result.stopPropagation) e.stopPropagation();\r\n if (result.startDrag === \"fill\") {\r\n dragState.value = core.input.getDragState();\r\n startGlobalDragListeners();\r\n }\r\n }\r\n\r\n function handleHeaderClick(colIndex: number, e: MouseEvent): void {\r\n const core = coreRef.value;\r\n if (!core?.input) return;\r\n\r\n const column = columns.value[colIndex];\r\n if (!column) return;\r\n\r\n const colId = column.colId ?? column.field;\r\n core.input.handleHeaderClick(colId, e.shiftKey);\r\n }\r\n\r\n function handleKeyDown(e: KeyboardEvent): void {\r\n const core = coreRef.value;\r\n const container = containerRef.value;\r\n if (!core?.input) return;\r\n\r\n const result = core.input.handleKeyDown(\r\n {\r\n key: e.key,\r\n shiftKey: e.shiftKey,\r\n ctrlKey: e.ctrlKey,\r\n metaKey: e.metaKey,\r\n },\r\n activeCell.value,\r\n editingCell.value,\r\n filterPopupOpen.value,\r\n );\r\n\r\n if (result.preventDefault) {\r\n e.preventDefault();\r\n }\r\n if (result.scrollToCell && container) {\r\n scrollCellIntoView(\r\n core,\r\n container,\r\n result.scrollToCell.row,\r\n rowHeight,\r\n headerHeight,\r\n slots.value,\r\n );\r\n }\r\n }\r\n\r\n function handleWheel(e: WheelEvent, wheelDampening: number): void {\r\n const core = coreRef.value;\r\n const container = containerRef.value;\r\n if (!core?.input || !container) return;\r\n\r\n const dampened = core.input.handleWheel(e.deltaY, e.deltaX, wheelDampening);\r\n if (dampened) {\r\n e.preventDefault();\r\n container.scrollTop += dampened.dy;\r\n container.scrollLeft += dampened.dx;\r\n }\r\n }\r\n\r\n // Cleanup on unmount\r\n onUnmounted(() => {\r\n stopAutoScroll();\r\n });\r\n\r\n return {\r\n handleCellMouseDown,\r\n handleCellDoubleClick,\r\n handleFillHandleMouseDown,\r\n handleHeaderClick,\r\n handleKeyDown,\r\n handleWheel,\r\n dragState,\r\n };\r\n}\r\n","// packages/vue/src/composables/useFillHandle.ts\r\n\r\nimport { computed, type ComputedRef } from \"vue\";\r\nimport type { CellPosition, CellRange, ColumnDefinition, SlotData } from \"gp-grid-core\";\r\n\r\nexport interface UseFillHandleOptions {\r\n activeCell: ComputedRef<CellPosition | null>;\r\n selectionRange: ComputedRef<CellRange | null>;\r\n slots: ComputedRef<Map<string, SlotData>>;\r\n columns: ComputedRef<ColumnDefinition[]>;\r\n columnPositions: ComputedRef<number[]>;\r\n rowHeight: number;\r\n}\r\n\r\nexport interface UseFillHandleResult {\r\n fillHandlePosition: ComputedRef<{ top: number; left: number } | null>;\r\n}\r\n\r\n/**\r\n * Composable for calculating the fill handle position.\r\n * The fill handle appears at the bottom-right corner of the selection\r\n * when all selected columns are editable.\r\n */\r\nexport function useFillHandle(options: UseFillHandleOptions): UseFillHandleResult {\r\n const { activeCell, selectionRange, slots, columns, columnPositions, rowHeight } = options;\r\n\r\n const fillHandlePosition = computed(() => {\r\n const active = activeCell.value;\r\n const selection = selectionRange.value;\r\n const slotsMap = slots.value;\r\n\r\n if (!active && !selection) return null;\r\n\r\n let row: number, col: number;\r\n let minCol: number, maxCol: number;\r\n\r\n if (selection) {\r\n row = Math.max(selection.startRow, selection.endRow);\r\n col = Math.max(selection.startCol, selection.endCol);\r\n minCol = Math.min(selection.startCol, selection.endCol);\r\n maxCol = Math.max(selection.startCol, selection.endCol);\r\n } else if (active) {\r\n row = active.row;\r\n col = active.col;\r\n minCol = col;\r\n maxCol = col;\r\n } else {\r\n return null;\r\n }\r\n\r\n // Check if ALL columns in the selection are editable\r\n const cols = columns.value;\r\n for (let c = minCol; c <= maxCol; c++) {\r\n const column = cols[c];\r\n if (!column || column.editable !== true) {\r\n return null;\r\n }\r\n }\r\n\r\n // Find the slot for this row and use its actual translateY\r\n let cellTop: number | null = null;\r\n for (const slot of slotsMap.values()) {\r\n if (slot.rowIndex === row) {\r\n cellTop = slot.translateY;\r\n break;\r\n }\r\n }\r\n\r\n if (cellTop === null) return null;\r\n\r\n const cellLeft = columnPositions.value[col] ?? 0;\r\n const cellWidth = cols[col]?.width ?? 0;\r\n\r\n return {\r\n top: cellTop + rowHeight - 5,\r\n left: cellLeft + cellWidth - 20,\r\n };\r\n });\r\n\r\n return { fillHandlePosition };\r\n}\r\n","// packages/vue/src/renderers/cellRenderer.ts\r\n\r\nimport { h, createTextVNode, type VNode } from \"vue\";\r\nimport type { ColumnDefinition, Row, CellValue, CellRendererParams } from \"gp-grid-core\";\r\nimport type { VueCellRenderer } from \"../types\";\r\n\r\n/**\r\n * Ensure we always return a VNode, never a plain string\r\n */\r\nfunction toVNode(value: VNode | string | null | undefined): VNode {\r\n if (value == null || value === \"\") {\r\n return createTextVNode(\"\");\r\n }\r\n if (typeof value === \"string\") {\r\n return createTextVNode(value);\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Get cell value from row data, supporting dot-notation for nested fields\r\n */\r\nexport function getCellValue(rowData: Row, field: string): CellValue {\r\n const parts = field.split(\".\");\r\n let value: unknown = rowData;\r\n\r\n for (const part of parts) {\r\n if (value == null || typeof value !== \"object\") {\r\n return null;\r\n }\r\n value = (value as Record<string, unknown>)[part];\r\n }\r\n\r\n return (value ?? null) as CellValue;\r\n}\r\n\r\nexport interface RenderCellOptions {\r\n column: ColumnDefinition;\r\n rowData: Row;\r\n rowIndex: number;\r\n colIndex: number;\r\n isActive: boolean;\r\n isSelected: boolean;\r\n isEditing: boolean;\r\n cellRenderers: Record<string, VueCellRenderer>;\r\n globalCellRenderer?: VueCellRenderer;\r\n}\r\n\r\n/**\r\n * Render cell content based on column configuration and renderer registries\r\n */\r\nexport function renderCell(options: RenderCellOptions): VNode {\r\n const {\r\n column,\r\n rowData,\r\n rowIndex,\r\n colIndex,\r\n isActive,\r\n isSelected,\r\n isEditing,\r\n cellRenderers,\r\n globalCellRenderer,\r\n } = options;\r\n\r\n const value = getCellValue(rowData, column.field);\r\n const params: CellRendererParams = {\r\n value,\r\n rowData,\r\n column,\r\n rowIndex,\r\n colIndex,\r\n isActive,\r\n isSelected,\r\n isEditing,\r\n };\r\n\r\n // Check for column-specific renderer\r\n if (column.cellRenderer && typeof column.cellRenderer === \"string\") {\r\n const renderer = cellRenderers[column.cellRenderer];\r\n if (renderer) {\r\n return toVNode(renderer(params));\r\n }\r\n }\r\n\r\n // Fall back to global renderer\r\n if (globalCellRenderer) {\r\n return toVNode(globalCellRenderer(params));\r\n }\r\n\r\n // Default text rendering\r\n return createTextVNode(value == null ? \"\" : String(value));\r\n}\r\n","// packages/vue/src/renderers/editRenderer.ts\r\n\r\nimport { h, createTextVNode, type VNode } from \"vue\";\r\nimport type { GridCore, ColumnDefinition, Row, CellValue, EditRendererParams } from \"gp-grid-core\";\r\nimport type { VueEditRenderer } from \"../types\";\r\nimport { getCellValue } from \"./cellRenderer\";\r\n\r\n/**\r\n * Ensure we always return a VNode, never a plain string\r\n */\r\nfunction toVNode(value: VNode | string | null | undefined): VNode {\r\n if (value == null || value === \"\") {\r\n return createTextVNode(\"\");\r\n }\r\n if (typeof value === \"string\") {\r\n return createTextVNode(value);\r\n }\r\n return value;\r\n}\r\n\r\nexport interface RenderEditCellOptions {\r\n column: ColumnDefinition;\r\n rowData: Row;\r\n rowIndex: number;\r\n colIndex: number;\r\n initialValue: CellValue;\r\n core: GridCore | null;\r\n editRenderers: Record<string, VueEditRenderer>;\r\n globalEditRenderer?: VueEditRenderer;\r\n}\r\n\r\n/**\r\n * Render edit cell content based on column configuration and renderer registries\r\n */\r\nexport function renderEditCell(\r\n options: RenderEditCellOptions,\r\n): VNode {\r\n const {\r\n column,\r\n rowData,\r\n rowIndex,\r\n colIndex,\r\n initialValue,\r\n core,\r\n editRenderers,\r\n globalEditRenderer,\r\n } = options;\r\n\r\n if (!core) return createTextVNode(\"\");\r\n\r\n const value = getCellValue(rowData, column.field);\r\n const params: EditRendererParams = {\r\n value,\r\n rowData,\r\n column,\r\n rowIndex,\r\n colIndex,\r\n isActive: true,\r\n isSelected: true,\r\n isEditing: true,\r\n initialValue,\r\n onValueChange: (newValue) => core.updateEditValue(newValue),\r\n onCommit: () => core.commitEdit(),\r\n onCancel: () => core.cancelEdit(),\r\n };\r\n\r\n // Check for column-specific renderer\r\n if (column.editRenderer && typeof column.editRenderer === \"string\") {\r\n const renderer = editRenderers[column.editRenderer];\r\n if (renderer) {\r\n return toVNode(renderer(params));\r\n }\r\n }\r\n\r\n // Fall back to global renderer\r\n if (globalEditRenderer) {\r\n return toVNode(globalEditRenderer(params));\r\n }\r\n\r\n // Default input\r\n return h(\"input\", {\r\n class: \"gp-grid-edit-input\",\r\n type: \"text\",\r\n value: initialValue == null ? \"\" : String(initialValue),\r\n autofocus: true,\r\n onFocus: (e: FocusEvent) => (e.target as HTMLInputElement).select(),\r\n onInput: (e: Event) => core.updateEditValue((e.target as HTMLInputElement).value),\r\n onKeydown: (e: KeyboardEvent) => {\r\n e.stopPropagation();\r\n if (e.key === \"Enter\") {\r\n core.commitEdit();\r\n } else if (e.key === \"Escape\") {\r\n core.cancelEdit();\r\n } else if (e.key === \"Tab\") {\r\n e.preventDefault();\r\n core.commitEdit();\r\n core.selection.moveFocus(e.shiftKey ? \"left\" : \"right\", false);\r\n }\r\n },\r\n onBlur: () => core.commitEdit(),\r\n });\r\n}\r\n","// packages/vue/src/renderers/headerRenderer.ts\r\n\r\nimport { h, Fragment, createTextVNode, type VNode } from \"vue\";\r\nimport type { GridCore, ColumnDefinition, SortDirection, HeaderRendererParams } from \"gp-grid-core\";\r\nimport type { VueHeaderRenderer } from \"../types\";\r\n\r\n/**\r\n * Ensure we always return a VNode, never a plain string\r\n */\r\nfunction toVNode(value: VNode | string | null | undefined): VNode {\r\n if (value == null || value === \"\") {\r\n return createTextVNode(\"\");\r\n }\r\n if (typeof value === \"string\") {\r\n return createTextVNode(value);\r\n }\r\n return value;\r\n}\r\n\r\nexport interface RenderHeaderOptions {\r\n column: ColumnDefinition;\r\n colIndex: number;\r\n sortDirection?: SortDirection;\r\n sortIndex?: number;\r\n sortable: boolean;\r\n filterable: boolean;\r\n hasFilter: boolean;\r\n core: GridCore | null;\r\n container: HTMLDivElement | null;\r\n headerRenderers: Record<string, VueHeaderRenderer>;\r\n globalHeaderRenderer?: VueHeaderRenderer;\r\n}\r\n\r\n/**\r\n * Render header content based on column configuration and renderer registries\r\n */\r\nexport function renderHeader(\r\n options: RenderHeaderOptions,\r\n): VNode {\r\n const {\r\n column,\r\n colIndex,\r\n sortDirection,\r\n sortIndex,\r\n sortable,\r\n filterable,\r\n hasFilter,\r\n core,\r\n container,\r\n headerRenderers,\r\n globalHeaderRenderer,\r\n } = options;\r\n const params: HeaderRendererParams = {\r\n column,\r\n colIndex,\r\n sortDirection,\r\n sortIndex,\r\n sortable,\r\n filterable,\r\n hasFilter,\r\n onSort: (direction, addToExisting) => {\r\n if (core && sortable) {\r\n core.setSort(column.colId ?? column.field, direction, addToExisting);\r\n }\r\n },\r\n onFilterClick: () => {\r\n if (core && filterable) {\r\n const headerCell = container?.querySelector(\r\n `[data-col-index=\"${colIndex}\"]`,\r\n ) as HTMLElement | null;\r\n if (headerCell) {\r\n const rect = headerCell.getBoundingClientRect();\r\n core.openFilterPopup(colIndex, {\r\n top: rect.top,\r\n left: rect.left,\r\n width: rect.width,\r\n height: rect.height,\r\n });\r\n }\r\n }\r\n },\r\n };\r\n\r\n // Check for column-specific renderer\r\n if (column.headerRenderer && typeof column.headerRenderer === \"string\") {\r\n const renderer = headerRenderers[column.headerRenderer];\r\n if (renderer) {\r\n return toVNode(renderer(params));\r\n }\r\n }\r\n\r\n // Fall back to global renderer\r\n if (globalHeaderRenderer) {\r\n return toVNode(globalHeaderRenderer(params));\r\n }\r\n\r\n // Default header with stacked sort arrows and filter icon\r\n const children: VNode[] = [\r\n h(\"span\", { class: \"gp-grid-header-text\" }, column.headerName ?? column.field),\r\n ];\r\n\r\n const iconsChildren: VNode[] = [];\r\n\r\n // Stacked sort arrows - always show when sortable\r\n if (sortable) {\r\n const arrowsChildren: VNode[] = [\r\n h(\"span\", { class: \"gp-grid-sort-arrows-stack\" }, [\r\n h(\r\n \"svg\",\r\n {\r\n class: `gp-grid-sort-arrow-up${sortDirection === \"asc\" ? \" active\" : \"\"}`,\r\n width: \"8\",\r\n height: \"6\",\r\n viewBox: \"0 0 8 6\",\r\n },\r\n [h(\"path\", { d: \"M4 0L8 6H0L4 0Z\", fill: \"currentColor\" })],\r\n ),\r\n h(\r\n \"svg\",\r\n {\r\n class: `gp-grid-sort-arrow-down${sortDirection === \"desc\" ? \" active\" : \"\"}`,\r\n width: \"8\",\r\n height: \"6\",\r\n viewBox: \"0 0 8 6\",\r\n },\r\n [h(\"path\", { d: \"M4 6L0 0H8L4 6Z\", fill: \"currentColor\" })],\r\n ),\r\n ]),\r\n ];\r\n\r\n if (sortIndex !== undefined && sortIndex > 0) {\r\n arrowsChildren.push(h(\"span\", { class: \"gp-grid-sort-index\" }, String(sortIndex)));\r\n }\r\n\r\n iconsChildren.push(h(\"span\", { class: \"gp-grid-sort-arrows\" }, arrowsChildren));\r\n }\r\n\r\n // Filter icon\r\n if (filterable) {\r\n iconsChildren.push(\r\n h(\r\n \"span\",\r\n {\r\n class: `gp-grid-filter-icon${hasFilter ? \" active\" : \"\"}`,\r\n onMousedown: (e: MouseEvent) => {\r\n e.stopPropagation();\r\n e.preventDefault();\r\n params.onFilterClick();\r\n },\r\n onClick: (e: MouseEvent) => {\r\n e.stopPropagation();\r\n },\r\n },\r\n [\r\n h(\r\n \"svg\",\r\n { width: \"16\", height: \"16\", viewBox: \"0 0 24 24\", fill: \"currentColor\" },\r\n [h(\"path\", { d: \"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\" })],\r\n ),\r\n ],\r\n ),\r\n );\r\n }\r\n\r\n if (iconsChildren.length > 0) {\r\n children.push(h(\"span\", { class: \"gp-grid-header-icons\" }, iconsChildren));\r\n }\r\n\r\n return h(Fragment, children);\r\n}\r\n","// packages/vue/src/composables/useFilterPopup.ts\r\n\r\nimport { onMounted, onUnmounted, type Ref } from \"vue\";\r\n\r\nexport interface UseFilterPopupOptions {\r\n onClose: () => void;\r\n ignoreSelector?: string;\r\n}\r\n\r\n/**\r\n * Composable for filter popup behavior.\r\n * Handles click-outside detection and escape key to close the popup.\r\n */\r\nexport function useFilterPopup(\r\n popupRef: Ref<HTMLElement | null>,\r\n options: UseFilterPopupOptions,\r\n): void {\r\n const { onClose, ignoreSelector = \".gp-grid-filter-icon\" } = options;\r\n\r\n let handleClickOutside: ((e: MouseEvent) => void) | null = null;\r\n let handleKeyDown: ((e: KeyboardEvent) => void) | null = null;\r\n\r\n onMounted(() => {\r\n handleClickOutside = (e: MouseEvent): void => {\r\n const target = e.target as HTMLElement;\r\n // Ignore clicks on filter icons\r\n if (ignoreSelector && target.closest(ignoreSelector)) {\r\n return;\r\n }\r\n if (popupRef.value && !popupRef.value.contains(target)) {\r\n onClose();\r\n }\r\n };\r\n\r\n handleKeyDown = (e: KeyboardEvent): void => {\r\n if (e.key === \"Escape\") {\r\n onClose();\r\n }\r\n };\r\n\r\n // Add listeners after a frame to avoid immediate close\r\n requestAnimationFrame(() => {\r\n if (handleClickOutside) {\r\n document.addEventListener(\"mousedown\", handleClickOutside);\r\n }\r\n if (handleKeyDown) {\r\n document.addEventListener(\"keydown\", handleKeyDown);\r\n }\r\n });\r\n });\r\n\r\n onUnmounted(() => {\r\n if (handleClickOutside) {\r\n document.removeEventListener(\"mousedown\", handleClickOutside);\r\n }\r\n if (handleKeyDown) {\r\n document.removeEventListener(\"keydown\", handleKeyDown);\r\n }\r\n });\r\n}\r\n","// packages/vue/src/composables/useFilterConditions.ts\r\n\r\nimport { ref, type Ref } from \"vue\";\r\n\r\nexport interface LocalFilterCondition<TOperator extends string> {\r\n operator: TOperator;\r\n value: string;\r\n valueTo: string;\r\n nextOperator: \"and\" | \"or\";\r\n}\r\n\r\nexport interface UseFilterConditionsResult<TOperator extends string> {\r\n conditions: Ref<LocalFilterCondition<TOperator>[]>;\r\n combination: Ref<\"and\" | \"or\">;\r\n updateCondition: (index: number, updates: Partial<LocalFilterCondition<TOperator>>) => void;\r\n addCondition: (defaultOperator: TOperator) => void;\r\n removeCondition: (index: number) => void;\r\n}\r\n\r\n/**\r\n * Composable for managing filter conditions.\r\n * Used by NumberFilterContent, DateFilterContent, and TextFilterContent (condition mode).\r\n */\r\nexport function useFilterConditions<TOperator extends string>(\r\n initialConditions: LocalFilterCondition<TOperator>[],\r\n initialCombination: \"and\" | \"or\" = \"and\",\r\n): UseFilterConditionsResult<TOperator> {\r\n const conditions = ref<LocalFilterCondition<TOperator>[]>([...initialConditions]) as Ref<\r\n LocalFilterCondition<TOperator>[]\r\n >;\r\n const combination = ref<\"and\" | \"or\">(initialCombination);\r\n\r\n const updateCondition = (index: number, updates: Partial<LocalFilterCondition<TOperator>>): void => {\r\n const next = [...conditions.value];\r\n next[index] = { ...next[index]!, ...updates };\r\n conditions.value = next;\r\n };\r\n\r\n const addCondition = (defaultOperator: TOperator): void => {\r\n conditions.value = [...conditions.value, { operator: defaultOperator, value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n };\r\n\r\n const removeCondition = (index: number): void => {\r\n conditions.value = conditions.value.filter((_, i) => i !== index);\r\n };\r\n\r\n return {\r\n conditions,\r\n combination,\r\n updateCondition,\r\n addCondition,\r\n removeCondition,\r\n };\r\n}\r\n","<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport type { CellValue, ColumnFilterModel, TextFilterCondition, TextFilterOperator } from \"gp-grid-core\";\r\nimport { useFilterConditions, type LocalFilterCondition } from \"../composables/useFilterConditions\";\r\n\r\nconst MAX_VALUES_FOR_LIST = 100;\r\n\r\nconst OPERATORS: { value: TextFilterOperator; label: string }[] = [\r\n { value: \"contains\", label: \"Contains\" },\r\n { value: \"notContains\", label: \"Does not contain\" },\r\n { value: \"equals\", label: \"Equals\" },\r\n { value: \"notEquals\", label: \"Does not equal\" },\r\n { value: \"startsWith\", label: \"Starts with\" },\r\n { value: \"endsWith\", label: \"Ends with\" },\r\n { value: \"blank\", label: \"Is blank\" },\r\n { value: \"notBlank\", label: \"Is not blank\" },\r\n];\r\n\r\ntype FilterMode = \"values\" | \"condition\";\r\n\r\nconst props = defineProps<{\r\n distinctValues: CellValue[];\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\n// Helper to convert value to display string\r\nfunction valueToString(v: CellValue): string {\r\n if (Array.isArray(v)) {\r\n return v.join(\", \");\r\n }\r\n return String(v ?? \"\");\r\n}\r\n\r\n// Computed unique values\r\nconst uniqueValues = computed(() => {\r\n const values = props.distinctValues\r\n .filter((v) => v != null && v !== \"\" && !(Array.isArray(v) && v.length === 0))\r\n .map((v) => valueToString(v));\r\n return Array.from(new Set(values)).sort((a, b) => {\r\n const numA = parseFloat(a);\r\n const numB = parseFloat(b);\r\n if (!isNaN(numA) && !isNaN(numB)) {\r\n return numA - numB;\r\n }\r\n return a.localeCompare(b, undefined, { numeric: true, sensitivity: \"base\" });\r\n });\r\n});\r\n\r\nconst hasTooManyValues = computed(() => uniqueValues.value.length > MAX_VALUES_FOR_LIST);\r\n\r\n// Detect initial mode from existing filter\r\nconst initialMode = computed((): FilterMode => {\r\n if (!props.currentFilter?.conditions[0]) {\r\n return hasTooManyValues.value ? \"condition\" : \"values\";\r\n }\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n if (cond.selectedValues && cond.selectedValues.size > 0) {\r\n return \"values\";\r\n }\r\n return \"condition\";\r\n});\r\n\r\nconst mode = ref<FilterMode>(initialMode.value);\r\n\r\n// ============= VALUES MODE STATE =============\r\nconst initialSelected = computed(() => {\r\n if (!props.currentFilter?.conditions[0]) return new Set<string>();\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n return cond.selectedValues ?? new Set<string>();\r\n});\r\n\r\nconst initialIncludeBlanks = computed(() => {\r\n if (!props.currentFilter?.conditions[0]) return true;\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n return cond.includeBlank ?? true;\r\n});\r\n\r\nconst searchText = ref(\"\");\r\nconst selectedValues = ref<Set<string>>(new Set(initialSelected.value));\r\nconst includeBlanks = ref(initialIncludeBlanks.value);\r\n\r\n// ============= CONDITION MODE STATE =============\r\nconst initialConditions = computed((): LocalFilterCondition<TextFilterOperator>[] => {\r\n if (!props.currentFilter?.conditions.length) {\r\n return [{ operator: \"contains\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n if (cond.selectedValues && cond.selectedValues.size > 0) {\r\n return [{ operator: \"contains\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const defaultCombination = props.currentFilter.combination ?? \"and\";\r\n return props.currentFilter.conditions.map((c) => {\r\n const tc = c as TextFilterCondition;\r\n return {\r\n operator: tc.operator,\r\n value: tc.value ?? \"\",\r\n valueTo: \"\",\r\n nextOperator: tc.nextOperator ?? defaultCombination,\r\n };\r\n });\r\n});\r\n\r\nconst { conditions, combination, updateCondition, addCondition, removeCondition } =\r\n useFilterConditions<TextFilterOperator>(\r\n initialConditions.value,\r\n props.currentFilter?.combination ?? \"and\",\r\n );\r\n\r\n// ============= VALUES MODE LOGIC =============\r\nconst displayValues = computed(() => {\r\n if (!searchText.value) return uniqueValues.value;\r\n const lower = searchText.value.toLowerCase();\r\n return uniqueValues.value.filter((v) => v.toLowerCase().includes(lower));\r\n});\r\n\r\nconst hasBlanks = computed(() => {\r\n return props.distinctValues.some((v) => v == null || v === \"\");\r\n});\r\n\r\nconst allSelected = computed(() => {\r\n const allNonBlank = displayValues.value.every((v) => selectedValues.value.has(v));\r\n return allNonBlank && (!hasBlanks.value || includeBlanks.value);\r\n});\r\n\r\nfunction handleSelectAll(): void {\r\n selectedValues.value = new Set(displayValues.value);\r\n if (hasBlanks.value) includeBlanks.value = true;\r\n}\r\n\r\nfunction handleDeselectAll(): void {\r\n selectedValues.value = new Set();\r\n includeBlanks.value = false;\r\n}\r\n\r\nfunction handleValueToggle(value: string): void {\r\n const next = new Set(selectedValues.value);\r\n if (next.has(value)) {\r\n next.delete(value);\r\n } else {\r\n next.add(value);\r\n }\r\n selectedValues.value = next;\r\n}\r\n\r\n// ============= APPLY LOGIC =============\r\nfunction handleApply(): void {\r\n if (mode.value === \"values\") {\r\n const allNonBlankSelected = uniqueValues.value.every((v) => selectedValues.value.has(v));\r\n const isAllSelected = allNonBlankSelected && (!hasBlanks.value || includeBlanks.value);\r\n\r\n if (isAllSelected) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: [\r\n {\r\n type: \"text\",\r\n operator: \"equals\",\r\n selectedValues: selectedValues.value,\r\n includeBlank: includeBlanks.value,\r\n },\r\n ],\r\n combination: \"and\",\r\n };\r\n emit(\"apply\", filter);\r\n } else {\r\n const validConditions = conditions.value.filter((c) => {\r\n if (c.operator === \"blank\" || c.operator === \"notBlank\") return true;\r\n return c.value.trim() !== \"\";\r\n });\r\n\r\n if (validConditions.length === 0) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: validConditions.map((c) => ({\r\n type: \"text\" as const,\r\n operator: c.operator,\r\n value: c.value,\r\n nextOperator: c.nextOperator,\r\n })),\r\n combination: \"and\", // Default combination for backwards compatibility\r\n };\r\n emit(\"apply\", filter);\r\n }\r\n}\r\n\r\nfunction handleClear(): void {\r\n emit(\"apply\", null);\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"gp-grid-filter-content gp-grid-filter-text\">\r\n <!-- Mode toggle - only show if not too many values -->\r\n <div v-if=\"!hasTooManyValues\" class=\"gp-grid-filter-mode-toggle\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: mode === 'values' }\"\r\n @click=\"mode = 'values'\"\r\n >\r\n Values\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: mode === 'condition' }\"\r\n @click=\"mode = 'condition'\"\r\n >\r\n Condition\r\n </button>\r\n </div>\r\n\r\n <!-- Too many values message -->\r\n <div v-if=\"hasTooManyValues && mode === 'condition'\" class=\"gp-grid-filter-info\">\r\n Too many unique values ({{ uniqueValues.length }}). Use conditions to filter.\r\n </div>\r\n\r\n <!-- VALUES MODE -->\r\n <template v-if=\"mode === 'values'\">\r\n <!-- Search input -->\r\n <input\r\n v-model=\"searchText\"\r\n class=\"gp-grid-filter-search\"\r\n type=\"text\"\r\n placeholder=\"Search...\"\r\n autofocus\r\n />\r\n\r\n <!-- Select all / Deselect all -->\r\n <div class=\"gp-grid-filter-actions\">\r\n <button type=\"button\" :disabled=\"allSelected\" @click=\"handleSelectAll\">\r\n Select All\r\n </button>\r\n <button type=\"button\" @click=\"handleDeselectAll\">\r\n Deselect All\r\n </button>\r\n </div>\r\n\r\n <!-- Checkbox list -->\r\n <div class=\"gp-grid-filter-list\">\r\n <!-- Blanks option -->\r\n <label v-if=\"hasBlanks\" class=\"gp-grid-filter-option\">\r\n <input\r\n type=\"checkbox\"\r\n :checked=\"includeBlanks\"\r\n @change=\"includeBlanks = !includeBlanks\"\r\n />\r\n <span class=\"gp-grid-filter-blank\">(Blanks)</span>\r\n </label>\r\n\r\n <!-- Values -->\r\n <label\r\n v-for=\"value in displayValues\"\r\n :key=\"value\"\r\n class=\"gp-grid-filter-option\"\r\n >\r\n <input\r\n type=\"checkbox\"\r\n :checked=\"selectedValues.has(value)\"\r\n @change=\"handleValueToggle(value)\"\r\n />\r\n <span>{{ value }}</span>\r\n </label>\r\n </div>\r\n </template>\r\n\r\n <!-- CONDITION MODE -->\r\n <template v-if=\"mode === 'condition'\">\r\n <div\r\n v-for=\"(cond, index) in conditions\"\r\n :key=\"index\"\r\n class=\"gp-grid-filter-condition\"\r\n >\r\n <!-- Combination toggle (AND/OR) for conditions after the first -->\r\n <div v-if=\"index > 0\" class=\"gp-grid-filter-combination\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'and' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'and' })\"\r\n >\r\n AND\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'or' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'or' })\"\r\n >\r\n OR\r\n </button>\r\n </div>\r\n\r\n <div class=\"gp-grid-filter-row\">\r\n <!-- Operator select -->\r\n <select\r\n :value=\"cond.operator\"\r\n :autofocus=\"index === 0\"\r\n @change=\"updateCondition(index, { operator: ($event.target as HTMLSelectElement).value as TextFilterOperator })\"\r\n >\r\n <option v-for=\"op in OPERATORS\" :key=\"op.value\" :value=\"op.value\">\r\n {{ op.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- Text input (hidden for blank/notBlank) -->\r\n <input\r\n v-if=\"cond.operator !== 'blank' && cond.operator !== 'notBlank'\"\r\n type=\"text\"\r\n :value=\"cond.value\"\r\n placeholder=\"Value\"\r\n class=\"gp-grid-filter-text-input\"\r\n @input=\"updateCondition(index, { value: ($event.target as HTMLInputElement).value })\"\r\n />\r\n\r\n <!-- Remove button (only if more than one condition) -->\r\n <button\r\n v-if=\"conditions.length > 1\"\r\n type=\"button\"\r\n class=\"gp-grid-filter-remove\"\r\n @click=\"removeCondition(index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Add condition button -->\r\n <button type=\"button\" class=\"gp-grid-filter-add\" @click=\"addCondition('contains')\">\r\n + Add condition\r\n </button>\r\n </template>\r\n\r\n <!-- Apply/Clear buttons -->\r\n <div class=\"gp-grid-filter-buttons\">\r\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" @click=\"handleClear\">\r\n Clear\r\n </button>\r\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" @click=\"handleApply\">\r\n Apply\r\n </button>\r\n </div>\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport type { CellValue, ColumnFilterModel, TextFilterCondition, TextFilterOperator } from \"gp-grid-core\";\r\nimport { useFilterConditions, type LocalFilterCondition } from \"../composables/useFilterConditions\";\r\n\r\nconst MAX_VALUES_FOR_LIST = 100;\r\n\r\nconst OPERATORS: { value: TextFilterOperator; label: string }[] = [\r\n { value: \"contains\", label: \"Contains\" },\r\n { value: \"notContains\", label: \"Does not contain\" },\r\n { value: \"equals\", label: \"Equals\" },\r\n { value: \"notEquals\", label: \"Does not equal\" },\r\n { value: \"startsWith\", label: \"Starts with\" },\r\n { value: \"endsWith\", label: \"Ends with\" },\r\n { value: \"blank\", label: \"Is blank\" },\r\n { value: \"notBlank\", label: \"Is not blank\" },\r\n];\r\n\r\ntype FilterMode = \"values\" | \"condition\";\r\n\r\nconst props = defineProps<{\r\n distinctValues: CellValue[];\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\n// Helper to convert value to display string\r\nfunction valueToString(v: CellValue): string {\r\n if (Array.isArray(v)) {\r\n return v.join(\", \");\r\n }\r\n return String(v ?? \"\");\r\n}\r\n\r\n// Computed unique values\r\nconst uniqueValues = computed(() => {\r\n const values = props.distinctValues\r\n .filter((v) => v != null && v !== \"\" && !(Array.isArray(v) && v.length === 0))\r\n .map((v) => valueToString(v));\r\n return Array.from(new Set(values)).sort((a, b) => {\r\n const numA = parseFloat(a);\r\n const numB = parseFloat(b);\r\n if (!isNaN(numA) && !isNaN(numB)) {\r\n return numA - numB;\r\n }\r\n return a.localeCompare(b, undefined, { numeric: true, sensitivity: \"base\" });\r\n });\r\n});\r\n\r\nconst hasTooManyValues = computed(() => uniqueValues.value.length > MAX_VALUES_FOR_LIST);\r\n\r\n// Detect initial mode from existing filter\r\nconst initialMode = computed((): FilterMode => {\r\n if (!props.currentFilter?.conditions[0]) {\r\n return hasTooManyValues.value ? \"condition\" : \"values\";\r\n }\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n if (cond.selectedValues && cond.selectedValues.size > 0) {\r\n return \"values\";\r\n }\r\n return \"condition\";\r\n});\r\n\r\nconst mode = ref<FilterMode>(initialMode.value);\r\n\r\n// ============= VALUES MODE STATE =============\r\nconst initialSelected = computed(() => {\r\n if (!props.currentFilter?.conditions[0]) return new Set<string>();\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n return cond.selectedValues ?? new Set<string>();\r\n});\r\n\r\nconst initialIncludeBlanks = computed(() => {\r\n if (!props.currentFilter?.conditions[0]) return true;\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n return cond.includeBlank ?? true;\r\n});\r\n\r\nconst searchText = ref(\"\");\r\nconst selectedValues = ref<Set<string>>(new Set(initialSelected.value));\r\nconst includeBlanks = ref(initialIncludeBlanks.value);\r\n\r\n// ============= CONDITION MODE STATE =============\r\nconst initialConditions = computed((): LocalFilterCondition<TextFilterOperator>[] => {\r\n if (!props.currentFilter?.conditions.length) {\r\n return [{ operator: \"contains\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const cond = props.currentFilter.conditions[0] as TextFilterCondition;\r\n if (cond.selectedValues && cond.selectedValues.size > 0) {\r\n return [{ operator: \"contains\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const defaultCombination = props.currentFilter.combination ?? \"and\";\r\n return props.currentFilter.conditions.map((c) => {\r\n const tc = c as TextFilterCondition;\r\n return {\r\n operator: tc.operator,\r\n value: tc.value ?? \"\",\r\n valueTo: \"\",\r\n nextOperator: tc.nextOperator ?? defaultCombination,\r\n };\r\n });\r\n});\r\n\r\nconst { conditions, combination, updateCondition, addCondition, removeCondition } =\r\n useFilterConditions<TextFilterOperator>(\r\n initialConditions.value,\r\n props.currentFilter?.combination ?? \"and\",\r\n );\r\n\r\n// ============= VALUES MODE LOGIC =============\r\nconst displayValues = computed(() => {\r\n if (!searchText.value) return uniqueValues.value;\r\n const lower = searchText.value.toLowerCase();\r\n return uniqueValues.value.filter((v) => v.toLowerCase().includes(lower));\r\n});\r\n\r\nconst hasBlanks = computed(() => {\r\n return props.distinctValues.some((v) => v == null || v === \"\");\r\n});\r\n\r\nconst allSelected = computed(() => {\r\n const allNonBlank = displayValues.value.every((v) => selectedValues.value.has(v));\r\n return allNonBlank && (!hasBlanks.value || includeBlanks.value);\r\n});\r\n\r\nfunction handleSelectAll(): void {\r\n selectedValues.value = new Set(displayValues.value);\r\n if (hasBlanks.value) includeBlanks.value = true;\r\n}\r\n\r\nfunction handleDeselectAll(): void {\r\n selectedValues.value = new Set();\r\n includeBlanks.value = false;\r\n}\r\n\r\nfunction handleValueToggle(value: string): void {\r\n const next = new Set(selectedValues.value);\r\n if (next.has(value)) {\r\n next.delete(value);\r\n } else {\r\n next.add(value);\r\n }\r\n selectedValues.value = next;\r\n}\r\n\r\n// ============= APPLY LOGIC =============\r\nfunction handleApply(): void {\r\n if (mode.value === \"values\") {\r\n const allNonBlankSelected = uniqueValues.value.every((v) => selectedValues.value.has(v));\r\n const isAllSelected = allNonBlankSelected && (!hasBlanks.value || includeBlanks.value);\r\n\r\n if (isAllSelected) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: [\r\n {\r\n type: \"text\",\r\n operator: \"equals\",\r\n selectedValues: selectedValues.value,\r\n includeBlank: includeBlanks.value,\r\n },\r\n ],\r\n combination: \"and\",\r\n };\r\n emit(\"apply\", filter);\r\n } else {\r\n const validConditions = conditions.value.filter((c) => {\r\n if (c.operator === \"blank\" || c.operator === \"notBlank\") return true;\r\n return c.value.trim() !== \"\";\r\n });\r\n\r\n if (validConditions.length === 0) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: validConditions.map((c) => ({\r\n type: \"text\" as const,\r\n operator: c.operator,\r\n value: c.value,\r\n nextOperator: c.nextOperator,\r\n })),\r\n combination: \"and\", // Default combination for backwards compatibility\r\n };\r\n emit(\"apply\", filter);\r\n }\r\n}\r\n\r\nfunction handleClear(): void {\r\n emit(\"apply\", null);\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"gp-grid-filter-content gp-grid-filter-text\">\r\n <!-- Mode toggle - only show if not too many values -->\r\n <div v-if=\"!hasTooManyValues\" class=\"gp-grid-filter-mode-toggle\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: mode === 'values' }\"\r\n @click=\"mode = 'values'\"\r\n >\r\n Values\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: mode === 'condition' }\"\r\n @click=\"mode = 'condition'\"\r\n >\r\n Condition\r\n </button>\r\n </div>\r\n\r\n <!-- Too many values message -->\r\n <div v-if=\"hasTooManyValues && mode === 'condition'\" class=\"gp-grid-filter-info\">\r\n Too many unique values ({{ uniqueValues.length }}). Use conditions to filter.\r\n </div>\r\n\r\n <!-- VALUES MODE -->\r\n <template v-if=\"mode === 'values'\">\r\n <!-- Search input -->\r\n <input\r\n v-model=\"searchText\"\r\n class=\"gp-grid-filter-search\"\r\n type=\"text\"\r\n placeholder=\"Search...\"\r\n autofocus\r\n />\r\n\r\n <!-- Select all / Deselect all -->\r\n <div class=\"gp-grid-filter-actions\">\r\n <button type=\"button\" :disabled=\"allSelected\" @click=\"handleSelectAll\">\r\n Select All\r\n </button>\r\n <button type=\"button\" @click=\"handleDeselectAll\">\r\n Deselect All\r\n </button>\r\n </div>\r\n\r\n <!-- Checkbox list -->\r\n <div class=\"gp-grid-filter-list\">\r\n <!-- Blanks option -->\r\n <label v-if=\"hasBlanks\" class=\"gp-grid-filter-option\">\r\n <input\r\n type=\"checkbox\"\r\n :checked=\"includeBlanks\"\r\n @change=\"includeBlanks = !includeBlanks\"\r\n />\r\n <span class=\"gp-grid-filter-blank\">(Blanks)</span>\r\n </label>\r\n\r\n <!-- Values -->\r\n <label\r\n v-for=\"value in displayValues\"\r\n :key=\"value\"\r\n class=\"gp-grid-filter-option\"\r\n >\r\n <input\r\n type=\"checkbox\"\r\n :checked=\"selectedValues.has(value)\"\r\n @change=\"handleValueToggle(value)\"\r\n />\r\n <span>{{ value }}</span>\r\n </label>\r\n </div>\r\n </template>\r\n\r\n <!-- CONDITION MODE -->\r\n <template v-if=\"mode === 'condition'\">\r\n <div\r\n v-for=\"(cond, index) in conditions\"\r\n :key=\"index\"\r\n class=\"gp-grid-filter-condition\"\r\n >\r\n <!-- Combination toggle (AND/OR) for conditions after the first -->\r\n <div v-if=\"index > 0\" class=\"gp-grid-filter-combination\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'and' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'and' })\"\r\n >\r\n AND\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'or' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'or' })\"\r\n >\r\n OR\r\n </button>\r\n </div>\r\n\r\n <div class=\"gp-grid-filter-row\">\r\n <!-- Operator select -->\r\n <select\r\n :value=\"cond.operator\"\r\n :autofocus=\"index === 0\"\r\n @change=\"updateCondition(index, { operator: ($event.target as HTMLSelectElement).value as TextFilterOperator })\"\r\n >\r\n <option v-for=\"op in OPERATORS\" :key=\"op.value\" :value=\"op.value\">\r\n {{ op.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- Text input (hidden for blank/notBlank) -->\r\n <input\r\n v-if=\"cond.operator !== 'blank' && cond.operator !== 'notBlank'\"\r\n type=\"text\"\r\n :value=\"cond.value\"\r\n placeholder=\"Value\"\r\n class=\"gp-grid-filter-text-input\"\r\n @input=\"updateCondition(index, { value: ($event.target as HTMLInputElement).value })\"\r\n />\r\n\r\n <!-- Remove button (only if more than one condition) -->\r\n <button\r\n v-if=\"conditions.length > 1\"\r\n type=\"button\"\r\n class=\"gp-grid-filter-remove\"\r\n @click=\"removeCondition(index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Add condition button -->\r\n <button type=\"button\" class=\"gp-grid-filter-add\" @click=\"addCondition('contains')\">\r\n + Add condition\r\n </button>\r\n </template>\r\n\r\n <!-- Apply/Clear buttons -->\r\n <div class=\"gp-grid-filter-buttons\">\r\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" @click=\"handleClear\">\r\n Clear\r\n </button>\r\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" @click=\"handleApply\">\r\n Apply\r\n </button>\r\n </div>\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport type { ColumnFilterModel, NumberFilterCondition, NumberFilterOperator } from \"gp-grid-core\";\r\nimport { useFilterConditions, type LocalFilterCondition } from \"../composables/useFilterConditions\";\r\n\r\nconst OPERATORS: { value: NumberFilterOperator; label: string }[] = [\r\n { value: \"=\", label: \"=\" },\r\n { value: \"!=\", label: \"\\u2260\" },\r\n { value: \">\", label: \">\" },\r\n { value: \"<\", label: \"<\" },\r\n { value: \">=\", label: \"\\u2265\" },\r\n { value: \"<=\", label: \"\\u2264\" },\r\n { value: \"between\", label: \"\\u2194\" },\r\n { value: \"blank\", label: \"Is blank\" },\r\n { value: \"notBlank\", label: \"Not blank\" },\r\n];\r\n\r\nconst props = defineProps<{\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\n// Parse initial conditions from current filter\r\nconst initialConditions = computed((): LocalFilterCondition<NumberFilterOperator>[] => {\r\n if (!props.currentFilter?.conditions.length) {\r\n return [{ operator: \"=\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const defaultCombination = props.currentFilter.combination ?? \"and\";\r\n return props.currentFilter.conditions.map((c) => {\r\n const cond = c as NumberFilterCondition;\r\n return {\r\n operator: cond.operator,\r\n value: cond.value != null ? String(cond.value) : \"\",\r\n valueTo: cond.valueTo != null ? String(cond.valueTo) : \"\",\r\n nextOperator: cond.nextOperator ?? defaultCombination,\r\n };\r\n });\r\n});\r\n\r\nconst { conditions, combination, updateCondition, addCondition, removeCondition } =\r\n useFilterConditions<NumberFilterOperator>(\r\n initialConditions.value,\r\n props.currentFilter?.combination ?? \"and\",\r\n );\r\n\r\nfunction handleApply(): void {\r\n const validConditions = conditions.value.filter((c) => {\r\n if (c.operator === \"blank\" || c.operator === \"notBlank\") return true;\r\n if (c.operator === \"between\") {\r\n return c.value !== \"\" && c.valueTo !== \"\";\r\n }\r\n return c.value !== \"\";\r\n });\r\n\r\n if (validConditions.length === 0) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: validConditions.map((c) => ({\r\n type: \"number\" as const,\r\n operator: c.operator,\r\n value: c.value ? parseFloat(c.value) : undefined,\r\n valueTo: c.valueTo ? parseFloat(c.valueTo) : undefined,\r\n nextOperator: c.nextOperator,\r\n })),\r\n combination: \"and\", // Default combination for backwards compatibility\r\n };\r\n emit(\"apply\", filter);\r\n}\r\n\r\nfunction handleClear(): void {\r\n emit(\"apply\", null);\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"gp-grid-filter-content gp-grid-filter-number\">\r\n <div\r\n v-for=\"(cond, index) in conditions\"\r\n :key=\"index\"\r\n class=\"gp-grid-filter-condition\"\r\n >\r\n <!-- Combination toggle (AND/OR) for conditions after the first -->\r\n <div v-if=\"index > 0\" class=\"gp-grid-filter-combination\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'and' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'and' })\"\r\n >\r\n AND\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'or' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'or' })\"\r\n >\r\n OR\r\n </button>\r\n </div>\r\n\r\n <div class=\"gp-grid-filter-row\">\r\n <!-- Operator select -->\r\n <select\r\n :value=\"cond.operator\"\r\n @change=\"updateCondition(index, { operator: ($event.target as HTMLSelectElement).value as NumberFilterOperator })\"\r\n >\r\n <option v-for=\"op in OPERATORS\" :key=\"op.value\" :value=\"op.value\">\r\n {{ op.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- Number input (hidden for blank/notBlank) -->\r\n <input\r\n v-if=\"cond.operator !== 'blank' && cond.operator !== 'notBlank'\"\r\n type=\"number\"\r\n :value=\"cond.value\"\r\n placeholder=\"Value\"\r\n @input=\"updateCondition(index, { value: ($event.target as HTMLInputElement).value })\"\r\n />\r\n\r\n <!-- Second number input for \"between\" -->\r\n <template v-if=\"cond.operator === 'between'\">\r\n <span class=\"gp-grid-filter-to\">to</span>\r\n <input\r\n type=\"number\"\r\n :value=\"cond.valueTo\"\r\n placeholder=\"Value\"\r\n @input=\"updateCondition(index, { valueTo: ($event.target as HTMLInputElement).value })\"\r\n />\r\n </template>\r\n\r\n <!-- Remove button (only if more than one condition) -->\r\n <button\r\n v-if=\"conditions.length > 1\"\r\n type=\"button\"\r\n class=\"gp-grid-filter-remove\"\r\n @click=\"removeCondition(index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Add condition button -->\r\n <button type=\"button\" class=\"gp-grid-filter-add\" @click=\"addCondition('=')\">\r\n + Add condition\r\n </button>\r\n\r\n <!-- Apply/Clear buttons -->\r\n <div class=\"gp-grid-filter-buttons\">\r\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" @click=\"handleClear\">\r\n Clear\r\n </button>\r\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" @click=\"handleApply\">\r\n Apply\r\n </button>\r\n </div>\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport type { ColumnFilterModel, NumberFilterCondition, NumberFilterOperator } from \"gp-grid-core\";\r\nimport { useFilterConditions, type LocalFilterCondition } from \"../composables/useFilterConditions\";\r\n\r\nconst OPERATORS: { value: NumberFilterOperator; label: string }[] = [\r\n { value: \"=\", label: \"=\" },\r\n { value: \"!=\", label: \"\\u2260\" },\r\n { value: \">\", label: \">\" },\r\n { value: \"<\", label: \"<\" },\r\n { value: \">=\", label: \"\\u2265\" },\r\n { value: \"<=\", label: \"\\u2264\" },\r\n { value: \"between\", label: \"\\u2194\" },\r\n { value: \"blank\", label: \"Is blank\" },\r\n { value: \"notBlank\", label: \"Not blank\" },\r\n];\r\n\r\nconst props = defineProps<{\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\n// Parse initial conditions from current filter\r\nconst initialConditions = computed((): LocalFilterCondition<NumberFilterOperator>[] => {\r\n if (!props.currentFilter?.conditions.length) {\r\n return [{ operator: \"=\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const defaultCombination = props.currentFilter.combination ?? \"and\";\r\n return props.currentFilter.conditions.map((c) => {\r\n const cond = c as NumberFilterCondition;\r\n return {\r\n operator: cond.operator,\r\n value: cond.value != null ? String(cond.value) : \"\",\r\n valueTo: cond.valueTo != null ? String(cond.valueTo) : \"\",\r\n nextOperator: cond.nextOperator ?? defaultCombination,\r\n };\r\n });\r\n});\r\n\r\nconst { conditions, combination, updateCondition, addCondition, removeCondition } =\r\n useFilterConditions<NumberFilterOperator>(\r\n initialConditions.value,\r\n props.currentFilter?.combination ?? \"and\",\r\n );\r\n\r\nfunction handleApply(): void {\r\n const validConditions = conditions.value.filter((c) => {\r\n if (c.operator === \"blank\" || c.operator === \"notBlank\") return true;\r\n if (c.operator === \"between\") {\r\n return c.value !== \"\" && c.valueTo !== \"\";\r\n }\r\n return c.value !== \"\";\r\n });\r\n\r\n if (validConditions.length === 0) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: validConditions.map((c) => ({\r\n type: \"number\" as const,\r\n operator: c.operator,\r\n value: c.value ? parseFloat(c.value) : undefined,\r\n valueTo: c.valueTo ? parseFloat(c.valueTo) : undefined,\r\n nextOperator: c.nextOperator,\r\n })),\r\n combination: \"and\", // Default combination for backwards compatibility\r\n };\r\n emit(\"apply\", filter);\r\n}\r\n\r\nfunction handleClear(): void {\r\n emit(\"apply\", null);\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"gp-grid-filter-content gp-grid-filter-number\">\r\n <div\r\n v-for=\"(cond, index) in conditions\"\r\n :key=\"index\"\r\n class=\"gp-grid-filter-condition\"\r\n >\r\n <!-- Combination toggle (AND/OR) for conditions after the first -->\r\n <div v-if=\"index > 0\" class=\"gp-grid-filter-combination\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'and' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'and' })\"\r\n >\r\n AND\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'or' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'or' })\"\r\n >\r\n OR\r\n </button>\r\n </div>\r\n\r\n <div class=\"gp-grid-filter-row\">\r\n <!-- Operator select -->\r\n <select\r\n :value=\"cond.operator\"\r\n @change=\"updateCondition(index, { operator: ($event.target as HTMLSelectElement).value as NumberFilterOperator })\"\r\n >\r\n <option v-for=\"op in OPERATORS\" :key=\"op.value\" :value=\"op.value\">\r\n {{ op.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- Number input (hidden for blank/notBlank) -->\r\n <input\r\n v-if=\"cond.operator !== 'blank' && cond.operator !== 'notBlank'\"\r\n type=\"number\"\r\n :value=\"cond.value\"\r\n placeholder=\"Value\"\r\n @input=\"updateCondition(index, { value: ($event.target as HTMLInputElement).value })\"\r\n />\r\n\r\n <!-- Second number input for \"between\" -->\r\n <template v-if=\"cond.operator === 'between'\">\r\n <span class=\"gp-grid-filter-to\">to</span>\r\n <input\r\n type=\"number\"\r\n :value=\"cond.valueTo\"\r\n placeholder=\"Value\"\r\n @input=\"updateCondition(index, { valueTo: ($event.target as HTMLInputElement).value })\"\r\n />\r\n </template>\r\n\r\n <!-- Remove button (only if more than one condition) -->\r\n <button\r\n v-if=\"conditions.length > 1\"\r\n type=\"button\"\r\n class=\"gp-grid-filter-remove\"\r\n @click=\"removeCondition(index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Add condition button -->\r\n <button type=\"button\" class=\"gp-grid-filter-add\" @click=\"addCondition('=')\">\r\n + Add condition\r\n </button>\r\n\r\n <!-- Apply/Clear buttons -->\r\n <div class=\"gp-grid-filter-buttons\">\r\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" @click=\"handleClear\">\r\n Clear\r\n </button>\r\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" @click=\"handleApply\">\r\n Apply\r\n </button>\r\n </div>\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport type { ColumnFilterModel, DateFilterCondition, DateFilterOperator } from \"gp-grid-core\";\r\nimport { useFilterConditions, type LocalFilterCondition } from \"../composables/useFilterConditions\";\r\n\r\nconst OPERATORS: { value: DateFilterOperator; label: string }[] = [\r\n { value: \"=\", label: \"=\" },\r\n { value: \"!=\", label: \"\\u2260\" },\r\n { value: \">\", label: \">\" },\r\n { value: \"<\", label: \"<\" },\r\n { value: \"between\", label: \"\\u2194\" },\r\n { value: \"blank\", label: \"Is blank\" },\r\n { value: \"notBlank\", label: \"Not blank\" },\r\n];\r\n\r\nconst props = defineProps<{\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\n// Convert Date to YYYY-MM-DD string for input\r\nfunction formatDateForInput(date: Date | string | undefined): string {\r\n if (!date) return \"\";\r\n const d = typeof date === \"string\" ? new Date(date) : date;\r\n if (isNaN(d.getTime())) return \"\";\r\n return d.toISOString().split(\"T\")[0]!;\r\n}\r\n\r\n// Parse initial conditions from current filter\r\nconst initialConditions = computed((): LocalFilterCondition<DateFilterOperator>[] => {\r\n if (!props.currentFilter?.conditions.length) {\r\n return [{ operator: \"=\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const defaultCombination = props.currentFilter.combination ?? \"and\";\r\n return props.currentFilter.conditions.map((c) => {\r\n const cond = c as DateFilterCondition;\r\n return {\r\n operator: cond.operator,\r\n value: formatDateForInput(cond.value),\r\n valueTo: formatDateForInput(cond.valueTo),\r\n nextOperator: cond.nextOperator ?? defaultCombination,\r\n };\r\n });\r\n});\r\n\r\nconst { conditions, combination, updateCondition, addCondition, removeCondition } =\r\n useFilterConditions<DateFilterOperator>(\r\n initialConditions.value,\r\n props.currentFilter?.combination ?? \"and\",\r\n );\r\n\r\nfunction handleApply(): void {\r\n const validConditions = conditions.value.filter((c) => {\r\n if (c.operator === \"blank\" || c.operator === \"notBlank\") return true;\r\n if (c.operator === \"between\") {\r\n return c.value !== \"\" && c.valueTo !== \"\";\r\n }\r\n return c.value !== \"\";\r\n });\r\n\r\n if (validConditions.length === 0) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: validConditions.map((c) => ({\r\n type: \"date\" as const,\r\n operator: c.operator,\r\n value: c.value || undefined,\r\n valueTo: c.valueTo || undefined,\r\n nextOperator: c.nextOperator,\r\n })),\r\n combination: \"and\", // Default combination for backwards compatibility\r\n };\r\n emit(\"apply\", filter);\r\n}\r\n\r\nfunction handleClear(): void {\r\n emit(\"apply\", null);\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"gp-grid-filter-content gp-grid-filter-date\">\r\n <div\r\n v-for=\"(cond, index) in conditions\"\r\n :key=\"index\"\r\n class=\"gp-grid-filter-condition\"\r\n >\r\n <!-- Combination toggle (AND/OR) for conditions after the first -->\r\n <div v-if=\"index > 0\" class=\"gp-grid-filter-combination\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'and' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'and' })\"\r\n >\r\n AND\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'or' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'or' })\"\r\n >\r\n OR\r\n </button>\r\n </div>\r\n\r\n <div class=\"gp-grid-filter-row\">\r\n <!-- Operator select -->\r\n <select\r\n :value=\"cond.operator\"\r\n @change=\"updateCondition(index, { operator: ($event.target as HTMLSelectElement).value as DateFilterOperator })\"\r\n >\r\n <option v-for=\"op in OPERATORS\" :key=\"op.value\" :value=\"op.value\">\r\n {{ op.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- Date input (hidden for blank/notBlank) -->\r\n <input\r\n v-if=\"cond.operator !== 'blank' && cond.operator !== 'notBlank'\"\r\n type=\"date\"\r\n :value=\"cond.value\"\r\n @input=\"updateCondition(index, { value: ($event.target as HTMLInputElement).value })\"\r\n />\r\n\r\n <!-- Second date input for \"between\" -->\r\n <template v-if=\"cond.operator === 'between'\">\r\n <span class=\"gp-grid-filter-to\">to</span>\r\n <input\r\n type=\"date\"\r\n :value=\"cond.valueTo\"\r\n @input=\"updateCondition(index, { valueTo: ($event.target as HTMLInputElement).value })\"\r\n />\r\n </template>\r\n\r\n <!-- Remove button (only if more than one condition) -->\r\n <button\r\n v-if=\"conditions.length > 1\"\r\n type=\"button\"\r\n class=\"gp-grid-filter-remove\"\r\n @click=\"removeCondition(index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Add condition button -->\r\n <button type=\"button\" class=\"gp-grid-filter-add\" @click=\"addCondition('=')\">\r\n + Add condition\r\n </button>\r\n\r\n <!-- Apply/Clear buttons -->\r\n <div class=\"gp-grid-filter-buttons\">\r\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" @click=\"handleClear\">\r\n Clear\r\n </button>\r\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" @click=\"handleApply\">\r\n Apply\r\n </button>\r\n </div>\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport type { ColumnFilterModel, DateFilterCondition, DateFilterOperator } from \"gp-grid-core\";\r\nimport { useFilterConditions, type LocalFilterCondition } from \"../composables/useFilterConditions\";\r\n\r\nconst OPERATORS: { value: DateFilterOperator; label: string }[] = [\r\n { value: \"=\", label: \"=\" },\r\n { value: \"!=\", label: \"\\u2260\" },\r\n { value: \">\", label: \">\" },\r\n { value: \"<\", label: \"<\" },\r\n { value: \"between\", label: \"\\u2194\" },\r\n { value: \"blank\", label: \"Is blank\" },\r\n { value: \"notBlank\", label: \"Not blank\" },\r\n];\r\n\r\nconst props = defineProps<{\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\n// Convert Date to YYYY-MM-DD string for input\r\nfunction formatDateForInput(date: Date | string | undefined): string {\r\n if (!date) return \"\";\r\n const d = typeof date === \"string\" ? new Date(date) : date;\r\n if (isNaN(d.getTime())) return \"\";\r\n return d.toISOString().split(\"T\")[0]!;\r\n}\r\n\r\n// Parse initial conditions from current filter\r\nconst initialConditions = computed((): LocalFilterCondition<DateFilterOperator>[] => {\r\n if (!props.currentFilter?.conditions.length) {\r\n return [{ operator: \"=\", value: \"\", valueTo: \"\", nextOperator: \"and\" }];\r\n }\r\n const defaultCombination = props.currentFilter.combination ?? \"and\";\r\n return props.currentFilter.conditions.map((c) => {\r\n const cond = c as DateFilterCondition;\r\n return {\r\n operator: cond.operator,\r\n value: formatDateForInput(cond.value),\r\n valueTo: formatDateForInput(cond.valueTo),\r\n nextOperator: cond.nextOperator ?? defaultCombination,\r\n };\r\n });\r\n});\r\n\r\nconst { conditions, combination, updateCondition, addCondition, removeCondition } =\r\n useFilterConditions<DateFilterOperator>(\r\n initialConditions.value,\r\n props.currentFilter?.combination ?? \"and\",\r\n );\r\n\r\nfunction handleApply(): void {\r\n const validConditions = conditions.value.filter((c) => {\r\n if (c.operator === \"blank\" || c.operator === \"notBlank\") return true;\r\n if (c.operator === \"between\") {\r\n return c.value !== \"\" && c.valueTo !== \"\";\r\n }\r\n return c.value !== \"\";\r\n });\r\n\r\n if (validConditions.length === 0) {\r\n emit(\"apply\", null);\r\n return;\r\n }\r\n\r\n const filter: ColumnFilterModel = {\r\n conditions: validConditions.map((c) => ({\r\n type: \"date\" as const,\r\n operator: c.operator,\r\n value: c.value || undefined,\r\n valueTo: c.valueTo || undefined,\r\n nextOperator: c.nextOperator,\r\n })),\r\n combination: \"and\", // Default combination for backwards compatibility\r\n };\r\n emit(\"apply\", filter);\r\n}\r\n\r\nfunction handleClear(): void {\r\n emit(\"apply\", null);\r\n}\r\n</script>\r\n\r\n<template>\r\n <div class=\"gp-grid-filter-content gp-grid-filter-date\">\r\n <div\r\n v-for=\"(cond, index) in conditions\"\r\n :key=\"index\"\r\n class=\"gp-grid-filter-condition\"\r\n >\r\n <!-- Combination toggle (AND/OR) for conditions after the first -->\r\n <div v-if=\"index > 0\" class=\"gp-grid-filter-combination\">\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'and' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'and' })\"\r\n >\r\n AND\r\n </button>\r\n <button\r\n type=\"button\"\r\n :class=\"{ active: conditions[index - 1]?.nextOperator === 'or' }\"\r\n @click=\"updateCondition(index - 1, { nextOperator: 'or' })\"\r\n >\r\n OR\r\n </button>\r\n </div>\r\n\r\n <div class=\"gp-grid-filter-row\">\r\n <!-- Operator select -->\r\n <select\r\n :value=\"cond.operator\"\r\n @change=\"updateCondition(index, { operator: ($event.target as HTMLSelectElement).value as DateFilterOperator })\"\r\n >\r\n <option v-for=\"op in OPERATORS\" :key=\"op.value\" :value=\"op.value\">\r\n {{ op.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- Date input (hidden for blank/notBlank) -->\r\n <input\r\n v-if=\"cond.operator !== 'blank' && cond.operator !== 'notBlank'\"\r\n type=\"date\"\r\n :value=\"cond.value\"\r\n @input=\"updateCondition(index, { value: ($event.target as HTMLInputElement).value })\"\r\n />\r\n\r\n <!-- Second date input for \"between\" -->\r\n <template v-if=\"cond.operator === 'between'\">\r\n <span class=\"gp-grid-filter-to\">to</span>\r\n <input\r\n type=\"date\"\r\n :value=\"cond.valueTo\"\r\n @input=\"updateCondition(index, { valueTo: ($event.target as HTMLInputElement).value })\"\r\n />\r\n </template>\r\n\r\n <!-- Remove button (only if more than one condition) -->\r\n <button\r\n v-if=\"conditions.length > 1\"\r\n type=\"button\"\r\n class=\"gp-grid-filter-remove\"\r\n @click=\"removeCondition(index)\"\r\n >\r\n ×\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Add condition button -->\r\n <button type=\"button\" class=\"gp-grid-filter-add\" @click=\"addCondition('=')\">\r\n + Add condition\r\n </button>\r\n\r\n <!-- Apply/Clear buttons -->\r\n <div class=\"gp-grid-filter-buttons\">\r\n <button type=\"button\" class=\"gp-grid-filter-btn-clear\" @click=\"handleClear\">\r\n Clear\r\n </button>\r\n <button type=\"button\" class=\"gp-grid-filter-btn-apply\" @click=\"handleApply\">\r\n Apply\r\n </button>\r\n </div>\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport type { ColumnDefinition, CellValue, ColumnFilterModel } from \"gp-grid-core\";\r\nimport { useFilterPopup } from \"../composables/useFilterPopup\";\r\nimport TextFilterContent from \"./TextFilterContent.vue\";\r\nimport NumberFilterContent from \"./NumberFilterContent.vue\";\r\nimport DateFilterContent from \"./DateFilterContent.vue\";\r\n\r\nconst props = defineProps<{\r\n column: ColumnDefinition;\r\n colIndex: number;\r\n anchorRect: { top: number; left: number; width: number; height: number };\r\n distinctValues: CellValue[];\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [colId: string, filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\nconst popupRef = ref<HTMLDivElement | null>(null);\r\n\r\n// Use the filter popup composable for click-outside and escape key handling\r\nuseFilterPopup(popupRef, {\r\n onClose: () => emit(\"close\"),\r\n ignoreSelector: \".gp-grid-filter-icon\",\r\n});\r\n\r\nconst colId = computed(() => props.column.colId ?? props.column.field);\r\n\r\nfunction handleApply(filter: ColumnFilterModel | null): void {\r\n emit(\"apply\", colId.value, filter);\r\n emit(\"close\");\r\n}\r\n\r\nfunction handleClose(): void {\r\n emit(\"close\");\r\n}\r\n\r\n// Determine filter type based on column data type\r\nconst dataType = computed(() => props.column.cellDataType);\r\n\r\nconst isTextType = computed(() => dataType.value === \"text\" || dataType.value === \"object\");\r\n\r\nconst isNumberType = computed(() => dataType.value === \"number\");\r\n\r\nconst isDateType = computed(() =>\r\n dataType.value === \"date\" ||\r\n dataType.value === \"dateString\" ||\r\n dataType.value === \"dateTime\" ||\r\n dataType.value === \"dateTimeString\"\r\n);\r\n\r\n// Position popup below the header\r\nconst popupStyle = computed(() => ({\r\n position: \"fixed\" as const,\r\n top: `${props.anchorRect.top + props.anchorRect.height + 4}px`,\r\n left: `${props.anchorRect.left}px`,\r\n minWidth: `${Math.max(200, props.anchorRect.width)}px`,\r\n zIndex: 10000,\r\n}));\r\n</script>\r\n\r\n<template>\r\n <div ref=\"popupRef\" class=\"gp-grid-filter-popup\" :style=\"popupStyle\">\r\n <div class=\"gp-grid-filter-header\">\r\n Filter: {{ column.headerName ?? column.field }}\r\n </div>\r\n\r\n <!-- Number filter -->\r\n <NumberFilterContent\r\n v-if=\"isNumberType\"\r\n :current-filter=\"currentFilter\"\r\n @apply=\"handleApply\"\r\n @close=\"handleClose\"\r\n />\r\n\r\n <!-- Date filter -->\r\n <DateFilterContent\r\n v-else-if=\"isDateType\"\r\n :current-filter=\"currentFilter\"\r\n @apply=\"handleApply\"\r\n @close=\"handleClose\"\r\n />\r\n\r\n <!-- Text filter (default) -->\r\n <TextFilterContent\r\n v-else\r\n :distinct-values=\"distinctValues\"\r\n :current-filter=\"currentFilter\"\r\n @apply=\"handleApply\"\r\n @close=\"handleClose\"\r\n />\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport type { ColumnDefinition, CellValue, ColumnFilterModel } from \"gp-grid-core\";\r\nimport { useFilterPopup } from \"../composables/useFilterPopup\";\r\nimport TextFilterContent from \"./TextFilterContent.vue\";\r\nimport NumberFilterContent from \"./NumberFilterContent.vue\";\r\nimport DateFilterContent from \"./DateFilterContent.vue\";\r\n\r\nconst props = defineProps<{\r\n column: ColumnDefinition;\r\n colIndex: number;\r\n anchorRect: { top: number; left: number; width: number; height: number };\r\n distinctValues: CellValue[];\r\n currentFilter?: ColumnFilterModel;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n apply: [colId: string, filter: ColumnFilterModel | null];\r\n close: [];\r\n}>();\r\n\r\nconst popupRef = ref<HTMLDivElement | null>(null);\r\n\r\n// Use the filter popup composable for click-outside and escape key handling\r\nuseFilterPopup(popupRef, {\r\n onClose: () => emit(\"close\"),\r\n ignoreSelector: \".gp-grid-filter-icon\",\r\n});\r\n\r\nconst colId = computed(() => props.column.colId ?? props.column.field);\r\n\r\nfunction handleApply(filter: ColumnFilterModel | null): void {\r\n emit(\"apply\", colId.value, filter);\r\n emit(\"close\");\r\n}\r\n\r\nfunction handleClose(): void {\r\n emit(\"close\");\r\n}\r\n\r\n// Determine filter type based on column data type\r\nconst dataType = computed(() => props.column.cellDataType);\r\n\r\nconst isTextType = computed(() => dataType.value === \"text\" || dataType.value === \"object\");\r\n\r\nconst isNumberType = computed(() => dataType.value === \"number\");\r\n\r\nconst isDateType = computed(() =>\r\n dataType.value === \"date\" ||\r\n dataType.value === \"dateString\" ||\r\n dataType.value === \"dateTime\" ||\r\n dataType.value === \"dateTimeString\"\r\n);\r\n\r\n// Position popup below the header\r\nconst popupStyle = computed(() => ({\r\n position: \"fixed\" as const,\r\n top: `${props.anchorRect.top + props.anchorRect.height + 4}px`,\r\n left: `${props.anchorRect.left}px`,\r\n minWidth: `${Math.max(200, props.anchorRect.width)}px`,\r\n zIndex: 10000,\r\n}));\r\n</script>\r\n\r\n<template>\r\n <div ref=\"popupRef\" class=\"gp-grid-filter-popup\" :style=\"popupStyle\">\r\n <div class=\"gp-grid-filter-header\">\r\n Filter: {{ column.headerName ?? column.field }}\r\n </div>\r\n\r\n <!-- Number filter -->\r\n <NumberFilterContent\r\n v-if=\"isNumberType\"\r\n :current-filter=\"currentFilter\"\r\n @apply=\"handleApply\"\r\n @close=\"handleClose\"\r\n />\r\n\r\n <!-- Date filter -->\r\n <DateFilterContent\r\n v-else-if=\"isDateType\"\r\n :current-filter=\"currentFilter\"\r\n @apply=\"handleApply\"\r\n @close=\"handleClose\"\r\n />\r\n\r\n <!-- Text filter (default) -->\r\n <TextFilterContent\r\n v-else\r\n :distinct-values=\"distinctValues\"\r\n :current-filter=\"currentFilter\"\r\n @apply=\"handleApply\"\r\n @close=\"handleClose\"\r\n />\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n shallowRef,\r\n computed,\r\n onMounted,\r\n onUnmounted,\r\n watch,\r\n} from \"vue\";\r\nimport {\r\n GridCore,\r\n createClientDataSource,\r\n createDataSourceFromArray,\r\n injectStyles,\r\n calculateColumnPositions,\r\n getTotalWidth,\r\n isCellSelected,\r\n isCellActive,\r\n isCellEditing,\r\n isCellInFillPreview,\r\n buildCellClasses,\r\n} from \"gp-grid-core\";\r\nimport type { Row, ColumnDefinition, ColumnFilterModel, DataSource, CellRange } from \"gp-grid-core\";\r\nimport { useGridState } from \"./gridState\";\r\nimport { useInputHandler } from \"./composables/useInputHandler\";\r\nimport { useFillHandle } from \"./composables/useFillHandle\";\r\nimport { renderCell } from \"./renderers/cellRenderer\";\r\nimport { renderEditCell } from \"./renderers/editRenderer\";\r\nimport { renderHeader } from \"./renderers/headerRenderer\";\r\nimport type { VueCellRenderer, VueEditRenderer, VueHeaderRenderer } from \"./types\";\r\nimport FilterPopup from \"./components/FilterPopup.vue\";\r\n\r\n// Inject styles on first render\r\ninjectStyles();\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n columns: ColumnDefinition[];\r\n dataSource?: DataSource<Row>;\r\n rowData?: Row[];\r\n rowHeight: number;\r\n headerHeight?: number;\r\n overscan?: number;\r\n sortingEnabled?: boolean;\r\n darkMode?: boolean;\r\n wheelDampening?: number;\r\n cellRenderers?: Record<string, VueCellRenderer>;\r\n editRenderers?: Record<string, VueEditRenderer>;\r\n headerRenderers?: Record<string, VueHeaderRenderer>;\r\n cellRenderer?: VueCellRenderer;\r\n editRenderer?: VueEditRenderer;\r\n headerRenderer?: VueHeaderRenderer;\r\n }>(),\r\n {\r\n overscan: 3,\r\n sortingEnabled: true,\r\n darkMode: false,\r\n wheelDampening: 0.1,\r\n cellRenderers: () => ({}),\r\n editRenderers: () => ({}),\r\n headerRenderers: () => ({}),\r\n },\r\n);\r\n\r\n// Refs\r\nconst containerRef = ref<HTMLDivElement | null>(null);\r\nconst coreRef = shallowRef<GridCore | null>(null);\r\n\r\n// State\r\nconst { state, applyInstructions } = useGridState();\r\n\r\n// Computed values\r\nconst totalHeaderHeight = computed(() => props.headerHeight ?? props.rowHeight);\r\nconst columnPositions = computed(() => calculateColumnPositions(props.columns));\r\nconst totalWidth = computed(() => getTotalWidth(columnPositions.value));\r\nconst slotsArray = computed(() => Array.from(state.slots.values()));\r\n\r\n// Input handling\r\nconst {\r\n handleCellMouseDown,\r\n handleCellDoubleClick,\r\n handleFillHandleMouseDown,\r\n handleHeaderClick,\r\n handleKeyDown,\r\n handleWheel,\r\n dragState,\r\n} = useInputHandler(coreRef, containerRef, computed(() => props.columns), {\r\n activeCell: computed(() => state.activeCell),\r\n selectionRange: computed(() => state.selectionRange),\r\n editingCell: computed(() => state.editingCell),\r\n filterPopupOpen: computed(() => state.filterPopup?.isOpen ?? false),\r\n rowHeight: props.rowHeight,\r\n headerHeight: totalHeaderHeight.value,\r\n columnPositions,\r\n slots: computed(() => state.slots),\r\n});\r\n\r\n// Fill handle position\r\nconst { fillHandlePosition } = useFillHandle({\r\n activeCell: computed(() => state.activeCell),\r\n selectionRange: computed(() => state.selectionRange),\r\n slots: computed(() => state.slots),\r\n columns: computed(() => props.columns),\r\n columnPositions,\r\n rowHeight: props.rowHeight,\r\n});\r\n\r\n// Handle scroll\r\nfunction handleScroll(): void {\r\n const container = containerRef.value;\r\n const core = coreRef.value;\r\n if (!container || !core) return;\r\n\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n}\r\n\r\n// Handle filter apply\r\nfunction handleFilterApply(colId: string, filter: ColumnFilterModel | null): void {\r\n const core = coreRef.value;\r\n if (core) {\r\n core.setFilter(colId, filter);\r\n }\r\n}\r\n\r\n// Handle filter popup close\r\nfunction handleFilterPopupClose(): void {\r\n const core = coreRef.value;\r\n if (core) {\r\n core.closeFilterPopup();\r\n }\r\n}\r\n\r\n// Get cell classes\r\nfunction getCellClasses(rowIndex: number, colIndex: number): string {\r\n const isEditing = isCellEditing(rowIndex, colIndex, state.editingCell);\r\n const active = isCellActive(rowIndex, colIndex, state.activeCell);\r\n const selected = isCellSelected(rowIndex, colIndex, state.selectionRange);\r\n const inFillPreview = isCellInFillPreview(\r\n rowIndex,\r\n colIndex,\r\n dragState.value.dragType === \"fill\",\r\n dragState.value.fillSourceRange,\r\n dragState.value.fillTarget,\r\n );\r\n return buildCellClasses(active, selected, isEditing, inFillPreview);\r\n}\r\n\r\n// Initialize GridCore\r\nonMounted(() => {\r\n const dataSource =\r\n props.dataSource ??\r\n (props.rowData ? createDataSourceFromArray(props.rowData) : createClientDataSource<Row>([]));\r\n\r\n const core = new GridCore<Row>({\r\n columns: props.columns,\r\n dataSource,\r\n rowHeight: props.rowHeight,\r\n headerHeight: totalHeaderHeight.value,\r\n overscan: props.overscan,\r\n sortingEnabled: props.sortingEnabled,\r\n });\r\n\r\n coreRef.value = core;\r\n\r\n // Subscribe to batched instructions\r\n const unsubscribe = core.onBatchInstruction((instructions) => {\r\n applyInstructions(instructions);\r\n });\r\n\r\n // Initialize\r\n core.initialize();\r\n\r\n // Initial measurement\r\n const container = containerRef.value;\r\n if (container) {\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n\r\n // Resize observer\r\n const resizeObserver = new ResizeObserver(() => {\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n });\r\n resizeObserver.observe(container);\r\n\r\n onUnmounted(() => {\r\n resizeObserver.disconnect();\r\n unsubscribe();\r\n coreRef.value = null;\r\n });\r\n }\r\n});\r\n\r\n// Subscribe to data source changes\r\nwatch(\r\n () => props.dataSource,\r\n (dataSource) => {\r\n if (dataSource) {\r\n const mutableDataSource = dataSource as {\r\n subscribe?: (listener: () => void) => () => void;\r\n };\r\n if (mutableDataSource.subscribe) {\r\n const unsubscribe = mutableDataSource.subscribe(() => {\r\n coreRef.value?.refresh();\r\n });\r\n onUnmounted(() => unsubscribe());\r\n }\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n</script>\r\n\r\n<template>\r\n <div\r\n ref=\"containerRef\"\r\n :class=\"['gp-grid-container', { 'gp-grid-container--dark': darkMode }]\"\r\n style=\"width: 100%; height: 100%; overflow: auto; position: relative\"\r\n tabindex=\"0\"\r\n @scroll=\"handleScroll\"\r\n @wheel=\"(e) => handleWheel(e, wheelDampening)\"\r\n @keydown=\"handleKeyDown\"\r\n >\r\n <!-- Content sizer -->\r\n <div\r\n :style=\"{\r\n width: `${Math.max(state.contentWidth, totalWidth)}px`,\r\n height: `${Math.max(state.contentHeight, totalHeaderHeight)}px`,\r\n position: 'relative',\r\n minWidth: '100%',\r\n }\"\r\n >\r\n <!-- Headers -->\r\n <div\r\n class=\"gp-grid-header\"\r\n :style=\"{\r\n position: 'sticky',\r\n top: 0,\r\n left: 0,\r\n height: `${totalHeaderHeight}px`,\r\n width: `${Math.max(state.contentWidth, totalWidth)}px`,\r\n minWidth: '100%',\r\n zIndex: 100,\r\n }\"\r\n >\r\n <div\r\n v-for=\"(column, colIndex) in columns\"\r\n :key=\"column.colId ?? column.field\"\r\n class=\"gp-grid-header-cell\"\r\n :data-col-index=\"colIndex\"\r\n :style=\"{\r\n position: 'absolute',\r\n left: `${columnPositions[colIndex]}px`,\r\n top: 0,\r\n width: `${column.width}px`,\r\n height: `${totalHeaderHeight}px`,\r\n background: 'transparent',\r\n }\"\r\n @click=\"(e) => handleHeaderClick(colIndex, e)\"\r\n >\r\n <component\r\n :is=\"renderHeader({\r\n column,\r\n colIndex,\r\n sortDirection: state.headers.get(colIndex)?.sortDirection,\r\n sortIndex: state.headers.get(colIndex)?.sortIndex,\r\n sortable: state.headers.get(colIndex)?.sortable ?? true,\r\n filterable: state.headers.get(colIndex)?.filterable ?? true,\r\n hasFilter: state.headers.get(colIndex)?.hasFilter ?? false,\r\n core: coreRef,\r\n container: containerRef,\r\n headerRenderers: headerRenderers ?? {},\r\n globalHeaderRenderer: headerRenderer,\r\n })\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- Row slots -->\r\n <div\r\n v-for=\"slot in slotsArray.filter((s) => s.rowIndex >= 0)\"\r\n :key=\"slot.slotId\"\r\n :class=\"['gp-grid-row', { 'gp-grid-row--even': slot.rowIndex % 2 === 0 }]\"\r\n :style=\"{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n transform: `translateY(${slot.translateY}px)`,\r\n width: `${Math.max(state.contentWidth, totalWidth)}px`,\r\n height: `${rowHeight}px`,\r\n }\"\r\n >\r\n <div\r\n v-for=\"(column, colIndex) in columns\"\r\n :key=\"`${slot.slotId}-${colIndex}`\"\r\n :class=\"getCellClasses(slot.rowIndex, colIndex)\"\r\n :style=\"{\r\n position: 'absolute',\r\n left: `${columnPositions[colIndex]}px`,\r\n top: 0,\r\n width: `${column.width}px`,\r\n height: `${rowHeight}px`,\r\n }\"\r\n @mousedown=\"(e) => handleCellMouseDown(slot.rowIndex, colIndex, e)\"\r\n @dblclick=\"() => handleCellDoubleClick(slot.rowIndex, colIndex)\"\r\n >\r\n <!-- Edit mode -->\r\n <template v-if=\"isCellEditing(slot.rowIndex, colIndex, state.editingCell) && state.editingCell\">\r\n <component\r\n :is=\"renderEditCell({\r\n column,\r\n rowData: slot.rowData,\r\n rowIndex: slot.rowIndex,\r\n colIndex,\r\n initialValue: state.editingCell.initialValue,\r\n core: coreRef,\r\n editRenderers: editRenderers ?? {},\r\n globalEditRenderer: editRenderer,\r\n })\"\r\n />\r\n </template>\r\n <!-- View mode -->\r\n <template v-else>\r\n <component\r\n :is=\"renderCell({\r\n column,\r\n rowData: slot.rowData,\r\n rowIndex: slot.rowIndex,\r\n colIndex,\r\n isActive: isCellActive(slot.rowIndex, colIndex, state.activeCell),\r\n isSelected: isCellSelected(slot.rowIndex, colIndex, state.selectionRange),\r\n isEditing: false,\r\n cellRenderers: cellRenderers ?? {},\r\n globalCellRenderer: cellRenderer,\r\n })\"\r\n />\r\n </template>\r\n </div>\r\n </div>\r\n\r\n <!-- Fill handle -->\r\n <div\r\n v-if=\"fillHandlePosition && !state.editingCell\"\r\n class=\"gp-grid-fill-handle\"\r\n :style=\"{\r\n position: 'absolute',\r\n top: `${fillHandlePosition.top}px`,\r\n left: `${fillHandlePosition.left}px`,\r\n zIndex: 200,\r\n }\"\r\n @mousedown=\"handleFillHandleMouseDown\"\r\n />\r\n\r\n <!-- Loading indicator -->\r\n <div v-if=\"state.isLoading\" class=\"gp-grid-loading\">\r\n <div class=\"gp-grid-loading-spinner\" />\r\n Loading...\r\n </div>\r\n\r\n <!-- Error message -->\r\n <div v-if=\"state.error\" class=\"gp-grid-error\">\r\n Error: {{ state.error }}\r\n </div>\r\n\r\n <!-- Empty state -->\r\n <div\r\n v-if=\"!state.isLoading && !state.error && state.totalRows === 0\"\r\n class=\"gp-grid-empty\"\r\n >\r\n No data to display\r\n </div>\r\n </div>\r\n\r\n <!-- Filter Popup -->\r\n <FilterPopup\r\n v-if=\"state.filterPopup?.isOpen && state.filterPopup.column && state.filterPopup.anchorRect\"\r\n :column=\"state.filterPopup.column\"\r\n :col-index=\"state.filterPopup.colIndex\"\r\n :anchor-rect=\"state.filterPopup.anchorRect\"\r\n :distinct-values=\"state.filterPopup.distinctValues\"\r\n :current-filter=\"state.filterPopup.currentFilter\"\r\n @apply=\"handleFilterApply\"\r\n @close=\"handleFilterPopupClose\"\r\n />\r\n </div>\r\n</template>\r\n","<script setup lang=\"ts\">\r\nimport {\r\n ref,\r\n shallowRef,\r\n computed,\r\n onMounted,\r\n onUnmounted,\r\n watch,\r\n} from \"vue\";\r\nimport {\r\n GridCore,\r\n createClientDataSource,\r\n createDataSourceFromArray,\r\n injectStyles,\r\n calculateColumnPositions,\r\n getTotalWidth,\r\n isCellSelected,\r\n isCellActive,\r\n isCellEditing,\r\n isCellInFillPreview,\r\n buildCellClasses,\r\n} from \"gp-grid-core\";\r\nimport type { Row, ColumnDefinition, ColumnFilterModel, DataSource, CellRange } from \"gp-grid-core\";\r\nimport { useGridState } from \"./gridState\";\r\nimport { useInputHandler } from \"./composables/useInputHandler\";\r\nimport { useFillHandle } from \"./composables/useFillHandle\";\r\nimport { renderCell } from \"./renderers/cellRenderer\";\r\nimport { renderEditCell } from \"./renderers/editRenderer\";\r\nimport { renderHeader } from \"./renderers/headerRenderer\";\r\nimport type { VueCellRenderer, VueEditRenderer, VueHeaderRenderer } from \"./types\";\r\nimport FilterPopup from \"./components/FilterPopup.vue\";\r\n\r\n// Inject styles on first render\r\ninjectStyles();\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n columns: ColumnDefinition[];\r\n dataSource?: DataSource<Row>;\r\n rowData?: Row[];\r\n rowHeight: number;\r\n headerHeight?: number;\r\n overscan?: number;\r\n sortingEnabled?: boolean;\r\n darkMode?: boolean;\r\n wheelDampening?: number;\r\n cellRenderers?: Record<string, VueCellRenderer>;\r\n editRenderers?: Record<string, VueEditRenderer>;\r\n headerRenderers?: Record<string, VueHeaderRenderer>;\r\n cellRenderer?: VueCellRenderer;\r\n editRenderer?: VueEditRenderer;\r\n headerRenderer?: VueHeaderRenderer;\r\n }>(),\r\n {\r\n overscan: 3,\r\n sortingEnabled: true,\r\n darkMode: false,\r\n wheelDampening: 0.1,\r\n cellRenderers: () => ({}),\r\n editRenderers: () => ({}),\r\n headerRenderers: () => ({}),\r\n },\r\n);\r\n\r\n// Refs\r\nconst containerRef = ref<HTMLDivElement | null>(null);\r\nconst coreRef = shallowRef<GridCore | null>(null);\r\n\r\n// State\r\nconst { state, applyInstructions } = useGridState();\r\n\r\n// Computed values\r\nconst totalHeaderHeight = computed(() => props.headerHeight ?? props.rowHeight);\r\nconst columnPositions = computed(() => calculateColumnPositions(props.columns));\r\nconst totalWidth = computed(() => getTotalWidth(columnPositions.value));\r\nconst slotsArray = computed(() => Array.from(state.slots.values()));\r\n\r\n// Input handling\r\nconst {\r\n handleCellMouseDown,\r\n handleCellDoubleClick,\r\n handleFillHandleMouseDown,\r\n handleHeaderClick,\r\n handleKeyDown,\r\n handleWheel,\r\n dragState,\r\n} = useInputHandler(coreRef, containerRef, computed(() => props.columns), {\r\n activeCell: computed(() => state.activeCell),\r\n selectionRange: computed(() => state.selectionRange),\r\n editingCell: computed(() => state.editingCell),\r\n filterPopupOpen: computed(() => state.filterPopup?.isOpen ?? false),\r\n rowHeight: props.rowHeight,\r\n headerHeight: totalHeaderHeight.value,\r\n columnPositions,\r\n slots: computed(() => state.slots),\r\n});\r\n\r\n// Fill handle position\r\nconst { fillHandlePosition } = useFillHandle({\r\n activeCell: computed(() => state.activeCell),\r\n selectionRange: computed(() => state.selectionRange),\r\n slots: computed(() => state.slots),\r\n columns: computed(() => props.columns),\r\n columnPositions,\r\n rowHeight: props.rowHeight,\r\n});\r\n\r\n// Handle scroll\r\nfunction handleScroll(): void {\r\n const container = containerRef.value;\r\n const core = coreRef.value;\r\n if (!container || !core) return;\r\n\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n}\r\n\r\n// Handle filter apply\r\nfunction handleFilterApply(colId: string, filter: ColumnFilterModel | null): void {\r\n const core = coreRef.value;\r\n if (core) {\r\n core.setFilter(colId, filter);\r\n }\r\n}\r\n\r\n// Handle filter popup close\r\nfunction handleFilterPopupClose(): void {\r\n const core = coreRef.value;\r\n if (core) {\r\n core.closeFilterPopup();\r\n }\r\n}\r\n\r\n// Get cell classes\r\nfunction getCellClasses(rowIndex: number, colIndex: number): string {\r\n const isEditing = isCellEditing(rowIndex, colIndex, state.editingCell);\r\n const active = isCellActive(rowIndex, colIndex, state.activeCell);\r\n const selected = isCellSelected(rowIndex, colIndex, state.selectionRange);\r\n const inFillPreview = isCellInFillPreview(\r\n rowIndex,\r\n colIndex,\r\n dragState.value.dragType === \"fill\",\r\n dragState.value.fillSourceRange,\r\n dragState.value.fillTarget,\r\n );\r\n return buildCellClasses(active, selected, isEditing, inFillPreview);\r\n}\r\n\r\n// Initialize GridCore\r\nonMounted(() => {\r\n const dataSource =\r\n props.dataSource ??\r\n (props.rowData ? createDataSourceFromArray(props.rowData) : createClientDataSource<Row>([]));\r\n\r\n const core = new GridCore<Row>({\r\n columns: props.columns,\r\n dataSource,\r\n rowHeight: props.rowHeight,\r\n headerHeight: totalHeaderHeight.value,\r\n overscan: props.overscan,\r\n sortingEnabled: props.sortingEnabled,\r\n });\r\n\r\n coreRef.value = core;\r\n\r\n // Subscribe to batched instructions\r\n const unsubscribe = core.onBatchInstruction((instructions) => {\r\n applyInstructions(instructions);\r\n });\r\n\r\n // Initialize\r\n core.initialize();\r\n\r\n // Initial measurement\r\n const container = containerRef.value;\r\n if (container) {\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n\r\n // Resize observer\r\n const resizeObserver = new ResizeObserver(() => {\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n });\r\n resizeObserver.observe(container);\r\n\r\n onUnmounted(() => {\r\n resizeObserver.disconnect();\r\n unsubscribe();\r\n coreRef.value = null;\r\n });\r\n }\r\n});\r\n\r\n// Subscribe to data source changes\r\nwatch(\r\n () => props.dataSource,\r\n (dataSource) => {\r\n if (dataSource) {\r\n const mutableDataSource = dataSource as {\r\n subscribe?: (listener: () => void) => () => void;\r\n };\r\n if (mutableDataSource.subscribe) {\r\n const unsubscribe = mutableDataSource.subscribe(() => {\r\n coreRef.value?.refresh();\r\n });\r\n onUnmounted(() => unsubscribe());\r\n }\r\n }\r\n },\r\n { immediate: true },\r\n);\r\n</script>\r\n\r\n<template>\r\n <div\r\n ref=\"containerRef\"\r\n :class=\"['gp-grid-container', { 'gp-grid-container--dark': darkMode }]\"\r\n style=\"width: 100%; height: 100%; overflow: auto; position: relative\"\r\n tabindex=\"0\"\r\n @scroll=\"handleScroll\"\r\n @wheel=\"(e) => handleWheel(e, wheelDampening)\"\r\n @keydown=\"handleKeyDown\"\r\n >\r\n <!-- Content sizer -->\r\n <div\r\n :style=\"{\r\n width: `${Math.max(state.contentWidth, totalWidth)}px`,\r\n height: `${Math.max(state.contentHeight, totalHeaderHeight)}px`,\r\n position: 'relative',\r\n minWidth: '100%',\r\n }\"\r\n >\r\n <!-- Headers -->\r\n <div\r\n class=\"gp-grid-header\"\r\n :style=\"{\r\n position: 'sticky',\r\n top: 0,\r\n left: 0,\r\n height: `${totalHeaderHeight}px`,\r\n width: `${Math.max(state.contentWidth, totalWidth)}px`,\r\n minWidth: '100%',\r\n zIndex: 100,\r\n }\"\r\n >\r\n <div\r\n v-for=\"(column, colIndex) in columns\"\r\n :key=\"column.colId ?? column.field\"\r\n class=\"gp-grid-header-cell\"\r\n :data-col-index=\"colIndex\"\r\n :style=\"{\r\n position: 'absolute',\r\n left: `${columnPositions[colIndex]}px`,\r\n top: 0,\r\n width: `${column.width}px`,\r\n height: `${totalHeaderHeight}px`,\r\n background: 'transparent',\r\n }\"\r\n @click=\"(e) => handleHeaderClick(colIndex, e)\"\r\n >\r\n <component\r\n :is=\"renderHeader({\r\n column,\r\n colIndex,\r\n sortDirection: state.headers.get(colIndex)?.sortDirection,\r\n sortIndex: state.headers.get(colIndex)?.sortIndex,\r\n sortable: state.headers.get(colIndex)?.sortable ?? true,\r\n filterable: state.headers.get(colIndex)?.filterable ?? true,\r\n hasFilter: state.headers.get(colIndex)?.hasFilter ?? false,\r\n core: coreRef,\r\n container: containerRef,\r\n headerRenderers: headerRenderers ?? {},\r\n globalHeaderRenderer: headerRenderer,\r\n })\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- Row slots -->\r\n <div\r\n v-for=\"slot in slotsArray.filter((s) => s.rowIndex >= 0)\"\r\n :key=\"slot.slotId\"\r\n :class=\"['gp-grid-row', { 'gp-grid-row--even': slot.rowIndex % 2 === 0 }]\"\r\n :style=\"{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n transform: `translateY(${slot.translateY}px)`,\r\n width: `${Math.max(state.contentWidth, totalWidth)}px`,\r\n height: `${rowHeight}px`,\r\n }\"\r\n >\r\n <div\r\n v-for=\"(column, colIndex) in columns\"\r\n :key=\"`${slot.slotId}-${colIndex}`\"\r\n :class=\"getCellClasses(slot.rowIndex, colIndex)\"\r\n :style=\"{\r\n position: 'absolute',\r\n left: `${columnPositions[colIndex]}px`,\r\n top: 0,\r\n width: `${column.width}px`,\r\n height: `${rowHeight}px`,\r\n }\"\r\n @mousedown=\"(e) => handleCellMouseDown(slot.rowIndex, colIndex, e)\"\r\n @dblclick=\"() => handleCellDoubleClick(slot.rowIndex, colIndex)\"\r\n >\r\n <!-- Edit mode -->\r\n <template v-if=\"isCellEditing(slot.rowIndex, colIndex, state.editingCell) && state.editingCell\">\r\n <component\r\n :is=\"renderEditCell({\r\n column,\r\n rowData: slot.rowData,\r\n rowIndex: slot.rowIndex,\r\n colIndex,\r\n initialValue: state.editingCell.initialValue,\r\n core: coreRef,\r\n editRenderers: editRenderers ?? {},\r\n globalEditRenderer: editRenderer,\r\n })\"\r\n />\r\n </template>\r\n <!-- View mode -->\r\n <template v-else>\r\n <component\r\n :is=\"renderCell({\r\n column,\r\n rowData: slot.rowData,\r\n rowIndex: slot.rowIndex,\r\n colIndex,\r\n isActive: isCellActive(slot.rowIndex, colIndex, state.activeCell),\r\n isSelected: isCellSelected(slot.rowIndex, colIndex, state.selectionRange),\r\n isEditing: false,\r\n cellRenderers: cellRenderers ?? {},\r\n globalCellRenderer: cellRenderer,\r\n })\"\r\n />\r\n </template>\r\n </div>\r\n </div>\r\n\r\n <!-- Fill handle -->\r\n <div\r\n v-if=\"fillHandlePosition && !state.editingCell\"\r\n class=\"gp-grid-fill-handle\"\r\n :style=\"{\r\n position: 'absolute',\r\n top: `${fillHandlePosition.top}px`,\r\n left: `${fillHandlePosition.left}px`,\r\n zIndex: 200,\r\n }\"\r\n @mousedown=\"handleFillHandleMouseDown\"\r\n />\r\n\r\n <!-- Loading indicator -->\r\n <div v-if=\"state.isLoading\" class=\"gp-grid-loading\">\r\n <div class=\"gp-grid-loading-spinner\" />\r\n Loading...\r\n </div>\r\n\r\n <!-- Error message -->\r\n <div v-if=\"state.error\" class=\"gp-grid-error\">\r\n Error: {{ state.error }}\r\n </div>\r\n\r\n <!-- Empty state -->\r\n <div\r\n v-if=\"!state.isLoading && !state.error && state.totalRows === 0\"\r\n class=\"gp-grid-empty\"\r\n >\r\n No data to display\r\n </div>\r\n </div>\r\n\r\n <!-- Filter Popup -->\r\n <FilterPopup\r\n v-if=\"state.filterPopup?.isOpen && state.filterPopup.column && state.filterPopup.anchorRect\"\r\n :column=\"state.filterPopup.column\"\r\n :col-index=\"state.filterPopup.colIndex\"\r\n :anchor-rect=\"state.filterPopup.anchorRect\"\r\n :distinct-values=\"state.filterPopup.distinctValues\"\r\n :current-filter=\"state.filterPopup.currentFilter\"\r\n @apply=\"handleFilterApply\"\r\n @close=\"handleFilterPopupClose\"\r\n />\r\n </div>\r\n</template>\r\n","// packages/vue/src/composables/useGpGrid.ts\r\n\r\nimport { ref, computed, onMounted, onUnmounted, watch, type Ref, type ComputedRef } from \"vue\";\r\nimport {\r\n GridCore,\r\n createClientDataSource,\r\n createDataSourceFromArray,\r\n injectStyles,\r\n calculateColumnPositions,\r\n getTotalWidth,\r\n isCellSelected,\r\n isCellActive,\r\n isCellEditing,\r\n isCellInFillPreview,\r\n buildCellClasses,\r\n} from \"gp-grid-core\";\r\nimport type {\r\n Row,\r\n ColumnDefinition,\r\n ColumnFilterModel,\r\n DataSource,\r\n GridState,\r\n SlotData,\r\n} from \"gp-grid-core\";\r\nimport { useGridState } from \"../gridState\";\r\nimport { useInputHandler } from \"./useInputHandler\";\r\nimport { useFillHandle } from \"./useFillHandle\";\r\nimport type { VueCellRenderer, VueEditRenderer, VueHeaderRenderer } from \"../types\";\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface UseGpGridOptions<TData extends Row = Row> {\r\n columns: ColumnDefinition[];\r\n dataSource?: DataSource<TData>;\r\n rowData?: TData[];\r\n rowHeight: number;\r\n headerHeight?: number;\r\n overscan?: number;\r\n sortingEnabled?: boolean;\r\n darkMode?: boolean;\r\n wheelDampening?: number;\r\n cellRenderers?: Record<string, VueCellRenderer<TData>>;\r\n editRenderers?: Record<string, VueEditRenderer<TData>>;\r\n headerRenderers?: Record<string, VueHeaderRenderer>;\r\n cellRenderer?: VueCellRenderer<TData>;\r\n editRenderer?: VueEditRenderer<TData>;\r\n headerRenderer?: VueHeaderRenderer;\r\n}\r\n\r\nexport interface UseGpGridResult<TData extends Row = Row> {\r\n // Refs\r\n containerRef: Ref<HTMLDivElement | null>;\r\n coreRef: Ref<GridCore<TData> | null>;\r\n\r\n // State\r\n state: GridState;\r\n slotsArray: ComputedRef<SlotData[]>;\r\n\r\n // Computed\r\n totalHeaderHeight: ComputedRef<number>;\r\n columnPositions: ComputedRef<number[]>;\r\n totalWidth: ComputedRef<number>;\r\n fillHandlePosition: ComputedRef<{ top: number; left: number } | null>;\r\n\r\n // Event handlers\r\n handleScroll: () => void;\r\n handleCellMouseDown: (rowIndex: number, colIndex: number, e: MouseEvent) => void;\r\n handleCellDoubleClick: (rowIndex: number, colIndex: number) => void;\r\n handleFillHandleMouseDown: (e: MouseEvent) => void;\r\n handleHeaderClick: (colIndex: number, e: MouseEvent) => void;\r\n handleKeyDown: (e: KeyboardEvent) => void;\r\n handleWheel: (e: WheelEvent, wheelDampening: number) => void;\r\n handleFilterApply: (colId: string, filter: ColumnFilterModel | null) => void;\r\n handleFilterPopupClose: () => void;\r\n\r\n // Drag state\r\n dragState: Ref<{\r\n isDragging: boolean;\r\n dragType: \"selection\" | \"fill\" | null;\r\n fillSourceRange: { startRow: number; startCol: number; endRow: number; endCol: number } | null;\r\n fillTarget: { row: number; col: number } | null;\r\n }>;\r\n\r\n // Helpers\r\n isCellSelected: typeof isCellSelected;\r\n isCellActive: typeof isCellActive;\r\n isCellEditing: typeof isCellEditing;\r\n isCellInFillPreview: typeof isCellInFillPreview;\r\n buildCellClasses: typeof buildCellClasses;\r\n}\r\n\r\n// =============================================================================\r\n// Composable\r\n// =============================================================================\r\n\r\n/**\r\n * Nuxt-friendly composable for using gp-grid.\r\n * Returns all the pieces needed to build a custom grid component.\r\n */\r\nexport function useGpGrid<TData extends Row = Row>(\r\n options: UseGpGridOptions<TData>,\r\n): UseGpGridResult<TData> {\r\n // Inject styles on first use\r\n injectStyles();\r\n\r\n // Refs\r\n const containerRef = ref<HTMLDivElement | null>(null);\r\n const coreRef = ref<GridCore<TData> | null>(null);\r\n\r\n // State\r\n const { state, applyInstructions } = useGridState();\r\n\r\n // Computed values\r\n const totalHeaderHeight = computed(() => options.headerHeight ?? options.rowHeight);\r\n const columnPositions = computed(() => calculateColumnPositions(options.columns));\r\n const totalWidth = computed(() => getTotalWidth(columnPositions.value));\r\n const slotsArray = computed(() => Array.from(state.slots.values()));\r\n\r\n // Input handling\r\n const {\r\n handleCellMouseDown,\r\n handleCellDoubleClick,\r\n handleFillHandleMouseDown,\r\n handleHeaderClick,\r\n handleKeyDown,\r\n handleWheel,\r\n dragState,\r\n } = useInputHandler<TData>(\r\n coreRef as Ref<GridCore<TData> | null>,\r\n containerRef,\r\n computed(() => options.columns),\r\n {\r\n activeCell: computed(() => state.activeCell),\r\n selectionRange: computed(() => state.selectionRange),\r\n editingCell: computed(() => state.editingCell),\r\n filterPopupOpen: computed(() => state.filterPopup?.isOpen ?? false),\r\n rowHeight: options.rowHeight,\r\n headerHeight: totalHeaderHeight.value,\r\n columnPositions,\r\n slots: computed(() => state.slots),\r\n },\r\n );\r\n\r\n // Handle scroll\r\n const handleScroll = (): void => {\r\n const container = containerRef.value;\r\n const core = coreRef.value;\r\n if (!container || !core) return;\r\n\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n };\r\n\r\n // Handle filter apply\r\n const handleFilterApply = (colId: string, filter: ColumnFilterModel | null): void => {\r\n const core = coreRef.value;\r\n if (core) {\r\n core.setFilter(colId, filter);\r\n }\r\n };\r\n\r\n // Handle filter popup close\r\n const handleFilterPopupClose = (): void => {\r\n const core = coreRef.value;\r\n if (core) {\r\n core.closeFilterPopup();\r\n }\r\n };\r\n\r\n // Initialize GridCore\r\n onMounted(() => {\r\n const dataSource = options.dataSource ??\r\n (options.rowData\r\n ? createDataSourceFromArray(options.rowData)\r\n : createClientDataSource<TData>([]));\r\n\r\n const core = new GridCore<TData>({\r\n columns: options.columns,\r\n dataSource,\r\n rowHeight: options.rowHeight,\r\n headerHeight: totalHeaderHeight.value,\r\n overscan: options.overscan ?? 3,\r\n sortingEnabled: options.sortingEnabled ?? true,\r\n });\r\n\r\n coreRef.value = core;\r\n\r\n // Subscribe to batched instructions\r\n const unsubscribe = core.onBatchInstruction((instructions) => {\r\n applyInstructions(instructions);\r\n });\r\n\r\n // Initialize\r\n core.initialize();\r\n\r\n // Initial measurement\r\n const container = containerRef.value;\r\n if (container) {\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n\r\n // Resize observer\r\n const resizeObserver = new ResizeObserver(() => {\r\n core.setViewport(\r\n container.scrollTop,\r\n container.scrollLeft,\r\n container.clientWidth,\r\n container.clientHeight,\r\n );\r\n });\r\n resizeObserver.observe(container);\r\n\r\n onUnmounted(() => {\r\n resizeObserver.disconnect();\r\n unsubscribe();\r\n coreRef.value = null;\r\n });\r\n }\r\n });\r\n\r\n // Subscribe to data source changes\r\n watch(\r\n () => options.dataSource,\r\n (dataSource) => {\r\n if (dataSource) {\r\n const mutableDataSource = dataSource as {\r\n subscribe?: (listener: () => void) => () => void;\r\n };\r\n if (mutableDataSource.subscribe) {\r\n const unsubscribe = mutableDataSource.subscribe(() => {\r\n coreRef.value?.refresh();\r\n });\r\n onUnmounted(() => unsubscribe());\r\n }\r\n }\r\n },\r\n { immediate: true },\r\n );\r\n\r\n // Calculate fill handle position using composable\r\n const { fillHandlePosition } = useFillHandle({\r\n activeCell: computed(() => state.activeCell),\r\n selectionRange: computed(() => state.selectionRange),\r\n slots: computed(() => state.slots),\r\n columns: computed(() => options.columns),\r\n columnPositions,\r\n rowHeight: options.rowHeight,\r\n });\r\n\r\n return {\r\n // Refs\r\n containerRef,\r\n coreRef: coreRef as Ref<GridCore<TData> | null>,\r\n\r\n // State\r\n state,\r\n slotsArray,\r\n\r\n // Computed\r\n totalHeaderHeight,\r\n columnPositions,\r\n totalWidth,\r\n fillHandlePosition,\r\n\r\n // Event handlers\r\n handleScroll,\r\n handleCellMouseDown,\r\n handleCellDoubleClick,\r\n handleFillHandleMouseDown,\r\n handleHeaderClick,\r\n handleKeyDown,\r\n handleWheel,\r\n handleFilterApply,\r\n handleFilterPopupClose,\r\n\r\n // Drag state\r\n dragState,\r\n\r\n // Helpers (re-exported for convenience)\r\n isCellSelected,\r\n isCellActive,\r\n isCellEditing,\r\n isCellInFillPreview,\r\n buildCellClasses,\r\n };\r\n}\r\n"],"mappings":"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"}
|