@platforma-sdk/ui-vue 1.58.9 → 1.58.10

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.
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @platforma-sdk/ui-vue@1.58.9 build /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.58.10 build /home/runner/_work/platforma/platforma/sdk/ui-vue
4
4
  > ts-builder build --target browser-lib
5
5
 
6
6
  Building browser-lib project...
@@ -11,7 +11,7 @@ Building browser-lib project...
11
11
  rendering chunks...
12
12
 
13
13
  [vite:dts] Start generate declaration files...
14
- [vite:dts] Declaration files built in 5167ms.
14
+ [vite:dts] Declaration files built in 4533ms.
15
15
 
16
16
  computing gzip size...
17
17
  dist/components/PlAnnotations/components/PlAnnotations.vue?vue&type=style&index=0&lang.css 0.04 kB │ gzip: 0.06 kB
@@ -102,7 +102,7 @@ dist/components/PlAdvancedFilter/FilterEditor.js
102
102
  dist/components/PlAdvancedFilter/OperandButton.js 0.34 kB │ gzip: 0.23 kB │ map: 1.31 kB
103
103
  dist/components/PlAnnotations/components/FilterSidebar.js 0.34 kB │ gzip: 0.23 kB │ map: 4.72 kB
104
104
  dist/components/PlAnnotations/components/PlAnnotations.js 0.34 kB │ gzip: 0.23 kB │ map: 2.65 kB
105
- dist/components/PlAgDataTable/PlAgDataTableV2.js 0.34 kB │ gzip: 0.24 kB │ map: 19.32 kB
105
+ dist/components/PlAgDataTable/PlAgDataTableV2.js 0.34 kB │ gzip: 0.24 kB │ map: 20.81 kB
106
106
  dist/plugins/Monetization/UserCabinetCard.js 0.34 kB │ gzip: 0.23 kB │ map: 3.00 kB
107
107
  dist/components/PlAdvancedFilter/PlAdvancedFilter.js 0.34 kB │ gzip: 0.24 kB │ map: 10.70 kB
108
108
  dist/components/PlTableFilters/PlTableFiltersV2.js 0.34 kB │ gzip: 0.24 kB │ map: 4.58 kB
@@ -193,15 +193,15 @@ dist/components/PlAgDataTable/sources/table-source-v2.js
193
193
  dist/internal/createAppV3.js 6.54 kB │ gzip: 2.51 kB │ map: 19.87 kB
194
194
  dist/components/PlAdvancedFilter/PlAdvancedFilter.vue_vue_type_script_setup_true_lang.js 7.06 kB │ gzip: 2.28 kB │ map: 14.91 kB
195
195
  dist/components/PlAdvancedFilter/FilterEditor.vue_vue_type_script_setup_true_lang.js 10.20 kB │ gzip: 3.05 kB │ map: 21.73 kB
196
- dist/components/PlAgDataTable/PlAgDataTableV2.vue_vue_type_script_setup_true_lang.js 10.85 kB │ gzip: 3.49 kB │ map: 25.86 kB
196
+ dist/components/PlAgDataTable/PlAgDataTableV2.vue_vue_type_script_setup_true_lang.js 11.31 kB │ gzip: 3.63 kB │ map: 27.79 kB
197
197
 
198
198
  [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugins. Here is a breakdown:
199
199
  - sourcemaps (38%)
200
- - vite:dts (22%)
201
- - vite:vue (11%)
200
+ - vite:dts (19%)
201
+ - vite:vue (13%)
202
202
  - vite:css-post (8%)
203
- - vite:build-import-analysis (6%)
203
+ - vite:css (8%)
204
204
  See https://rolldown.rs/options/checks#plugintimings for more details.
205
205
  
206
- ✓ built in 6.06s
206
+ ✓ built in 5.76s
207
207
  Build completed successfully
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @platforma-sdk/ui-vue@1.58.9 formatter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.58.10 formatter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
4
4
  > ts-builder formatter --check
5
5
 
6
6
  Checking formatting...
@@ -8,5 +8,5 @@ Checking formatting...
8
8
  Checking formatting...
9
9
 
10
10
  All matched files use the correct format.
11
- Finished in 2739ms on 126 files using 8 threads.
11
+ Finished in 1498ms on 126 files using 8 threads.
12
12
  Format check completed successfully
@@ -1,10 +1,10 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @platforma-sdk/ui-vue@1.58.9 linter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.58.10 linter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
4
4
  > ts-builder linter --check
5
5
 
6
6
  Linting project...
7
7
  ↳ oxlint --config /home/runner/_work/platforma/platforma/sdk/ui-vue/.oxlintrc.json --deny-warnings
8
8
  Found 0 warnings and 0 errors.
9
- Finished in 33ms on 109 files with 98 rules using 8 threads.
9
+ Finished in 28ms on 109 files with 98 rules using 8 threads.
10
10
  Linting completed successfully
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @platforma-sdk/ui-vue@1.58.9 types:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.58.10 types:check /home/runner/_work/platforma/platforma/sdk/ui-vue
4
4
  > ts-builder type-check --target browser-lib
5
5
 
6
6
  ↳ vue-tsc.js --noEmit --project ./tsconfig.json --customConditions ,
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @platforma-sdk/ui-vue
2
2
 
3
+ ## 1.58.10
4
+
5
+ ### Patch Changes
6
+
7
+ - d2c7384: Fix table column visibility state: prevent reload loop, preserve defaults for optional columns
8
+
3
9
  ## 1.58.9
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"PlAgDataTableV2.js","names":[],"sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { promiseTimeout, isJsonEqual } from \"@milaboratories/helpers\";\nimport type {\n AxisId,\n PlDataTableGridStateCore,\n PlDataTableStateV2,\n PlSelectionModel,\n PlTableColumnIdJson,\n PTableKey,\n} from \"@platforma-sdk/model\";\nimport {\n getRawPlatformaInstance,\n createPlSelectionModel,\n matchAxisId,\n getAxisId,\n canonicalizeJson,\n isAbortError,\n} from \"@platforma-sdk/model\";\nimport type { CellRendererSelectorFunc, GridApi, GridState } from \"ag-grid-enterprise\";\nimport { AgGridVue } from \"ag-grid-vue3\";\nimport { computed, effectScope, ref, toRefs, watch, watchEffect } from \"vue\";\nimport { AgGridTheme } from \"../../aggrid\";\nimport PlAgCsvExporter from \"../PlAgCsvExporter/PlAgCsvExporter.vue\";\nimport { PlAgGridColumnManager } from \"../PlAgGridColumnManager\";\nimport PlTableFiltersV2 from \"../PlTableFilters/PlTableFiltersV2.vue\";\nimport { PlTableFastSearch } from \"../PlTableFastSearch\";\nimport PlAgDataTableSheets from \"./PlAgDataTableSheets.vue\";\nimport PlAgRowCount from \"./PlAgRowCount.vue\";\nimport { DeferredCircular, ensureNodeVisible } from \"./sources/focus-row\";\nimport { PlAgDataTableRowNumberColId } from \"./sources/row-number\";\nimport type { PlAgCellButtonAxisParams } from \"./sources/table-source-v2\";\nimport { calculateGridOptions } from \"./sources/table-source-v2\";\nimport { useTableState } from \"./sources/table-state-v2\";\nimport type {\n PlAgDataTableV2Controller,\n PlAgDataTableV2Row,\n PlAgOverlayLoadingParams,\n PlAgOverlayNoRowsParams,\n PlDataTableSettingsV2,\n PlDataTableSheetsSettings,\n PlTableRowId,\n} from \"./types\";\nimport { useFilterableColumns } from \"./compositions/useFilterableColumns\";\nimport { useGrid } from \"./compositions/useGrid\";\n\nconst tableState = defineModel<PlDataTableStateV2>({\n required: true,\n});\n/** Warning: selection model value updates are ignored, use updateSelection instead */\nconst selection = defineModel<PlSelectionModel>(\"selection\");\nconst props = defineProps<{\n /** Required component settings */\n settings: Readonly<PlDataTableSettingsV2>;\n\n /**\n * The disableColumnsPanel prop controls the display of a button that activates\n * the columns management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableColumnsPanel?: boolean;\n\n /**\n * The disableFiltersPanel prop controls the display of a button that activates\n * the filters management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableFiltersPanel?: boolean;\n\n /**\n * The showExportButton prop controls the display of a button that allows\n * to export table data in CSV format. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n showExportButton?: boolean;\n\n /**\n * The AxisId property is used to configure and display the PlAgTextAndButtonCell component\n */\n showCellButtonForAxisId?: AxisId;\n\n /**\n * If cellButtonInvokeRowsOnDoubleClick = true, clicking a button inside the row\n * triggers the doubleClick event for the entire row.\n *\n * If cellButtonInvokeRowsOnDoubleClick = false, the doubleClick event for the row\n * is not triggered, but will triggered cellButtonClicked event with (key: PTableRowKey) argument.\n */\n cellButtonInvokeRowsOnDoubleClick?: boolean;\n\n /** @see {@link PlAgOverlayLoadingParams.loadingText} */\n loadingText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.runningText} */\n runningText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.notReadyText} */\n notReadyText?: string;\n\n /** @see {@link PlAgOverlayNoRowsParams.text} */\n noRowsText?: string;\n\n /**\n * Callback to override the default renderer for a given cell.\n * @see https://www.ag-grid.com/vue-data-grid/component-cell-renderer/#dynamic-component-selection\n */\n cellRendererSelector?: CellRendererSelectorFunc<PlAgDataTableV2Row>;\n}>();\nconst { settings } = toRefs(props);\nconst emit = defineEmits<{\n rowDoubleClicked: [key?: PTableKey];\n cellButtonClicked: [key?: PTableKey];\n newDataRendered: [];\n}>();\n\nconst dataRenderedTracker = new DeferredCircular<GridApi<PlAgDataTableV2Row>>();\nconst { gridApi, gridOptions } = useGrid({\n selection: selection.value,\n noRowsText: props.noRowsText,\n runningText: props.runningText,\n loadingText: props.loadingText,\n notReadyText: props.notReadyText,\n cellRendererSelector: props.cellRendererSelector,\n});\ngridOptions.value.onGridPreDestroyed = (event) => {\n gridOptions.value.initialState = gridState.value = makePartialState(event.api.getState());\n gridApi.value = null;\n};\ngridOptions.value.onRowDoubleClicked = (event) => {\n if (event.data && event.data.axesKey) emit(\"rowDoubleClicked\", event.data.axesKey);\n};\ngridOptions.value.onStateUpdated = (event) => {\n let partialState = makePartialState(event.state);\n // AG Grid omits columnVisibility when no columns are hidden. If we previously had\n // hidden columns and now get undefined, treat as \"all visible\" so we don't revert to default.\n const hadHiddenCols = gridState.value.columnVisibility?.hiddenColIds !== undefined;\n if (partialState.columnVisibility === undefined && hadHiddenCols) {\n partialState = { ...partialState, columnVisibility: { hiddenColIds: [] } };\n }\n // We have to keep initialState synchronized with gridState for gridState recovery after key updating.\n gridOptions.value.initialState = gridState.value = partialState;\n\n if (!isJsonEqual(event.sources, [\"columnSizing\"])) {\n event.api.autoSizeColumns(\n event.api\n .getAllDisplayedColumns()\n .filter((column) => column.getColId() !== PlAgDataTableRowNumberColId),\n );\n }\n};\n\nconst [filterableColumns, visibleFilterableColumns] = useFilterableColumns(\n () => settings.value.sourceId,\n () => gridOptions.value.columnDefs ?? null,\n);\nconst { gridState, sheetsState, filtersState, searchString } = useTableState(\n tableState,\n settings,\n visibleFilterableColumns,\n);\nconst sheetsSettings = computed<PlDataTableSheetsSettings>(() => {\n const settingsCopy = { ...settings.value };\n return settingsCopy.sourceId !== null\n ? {\n sheets: settingsCopy.sheets ?? [],\n cachedState: [...sheetsState.value],\n }\n : {\n sheets: [],\n cachedState: [],\n };\n});\ngridOptions.value.initialState = gridState.value;\n\n// Restore proper types erased by AgGrid\nfunction makePartialState(state: GridState): PlDataTableGridStateCore {\n return {\n columnOrder: state.columnOrder as\n | {\n orderedColIds: PlTableColumnIdJson[];\n }\n | undefined,\n sort: state.sort as\n | {\n sortModel: {\n colId: PlTableColumnIdJson;\n sort: \"asc\" | \"desc\";\n }[];\n }\n | undefined,\n columnVisibility: state.columnVisibility as\n | {\n hiddenColIds: PlTableColumnIdJson[];\n }\n | undefined,\n };\n}\n\n// Normalize columnVisibility for comparison: undefined and { hiddenColIds: [] } are equivalent.\nfunction stateForReloadCompare(state: PlDataTableGridStateCore): PlDataTableGridStateCore {\n const cv = state.columnVisibility;\n const normalizedCv = !cv || cv.hiddenColIds.length === 0 ? undefined : state.columnVisibility;\n return { ...state, columnVisibility: normalizedCv };\n}\n\n// Reload AgGrid when new state arrives from server\nconst reloadKey = ref(0);\nwatch(\n () => [gridApi.value, gridState.value] as const,\n ([gridApi, gridState]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n const selfState = makePartialState(gridApi.getState());\n if (\n !isJsonEqual(gridState, {}) &&\n !isJsonEqual(stateForReloadCompare(gridState), stateForReloadCompare(selfState))\n ) {\n gridOptions.value.initialState = gridState;\n ++reloadKey.value;\n }\n },\n);\n\n// Make cellRendererSelector reactive\nconst cellRendererSelector = computed(() => props.cellRendererSelector ?? null);\nwatch(\n () => [gridApi.value, cellRendererSelector.value] as const,\n ([gridApi, cellRendererSelector]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.setGridOption(\"defaultColDef\", {\n ...gridOptions.value.defaultColDef,\n cellRendererSelector: cellRendererSelector ?? undefined,\n });\n },\n);\n\ndefineExpose<PlAgDataTableV2Controller>({\n focusRow: async (rowKey) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n return ensureNodeVisible(gridApi, (row) => isJsonEqual(row.data?.axesKey, rowKey));\n },\n updateSelection: async ({ axesSpec, selectedKeys }) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n const axes = selection.value?.axesSpec;\n if (!axes || axes.length !== axesSpec.length) return false;\n\n const mapping = axesSpec.map((spec) => {\n const id = getAxisId(spec);\n return axes.findIndex((axis) => matchAxisId(axis, id));\n });\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) return false;\n\n const selectedNodes = selectedKeys.map((key) =>\n canonicalizeJson<PlTableRowId>(mapping.map((index) => key[index])),\n );\n const oldSelectedKeys = gridApi.getServerSideSelectionState()?.toggledNodes ?? [];\n if (!isJsonEqual(oldSelectedKeys, selectedNodes)) {\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes,\n });\n\n // wait for `onSelectionChanged` to update `selection` model\n const scope = effectScope();\n const { resolve, promise } = Promise.withResolvers();\n scope.run(() => watch(selection, resolve, { once: true }));\n try {\n await promiseTimeout(promise, 500);\n } catch {\n return false;\n } finally {\n scope.stop();\n }\n }\n return true;\n },\n});\n\n// Update AgGrid when settings change\nconst defaultSelection = createPlSelectionModel();\nlet oldSettings: PlDataTableSettingsV2 | null = null;\nconst generation = ref(0);\nwatch(\n () => [gridApi.value, settings.value] as const,\n ([gridApi, settings]) => {\n // Wait for AgGrid reinitialization, gridApi will eventually become initialized\n if (!gridApi || gridApi.isDestroyed()) return;\n // Verify that this is not a false watch trigger\n if (isJsonEqual(settings, oldSettings)) return;\n ++generation.value;\n try {\n // Hide no rows overlay if it is shown, or else loading overlay will not be shown\n gridApi.hideOverlay();\n dataRenderedTracker.reset();\n\n // No data source selected -> reset state to default\n if (settings.sourceId === null) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: settings.pending ? \"running\" : \"not-ready\",\n } satisfies PlAgOverlayLoadingParams,\n columnDefs: undefined,\n serverSideDatasource: undefined,\n });\n if (selection.value) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n return;\n }\n\n // Data source changed -> show full page loader, clear selection\n if (settings.sourceId !== oldSettings?.sourceId) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: \"loading\",\n } satisfies PlAgOverlayLoadingParams,\n });\n if (selection.value && oldSettings?.sourceId) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n }\n\n // Model updated -> show skeletons instead of data\n const sourceChanged =\n settings.model?.sourceId && settings.model.sourceId !== settings.sourceId;\n if (!settings.model || sourceChanged) {\n const state = gridApi.getServerSideGroupLevelState();\n const rowCount = !sourceChanged && state.length > 0 ? state[0].rowCount : 1;\n return gridApi.updateGridOptions({\n serverSideDatasource: {\n getRows: (params) => {\n params.success({ rowData: [], rowCount });\n },\n },\n });\n }\n\n // Model ready -> calculate new state\n const stateGeneration = generation.value;\n calculateGridOptions({\n generation,\n pfDriver: getRawPlatformaInstance().pFrameDriver,\n model: settings.model,\n sheets: settings.sheets ?? [],\n dataRenderedTracker,\n hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,\n cellButtonAxisParams: {\n showCellButtonForAxisId: props.showCellButtonForAxisId,\n cellButtonInvokeRowsOnDoubleClick: props.cellButtonInvokeRowsOnDoubleClick,\n trigger: (key?: PTableKey) => emit(\"cellButtonClicked\", key),\n } satisfies PlAgCellButtonAxisParams,\n })\n .then((result) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n const { axesSpec, ...options } = result;\n gridApi.updateGridOptions({\n ...options,\n });\n if (selection.value) {\n // Update selection if axesSpec changed, as order of axes may have changed and so we need to remap selected keys\n const { axesSpec: oldAxesSpec, selectedKeys: oldSelectedKeys } = selection.value;\n if (!isJsonEqual(oldAxesSpec, axesSpec)) {\n if (!oldAxesSpec || axesSpec.length !== oldAxesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const mapping = oldAxesSpec\n .map(getAxisId)\n .map((id) => axesSpec.findIndex((axis) => matchAxisId(axis, id)));\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const selectedNodes = oldSelectedKeys.map((key) =>\n mapping.map((index) => key[index]),\n );\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: selectedNodes };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes.map((key) => canonicalizeJson<PlTableRowId>(key)),\n });\n }\n }\n })\n .catch((error: unknown) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n if (isAbortError(error)) return;\n console.trace(error);\n })\n .finally(() => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n gridApi.updateGridOptions({\n loading: false,\n });\n });\n dataRenderedTracker.promise.then(() => emit(\"newDataRendered\"));\n } catch (error: unknown) {\n console.trace(error);\n } finally {\n oldSettings = settings;\n }\n },\n);\n\nwatch(\n () => ({\n gridApi: gridApi.value,\n loadingText: props.loadingText,\n runningText: props.runningText,\n notReadyText: props.notReadyText,\n noRowsText: props.noRowsText,\n }),\n ({ gridApi, loadingText, runningText, notReadyText, noRowsText }) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.updateGridOptions({\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n loadingText,\n runningText,\n notReadyText,\n },\n noRowsOverlayComponentParams: {\n ...gridOptions.value.noRowsOverlayComponentParams,\n text: noRowsText,\n },\n });\n },\n);\n\nwatchEffect(() => {\n if (!gridApi.value || gridApi.value?.isDestroyed()) return;\n gridApi.value.updateGridOptions({\n statusBar: gridOptions.value.loading\n ? undefined\n : {\n statusPanels: [{ statusPanel: PlAgRowCount, align: \"left\" }],\n },\n });\n});\n</script>\n\n<template>\n <div :class=\"$style.container\">\n <PlAgGridColumnManager v-if=\"gridApi && !disableColumnsPanel\" :api=\"gridApi\" />\n <PlTableFiltersV2\n v-if=\"!disableFiltersPanel\"\n v-model=\"filtersState\"\n :columns=\"filterableColumns\"\n />\n <PlAgCsvExporter v-if=\"gridApi && showExportButton\" :api=\"gridApi\" />\n <PlAgDataTableSheets v-model=\"sheetsState\" :settings=\"sheetsSettings\">\n <template v-if=\"$slots['before-sheets']\" #before>\n <slot name=\"before-sheets\" />\n </template>\n <template v-if=\"$slots['after-sheets']\" #after>\n <slot name=\"after-sheets\" />\n </template>\n </PlAgDataTableSheets>\n <PlTableFastSearch v-model=\"searchString\" />\n <AgGridVue\n :key=\"reloadKey\"\n :theme=\"AgGridTheme\"\n :class=\"$style.grid\"\n :grid-options=\"gridOptions\"\n />\n </div>\n</template>\n\n<style lang=\"css\" module>\n.container {\n display: flex;\n flex-direction: column;\n height: 100%;\n gap: 12px;\n}\n\n.grid {\n flex: 1;\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"PlAgDataTableV2.js","names":[],"sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { promiseTimeout, isJsonEqual } from \"@milaboratories/helpers\";\nimport type {\n AxisId,\n PlDataTableGridStateCore,\n PlDataTableStateV2,\n PlSelectionModel,\n PlTableColumnIdJson,\n PTableColumnSpec,\n PTableKey,\n} from \"@platforma-sdk/model\";\nimport {\n getRawPlatformaInstance,\n createPlSelectionModel,\n matchAxisId,\n getAxisId,\n canonicalizeJson,\n isAbortError,\n isColumnOptional,\n} from \"@platforma-sdk/model\";\nimport type { CellRendererSelectorFunc, GridApi, GridState } from \"ag-grid-enterprise\";\nimport { AgGridVue } from \"ag-grid-vue3\";\nimport { computed, effectScope, nextTick, ref, toRefs, watch, watchEffect } from \"vue\";\nimport { AgGridTheme } from \"../../aggrid\";\nimport PlAgCsvExporter from \"../PlAgCsvExporter/PlAgCsvExporter.vue\";\nimport { PlAgGridColumnManager } from \"../PlAgGridColumnManager\";\nimport PlTableFiltersV2 from \"../PlTableFilters/PlTableFiltersV2.vue\";\nimport { PlTableFastSearch } from \"../PlTableFastSearch\";\nimport PlAgDataTableSheets from \"./PlAgDataTableSheets.vue\";\nimport PlAgRowCount from \"./PlAgRowCount.vue\";\nimport { DeferredCircular, ensureNodeVisible } from \"./sources/focus-row\";\nimport { PlAgDataTableRowNumberColId } from \"./sources/row-number\";\nimport type { PlAgCellButtonAxisParams } from \"./sources/table-source-v2\";\nimport { calculateGridOptions } from \"./sources/table-source-v2\";\nimport { useTableState } from \"./sources/table-state-v2\";\nimport type {\n PlAgDataTableV2Controller,\n PlAgDataTableV2Row,\n PlAgOverlayLoadingParams,\n PlAgOverlayNoRowsParams,\n PlDataTableSettingsV2,\n PlDataTableSheetsSettings,\n PlTableRowId,\n} from \"./types\";\nimport { useFilterableColumns } from \"./compositions/useFilterableColumns\";\nimport { useGrid } from \"./compositions/useGrid\";\n\nconst tableState = defineModel<PlDataTableStateV2>({\n required: true,\n});\n/** Warning: selection model value updates are ignored, use updateSelection instead */\nconst selection = defineModel<PlSelectionModel>(\"selection\");\nconst props = defineProps<{\n /** Required component settings */\n settings: Readonly<PlDataTableSettingsV2>;\n\n /**\n * The disableColumnsPanel prop controls the display of a button that activates\n * the columns management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableColumnsPanel?: boolean;\n\n /**\n * The disableFiltersPanel prop controls the display of a button that activates\n * the filters management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableFiltersPanel?: boolean;\n\n /**\n * The showExportButton prop controls the display of a button that allows\n * to export table data in CSV format. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n showExportButton?: boolean;\n\n /**\n * The AxisId property is used to configure and display the PlAgTextAndButtonCell component\n */\n showCellButtonForAxisId?: AxisId;\n\n /**\n * If cellButtonInvokeRowsOnDoubleClick = true, clicking a button inside the row\n * triggers the doubleClick event for the entire row.\n *\n * If cellButtonInvokeRowsOnDoubleClick = false, the doubleClick event for the row\n * is not triggered, but will triggered cellButtonClicked event with (key: PTableRowKey) argument.\n */\n cellButtonInvokeRowsOnDoubleClick?: boolean;\n\n /** @see {@link PlAgOverlayLoadingParams.loadingText} */\n loadingText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.runningText} */\n runningText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.notReadyText} */\n notReadyText?: string;\n\n /** @see {@link PlAgOverlayNoRowsParams.text} */\n noRowsText?: string;\n\n /**\n * Callback to override the default renderer for a given cell.\n * @see https://www.ag-grid.com/vue-data-grid/component-cell-renderer/#dynamic-component-selection\n */\n cellRendererSelector?: CellRendererSelectorFunc<PlAgDataTableV2Row>;\n}>();\nconst { settings } = toRefs(props);\nconst emit = defineEmits<{\n rowDoubleClicked: [key?: PTableKey];\n cellButtonClicked: [key?: PTableKey];\n newDataRendered: [];\n}>();\n\nconst dataRenderedTracker = new DeferredCircular<GridApi<PlAgDataTableV2Row>>();\nconst { gridApi, gridOptions } = useGrid({\n selection: selection.value,\n noRowsText: props.noRowsText,\n runningText: props.runningText,\n loadingText: props.loadingText,\n notReadyText: props.notReadyText,\n cellRendererSelector: props.cellRendererSelector,\n});\nlet isReloading = false;\ngridOptions.value.onGridPreDestroyed = (event) => {\n if (!isReloading) {\n gridOptions.value.initialState = gridState.value = normalizeColumnVisibility(\n makePartialState(event.api.getState()),\n gridState.value,\n event.api,\n );\n }\n gridApi.value = null;\n};\ngridOptions.value.onRowDoubleClicked = (event) => {\n if (event.data && event.data.axesKey) emit(\"rowDoubleClicked\", event.data.axesKey);\n};\ngridOptions.value.onStateUpdated = (event) => {\n const partialState = normalizeColumnVisibility(\n makePartialState(event.state),\n gridState.value,\n event.api,\n );\n // We have to keep initialState synchronized with gridState for gridState recovery after key updating.\n gridOptions.value.initialState = gridState.value = partialState;\n\n if (!isJsonEqual(event.sources, [\"columnSizing\"])) {\n event.api.autoSizeColumns(\n event.api\n .getAllDisplayedColumns()\n .filter((column) => column.getColId() !== PlAgDataTableRowNumberColId),\n );\n }\n};\n\nconst [filterableColumns, visibleFilterableColumns] = useFilterableColumns(\n () => settings.value.sourceId,\n () => gridOptions.value.columnDefs ?? null,\n);\nconst { gridState, sheetsState, filtersState, searchString } = useTableState(\n tableState,\n settings,\n visibleFilterableColumns,\n);\nconst sheetsSettings = computed<PlDataTableSheetsSettings>(() => {\n const settingsCopy = { ...settings.value };\n return settingsCopy.sourceId !== null\n ? {\n sheets: settingsCopy.sheets ?? [],\n cachedState: [...sheetsState.value],\n }\n : {\n sheets: [],\n cachedState: [],\n };\n});\ngridOptions.value.initialState = gridState.value;\n\n// Restore proper types erased by AgGrid\nfunction makePartialState(state: GridState): PlDataTableGridStateCore {\n return {\n columnOrder: state.columnOrder as\n | {\n orderedColIds: PlTableColumnIdJson[];\n }\n | undefined,\n sort: state.sort as\n | {\n sortModel: {\n colId: PlTableColumnIdJson;\n sort: \"asc\" | \"desc\";\n }[];\n }\n | undefined,\n columnVisibility: state.columnVisibility as\n | {\n hiddenColIds: PlTableColumnIdJson[];\n }\n | undefined,\n };\n}\n\n// AG Grid returns columnVisibility: undefined when all columns are visible.\n// We need to distinguish \"no state yet\" (use isColumnOptional defaults) from\n// \"user explicitly showed all columns\" (store []). This function normalizes\n// the undefined from AG Grid based on the previous state.\nfunction normalizeColumnVisibility(\n partialState: PlDataTableGridStateCore,\n prevState: PlDataTableGridStateCore,\n api: GridApi<PlAgDataTableV2Row>,\n): PlDataTableGridStateCore {\n if (partialState.columnVisibility !== undefined) return partialState;\n\n if (prevState.columnVisibility !== undefined) {\n // Had explicit visibility state before → user made all columns visible → store [].\n return { ...partialState, columnVisibility: { hiddenColIds: [] } };\n }\n\n // No previous explicit state → compute defaults from current columns\n // to replicate: hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec)\n const defaultHidden = getDefaultHiddenColIds(api);\n if (defaultHidden.length > 0) {\n return { ...partialState, columnVisibility: { hiddenColIds: defaultHidden } };\n }\n\n return partialState;\n}\n\nfunction getDefaultHiddenColIds(api: GridApi<PlAgDataTableV2Row>): PlTableColumnIdJson[] {\n const cols = api.getAllGridColumns();\n if (!cols) return [];\n return cols\n .filter((col) => {\n const spec = col.getColDef().context as PTableColumnSpec | undefined;\n return spec && isColumnOptional(spec.spec);\n })\n .map((col) => col.getColId() as PlTableColumnIdJson);\n}\n\n// Normalize columnVisibility for comparison: undefined and { hiddenColIds: [] } are equivalent.\nfunction stateForReloadCompare(state: PlDataTableGridStateCore): PlDataTableGridStateCore {\n const cv = state.columnVisibility;\n const normalizedCv = !cv || cv.hiddenColIds.length === 0 ? undefined : state.columnVisibility;\n return { ...state, columnVisibility: normalizedCv };\n}\n\n// Reload AgGrid when new state arrives from server\nconst reloadKey = ref(0);\nwatch(\n () => [gridApi.value, gridState.value] as const,\n ([gridApi, gridState]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n const selfState = makePartialState(gridApi.getState());\n if (\n !isJsonEqual(gridState, {}) &&\n !isJsonEqual(stateForReloadCompare(gridState), stateForReloadCompare(selfState))\n ) {\n isReloading = true;\n gridOptions.value.initialState = gridState;\n ++reloadKey.value;\n nextTick(() => {\n isReloading = false;\n });\n }\n },\n);\n\n// Make cellRendererSelector reactive\nconst cellRendererSelector = computed(() => props.cellRendererSelector ?? null);\nwatch(\n () => [gridApi.value, cellRendererSelector.value] as const,\n ([gridApi, cellRendererSelector]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.setGridOption(\"defaultColDef\", {\n ...gridOptions.value.defaultColDef,\n cellRendererSelector: cellRendererSelector ?? undefined,\n });\n },\n);\n\ndefineExpose<PlAgDataTableV2Controller>({\n focusRow: async (rowKey) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n return ensureNodeVisible(gridApi, (row) => isJsonEqual(row.data?.axesKey, rowKey));\n },\n updateSelection: async ({ axesSpec, selectedKeys }) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n const axes = selection.value?.axesSpec;\n if (!axes || axes.length !== axesSpec.length) return false;\n\n const mapping = axesSpec.map((spec) => {\n const id = getAxisId(spec);\n return axes.findIndex((axis) => matchAxisId(axis, id));\n });\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) return false;\n\n const selectedNodes = selectedKeys.map((key) =>\n canonicalizeJson<PlTableRowId>(mapping.map((index) => key[index])),\n );\n const oldSelectedKeys = gridApi.getServerSideSelectionState()?.toggledNodes ?? [];\n if (!isJsonEqual(oldSelectedKeys, selectedNodes)) {\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes,\n });\n\n // wait for `onSelectionChanged` to update `selection` model\n const scope = effectScope();\n const { resolve, promise } = Promise.withResolvers();\n scope.run(() => watch(selection, resolve, { once: true }));\n try {\n await promiseTimeout(promise, 500);\n } catch {\n return false;\n } finally {\n scope.stop();\n }\n }\n return true;\n },\n});\n\n// Update AgGrid when settings change\nconst defaultSelection = createPlSelectionModel();\nlet oldSettings: PlDataTableSettingsV2 | null = null;\nconst generation = ref(0);\nwatch(\n () => [gridApi.value, settings.value] as const,\n ([gridApi, settings]) => {\n // Wait for AgGrid reinitialization, gridApi will eventually become initialized\n if (!gridApi || gridApi.isDestroyed()) return;\n // Verify that this is not a false watch trigger\n if (isJsonEqual(settings, oldSettings)) return;\n ++generation.value;\n try {\n // Hide no rows overlay if it is shown, or else loading overlay will not be shown\n gridApi.hideOverlay();\n dataRenderedTracker.reset();\n\n // No data source selected -> reset state to default\n if (settings.sourceId === null) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: settings.pending ? \"running\" : \"not-ready\",\n } satisfies PlAgOverlayLoadingParams,\n columnDefs: undefined,\n serverSideDatasource: undefined,\n });\n if (selection.value) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n return;\n }\n\n // Data source changed -> show full page loader, clear selection\n if (settings.sourceId !== oldSettings?.sourceId) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: \"loading\",\n } satisfies PlAgOverlayLoadingParams,\n });\n if (selection.value && oldSettings?.sourceId) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n }\n\n // Model updated -> show skeletons instead of data\n const sourceChanged =\n settings.model?.sourceId && settings.model.sourceId !== settings.sourceId;\n if (!settings.model || sourceChanged) {\n const state = gridApi.getServerSideGroupLevelState();\n const rowCount = !sourceChanged && state.length > 0 ? state[0].rowCount : 1;\n return gridApi.updateGridOptions({\n serverSideDatasource: {\n getRows: (params) => {\n params.success({ rowData: [], rowCount });\n },\n },\n });\n }\n\n // Model ready -> calculate new state\n const stateGeneration = generation.value;\n calculateGridOptions({\n generation,\n pfDriver: getRawPlatformaInstance().pFrameDriver,\n model: settings.model,\n sheets: settings.sheets ?? [],\n dataRenderedTracker,\n hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,\n cellButtonAxisParams: {\n showCellButtonForAxisId: props.showCellButtonForAxisId,\n cellButtonInvokeRowsOnDoubleClick: props.cellButtonInvokeRowsOnDoubleClick,\n trigger: (key?: PTableKey) => emit(\"cellButtonClicked\", key),\n } satisfies PlAgCellButtonAxisParams,\n })\n .then((result) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n const { axesSpec, ...options } = result;\n gridApi.updateGridOptions({\n ...options,\n });\n if (selection.value) {\n // Update selection if axesSpec changed, as order of axes may have changed and so we need to remap selected keys\n const { axesSpec: oldAxesSpec, selectedKeys: oldSelectedKeys } = selection.value;\n if (!isJsonEqual(oldAxesSpec, axesSpec)) {\n if (!oldAxesSpec || axesSpec.length !== oldAxesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const mapping = oldAxesSpec\n .map(getAxisId)\n .map((id) => axesSpec.findIndex((axis) => matchAxisId(axis, id)));\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const selectedNodes = oldSelectedKeys.map((key) =>\n mapping.map((index) => key[index]),\n );\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: selectedNodes };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes.map((key) => canonicalizeJson<PlTableRowId>(key)),\n });\n }\n }\n })\n .catch((error: unknown) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n if (isAbortError(error)) return;\n console.trace(error);\n })\n .finally(() => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n gridApi.updateGridOptions({\n loading: false,\n });\n });\n dataRenderedTracker.promise.then(() => emit(\"newDataRendered\"));\n } catch (error: unknown) {\n console.trace(error);\n } finally {\n oldSettings = settings;\n }\n },\n);\n\nwatch(\n () => ({\n gridApi: gridApi.value,\n loadingText: props.loadingText,\n runningText: props.runningText,\n notReadyText: props.notReadyText,\n noRowsText: props.noRowsText,\n }),\n ({ gridApi, loadingText, runningText, notReadyText, noRowsText }) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.updateGridOptions({\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n loadingText,\n runningText,\n notReadyText,\n },\n noRowsOverlayComponentParams: {\n ...gridOptions.value.noRowsOverlayComponentParams,\n text: noRowsText,\n },\n });\n },\n);\n\nwatchEffect(() => {\n if (!gridApi.value || gridApi.value?.isDestroyed()) return;\n gridApi.value.updateGridOptions({\n statusBar: gridOptions.value.loading\n ? undefined\n : {\n statusPanels: [{ statusPanel: PlAgRowCount, align: \"left\" }],\n },\n });\n});\n</script>\n\n<template>\n <div :class=\"$style.container\">\n <PlAgGridColumnManager v-if=\"gridApi && !disableColumnsPanel\" :api=\"gridApi\" />\n <PlTableFiltersV2\n v-if=\"!disableFiltersPanel\"\n v-model=\"filtersState\"\n :columns=\"filterableColumns\"\n />\n <PlAgCsvExporter v-if=\"gridApi && showExportButton\" :api=\"gridApi\" />\n <PlAgDataTableSheets v-model=\"sheetsState\" :settings=\"sheetsSettings\">\n <template v-if=\"$slots['before-sheets']\" #before>\n <slot name=\"before-sheets\" />\n </template>\n <template v-if=\"$slots['after-sheets']\" #after>\n <slot name=\"after-sheets\" />\n </template>\n </PlAgDataTableSheets>\n <PlTableFastSearch v-model=\"searchString\" />\n <AgGridVue\n :key=\"reloadKey\"\n :theme=\"AgGridTheme\"\n :class=\"$style.grid\"\n :grid-options=\"gridOptions\"\n />\n </div>\n</template>\n\n<style lang=\"css\" module>\n.container {\n display: flex;\n flex-direction: column;\n height: 100%;\n gap: 12px;\n}\n\n.grid {\n flex: 1;\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"PlAgDataTableV2.vue_vue_type_style_index_0_lang.module.js","names":[],"sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { promiseTimeout, isJsonEqual } from \"@milaboratories/helpers\";\nimport type {\n AxisId,\n PlDataTableGridStateCore,\n PlDataTableStateV2,\n PlSelectionModel,\n PlTableColumnIdJson,\n PTableKey,\n} from \"@platforma-sdk/model\";\nimport {\n getRawPlatformaInstance,\n createPlSelectionModel,\n matchAxisId,\n getAxisId,\n canonicalizeJson,\n isAbortError,\n} from \"@platforma-sdk/model\";\nimport type { CellRendererSelectorFunc, GridApi, GridState } from \"ag-grid-enterprise\";\nimport { AgGridVue } from \"ag-grid-vue3\";\nimport { computed, effectScope, ref, toRefs, watch, watchEffect } from \"vue\";\nimport { AgGridTheme } from \"../../aggrid\";\nimport PlAgCsvExporter from \"../PlAgCsvExporter/PlAgCsvExporter.vue\";\nimport { PlAgGridColumnManager } from \"../PlAgGridColumnManager\";\nimport PlTableFiltersV2 from \"../PlTableFilters/PlTableFiltersV2.vue\";\nimport { PlTableFastSearch } from \"../PlTableFastSearch\";\nimport PlAgDataTableSheets from \"./PlAgDataTableSheets.vue\";\nimport PlAgRowCount from \"./PlAgRowCount.vue\";\nimport { DeferredCircular, ensureNodeVisible } from \"./sources/focus-row\";\nimport { PlAgDataTableRowNumberColId } from \"./sources/row-number\";\nimport type { PlAgCellButtonAxisParams } from \"./sources/table-source-v2\";\nimport { calculateGridOptions } from \"./sources/table-source-v2\";\nimport { useTableState } from \"./sources/table-state-v2\";\nimport type {\n PlAgDataTableV2Controller,\n PlAgDataTableV2Row,\n PlAgOverlayLoadingParams,\n PlAgOverlayNoRowsParams,\n PlDataTableSettingsV2,\n PlDataTableSheetsSettings,\n PlTableRowId,\n} from \"./types\";\nimport { useFilterableColumns } from \"./compositions/useFilterableColumns\";\nimport { useGrid } from \"./compositions/useGrid\";\n\nconst tableState = defineModel<PlDataTableStateV2>({\n required: true,\n});\n/** Warning: selection model value updates are ignored, use updateSelection instead */\nconst selection = defineModel<PlSelectionModel>(\"selection\");\nconst props = defineProps<{\n /** Required component settings */\n settings: Readonly<PlDataTableSettingsV2>;\n\n /**\n * The disableColumnsPanel prop controls the display of a button that activates\n * the columns management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableColumnsPanel?: boolean;\n\n /**\n * The disableFiltersPanel prop controls the display of a button that activates\n * the filters management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableFiltersPanel?: boolean;\n\n /**\n * The showExportButton prop controls the display of a button that allows\n * to export table data in CSV format. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n showExportButton?: boolean;\n\n /**\n * The AxisId property is used to configure and display the PlAgTextAndButtonCell component\n */\n showCellButtonForAxisId?: AxisId;\n\n /**\n * If cellButtonInvokeRowsOnDoubleClick = true, clicking a button inside the row\n * triggers the doubleClick event for the entire row.\n *\n * If cellButtonInvokeRowsOnDoubleClick = false, the doubleClick event for the row\n * is not triggered, but will triggered cellButtonClicked event with (key: PTableRowKey) argument.\n */\n cellButtonInvokeRowsOnDoubleClick?: boolean;\n\n /** @see {@link PlAgOverlayLoadingParams.loadingText} */\n loadingText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.runningText} */\n runningText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.notReadyText} */\n notReadyText?: string;\n\n /** @see {@link PlAgOverlayNoRowsParams.text} */\n noRowsText?: string;\n\n /**\n * Callback to override the default renderer for a given cell.\n * @see https://www.ag-grid.com/vue-data-grid/component-cell-renderer/#dynamic-component-selection\n */\n cellRendererSelector?: CellRendererSelectorFunc<PlAgDataTableV2Row>;\n}>();\nconst { settings } = toRefs(props);\nconst emit = defineEmits<{\n rowDoubleClicked: [key?: PTableKey];\n cellButtonClicked: [key?: PTableKey];\n newDataRendered: [];\n}>();\n\nconst dataRenderedTracker = new DeferredCircular<GridApi<PlAgDataTableV2Row>>();\nconst { gridApi, gridOptions } = useGrid({\n selection: selection.value,\n noRowsText: props.noRowsText,\n runningText: props.runningText,\n loadingText: props.loadingText,\n notReadyText: props.notReadyText,\n cellRendererSelector: props.cellRendererSelector,\n});\ngridOptions.value.onGridPreDestroyed = (event) => {\n gridOptions.value.initialState = gridState.value = makePartialState(event.api.getState());\n gridApi.value = null;\n};\ngridOptions.value.onRowDoubleClicked = (event) => {\n if (event.data && event.data.axesKey) emit(\"rowDoubleClicked\", event.data.axesKey);\n};\ngridOptions.value.onStateUpdated = (event) => {\n let partialState = makePartialState(event.state);\n // AG Grid omits columnVisibility when no columns are hidden. If we previously had\n // hidden columns and now get undefined, treat as \"all visible\" so we don't revert to default.\n const hadHiddenCols = gridState.value.columnVisibility?.hiddenColIds !== undefined;\n if (partialState.columnVisibility === undefined && hadHiddenCols) {\n partialState = { ...partialState, columnVisibility: { hiddenColIds: [] } };\n }\n // We have to keep initialState synchronized with gridState for gridState recovery after key updating.\n gridOptions.value.initialState = gridState.value = partialState;\n\n if (!isJsonEqual(event.sources, [\"columnSizing\"])) {\n event.api.autoSizeColumns(\n event.api\n .getAllDisplayedColumns()\n .filter((column) => column.getColId() !== PlAgDataTableRowNumberColId),\n );\n }\n};\n\nconst [filterableColumns, visibleFilterableColumns] = useFilterableColumns(\n () => settings.value.sourceId,\n () => gridOptions.value.columnDefs ?? null,\n);\nconst { gridState, sheetsState, filtersState, searchString } = useTableState(\n tableState,\n settings,\n visibleFilterableColumns,\n);\nconst sheetsSettings = computed<PlDataTableSheetsSettings>(() => {\n const settingsCopy = { ...settings.value };\n return settingsCopy.sourceId !== null\n ? {\n sheets: settingsCopy.sheets ?? [],\n cachedState: [...sheetsState.value],\n }\n : {\n sheets: [],\n cachedState: [],\n };\n});\ngridOptions.value.initialState = gridState.value;\n\n// Restore proper types erased by AgGrid\nfunction makePartialState(state: GridState): PlDataTableGridStateCore {\n return {\n columnOrder: state.columnOrder as\n | {\n orderedColIds: PlTableColumnIdJson[];\n }\n | undefined,\n sort: state.sort as\n | {\n sortModel: {\n colId: PlTableColumnIdJson;\n sort: \"asc\" | \"desc\";\n }[];\n }\n | undefined,\n columnVisibility: state.columnVisibility as\n | {\n hiddenColIds: PlTableColumnIdJson[];\n }\n | undefined,\n };\n}\n\n// Normalize columnVisibility for comparison: undefined and { hiddenColIds: [] } are equivalent.\nfunction stateForReloadCompare(state: PlDataTableGridStateCore): PlDataTableGridStateCore {\n const cv = state.columnVisibility;\n const normalizedCv = !cv || cv.hiddenColIds.length === 0 ? undefined : state.columnVisibility;\n return { ...state, columnVisibility: normalizedCv };\n}\n\n// Reload AgGrid when new state arrives from server\nconst reloadKey = ref(0);\nwatch(\n () => [gridApi.value, gridState.value] as const,\n ([gridApi, gridState]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n const selfState = makePartialState(gridApi.getState());\n if (\n !isJsonEqual(gridState, {}) &&\n !isJsonEqual(stateForReloadCompare(gridState), stateForReloadCompare(selfState))\n ) {\n gridOptions.value.initialState = gridState;\n ++reloadKey.value;\n }\n },\n);\n\n// Make cellRendererSelector reactive\nconst cellRendererSelector = computed(() => props.cellRendererSelector ?? null);\nwatch(\n () => [gridApi.value, cellRendererSelector.value] as const,\n ([gridApi, cellRendererSelector]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.setGridOption(\"defaultColDef\", {\n ...gridOptions.value.defaultColDef,\n cellRendererSelector: cellRendererSelector ?? undefined,\n });\n },\n);\n\ndefineExpose<PlAgDataTableV2Controller>({\n focusRow: async (rowKey) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n return ensureNodeVisible(gridApi, (row) => isJsonEqual(row.data?.axesKey, rowKey));\n },\n updateSelection: async ({ axesSpec, selectedKeys }) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n const axes = selection.value?.axesSpec;\n if (!axes || axes.length !== axesSpec.length) return false;\n\n const mapping = axesSpec.map((spec) => {\n const id = getAxisId(spec);\n return axes.findIndex((axis) => matchAxisId(axis, id));\n });\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) return false;\n\n const selectedNodes = selectedKeys.map((key) =>\n canonicalizeJson<PlTableRowId>(mapping.map((index) => key[index])),\n );\n const oldSelectedKeys = gridApi.getServerSideSelectionState()?.toggledNodes ?? [];\n if (!isJsonEqual(oldSelectedKeys, selectedNodes)) {\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes,\n });\n\n // wait for `onSelectionChanged` to update `selection` model\n const scope = effectScope();\n const { resolve, promise } = Promise.withResolvers();\n scope.run(() => watch(selection, resolve, { once: true }));\n try {\n await promiseTimeout(promise, 500);\n } catch {\n return false;\n } finally {\n scope.stop();\n }\n }\n return true;\n },\n});\n\n// Update AgGrid when settings change\nconst defaultSelection = createPlSelectionModel();\nlet oldSettings: PlDataTableSettingsV2 | null = null;\nconst generation = ref(0);\nwatch(\n () => [gridApi.value, settings.value] as const,\n ([gridApi, settings]) => {\n // Wait for AgGrid reinitialization, gridApi will eventually become initialized\n if (!gridApi || gridApi.isDestroyed()) return;\n // Verify that this is not a false watch trigger\n if (isJsonEqual(settings, oldSettings)) return;\n ++generation.value;\n try {\n // Hide no rows overlay if it is shown, or else loading overlay will not be shown\n gridApi.hideOverlay();\n dataRenderedTracker.reset();\n\n // No data source selected -> reset state to default\n if (settings.sourceId === null) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: settings.pending ? \"running\" : \"not-ready\",\n } satisfies PlAgOverlayLoadingParams,\n columnDefs: undefined,\n serverSideDatasource: undefined,\n });\n if (selection.value) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n return;\n }\n\n // Data source changed -> show full page loader, clear selection\n if (settings.sourceId !== oldSettings?.sourceId) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: \"loading\",\n } satisfies PlAgOverlayLoadingParams,\n });\n if (selection.value && oldSettings?.sourceId) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n }\n\n // Model updated -> show skeletons instead of data\n const sourceChanged =\n settings.model?.sourceId && settings.model.sourceId !== settings.sourceId;\n if (!settings.model || sourceChanged) {\n const state = gridApi.getServerSideGroupLevelState();\n const rowCount = !sourceChanged && state.length > 0 ? state[0].rowCount : 1;\n return gridApi.updateGridOptions({\n serverSideDatasource: {\n getRows: (params) => {\n params.success({ rowData: [], rowCount });\n },\n },\n });\n }\n\n // Model ready -> calculate new state\n const stateGeneration = generation.value;\n calculateGridOptions({\n generation,\n pfDriver: getRawPlatformaInstance().pFrameDriver,\n model: settings.model,\n sheets: settings.sheets ?? [],\n dataRenderedTracker,\n hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,\n cellButtonAxisParams: {\n showCellButtonForAxisId: props.showCellButtonForAxisId,\n cellButtonInvokeRowsOnDoubleClick: props.cellButtonInvokeRowsOnDoubleClick,\n trigger: (key?: PTableKey) => emit(\"cellButtonClicked\", key),\n } satisfies PlAgCellButtonAxisParams,\n })\n .then((result) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n const { axesSpec, ...options } = result;\n gridApi.updateGridOptions({\n ...options,\n });\n if (selection.value) {\n // Update selection if axesSpec changed, as order of axes may have changed and so we need to remap selected keys\n const { axesSpec: oldAxesSpec, selectedKeys: oldSelectedKeys } = selection.value;\n if (!isJsonEqual(oldAxesSpec, axesSpec)) {\n if (!oldAxesSpec || axesSpec.length !== oldAxesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const mapping = oldAxesSpec\n .map(getAxisId)\n .map((id) => axesSpec.findIndex((axis) => matchAxisId(axis, id)));\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const selectedNodes = oldSelectedKeys.map((key) =>\n mapping.map((index) => key[index]),\n );\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: selectedNodes };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes.map((key) => canonicalizeJson<PlTableRowId>(key)),\n });\n }\n }\n })\n .catch((error: unknown) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n if (isAbortError(error)) return;\n console.trace(error);\n })\n .finally(() => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n gridApi.updateGridOptions({\n loading: false,\n });\n });\n dataRenderedTracker.promise.then(() => emit(\"newDataRendered\"));\n } catch (error: unknown) {\n console.trace(error);\n } finally {\n oldSettings = settings;\n }\n },\n);\n\nwatch(\n () => ({\n gridApi: gridApi.value,\n loadingText: props.loadingText,\n runningText: props.runningText,\n notReadyText: props.notReadyText,\n noRowsText: props.noRowsText,\n }),\n ({ gridApi, loadingText, runningText, notReadyText, noRowsText }) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.updateGridOptions({\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n loadingText,\n runningText,\n notReadyText,\n },\n noRowsOverlayComponentParams: {\n ...gridOptions.value.noRowsOverlayComponentParams,\n text: noRowsText,\n },\n });\n },\n);\n\nwatchEffect(() => {\n if (!gridApi.value || gridApi.value?.isDestroyed()) return;\n gridApi.value.updateGridOptions({\n statusBar: gridOptions.value.loading\n ? undefined\n : {\n statusPanels: [{ statusPanel: PlAgRowCount, align: \"left\" }],\n },\n });\n});\n</script>\n\n<template>\n <div :class=\"$style.container\">\n <PlAgGridColumnManager v-if=\"gridApi && !disableColumnsPanel\" :api=\"gridApi\" />\n <PlTableFiltersV2\n v-if=\"!disableFiltersPanel\"\n v-model=\"filtersState\"\n :columns=\"filterableColumns\"\n />\n <PlAgCsvExporter v-if=\"gridApi && showExportButton\" :api=\"gridApi\" />\n <PlAgDataTableSheets v-model=\"sheetsState\" :settings=\"sheetsSettings\">\n <template v-if=\"$slots['before-sheets']\" #before>\n <slot name=\"before-sheets\" />\n </template>\n <template v-if=\"$slots['after-sheets']\" #after>\n <slot name=\"after-sheets\" />\n </template>\n </PlAgDataTableSheets>\n <PlTableFastSearch v-model=\"searchString\" />\n <AgGridVue\n :key=\"reloadKey\"\n :theme=\"AgGridTheme\"\n :class=\"$style.grid\"\n :grid-options=\"gridOptions\"\n />\n </div>\n</template>\n\n<style lang=\"css\" module>\n.container {\n display: flex;\n flex-direction: column;\n height: 100%;\n gap: 12px;\n}\n\n.grid {\n flex: 1;\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"PlAgDataTableV2.vue_vue_type_style_index_0_lang.module.js","names":[],"sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { promiseTimeout, isJsonEqual } from \"@milaboratories/helpers\";\nimport type {\n AxisId,\n PlDataTableGridStateCore,\n PlDataTableStateV2,\n PlSelectionModel,\n PlTableColumnIdJson,\n PTableColumnSpec,\n PTableKey,\n} from \"@platforma-sdk/model\";\nimport {\n getRawPlatformaInstance,\n createPlSelectionModel,\n matchAxisId,\n getAxisId,\n canonicalizeJson,\n isAbortError,\n isColumnOptional,\n} from \"@platforma-sdk/model\";\nimport type { CellRendererSelectorFunc, GridApi, GridState } from \"ag-grid-enterprise\";\nimport { AgGridVue } from \"ag-grid-vue3\";\nimport { computed, effectScope, nextTick, ref, toRefs, watch, watchEffect } from \"vue\";\nimport { AgGridTheme } from \"../../aggrid\";\nimport PlAgCsvExporter from \"../PlAgCsvExporter/PlAgCsvExporter.vue\";\nimport { PlAgGridColumnManager } from \"../PlAgGridColumnManager\";\nimport PlTableFiltersV2 from \"../PlTableFilters/PlTableFiltersV2.vue\";\nimport { PlTableFastSearch } from \"../PlTableFastSearch\";\nimport PlAgDataTableSheets from \"./PlAgDataTableSheets.vue\";\nimport PlAgRowCount from \"./PlAgRowCount.vue\";\nimport { DeferredCircular, ensureNodeVisible } from \"./sources/focus-row\";\nimport { PlAgDataTableRowNumberColId } from \"./sources/row-number\";\nimport type { PlAgCellButtonAxisParams } from \"./sources/table-source-v2\";\nimport { calculateGridOptions } from \"./sources/table-source-v2\";\nimport { useTableState } from \"./sources/table-state-v2\";\nimport type {\n PlAgDataTableV2Controller,\n PlAgDataTableV2Row,\n PlAgOverlayLoadingParams,\n PlAgOverlayNoRowsParams,\n PlDataTableSettingsV2,\n PlDataTableSheetsSettings,\n PlTableRowId,\n} from \"./types\";\nimport { useFilterableColumns } from \"./compositions/useFilterableColumns\";\nimport { useGrid } from \"./compositions/useGrid\";\n\nconst tableState = defineModel<PlDataTableStateV2>({\n required: true,\n});\n/** Warning: selection model value updates are ignored, use updateSelection instead */\nconst selection = defineModel<PlSelectionModel>(\"selection\");\nconst props = defineProps<{\n /** Required component settings */\n settings: Readonly<PlDataTableSettingsV2>;\n\n /**\n * The disableColumnsPanel prop controls the display of a button that activates\n * the columns management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableColumnsPanel?: boolean;\n\n /**\n * The disableFiltersPanel prop controls the display of a button that activates\n * the filters management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableFiltersPanel?: boolean;\n\n /**\n * The showExportButton prop controls the display of a button that allows\n * to export table data in CSV format. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n showExportButton?: boolean;\n\n /**\n * The AxisId property is used to configure and display the PlAgTextAndButtonCell component\n */\n showCellButtonForAxisId?: AxisId;\n\n /**\n * If cellButtonInvokeRowsOnDoubleClick = true, clicking a button inside the row\n * triggers the doubleClick event for the entire row.\n *\n * If cellButtonInvokeRowsOnDoubleClick = false, the doubleClick event for the row\n * is not triggered, but will triggered cellButtonClicked event with (key: PTableRowKey) argument.\n */\n cellButtonInvokeRowsOnDoubleClick?: boolean;\n\n /** @see {@link PlAgOverlayLoadingParams.loadingText} */\n loadingText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.runningText} */\n runningText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.notReadyText} */\n notReadyText?: string;\n\n /** @see {@link PlAgOverlayNoRowsParams.text} */\n noRowsText?: string;\n\n /**\n * Callback to override the default renderer for a given cell.\n * @see https://www.ag-grid.com/vue-data-grid/component-cell-renderer/#dynamic-component-selection\n */\n cellRendererSelector?: CellRendererSelectorFunc<PlAgDataTableV2Row>;\n}>();\nconst { settings } = toRefs(props);\nconst emit = defineEmits<{\n rowDoubleClicked: [key?: PTableKey];\n cellButtonClicked: [key?: PTableKey];\n newDataRendered: [];\n}>();\n\nconst dataRenderedTracker = new DeferredCircular<GridApi<PlAgDataTableV2Row>>();\nconst { gridApi, gridOptions } = useGrid({\n selection: selection.value,\n noRowsText: props.noRowsText,\n runningText: props.runningText,\n loadingText: props.loadingText,\n notReadyText: props.notReadyText,\n cellRendererSelector: props.cellRendererSelector,\n});\nlet isReloading = false;\ngridOptions.value.onGridPreDestroyed = (event) => {\n if (!isReloading) {\n gridOptions.value.initialState = gridState.value = normalizeColumnVisibility(\n makePartialState(event.api.getState()),\n gridState.value,\n event.api,\n );\n }\n gridApi.value = null;\n};\ngridOptions.value.onRowDoubleClicked = (event) => {\n if (event.data && event.data.axesKey) emit(\"rowDoubleClicked\", event.data.axesKey);\n};\ngridOptions.value.onStateUpdated = (event) => {\n const partialState = normalizeColumnVisibility(\n makePartialState(event.state),\n gridState.value,\n event.api,\n );\n // We have to keep initialState synchronized with gridState for gridState recovery after key updating.\n gridOptions.value.initialState = gridState.value = partialState;\n\n if (!isJsonEqual(event.sources, [\"columnSizing\"])) {\n event.api.autoSizeColumns(\n event.api\n .getAllDisplayedColumns()\n .filter((column) => column.getColId() !== PlAgDataTableRowNumberColId),\n );\n }\n};\n\nconst [filterableColumns, visibleFilterableColumns] = useFilterableColumns(\n () => settings.value.sourceId,\n () => gridOptions.value.columnDefs ?? null,\n);\nconst { gridState, sheetsState, filtersState, searchString } = useTableState(\n tableState,\n settings,\n visibleFilterableColumns,\n);\nconst sheetsSettings = computed<PlDataTableSheetsSettings>(() => {\n const settingsCopy = { ...settings.value };\n return settingsCopy.sourceId !== null\n ? {\n sheets: settingsCopy.sheets ?? [],\n cachedState: [...sheetsState.value],\n }\n : {\n sheets: [],\n cachedState: [],\n };\n});\ngridOptions.value.initialState = gridState.value;\n\n// Restore proper types erased by AgGrid\nfunction makePartialState(state: GridState): PlDataTableGridStateCore {\n return {\n columnOrder: state.columnOrder as\n | {\n orderedColIds: PlTableColumnIdJson[];\n }\n | undefined,\n sort: state.sort as\n | {\n sortModel: {\n colId: PlTableColumnIdJson;\n sort: \"asc\" | \"desc\";\n }[];\n }\n | undefined,\n columnVisibility: state.columnVisibility as\n | {\n hiddenColIds: PlTableColumnIdJson[];\n }\n | undefined,\n };\n}\n\n// AG Grid returns columnVisibility: undefined when all columns are visible.\n// We need to distinguish \"no state yet\" (use isColumnOptional defaults) from\n// \"user explicitly showed all columns\" (store []). This function normalizes\n// the undefined from AG Grid based on the previous state.\nfunction normalizeColumnVisibility(\n partialState: PlDataTableGridStateCore,\n prevState: PlDataTableGridStateCore,\n api: GridApi<PlAgDataTableV2Row>,\n): PlDataTableGridStateCore {\n if (partialState.columnVisibility !== undefined) return partialState;\n\n if (prevState.columnVisibility !== undefined) {\n // Had explicit visibility state before → user made all columns visible → store [].\n return { ...partialState, columnVisibility: { hiddenColIds: [] } };\n }\n\n // No previous explicit state → compute defaults from current columns\n // to replicate: hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec)\n const defaultHidden = getDefaultHiddenColIds(api);\n if (defaultHidden.length > 0) {\n return { ...partialState, columnVisibility: { hiddenColIds: defaultHidden } };\n }\n\n return partialState;\n}\n\nfunction getDefaultHiddenColIds(api: GridApi<PlAgDataTableV2Row>): PlTableColumnIdJson[] {\n const cols = api.getAllGridColumns();\n if (!cols) return [];\n return cols\n .filter((col) => {\n const spec = col.getColDef().context as PTableColumnSpec | undefined;\n return spec && isColumnOptional(spec.spec);\n })\n .map((col) => col.getColId() as PlTableColumnIdJson);\n}\n\n// Normalize columnVisibility for comparison: undefined and { hiddenColIds: [] } are equivalent.\nfunction stateForReloadCompare(state: PlDataTableGridStateCore): PlDataTableGridStateCore {\n const cv = state.columnVisibility;\n const normalizedCv = !cv || cv.hiddenColIds.length === 0 ? undefined : state.columnVisibility;\n return { ...state, columnVisibility: normalizedCv };\n}\n\n// Reload AgGrid when new state arrives from server\nconst reloadKey = ref(0);\nwatch(\n () => [gridApi.value, gridState.value] as const,\n ([gridApi, gridState]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n const selfState = makePartialState(gridApi.getState());\n if (\n !isJsonEqual(gridState, {}) &&\n !isJsonEqual(stateForReloadCompare(gridState), stateForReloadCompare(selfState))\n ) {\n isReloading = true;\n gridOptions.value.initialState = gridState;\n ++reloadKey.value;\n nextTick(() => {\n isReloading = false;\n });\n }\n },\n);\n\n// Make cellRendererSelector reactive\nconst cellRendererSelector = computed(() => props.cellRendererSelector ?? null);\nwatch(\n () => [gridApi.value, cellRendererSelector.value] as const,\n ([gridApi, cellRendererSelector]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.setGridOption(\"defaultColDef\", {\n ...gridOptions.value.defaultColDef,\n cellRendererSelector: cellRendererSelector ?? undefined,\n });\n },\n);\n\ndefineExpose<PlAgDataTableV2Controller>({\n focusRow: async (rowKey) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n return ensureNodeVisible(gridApi, (row) => isJsonEqual(row.data?.axesKey, rowKey));\n },\n updateSelection: async ({ axesSpec, selectedKeys }) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n const axes = selection.value?.axesSpec;\n if (!axes || axes.length !== axesSpec.length) return false;\n\n const mapping = axesSpec.map((spec) => {\n const id = getAxisId(spec);\n return axes.findIndex((axis) => matchAxisId(axis, id));\n });\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) return false;\n\n const selectedNodes = selectedKeys.map((key) =>\n canonicalizeJson<PlTableRowId>(mapping.map((index) => key[index])),\n );\n const oldSelectedKeys = gridApi.getServerSideSelectionState()?.toggledNodes ?? [];\n if (!isJsonEqual(oldSelectedKeys, selectedNodes)) {\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes,\n });\n\n // wait for `onSelectionChanged` to update `selection` model\n const scope = effectScope();\n const { resolve, promise } = Promise.withResolvers();\n scope.run(() => watch(selection, resolve, { once: true }));\n try {\n await promiseTimeout(promise, 500);\n } catch {\n return false;\n } finally {\n scope.stop();\n }\n }\n return true;\n },\n});\n\n// Update AgGrid when settings change\nconst defaultSelection = createPlSelectionModel();\nlet oldSettings: PlDataTableSettingsV2 | null = null;\nconst generation = ref(0);\nwatch(\n () => [gridApi.value, settings.value] as const,\n ([gridApi, settings]) => {\n // Wait for AgGrid reinitialization, gridApi will eventually become initialized\n if (!gridApi || gridApi.isDestroyed()) return;\n // Verify that this is not a false watch trigger\n if (isJsonEqual(settings, oldSettings)) return;\n ++generation.value;\n try {\n // Hide no rows overlay if it is shown, or else loading overlay will not be shown\n gridApi.hideOverlay();\n dataRenderedTracker.reset();\n\n // No data source selected -> reset state to default\n if (settings.sourceId === null) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: settings.pending ? \"running\" : \"not-ready\",\n } satisfies PlAgOverlayLoadingParams,\n columnDefs: undefined,\n serverSideDatasource: undefined,\n });\n if (selection.value) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n return;\n }\n\n // Data source changed -> show full page loader, clear selection\n if (settings.sourceId !== oldSettings?.sourceId) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: \"loading\",\n } satisfies PlAgOverlayLoadingParams,\n });\n if (selection.value && oldSettings?.sourceId) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n }\n\n // Model updated -> show skeletons instead of data\n const sourceChanged =\n settings.model?.sourceId && settings.model.sourceId !== settings.sourceId;\n if (!settings.model || sourceChanged) {\n const state = gridApi.getServerSideGroupLevelState();\n const rowCount = !sourceChanged && state.length > 0 ? state[0].rowCount : 1;\n return gridApi.updateGridOptions({\n serverSideDatasource: {\n getRows: (params) => {\n params.success({ rowData: [], rowCount });\n },\n },\n });\n }\n\n // Model ready -> calculate new state\n const stateGeneration = generation.value;\n calculateGridOptions({\n generation,\n pfDriver: getRawPlatformaInstance().pFrameDriver,\n model: settings.model,\n sheets: settings.sheets ?? [],\n dataRenderedTracker,\n hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,\n cellButtonAxisParams: {\n showCellButtonForAxisId: props.showCellButtonForAxisId,\n cellButtonInvokeRowsOnDoubleClick: props.cellButtonInvokeRowsOnDoubleClick,\n trigger: (key?: PTableKey) => emit(\"cellButtonClicked\", key),\n } satisfies PlAgCellButtonAxisParams,\n })\n .then((result) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n const { axesSpec, ...options } = result;\n gridApi.updateGridOptions({\n ...options,\n });\n if (selection.value) {\n // Update selection if axesSpec changed, as order of axes may have changed and so we need to remap selected keys\n const { axesSpec: oldAxesSpec, selectedKeys: oldSelectedKeys } = selection.value;\n if (!isJsonEqual(oldAxesSpec, axesSpec)) {\n if (!oldAxesSpec || axesSpec.length !== oldAxesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const mapping = oldAxesSpec\n .map(getAxisId)\n .map((id) => axesSpec.findIndex((axis) => matchAxisId(axis, id)));\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const selectedNodes = oldSelectedKeys.map((key) =>\n mapping.map((index) => key[index]),\n );\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: selectedNodes };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes.map((key) => canonicalizeJson<PlTableRowId>(key)),\n });\n }\n }\n })\n .catch((error: unknown) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n if (isAbortError(error)) return;\n console.trace(error);\n })\n .finally(() => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n gridApi.updateGridOptions({\n loading: false,\n });\n });\n dataRenderedTracker.promise.then(() => emit(\"newDataRendered\"));\n } catch (error: unknown) {\n console.trace(error);\n } finally {\n oldSettings = settings;\n }\n },\n);\n\nwatch(\n () => ({\n gridApi: gridApi.value,\n loadingText: props.loadingText,\n runningText: props.runningText,\n notReadyText: props.notReadyText,\n noRowsText: props.noRowsText,\n }),\n ({ gridApi, loadingText, runningText, notReadyText, noRowsText }) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.updateGridOptions({\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n loadingText,\n runningText,\n notReadyText,\n },\n noRowsOverlayComponentParams: {\n ...gridOptions.value.noRowsOverlayComponentParams,\n text: noRowsText,\n },\n });\n },\n);\n\nwatchEffect(() => {\n if (!gridApi.value || gridApi.value?.isDestroyed()) return;\n gridApi.value.updateGridOptions({\n statusBar: gridOptions.value.loading\n ? undefined\n : {\n statusPanels: [{ statusPanel: PlAgRowCount, align: \"left\" }],\n },\n });\n});\n</script>\n\n<template>\n <div :class=\"$style.container\">\n <PlAgGridColumnManager v-if=\"gridApi && !disableColumnsPanel\" :api=\"gridApi\" />\n <PlTableFiltersV2\n v-if=\"!disableFiltersPanel\"\n v-model=\"filtersState\"\n :columns=\"filterableColumns\"\n />\n <PlAgCsvExporter v-if=\"gridApi && showExportButton\" :api=\"gridApi\" />\n <PlAgDataTableSheets v-model=\"sheetsState\" :settings=\"sheetsSettings\">\n <template v-if=\"$slots['before-sheets']\" #before>\n <slot name=\"before-sheets\" />\n </template>\n <template v-if=\"$slots['after-sheets']\" #after>\n <slot name=\"after-sheets\" />\n </template>\n </PlAgDataTableSheets>\n <PlTableFastSearch v-model=\"searchString\" />\n <AgGridVue\n :key=\"reloadKey\"\n :theme=\"AgGridTheme\"\n :class=\"$style.grid\"\n :grid-options=\"gridOptions\"\n />\n </div>\n</template>\n\n<style lang=\"css\" module>\n.container {\n display: flex;\n flex-direction: column;\n height: 100%;\n gap: 12px;\n}\n\n.grid {\n flex: 1;\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"PlAgDataTableV2.vue.d.ts","sourceRoot":"","sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"names":[],"mappings":"AA2gBA,OAAO,KAAK,EACV,MAAM,EAEN,kBAAkB,EAClB,gBAAgB,EAEhB,SAAS,EACV,MAAM,sBAAsB,CAAC;AAS9B,OAAO,KAAK,EAAE,wBAAwB,EAAsB,MAAM,oBAAoB,CAAC;AAevF,OAAO,KAAK,EAEV,kBAAkB,EAGlB,qBAAqB,EAGtB,MAAM,SAAS,CAAC;AASjB,KAAK,WAAW,GAAG;IACjB,kCAAkC;IAClC,QAAQ,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IAE1C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;;;;OAMG;IACH,iCAAiC,CAAC,EAAE,OAAO,CAAC;IAE5C,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,wBAAwB,CAAC,kBAAkB,CAAC,CAAC;CACrE,CAAC;AAyXF,KAAK,iBAAiB,GAAG;IACzB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAC9B,GAAG,WAAW,CAAC;AAMhB,iBAAS,cAAc;WAoHT,OAAO,IAA6B;;iCAXZ,GAAG;gCACJ,GAAG;;;;EAevC;AAwBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;6FASnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"PlAgDataTableV2.vue.d.ts","sourceRoot":"","sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"names":[],"mappings":"AA2jBA,OAAO,KAAK,EACV,MAAM,EAEN,kBAAkB,EAClB,gBAAgB,EAGhB,SAAS,EACV,MAAM,sBAAsB,CAAC;AAU9B,OAAO,KAAK,EAAE,wBAAwB,EAAsB,MAAM,oBAAoB,CAAC;AAevF,OAAO,KAAK,EAEV,kBAAkB,EAGlB,qBAAqB,EAGtB,MAAM,SAAS,CAAC;AASjB,KAAK,WAAW,GAAG;IACjB,kCAAkC;IAClC,QAAQ,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IAE1C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;;;;OAMG;IACH,iCAAiC,CAAC,EAAE,OAAO,CAAC;IAE5C,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,wBAAwB,CAAC,kBAAkB,CAAC,CAAC;CACrE,CAAC;AAuaF,KAAK,iBAAiB,GAAG;IACzB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAC9B,GAAG,WAAW,CAAC;AAMhB,iBAAS,cAAc;WAoHT,OAAO,IAA6B;;iCAXZ,GAAG;gCACJ,GAAG;;;;EAevC;AAwBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;6FASnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -14,9 +14,9 @@ import { calculateGridOptions as u } from "./sources/table-source-v2.js";
14
14
  import { useTableState as ne } from "./sources/table-state-v2.js";
15
15
  import { useFilterableColumns as re } from "./compositions/useFilterableColumns.js";
16
16
  import { useGrid as ie } from "./compositions/useGrid.js";
17
- import { computed as d, createBlock as f, createCommentVNode as p, createElementBlock as ae, createSlots as m, createVNode as h, defineComponent as g, effectScope as _, isRef as v, mergeModels as y, normalizeClass as b, openBlock as x, ref as S, renderSlot as C, toRefs as oe, unref as w, useModel as T, watch as E, watchEffect as D, withCtx as O } from "vue";
18
- import { canonicalizeJson as k, createPlSelectionModel as A, getAxisId as j, getRawPlatformaInstance as se, isAbortError as M, matchAxisId as N } from "@platforma-sdk/model";
19
- import { AgGridVue as ce } from "ag-grid-vue3";
17
+ import { computed as d, createBlock as f, createCommentVNode as p, createElementBlock as ae, createSlots as m, createVNode as h, defineComponent as g, effectScope as _, isRef as v, mergeModels as y, nextTick as oe, normalizeClass as b, openBlock as x, ref as S, renderSlot as C, toRefs as se, unref as w, useModel as T, watch as E, watchEffect as D, withCtx as O } from "vue";
18
+ import { canonicalizeJson as k, createPlSelectionModel as A, getAxisId as j, getRawPlatformaInstance as ce, isAbortError as M, isColumnOptional as le, matchAxisId as N } from "@platforma-sdk/model";
19
+ import { AgGridVue as ue } from "ag-grid-vue3";
20
20
  var P = /* @__PURE__ */ g({
21
21
  __name: "PlAgDataTableV2",
22
22
  props: /* @__PURE__ */ y({
@@ -43,43 +43,59 @@ var P = /* @__PURE__ */ g({
43
43
  "newDataRendered"
44
44
  ], ["update:modelValue", "update:selection"]),
45
45
  setup(g, { expose: y, emit: P }) {
46
- let le = T(g, "modelValue"), F = T(g, "selection"), I = g, { settings: L } = oe(I), R = P, z = new te(), { gridApi: B, gridOptions: V } = ie({
46
+ let de = T(g, "modelValue"), F = T(g, "selection"), I = g, { settings: L } = se(I), R = P, z = new te(), { gridApi: B, gridOptions: V } = ie({
47
47
  selection: F.value,
48
48
  noRowsText: I.noRowsText,
49
49
  runningText: I.runningText,
50
50
  loadingText: I.loadingText,
51
51
  notReadyText: I.notReadyText,
52
52
  cellRendererSelector: I.cellRendererSelector
53
- });
53
+ }), H = !1;
54
54
  V.value.onGridPreDestroyed = (e) => {
55
- V.value.initialState = W.value = J(e.api.getState()), B.value = null;
55
+ H || (V.value.initialState = U.value = J(q(e.api.getState()), U.value, e.api)), B.value = null;
56
56
  }, V.value.onRowDoubleClicked = (e) => {
57
57
  e.data && e.data.axesKey && R("rowDoubleClicked", e.data.axesKey);
58
58
  }, V.value.onStateUpdated = (t) => {
59
- let n = J(t.state), r = W.value.columnVisibility?.hiddenColIds !== void 0;
60
- n.columnVisibility === void 0 && r && (n = {
61
- ...n,
62
- columnVisibility: { hiddenColIds: [] }
63
- }), V.value.initialState = W.value = n, e(t.sources, ["columnSizing"]) || t.api.autoSizeColumns(t.api.getAllDisplayedColumns().filter((e) => e.getColId() !== i));
59
+ let n = J(q(t.state), U.value, t.api);
60
+ V.value.initialState = U.value = n, e(t.sources, ["columnSizing"]) || t.api.autoSizeColumns(t.api.getAllDisplayedColumns().filter((e) => e.getColId() !== i));
64
61
  };
65
- let [H, U] = re(() => L.value.sourceId, () => V.value.columnDefs ?? null), { gridState: W, sheetsState: G, filtersState: K, searchString: q } = ne(le, L, U), ue = d(() => {
62
+ let [fe, pe] = re(() => L.value.sourceId, () => V.value.columnDefs ?? null), { gridState: U, sheetsState: W, filtersState: G, searchString: K } = ne(de, L, pe), me = d(() => {
66
63
  let e = { ...L.value };
67
64
  return e.sourceId === null ? {
68
65
  sheets: [],
69
66
  cachedState: []
70
67
  } : {
71
68
  sheets: e.sheets ?? [],
72
- cachedState: [...G.value]
69
+ cachedState: [...W.value]
73
70
  };
74
71
  });
75
- V.value.initialState = W.value;
76
- function J(e) {
72
+ V.value.initialState = U.value;
73
+ function q(e) {
77
74
  return {
78
75
  columnOrder: e.columnOrder,
79
76
  sort: e.sort,
80
77
  columnVisibility: e.columnVisibility
81
78
  };
82
79
  }
80
+ function J(e, t, n) {
81
+ if (e.columnVisibility !== void 0) return e;
82
+ if (t.columnVisibility !== void 0) return {
83
+ ...e,
84
+ columnVisibility: { hiddenColIds: [] }
85
+ };
86
+ let r = he(n);
87
+ return r.length > 0 ? {
88
+ ...e,
89
+ columnVisibility: { hiddenColIds: r }
90
+ } : e;
91
+ }
92
+ function he(e) {
93
+ let t = e.getAllGridColumns();
94
+ return t ? t.filter((e) => {
95
+ let t = e.getColDef().context;
96
+ return t && le(t.spec);
97
+ }).map((e) => e.getColId()) : [];
98
+ }
83
99
  function Y(e) {
84
100
  let t = e.columnVisibility, n = !t || t.hiddenColIds.length === 0 ? void 0 : e.columnVisibility;
85
101
  return {
@@ -88,13 +104,15 @@ var P = /* @__PURE__ */ g({
88
104
  };
89
105
  }
90
106
  let X = S(0);
91
- E(() => [B.value, W.value], ([t, n]) => {
107
+ E(() => [B.value, U.value], ([t, n]) => {
92
108
  if (!t || t.isDestroyed()) return;
93
- let r = J(t.getState());
94
- !e(n, {}) && !e(Y(n), Y(r)) && (V.value.initialState = n, ++X.value);
109
+ let r = q(t.getState());
110
+ !e(n, {}) && !e(Y(n), Y(r)) && (H = !0, V.value.initialState = n, ++X.value, oe(() => {
111
+ H = !1;
112
+ }));
95
113
  });
96
- let de = d(() => I.cellRendererSelector ?? null);
97
- E(() => [B.value, de.value], ([e, t]) => {
114
+ let ge = d(() => I.cellRendererSelector ?? null);
115
+ E(() => [B.value, ge.value], ([e, t]) => {
98
116
  !e || e.isDestroyed() || e.setGridOption("defaultColDef", {
99
117
  ...V.value.defaultColDef,
100
118
  cellRendererSelector: t ?? void 0
@@ -176,11 +194,11 @@ var P = /* @__PURE__ */ g({
176
194
  let i = $.value;
177
195
  u({
178
196
  generation: $,
179
- pfDriver: se().pFrameDriver,
197
+ pfDriver: ce().pFrameDriver,
180
198
  model: n.model,
181
199
  sheets: n.sheets ?? [],
182
200
  dataRenderedTracker: z,
183
- hiddenColIds: W.value.columnVisibility?.hiddenColIds,
201
+ hiddenColIds: U.value.columnVisibility?.hiddenColIds,
184
202
  cellButtonAxisParams: {
185
203
  showCellButtonForAxisId: I.showCellButtonForAxisId,
186
204
  cellButtonInvokeRowsOnDoubleClick: I.cellButtonInvokeRowsOnDoubleClick,
@@ -265,18 +283,18 @@ var P = /* @__PURE__ */ g({
265
283
  }, null, 8, ["api"])) : p("", !0),
266
284
  g.disableFiltersPanel ? p("", !0) : (x(), f(o, {
267
285
  key: 1,
268
- modelValue: w(K),
269
- "onUpdate:modelValue": t[0] ||= (e) => v(K) ? K.value = e : null,
270
- columns: w(H)
286
+ modelValue: w(G),
287
+ "onUpdate:modelValue": t[0] ||= (e) => v(G) ? G.value = e : null,
288
+ columns: w(fe)
271
289
  }, null, 8, ["modelValue", "columns"])),
272
290
  w(B) && g.showExportButton ? (x(), f(r, {
273
291
  key: 2,
274
292
  api: w(B)
275
293
  }, null, 8, ["api"])) : p("", !0),
276
294
  h(c, {
277
- modelValue: w(G),
278
- "onUpdate:modelValue": t[1] ||= (e) => v(G) ? G.value = e : null,
279
- settings: ue.value
295
+ modelValue: w(W),
296
+ "onUpdate:modelValue": t[1] ||= (e) => v(W) ? W.value = e : null,
297
+ settings: me.value
280
298
  }, m({ _: 2 }, [e.$slots["before-sheets"] ? {
281
299
  name: "before",
282
300
  fn: O(() => [C(e.$slots, "before-sheets")]),
@@ -287,10 +305,10 @@ var P = /* @__PURE__ */ g({
287
305
  key: "1"
288
306
  } : void 0]), 1032, ["modelValue", "settings"]),
289
307
  h(w(s), {
290
- modelValue: w(q),
291
- "onUpdate:modelValue": t[2] ||= (e) => v(q) ? q.value = e : null
308
+ modelValue: w(K),
309
+ "onUpdate:modelValue": t[2] ||= (e) => v(K) ? K.value = e : null
292
310
  }, null, 8, ["modelValue"]),
293
- (x(), f(w(ce), {
311
+ (x(), f(w(ue), {
294
312
  key: X.value,
295
313
  theme: w(n),
296
314
  class: b(e.$style.grid),
@@ -1 +1 @@
1
- {"version":3,"file":"PlAgDataTableV2.vue_vue_type_script_setup_true_lang.js","names":["$style","$slots"],"sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { promiseTimeout, isJsonEqual } from \"@milaboratories/helpers\";\nimport type {\n AxisId,\n PlDataTableGridStateCore,\n PlDataTableStateV2,\n PlSelectionModel,\n PlTableColumnIdJson,\n PTableKey,\n} from \"@platforma-sdk/model\";\nimport {\n getRawPlatformaInstance,\n createPlSelectionModel,\n matchAxisId,\n getAxisId,\n canonicalizeJson,\n isAbortError,\n} from \"@platforma-sdk/model\";\nimport type { CellRendererSelectorFunc, GridApi, GridState } from \"ag-grid-enterprise\";\nimport { AgGridVue } from \"ag-grid-vue3\";\nimport { computed, effectScope, ref, toRefs, watch, watchEffect } from \"vue\";\nimport { AgGridTheme } from \"../../aggrid\";\nimport PlAgCsvExporter from \"../PlAgCsvExporter/PlAgCsvExporter.vue\";\nimport { PlAgGridColumnManager } from \"../PlAgGridColumnManager\";\nimport PlTableFiltersV2 from \"../PlTableFilters/PlTableFiltersV2.vue\";\nimport { PlTableFastSearch } from \"../PlTableFastSearch\";\nimport PlAgDataTableSheets from \"./PlAgDataTableSheets.vue\";\nimport PlAgRowCount from \"./PlAgRowCount.vue\";\nimport { DeferredCircular, ensureNodeVisible } from \"./sources/focus-row\";\nimport { PlAgDataTableRowNumberColId } from \"./sources/row-number\";\nimport type { PlAgCellButtonAxisParams } from \"./sources/table-source-v2\";\nimport { calculateGridOptions } from \"./sources/table-source-v2\";\nimport { useTableState } from \"./sources/table-state-v2\";\nimport type {\n PlAgDataTableV2Controller,\n PlAgDataTableV2Row,\n PlAgOverlayLoadingParams,\n PlAgOverlayNoRowsParams,\n PlDataTableSettingsV2,\n PlDataTableSheetsSettings,\n PlTableRowId,\n} from \"./types\";\nimport { useFilterableColumns } from \"./compositions/useFilterableColumns\";\nimport { useGrid } from \"./compositions/useGrid\";\n\nconst tableState = defineModel<PlDataTableStateV2>({\n required: true,\n});\n/** Warning: selection model value updates are ignored, use updateSelection instead */\nconst selection = defineModel<PlSelectionModel>(\"selection\");\nconst props = defineProps<{\n /** Required component settings */\n settings: Readonly<PlDataTableSettingsV2>;\n\n /**\n * The disableColumnsPanel prop controls the display of a button that activates\n * the columns management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableColumnsPanel?: boolean;\n\n /**\n * The disableFiltersPanel prop controls the display of a button that activates\n * the filters management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableFiltersPanel?: boolean;\n\n /**\n * The showExportButton prop controls the display of a button that allows\n * to export table data in CSV format. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n showExportButton?: boolean;\n\n /**\n * The AxisId property is used to configure and display the PlAgTextAndButtonCell component\n */\n showCellButtonForAxisId?: AxisId;\n\n /**\n * If cellButtonInvokeRowsOnDoubleClick = true, clicking a button inside the row\n * triggers the doubleClick event for the entire row.\n *\n * If cellButtonInvokeRowsOnDoubleClick = false, the doubleClick event for the row\n * is not triggered, but will triggered cellButtonClicked event with (key: PTableRowKey) argument.\n */\n cellButtonInvokeRowsOnDoubleClick?: boolean;\n\n /** @see {@link PlAgOverlayLoadingParams.loadingText} */\n loadingText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.runningText} */\n runningText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.notReadyText} */\n notReadyText?: string;\n\n /** @see {@link PlAgOverlayNoRowsParams.text} */\n noRowsText?: string;\n\n /**\n * Callback to override the default renderer for a given cell.\n * @see https://www.ag-grid.com/vue-data-grid/component-cell-renderer/#dynamic-component-selection\n */\n cellRendererSelector?: CellRendererSelectorFunc<PlAgDataTableV2Row>;\n}>();\nconst { settings } = toRefs(props);\nconst emit = defineEmits<{\n rowDoubleClicked: [key?: PTableKey];\n cellButtonClicked: [key?: PTableKey];\n newDataRendered: [];\n}>();\n\nconst dataRenderedTracker = new DeferredCircular<GridApi<PlAgDataTableV2Row>>();\nconst { gridApi, gridOptions } = useGrid({\n selection: selection.value,\n noRowsText: props.noRowsText,\n runningText: props.runningText,\n loadingText: props.loadingText,\n notReadyText: props.notReadyText,\n cellRendererSelector: props.cellRendererSelector,\n});\ngridOptions.value.onGridPreDestroyed = (event) => {\n gridOptions.value.initialState = gridState.value = makePartialState(event.api.getState());\n gridApi.value = null;\n};\ngridOptions.value.onRowDoubleClicked = (event) => {\n if (event.data && event.data.axesKey) emit(\"rowDoubleClicked\", event.data.axesKey);\n};\ngridOptions.value.onStateUpdated = (event) => {\n let partialState = makePartialState(event.state);\n // AG Grid omits columnVisibility when no columns are hidden. If we previously had\n // hidden columns and now get undefined, treat as \"all visible\" so we don't revert to default.\n const hadHiddenCols = gridState.value.columnVisibility?.hiddenColIds !== undefined;\n if (partialState.columnVisibility === undefined && hadHiddenCols) {\n partialState = { ...partialState, columnVisibility: { hiddenColIds: [] } };\n }\n // We have to keep initialState synchronized with gridState for gridState recovery after key updating.\n gridOptions.value.initialState = gridState.value = partialState;\n\n if (!isJsonEqual(event.sources, [\"columnSizing\"])) {\n event.api.autoSizeColumns(\n event.api\n .getAllDisplayedColumns()\n .filter((column) => column.getColId() !== PlAgDataTableRowNumberColId),\n );\n }\n};\n\nconst [filterableColumns, visibleFilterableColumns] = useFilterableColumns(\n () => settings.value.sourceId,\n () => gridOptions.value.columnDefs ?? null,\n);\nconst { gridState, sheetsState, filtersState, searchString } = useTableState(\n tableState,\n settings,\n visibleFilterableColumns,\n);\nconst sheetsSettings = computed<PlDataTableSheetsSettings>(() => {\n const settingsCopy = { ...settings.value };\n return settingsCopy.sourceId !== null\n ? {\n sheets: settingsCopy.sheets ?? [],\n cachedState: [...sheetsState.value],\n }\n : {\n sheets: [],\n cachedState: [],\n };\n});\ngridOptions.value.initialState = gridState.value;\n\n// Restore proper types erased by AgGrid\nfunction makePartialState(state: GridState): PlDataTableGridStateCore {\n return {\n columnOrder: state.columnOrder as\n | {\n orderedColIds: PlTableColumnIdJson[];\n }\n | undefined,\n sort: state.sort as\n | {\n sortModel: {\n colId: PlTableColumnIdJson;\n sort: \"asc\" | \"desc\";\n }[];\n }\n | undefined,\n columnVisibility: state.columnVisibility as\n | {\n hiddenColIds: PlTableColumnIdJson[];\n }\n | undefined,\n };\n}\n\n// Normalize columnVisibility for comparison: undefined and { hiddenColIds: [] } are equivalent.\nfunction stateForReloadCompare(state: PlDataTableGridStateCore): PlDataTableGridStateCore {\n const cv = state.columnVisibility;\n const normalizedCv = !cv || cv.hiddenColIds.length === 0 ? undefined : state.columnVisibility;\n return { ...state, columnVisibility: normalizedCv };\n}\n\n// Reload AgGrid when new state arrives from server\nconst reloadKey = ref(0);\nwatch(\n () => [gridApi.value, gridState.value] as const,\n ([gridApi, gridState]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n const selfState = makePartialState(gridApi.getState());\n if (\n !isJsonEqual(gridState, {}) &&\n !isJsonEqual(stateForReloadCompare(gridState), stateForReloadCompare(selfState))\n ) {\n gridOptions.value.initialState = gridState;\n ++reloadKey.value;\n }\n },\n);\n\n// Make cellRendererSelector reactive\nconst cellRendererSelector = computed(() => props.cellRendererSelector ?? null);\nwatch(\n () => [gridApi.value, cellRendererSelector.value] as const,\n ([gridApi, cellRendererSelector]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.setGridOption(\"defaultColDef\", {\n ...gridOptions.value.defaultColDef,\n cellRendererSelector: cellRendererSelector ?? undefined,\n });\n },\n);\n\ndefineExpose<PlAgDataTableV2Controller>({\n focusRow: async (rowKey) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n return ensureNodeVisible(gridApi, (row) => isJsonEqual(row.data?.axesKey, rowKey));\n },\n updateSelection: async ({ axesSpec, selectedKeys }) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n const axes = selection.value?.axesSpec;\n if (!axes || axes.length !== axesSpec.length) return false;\n\n const mapping = axesSpec.map((spec) => {\n const id = getAxisId(spec);\n return axes.findIndex((axis) => matchAxisId(axis, id));\n });\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) return false;\n\n const selectedNodes = selectedKeys.map((key) =>\n canonicalizeJson<PlTableRowId>(mapping.map((index) => key[index])),\n );\n const oldSelectedKeys = gridApi.getServerSideSelectionState()?.toggledNodes ?? [];\n if (!isJsonEqual(oldSelectedKeys, selectedNodes)) {\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes,\n });\n\n // wait for `onSelectionChanged` to update `selection` model\n const scope = effectScope();\n const { resolve, promise } = Promise.withResolvers();\n scope.run(() => watch(selection, resolve, { once: true }));\n try {\n await promiseTimeout(promise, 500);\n } catch {\n return false;\n } finally {\n scope.stop();\n }\n }\n return true;\n },\n});\n\n// Update AgGrid when settings change\nconst defaultSelection = createPlSelectionModel();\nlet oldSettings: PlDataTableSettingsV2 | null = null;\nconst generation = ref(0);\nwatch(\n () => [gridApi.value, settings.value] as const,\n ([gridApi, settings]) => {\n // Wait for AgGrid reinitialization, gridApi will eventually become initialized\n if (!gridApi || gridApi.isDestroyed()) return;\n // Verify that this is not a false watch trigger\n if (isJsonEqual(settings, oldSettings)) return;\n ++generation.value;\n try {\n // Hide no rows overlay if it is shown, or else loading overlay will not be shown\n gridApi.hideOverlay();\n dataRenderedTracker.reset();\n\n // No data source selected -> reset state to default\n if (settings.sourceId === null) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: settings.pending ? \"running\" : \"not-ready\",\n } satisfies PlAgOverlayLoadingParams,\n columnDefs: undefined,\n serverSideDatasource: undefined,\n });\n if (selection.value) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n return;\n }\n\n // Data source changed -> show full page loader, clear selection\n if (settings.sourceId !== oldSettings?.sourceId) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: \"loading\",\n } satisfies PlAgOverlayLoadingParams,\n });\n if (selection.value && oldSettings?.sourceId) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n }\n\n // Model updated -> show skeletons instead of data\n const sourceChanged =\n settings.model?.sourceId && settings.model.sourceId !== settings.sourceId;\n if (!settings.model || sourceChanged) {\n const state = gridApi.getServerSideGroupLevelState();\n const rowCount = !sourceChanged && state.length > 0 ? state[0].rowCount : 1;\n return gridApi.updateGridOptions({\n serverSideDatasource: {\n getRows: (params) => {\n params.success({ rowData: [], rowCount });\n },\n },\n });\n }\n\n // Model ready -> calculate new state\n const stateGeneration = generation.value;\n calculateGridOptions({\n generation,\n pfDriver: getRawPlatformaInstance().pFrameDriver,\n model: settings.model,\n sheets: settings.sheets ?? [],\n dataRenderedTracker,\n hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,\n cellButtonAxisParams: {\n showCellButtonForAxisId: props.showCellButtonForAxisId,\n cellButtonInvokeRowsOnDoubleClick: props.cellButtonInvokeRowsOnDoubleClick,\n trigger: (key?: PTableKey) => emit(\"cellButtonClicked\", key),\n } satisfies PlAgCellButtonAxisParams,\n })\n .then((result) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n const { axesSpec, ...options } = result;\n gridApi.updateGridOptions({\n ...options,\n });\n if (selection.value) {\n // Update selection if axesSpec changed, as order of axes may have changed and so we need to remap selected keys\n const { axesSpec: oldAxesSpec, selectedKeys: oldSelectedKeys } = selection.value;\n if (!isJsonEqual(oldAxesSpec, axesSpec)) {\n if (!oldAxesSpec || axesSpec.length !== oldAxesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const mapping = oldAxesSpec\n .map(getAxisId)\n .map((id) => axesSpec.findIndex((axis) => matchAxisId(axis, id)));\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const selectedNodes = oldSelectedKeys.map((key) =>\n mapping.map((index) => key[index]),\n );\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: selectedNodes };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes.map((key) => canonicalizeJson<PlTableRowId>(key)),\n });\n }\n }\n })\n .catch((error: unknown) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n if (isAbortError(error)) return;\n console.trace(error);\n })\n .finally(() => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n gridApi.updateGridOptions({\n loading: false,\n });\n });\n dataRenderedTracker.promise.then(() => emit(\"newDataRendered\"));\n } catch (error: unknown) {\n console.trace(error);\n } finally {\n oldSettings = settings;\n }\n },\n);\n\nwatch(\n () => ({\n gridApi: gridApi.value,\n loadingText: props.loadingText,\n runningText: props.runningText,\n notReadyText: props.notReadyText,\n noRowsText: props.noRowsText,\n }),\n ({ gridApi, loadingText, runningText, notReadyText, noRowsText }) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.updateGridOptions({\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n loadingText,\n runningText,\n notReadyText,\n },\n noRowsOverlayComponentParams: {\n ...gridOptions.value.noRowsOverlayComponentParams,\n text: noRowsText,\n },\n });\n },\n);\n\nwatchEffect(() => {\n if (!gridApi.value || gridApi.value?.isDestroyed()) return;\n gridApi.value.updateGridOptions({\n statusBar: gridOptions.value.loading\n ? undefined\n : {\n statusPanels: [{ statusPanel: PlAgRowCount, align: \"left\" }],\n },\n });\n});\n</script>\n\n<template>\n <div :class=\"$style.container\">\n <PlAgGridColumnManager v-if=\"gridApi && !disableColumnsPanel\" :api=\"gridApi\" />\n <PlTableFiltersV2\n v-if=\"!disableFiltersPanel\"\n v-model=\"filtersState\"\n :columns=\"filterableColumns\"\n />\n <PlAgCsvExporter v-if=\"gridApi && showExportButton\" :api=\"gridApi\" />\n <PlAgDataTableSheets v-model=\"sheetsState\" :settings=\"sheetsSettings\">\n <template v-if=\"$slots['before-sheets']\" #before>\n <slot name=\"before-sheets\" />\n </template>\n <template v-if=\"$slots['after-sheets']\" #after>\n <slot name=\"after-sheets\" />\n </template>\n </PlAgDataTableSheets>\n <PlTableFastSearch v-model=\"searchString\" />\n <AgGridVue\n :key=\"reloadKey\"\n :theme=\"AgGridTheme\"\n :class=\"$style.grid\"\n :grid-options=\"gridOptions\"\n />\n </div>\n</template>\n\n<style lang=\"css\" module>\n.container {\n display: flex;\n flex-direction: column;\n height: 100%;\n gap: 12px;\n}\n\n.grid {\n flex: 1;\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PlAgDataTableV2.vue_vue_type_script_setup_true_lang.js","names":["$style","$slots"],"sources":["../../../src/components/PlAgDataTable/PlAgDataTableV2.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { promiseTimeout, isJsonEqual } from \"@milaboratories/helpers\";\nimport type {\n AxisId,\n PlDataTableGridStateCore,\n PlDataTableStateV2,\n PlSelectionModel,\n PlTableColumnIdJson,\n PTableColumnSpec,\n PTableKey,\n} from \"@platforma-sdk/model\";\nimport {\n getRawPlatformaInstance,\n createPlSelectionModel,\n matchAxisId,\n getAxisId,\n canonicalizeJson,\n isAbortError,\n isColumnOptional,\n} from \"@platforma-sdk/model\";\nimport type { CellRendererSelectorFunc, GridApi, GridState } from \"ag-grid-enterprise\";\nimport { AgGridVue } from \"ag-grid-vue3\";\nimport { computed, effectScope, nextTick, ref, toRefs, watch, watchEffect } from \"vue\";\nimport { AgGridTheme } from \"../../aggrid\";\nimport PlAgCsvExporter from \"../PlAgCsvExporter/PlAgCsvExporter.vue\";\nimport { PlAgGridColumnManager } from \"../PlAgGridColumnManager\";\nimport PlTableFiltersV2 from \"../PlTableFilters/PlTableFiltersV2.vue\";\nimport { PlTableFastSearch } from \"../PlTableFastSearch\";\nimport PlAgDataTableSheets from \"./PlAgDataTableSheets.vue\";\nimport PlAgRowCount from \"./PlAgRowCount.vue\";\nimport { DeferredCircular, ensureNodeVisible } from \"./sources/focus-row\";\nimport { PlAgDataTableRowNumberColId } from \"./sources/row-number\";\nimport type { PlAgCellButtonAxisParams } from \"./sources/table-source-v2\";\nimport { calculateGridOptions } from \"./sources/table-source-v2\";\nimport { useTableState } from \"./sources/table-state-v2\";\nimport type {\n PlAgDataTableV2Controller,\n PlAgDataTableV2Row,\n PlAgOverlayLoadingParams,\n PlAgOverlayNoRowsParams,\n PlDataTableSettingsV2,\n PlDataTableSheetsSettings,\n PlTableRowId,\n} from \"./types\";\nimport { useFilterableColumns } from \"./compositions/useFilterableColumns\";\nimport { useGrid } from \"./compositions/useGrid\";\n\nconst tableState = defineModel<PlDataTableStateV2>({\n required: true,\n});\n/** Warning: selection model value updates are ignored, use updateSelection instead */\nconst selection = defineModel<PlSelectionModel>(\"selection\");\nconst props = defineProps<{\n /** Required component settings */\n settings: Readonly<PlDataTableSettingsV2>;\n\n /**\n * The disableColumnsPanel prop controls the display of a button that activates\n * the columns management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableColumnsPanel?: boolean;\n\n /**\n * The disableFiltersPanel prop controls the display of a button that activates\n * the filters management panel in the table. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n disableFiltersPanel?: boolean;\n\n /**\n * The showExportButton prop controls the display of a button that allows\n * to export table data in CSV format. To make the button functional\n * and visible, you must also include the PlAgDataTableToolsPanel component in your layout.\n * This component serves as the target for teleporting the button.\n */\n showExportButton?: boolean;\n\n /**\n * The AxisId property is used to configure and display the PlAgTextAndButtonCell component\n */\n showCellButtonForAxisId?: AxisId;\n\n /**\n * If cellButtonInvokeRowsOnDoubleClick = true, clicking a button inside the row\n * triggers the doubleClick event for the entire row.\n *\n * If cellButtonInvokeRowsOnDoubleClick = false, the doubleClick event for the row\n * is not triggered, but will triggered cellButtonClicked event with (key: PTableRowKey) argument.\n */\n cellButtonInvokeRowsOnDoubleClick?: boolean;\n\n /** @see {@link PlAgOverlayLoadingParams.loadingText} */\n loadingText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.runningText} */\n runningText?: string;\n\n /** @see {@link PlAgOverlayLoadingParams.notReadyText} */\n notReadyText?: string;\n\n /** @see {@link PlAgOverlayNoRowsParams.text} */\n noRowsText?: string;\n\n /**\n * Callback to override the default renderer for a given cell.\n * @see https://www.ag-grid.com/vue-data-grid/component-cell-renderer/#dynamic-component-selection\n */\n cellRendererSelector?: CellRendererSelectorFunc<PlAgDataTableV2Row>;\n}>();\nconst { settings } = toRefs(props);\nconst emit = defineEmits<{\n rowDoubleClicked: [key?: PTableKey];\n cellButtonClicked: [key?: PTableKey];\n newDataRendered: [];\n}>();\n\nconst dataRenderedTracker = new DeferredCircular<GridApi<PlAgDataTableV2Row>>();\nconst { gridApi, gridOptions } = useGrid({\n selection: selection.value,\n noRowsText: props.noRowsText,\n runningText: props.runningText,\n loadingText: props.loadingText,\n notReadyText: props.notReadyText,\n cellRendererSelector: props.cellRendererSelector,\n});\nlet isReloading = false;\ngridOptions.value.onGridPreDestroyed = (event) => {\n if (!isReloading) {\n gridOptions.value.initialState = gridState.value = normalizeColumnVisibility(\n makePartialState(event.api.getState()),\n gridState.value,\n event.api,\n );\n }\n gridApi.value = null;\n};\ngridOptions.value.onRowDoubleClicked = (event) => {\n if (event.data && event.data.axesKey) emit(\"rowDoubleClicked\", event.data.axesKey);\n};\ngridOptions.value.onStateUpdated = (event) => {\n const partialState = normalizeColumnVisibility(\n makePartialState(event.state),\n gridState.value,\n event.api,\n );\n // We have to keep initialState synchronized with gridState for gridState recovery after key updating.\n gridOptions.value.initialState = gridState.value = partialState;\n\n if (!isJsonEqual(event.sources, [\"columnSizing\"])) {\n event.api.autoSizeColumns(\n event.api\n .getAllDisplayedColumns()\n .filter((column) => column.getColId() !== PlAgDataTableRowNumberColId),\n );\n }\n};\n\nconst [filterableColumns, visibleFilterableColumns] = useFilterableColumns(\n () => settings.value.sourceId,\n () => gridOptions.value.columnDefs ?? null,\n);\nconst { gridState, sheetsState, filtersState, searchString } = useTableState(\n tableState,\n settings,\n visibleFilterableColumns,\n);\nconst sheetsSettings = computed<PlDataTableSheetsSettings>(() => {\n const settingsCopy = { ...settings.value };\n return settingsCopy.sourceId !== null\n ? {\n sheets: settingsCopy.sheets ?? [],\n cachedState: [...sheetsState.value],\n }\n : {\n sheets: [],\n cachedState: [],\n };\n});\ngridOptions.value.initialState = gridState.value;\n\n// Restore proper types erased by AgGrid\nfunction makePartialState(state: GridState): PlDataTableGridStateCore {\n return {\n columnOrder: state.columnOrder as\n | {\n orderedColIds: PlTableColumnIdJson[];\n }\n | undefined,\n sort: state.sort as\n | {\n sortModel: {\n colId: PlTableColumnIdJson;\n sort: \"asc\" | \"desc\";\n }[];\n }\n | undefined,\n columnVisibility: state.columnVisibility as\n | {\n hiddenColIds: PlTableColumnIdJson[];\n }\n | undefined,\n };\n}\n\n// AG Grid returns columnVisibility: undefined when all columns are visible.\n// We need to distinguish \"no state yet\" (use isColumnOptional defaults) from\n// \"user explicitly showed all columns\" (store []). This function normalizes\n// the undefined from AG Grid based on the previous state.\nfunction normalizeColumnVisibility(\n partialState: PlDataTableGridStateCore,\n prevState: PlDataTableGridStateCore,\n api: GridApi<PlAgDataTableV2Row>,\n): PlDataTableGridStateCore {\n if (partialState.columnVisibility !== undefined) return partialState;\n\n if (prevState.columnVisibility !== undefined) {\n // Had explicit visibility state before → user made all columns visible → store [].\n return { ...partialState, columnVisibility: { hiddenColIds: [] } };\n }\n\n // No previous explicit state → compute defaults from current columns\n // to replicate: hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec)\n const defaultHidden = getDefaultHiddenColIds(api);\n if (defaultHidden.length > 0) {\n return { ...partialState, columnVisibility: { hiddenColIds: defaultHidden } };\n }\n\n return partialState;\n}\n\nfunction getDefaultHiddenColIds(api: GridApi<PlAgDataTableV2Row>): PlTableColumnIdJson[] {\n const cols = api.getAllGridColumns();\n if (!cols) return [];\n return cols\n .filter((col) => {\n const spec = col.getColDef().context as PTableColumnSpec | undefined;\n return spec && isColumnOptional(spec.spec);\n })\n .map((col) => col.getColId() as PlTableColumnIdJson);\n}\n\n// Normalize columnVisibility for comparison: undefined and { hiddenColIds: [] } are equivalent.\nfunction stateForReloadCompare(state: PlDataTableGridStateCore): PlDataTableGridStateCore {\n const cv = state.columnVisibility;\n const normalizedCv = !cv || cv.hiddenColIds.length === 0 ? undefined : state.columnVisibility;\n return { ...state, columnVisibility: normalizedCv };\n}\n\n// Reload AgGrid when new state arrives from server\nconst reloadKey = ref(0);\nwatch(\n () => [gridApi.value, gridState.value] as const,\n ([gridApi, gridState]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n const selfState = makePartialState(gridApi.getState());\n if (\n !isJsonEqual(gridState, {}) &&\n !isJsonEqual(stateForReloadCompare(gridState), stateForReloadCompare(selfState))\n ) {\n isReloading = true;\n gridOptions.value.initialState = gridState;\n ++reloadKey.value;\n nextTick(() => {\n isReloading = false;\n });\n }\n },\n);\n\n// Make cellRendererSelector reactive\nconst cellRendererSelector = computed(() => props.cellRendererSelector ?? null);\nwatch(\n () => [gridApi.value, cellRendererSelector.value] as const,\n ([gridApi, cellRendererSelector]) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.setGridOption(\"defaultColDef\", {\n ...gridOptions.value.defaultColDef,\n cellRendererSelector: cellRendererSelector ?? undefined,\n });\n },\n);\n\ndefineExpose<PlAgDataTableV2Controller>({\n focusRow: async (rowKey) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n return ensureNodeVisible(gridApi, (row) => isJsonEqual(row.data?.axesKey, rowKey));\n },\n updateSelection: async ({ axesSpec, selectedKeys }) => {\n const gridApi = await dataRenderedTracker.promise;\n if (gridApi.isDestroyed()) return false;\n\n const axes = selection.value?.axesSpec;\n if (!axes || axes.length !== axesSpec.length) return false;\n\n const mapping = axesSpec.map((spec) => {\n const id = getAxisId(spec);\n return axes.findIndex((axis) => matchAxisId(axis, id));\n });\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) return false;\n\n const selectedNodes = selectedKeys.map((key) =>\n canonicalizeJson<PlTableRowId>(mapping.map((index) => key[index])),\n );\n const oldSelectedKeys = gridApi.getServerSideSelectionState()?.toggledNodes ?? [];\n if (!isJsonEqual(oldSelectedKeys, selectedNodes)) {\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes,\n });\n\n // wait for `onSelectionChanged` to update `selection` model\n const scope = effectScope();\n const { resolve, promise } = Promise.withResolvers();\n scope.run(() => watch(selection, resolve, { once: true }));\n try {\n await promiseTimeout(promise, 500);\n } catch {\n return false;\n } finally {\n scope.stop();\n }\n }\n return true;\n },\n});\n\n// Update AgGrid when settings change\nconst defaultSelection = createPlSelectionModel();\nlet oldSettings: PlDataTableSettingsV2 | null = null;\nconst generation = ref(0);\nwatch(\n () => [gridApi.value, settings.value] as const,\n ([gridApi, settings]) => {\n // Wait for AgGrid reinitialization, gridApi will eventually become initialized\n if (!gridApi || gridApi.isDestroyed()) return;\n // Verify that this is not a false watch trigger\n if (isJsonEqual(settings, oldSettings)) return;\n ++generation.value;\n try {\n // Hide no rows overlay if it is shown, or else loading overlay will not be shown\n gridApi.hideOverlay();\n dataRenderedTracker.reset();\n\n // No data source selected -> reset state to default\n if (settings.sourceId === null) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: settings.pending ? \"running\" : \"not-ready\",\n } satisfies PlAgOverlayLoadingParams,\n columnDefs: undefined,\n serverSideDatasource: undefined,\n });\n if (selection.value) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n return;\n }\n\n // Data source changed -> show full page loader, clear selection\n if (settings.sourceId !== oldSettings?.sourceId) {\n gridApi.updateGridOptions({\n loading: true,\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n variant: \"loading\",\n } satisfies PlAgOverlayLoadingParams,\n });\n if (selection.value && oldSettings?.sourceId) {\n if (selection.value && !isJsonEqual(selection.value, defaultSelection)) {\n selection.value = createPlSelectionModel();\n }\n gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n }\n\n // Model updated -> show skeletons instead of data\n const sourceChanged =\n settings.model?.sourceId && settings.model.sourceId !== settings.sourceId;\n if (!settings.model || sourceChanged) {\n const state = gridApi.getServerSideGroupLevelState();\n const rowCount = !sourceChanged && state.length > 0 ? state[0].rowCount : 1;\n return gridApi.updateGridOptions({\n serverSideDatasource: {\n getRows: (params) => {\n params.success({ rowData: [], rowCount });\n },\n },\n });\n }\n\n // Model ready -> calculate new state\n const stateGeneration = generation.value;\n calculateGridOptions({\n generation,\n pfDriver: getRawPlatformaInstance().pFrameDriver,\n model: settings.model,\n sheets: settings.sheets ?? [],\n dataRenderedTracker,\n hiddenColIds: gridState.value.columnVisibility?.hiddenColIds,\n cellButtonAxisParams: {\n showCellButtonForAxisId: props.showCellButtonForAxisId,\n cellButtonInvokeRowsOnDoubleClick: props.cellButtonInvokeRowsOnDoubleClick,\n trigger: (key?: PTableKey) => emit(\"cellButtonClicked\", key),\n } satisfies PlAgCellButtonAxisParams,\n })\n .then((result) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n const { axesSpec, ...options } = result;\n gridApi.updateGridOptions({\n ...options,\n });\n if (selection.value) {\n // Update selection if axesSpec changed, as order of axes may have changed and so we need to remap selected keys\n const { axesSpec: oldAxesSpec, selectedKeys: oldSelectedKeys } = selection.value;\n if (!isJsonEqual(oldAxesSpec, axesSpec)) {\n if (!oldAxesSpec || axesSpec.length !== oldAxesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const mapping = oldAxesSpec\n .map(getAxisId)\n .map((id) => axesSpec.findIndex((axis) => matchAxisId(axis, id)));\n const mappingSet = new Set(mapping);\n if (mappingSet.has(-1) || mappingSet.size !== axesSpec.length) {\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: [] };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: [],\n });\n }\n\n const selectedNodes = oldSelectedKeys.map((key) =>\n mapping.map((index) => key[index]),\n );\n const newSelection: PlSelectionModel = { axesSpec, selectedKeys: selectedNodes };\n if (!isJsonEqual(selection.value, newSelection)) {\n selection.value = newSelection;\n }\n return gridApi.setServerSideSelectionState({\n selectAll: false,\n toggledNodes: selectedNodes.map((key) => canonicalizeJson<PlTableRowId>(key)),\n });\n }\n }\n })\n .catch((error: unknown) => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n if (isAbortError(error)) return;\n console.trace(error);\n })\n .finally(() => {\n if (gridApi.isDestroyed() || stateGeneration !== generation.value) return;\n gridApi.updateGridOptions({\n loading: false,\n });\n });\n dataRenderedTracker.promise.then(() => emit(\"newDataRendered\"));\n } catch (error: unknown) {\n console.trace(error);\n } finally {\n oldSettings = settings;\n }\n },\n);\n\nwatch(\n () => ({\n gridApi: gridApi.value,\n loadingText: props.loadingText,\n runningText: props.runningText,\n notReadyText: props.notReadyText,\n noRowsText: props.noRowsText,\n }),\n ({ gridApi, loadingText, runningText, notReadyText, noRowsText }) => {\n if (!gridApi || gridApi.isDestroyed()) return;\n gridApi.updateGridOptions({\n loadingOverlayComponentParams: {\n ...gridOptions.value.loadingOverlayComponentParams,\n loadingText,\n runningText,\n notReadyText,\n },\n noRowsOverlayComponentParams: {\n ...gridOptions.value.noRowsOverlayComponentParams,\n text: noRowsText,\n },\n });\n },\n);\n\nwatchEffect(() => {\n if (!gridApi.value || gridApi.value?.isDestroyed()) return;\n gridApi.value.updateGridOptions({\n statusBar: gridOptions.value.loading\n ? undefined\n : {\n statusPanels: [{ statusPanel: PlAgRowCount, align: \"left\" }],\n },\n });\n});\n</script>\n\n<template>\n <div :class=\"$style.container\">\n <PlAgGridColumnManager v-if=\"gridApi && !disableColumnsPanel\" :api=\"gridApi\" />\n <PlTableFiltersV2\n v-if=\"!disableFiltersPanel\"\n v-model=\"filtersState\"\n :columns=\"filterableColumns\"\n />\n <PlAgCsvExporter v-if=\"gridApi && showExportButton\" :api=\"gridApi\" />\n <PlAgDataTableSheets v-model=\"sheetsState\" :settings=\"sheetsSettings\">\n <template v-if=\"$slots['before-sheets']\" #before>\n <slot name=\"before-sheets\" />\n </template>\n <template v-if=\"$slots['after-sheets']\" #after>\n <slot name=\"after-sheets\" />\n </template>\n </PlAgDataTableSheets>\n <PlTableFastSearch v-model=\"searchString\" />\n <AgGridVue\n :key=\"reloadKey\"\n :theme=\"AgGridTheme\"\n :class=\"$style.grid\"\n :grid-options=\"gridOptions\"\n />\n </div>\n</template>\n\n<style lang=\"css\" module>\n.container {\n display: flex;\n flex-direction: column;\n height: 100%;\n gap: 12px;\n}\n\n.grid {\n flex: 1;\n}\n</style>\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.58.9",
3
+ "version": "1.58.10",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -28,8 +28,8 @@
28
28
  "lru-cache": "^11.2.2",
29
29
  "vue": "^3.5.24",
30
30
  "zod": "~3.23.8",
31
- "@milaboratories/uikit": "2.10.38",
32
- "@platforma-sdk/model": "1.58.9"
31
+ "@platforma-sdk/model": "1.58.9",
32
+ "@milaboratories/uikit": "2.10.38"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@faker-js/faker": "^9.2.0",
@@ -43,10 +43,10 @@
43
43
  "typescript": "~5.9.3",
44
44
  "vite": "8.0.0-beta.15",
45
45
  "vitest": "^4.0.18",
46
- "@milaboratories/build-configs": "1.5.0",
46
+ "@milaboratories/ts-builder": "1.2.12",
47
47
  "@milaboratories/ts-configs": "1.2.1",
48
48
  "@milaboratories/helpers": "1.13.5",
49
- "@milaboratories/ts-builder": "1.2.12"
49
+ "@milaboratories/build-configs": "1.5.0"
50
50
  },
51
51
  "scripts": {
52
52
  "dev": "ts-builder serve --target browser-lib",
@@ -6,6 +6,7 @@ import type {
6
6
  PlDataTableStateV2,
7
7
  PlSelectionModel,
8
8
  PlTableColumnIdJson,
9
+ PTableColumnSpec,
9
10
  PTableKey,
10
11
  } from "@platforma-sdk/model";
11
12
  import {
@@ -15,10 +16,11 @@ import {
15
16
  getAxisId,
16
17
  canonicalizeJson,
17
18
  isAbortError,
19
+ isColumnOptional,
18
20
  } from "@platforma-sdk/model";
19
21
  import type { CellRendererSelectorFunc, GridApi, GridState } from "ag-grid-enterprise";
20
22
  import { AgGridVue } from "ag-grid-vue3";
21
- import { computed, effectScope, ref, toRefs, watch, watchEffect } from "vue";
23
+ import { computed, effectScope, nextTick, ref, toRefs, watch, watchEffect } from "vue";
22
24
  import { AgGridTheme } from "../../aggrid";
23
25
  import PlAgCsvExporter from "../PlAgCsvExporter/PlAgCsvExporter.vue";
24
26
  import { PlAgGridColumnManager } from "../PlAgGridColumnManager";
@@ -124,21 +126,26 @@ const { gridApi, gridOptions } = useGrid({
124
126
  notReadyText: props.notReadyText,
125
127
  cellRendererSelector: props.cellRendererSelector,
126
128
  });
129
+ let isReloading = false;
127
130
  gridOptions.value.onGridPreDestroyed = (event) => {
128
- gridOptions.value.initialState = gridState.value = makePartialState(event.api.getState());
131
+ if (!isReloading) {
132
+ gridOptions.value.initialState = gridState.value = normalizeColumnVisibility(
133
+ makePartialState(event.api.getState()),
134
+ gridState.value,
135
+ event.api,
136
+ );
137
+ }
129
138
  gridApi.value = null;
130
139
  };
131
140
  gridOptions.value.onRowDoubleClicked = (event) => {
132
141
  if (event.data && event.data.axesKey) emit("rowDoubleClicked", event.data.axesKey);
133
142
  };
134
143
  gridOptions.value.onStateUpdated = (event) => {
135
- let partialState = makePartialState(event.state);
136
- // AG Grid omits columnVisibility when no columns are hidden. If we previously had
137
- // hidden columns and now get undefined, treat as "all visible" so we don't revert to default.
138
- const hadHiddenCols = gridState.value.columnVisibility?.hiddenColIds !== undefined;
139
- if (partialState.columnVisibility === undefined && hadHiddenCols) {
140
- partialState = { ...partialState, columnVisibility: { hiddenColIds: [] } };
141
- }
144
+ const partialState = normalizeColumnVisibility(
145
+ makePartialState(event.state),
146
+ gridState.value,
147
+ event.api,
148
+ );
142
149
  // We have to keep initialState synchronized with gridState for gridState recovery after key updating.
143
150
  gridOptions.value.initialState = gridState.value = partialState;
144
151
 
@@ -198,6 +205,43 @@ function makePartialState(state: GridState): PlDataTableGridStateCore {
198
205
  };
199
206
  }
200
207
 
208
+ // AG Grid returns columnVisibility: undefined when all columns are visible.
209
+ // We need to distinguish "no state yet" (use isColumnOptional defaults) from
210
+ // "user explicitly showed all columns" (store []). This function normalizes
211
+ // the undefined from AG Grid based on the previous state.
212
+ function normalizeColumnVisibility(
213
+ partialState: PlDataTableGridStateCore,
214
+ prevState: PlDataTableGridStateCore,
215
+ api: GridApi<PlAgDataTableV2Row>,
216
+ ): PlDataTableGridStateCore {
217
+ if (partialState.columnVisibility !== undefined) return partialState;
218
+
219
+ if (prevState.columnVisibility !== undefined) {
220
+ // Had explicit visibility state before → user made all columns visible → store [].
221
+ return { ...partialState, columnVisibility: { hiddenColIds: [] } };
222
+ }
223
+
224
+ // No previous explicit state → compute defaults from current columns
225
+ // to replicate: hide: hiddenColIds?.includes(colId) ?? isColumnOptional(spec.spec)
226
+ const defaultHidden = getDefaultHiddenColIds(api);
227
+ if (defaultHidden.length > 0) {
228
+ return { ...partialState, columnVisibility: { hiddenColIds: defaultHidden } };
229
+ }
230
+
231
+ return partialState;
232
+ }
233
+
234
+ function getDefaultHiddenColIds(api: GridApi<PlAgDataTableV2Row>): PlTableColumnIdJson[] {
235
+ const cols = api.getAllGridColumns();
236
+ if (!cols) return [];
237
+ return cols
238
+ .filter((col) => {
239
+ const spec = col.getColDef().context as PTableColumnSpec | undefined;
240
+ return spec && isColumnOptional(spec.spec);
241
+ })
242
+ .map((col) => col.getColId() as PlTableColumnIdJson);
243
+ }
244
+
201
245
  // Normalize columnVisibility for comparison: undefined and { hiddenColIds: [] } are equivalent.
202
246
  function stateForReloadCompare(state: PlDataTableGridStateCore): PlDataTableGridStateCore {
203
247
  const cv = state.columnVisibility;
@@ -216,8 +260,12 @@ watch(
216
260
  !isJsonEqual(gridState, {}) &&
217
261
  !isJsonEqual(stateForReloadCompare(gridState), stateForReloadCompare(selfState))
218
262
  ) {
263
+ isReloading = true;
219
264
  gridOptions.value.initialState = gridState;
220
265
  ++reloadKey.value;
266
+ nextTick(() => {
267
+ isReloading = false;
268
+ });
221
269
  }
222
270
  },
223
271
  );