@platforma-sdk/ui-vue 1.67.0 → 1.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +19 -19
  2. package/.turbo/turbo-formatter$colon$check.log +2 -2
  3. package/.turbo/turbo-linter$colon$check.log +2 -2
  4. package/.turbo/turbo-types$colon$check.log +1 -1
  5. package/CHANGELOG.md +22 -0
  6. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.js.map +1 -1
  7. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue.d.ts +2 -0
  8. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue.d.ts.map +1 -1
  9. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue2.js +25 -17
  10. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue2.js.map +1 -1
  11. package/dist/components/PlAgCsvExporter/export-csv.d.ts +27 -1
  12. package/dist/components/PlAgCsvExporter/export-csv.d.ts.map +1 -1
  13. package/dist/components/PlAgCsvExporter/export-csv.js +31 -33
  14. package/dist/components/PlAgCsvExporter/export-csv.js.map +1 -1
  15. package/dist/components/PlAgDataTable/PlAgDataTableV2.js.map +1 -1
  16. package/dist/components/PlAgDataTable/PlAgDataTableV2.style.js.map +1 -1
  17. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  18. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +68 -64
  19. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
  20. package/dist/components/PlAgDataTable/compositions/useGrid.d.ts.map +1 -1
  21. package/dist/components/PlAgDataTable/compositions/useGrid.js +1 -1
  22. package/dist/components/PlAgDataTable/compositions/useGrid.js.map +1 -1
  23. package/dist/components/PlAgDataTable/sources/table-source-v2.js +2 -2
  24. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  25. package/dist/components/PlAnnotations/components/PlAnnotations.vue2.js.map +1 -1
  26. package/dist/composition/usePlugin.d.ts.map +1 -0
  27. package/dist/{usePlugin.js → composition/usePlugin.js} +2 -2
  28. package/dist/composition/usePlugin.js.map +1 -0
  29. package/dist/composition/useServices.d.ts +2 -0
  30. package/dist/composition/useServices.d.ts.map +1 -0
  31. package/dist/index.js +1 -1
  32. package/dist/internal/createAppV3.d.ts +2 -3
  33. package/dist/internal/createAppV3.d.ts.map +1 -1
  34. package/dist/internal/createAppV3.js +1 -1
  35. package/dist/internal/createAppV3.js.map +1 -1
  36. package/dist/internal/service_factories.d.ts +1 -0
  37. package/dist/internal/service_factories.d.ts.map +1 -1
  38. package/dist/internal/service_factories.js +2 -1
  39. package/dist/internal/service_factories.js.map +1 -1
  40. package/dist/lib.d.ts +2 -2
  41. package/dist/lib.d.ts.map +1 -1
  42. package/dist/lib.js +1 -1
  43. package/package.json +8 -8
  44. package/src/components/PlAgCsvExporter/PlAgCsvExporter.vue +16 -3
  45. package/src/components/PlAgCsvExporter/export-csv.ts +101 -64
  46. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +6 -1
  47. package/src/components/PlAgDataTable/compositions/useGrid.ts +4 -1
  48. package/src/components/PlAgDataTable/sources/table-source-v2.ts +2 -2
  49. package/src/{usePlugin.ts → composition/usePlugin.ts} +1 -1
  50. package/src/composition/useServices.ts +5 -0
  51. package/src/internal/createAppV3.ts +4 -4
  52. package/src/internal/service_factories.ts +1 -0
  53. package/src/lib.ts +2 -2
  54. package/dist/usePlugin.d.ts.map +0 -1
  55. package/dist/usePlugin.js.map +0 -1
  56. /package/dist/{usePlugin.d.ts → composition/usePlugin.d.ts} +0 -0
@@ -1,74 +1,111 @@
1
- import {
2
- type ColDef,
3
- type ColGroupDef,
4
- createGrid,
5
- type GridApi,
6
- type GridOptions,
7
- ServerSideRowModelModule,
8
- } from "ag-grid-enterprise";
1
+ import { type ColDef, type ColGroupDef, type GridApi } from "ag-grid-enterprise";
2
+ import type {
3
+ PTableHandle,
4
+ PTableDownloadFormat,
5
+ PTableColumnSpec,
6
+ PFrameSpecDriver,
7
+ WritePTableToFsResult,
8
+ } from "@platforma-sdk/model";
9
+ import { getPTableColumnId } from "@platforma-sdk/model";
10
+ import { isNil } from "es-toolkit";
11
+ import { Nil } from "@milaboratories/helpers";
12
+ import { getServices } from "../../internal/getServices";
9
13
 
10
- function createGridDiv(): HTMLDivElement {
11
- const div = document.createElement("div");
14
+ /** Options for the native CSV export path. */
15
+ export interface ExportOptions {
16
+ tableHandle: PTableHandle;
17
+ format: PTableDownloadFormat;
18
+ defaultFileName?: string;
19
+ }
12
20
 
13
- div.style.visibility = "hidden";
14
- div.style.position = "absolute";
21
+ /**
22
+ * CSV export via the platforma desktop runtime. Prompts for a save
23
+ * destination via the `Dialog` service, then streams the PTable to the
24
+ * chosen path via `PFrame.writePTableToFs`.
25
+ */
26
+ export async function exportCsv(
27
+ gridApi: GridApi,
28
+ nativeOptions: ExportOptions,
29
+ ): Promise<undefined | WritePTableToFsResult> {
30
+ const { dialog, pframe, pframeSpec } = getServices();
31
+ if (isNil(dialog)) {
32
+ throw new Error("dialog service is not available in the current environment");
33
+ }
34
+ if (isNil(pframe)) {
35
+ throw new Error("pframe service is not available");
36
+ }
37
+ if (isNil(pframeSpec)) {
38
+ throw new Error("pframeSpec service is not available");
39
+ }
15
40
 
16
- document.body.appendChild(div);
17
- return div;
18
- }
41
+ const specs = await pframe.getSpec(nativeOptions.tableHandle);
42
+ const columnIndices = collectVisibleColumnIndices(gridApi, specs, pframeSpec);
43
+ if (isNil(columnIndices)) {
44
+ return undefined;
45
+ }
19
46
 
20
- function destroyGridDiv(cellFake: HTMLDivElement) {
21
- document.body.removeChild(cellFake);
22
- }
47
+ const { canceled, path } = await dialog.showSaveDialog({
48
+ defaultFileName:
49
+ (nativeOptions.defaultFileName ?? `table_${formatTimestamp(new Date())}`) +
50
+ `.${nativeOptions.format}.gz`,
51
+ });
52
+ if (canceled || isNil(path)) {
53
+ return undefined;
54
+ }
23
55
 
24
- export async function exportCsv(gridApi: GridApi, completed: () => void) {
25
- const rowModel = gridApi.getGridOption("rowModelType");
26
- switch (rowModel) {
27
- case "clientSide": {
28
- gridApi.exportDataAsCsv();
29
- return completed();
30
- }
56
+ return pframe.writePTableToFs(nativeOptions.tableHandle, {
57
+ path,
58
+ format: nativeOptions.format,
59
+ columnIndices,
60
+ compression: { type: "gzip" },
61
+ });
62
+ }
31
63
 
32
- case "serverSide": {
33
- const state = gridApi.getServerSideGroupLevelState();
34
- if (state.length === 0 || state[0].rowCount <= state[0].cacheBlockSize!) {
35
- gridApi.exportDataAsCsv();
36
- return completed();
37
- }
64
+ function formatTimestamp(d: Date): string {
65
+ const pad = (n: number) => String(n).padStart(2, "0");
66
+ return (
67
+ `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${d.getFullYear()}` +
68
+ `_${pad(d.getHours())}-${pad(d.getMinutes())}-${pad(d.getSeconds())}`
69
+ );
70
+ }
38
71
 
39
- let exportStarted = false;
40
- const gridDiv = createGridDiv();
41
- const gridOptions: GridOptions = {
42
- rowModelType: "serverSide",
43
- columnDefs:
44
- gridApi
45
- .getColumnDefs()
46
- ?.filter((def: ColDef | ColGroupDef): def is ColDef => !("children" in def))
47
- .map((def) => ({
48
- headerName: def.headerName,
49
- field: def.field,
50
- valueFormatter: def.valueFormatter,
51
- valueGetter: def.valueGetter,
52
- })) ?? [],
53
- serverSideDatasource: gridApi.getGridOption("serverSideDatasource"),
54
- cacheBlockSize: state[0].rowCount,
55
- onModelUpdated: (event) => {
56
- const state = event.api.getServerSideGroupLevelState();
57
- if (!exportStarted && state.length > 0 && state[0].rowCount === state[0].cacheBlockSize) {
58
- exportStarted = true;
59
- event.api.exportDataAsCsv();
60
- destroyGridDiv(gridDiv);
61
- return completed();
62
- }
63
- },
64
- defaultCsvExportParams: gridApi.getGridOption("defaultCsvExportParams"),
65
- };
66
- return createGrid(gridDiv, gridOptions, { modules: [ServerSideRowModelModule] });
67
- }
72
+ /**
73
+ * Checks whether the native CSV export capability is available in the
74
+ * current platforma runtime environment. Both `Dialog` and `PFrame`
75
+ * services must be present — desktop-app wires them, web/preview do not.
76
+ */
77
+ export function isCsvExportAvailable(): boolean {
78
+ try {
79
+ const services = getServices();
80
+ return !isNil(services?.dialog) && !isNil(services?.pframe);
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
68
85
 
69
- default: {
70
- completed();
71
- throw Error(`exportCsv unsupported for rowModelType = ${rowModel}`);
72
- }
86
+ /**
87
+ * Collect unified column indices for visible (non-hidden) columns from the
88
+ * ag-grid column defs, remapped onto the provided PTable spec array so the
89
+ * indices match the current table handle (ColDef.field indices may be stale
90
+ * or diverge from the spec order).
91
+ */
92
+ export function collectVisibleColumnIndices(
93
+ gridApi: GridApi,
94
+ specs: PTableColumnSpec[],
95
+ pframeSpec: PFrameSpecDriver,
96
+ ): Nil | number[] {
97
+ const columnDefs = gridApi.getColumnDefs();
98
+ if (isNil(columnDefs)) {
99
+ return;
73
100
  }
101
+
102
+ return columnDefs
103
+ .filter(
104
+ (def: ColDef | ColGroupDef): def is ColDef =>
105
+ !("children" in def) && def.hide !== true && !isNil(def.context),
106
+ )
107
+ .map((def) =>
108
+ pframeSpec.findTableColumn(specs, getPTableColumnId(def.context as PTableColumnSpec)),
109
+ )
110
+ .filter((index): index is number => index !== -1);
74
111
  }
@@ -554,8 +554,13 @@ watchEffect(() => {
554
554
  @updateFilters="(v) => (filtersState = v)"
555
555
  @resetDefaultFilters="resetDefaultFilters"
556
556
  @updateDefaultFilters="(v) => (defaultFiltersState = v)"
557
+ v-model="filtersState"
558
+ />
559
+ <PlAgCsvExporter
560
+ v-if="gridApi && showExportButton"
561
+ :api="gridApi"
562
+ :table-handle="'model' in settings ? settings?.model?.visibleTableHandle : undefined"
557
563
  />
558
- <PlAgCsvExporter v-if="gridApi && showExportButton" :api="gridApi" />
559
564
  <PlAgDataTableSheets v-model="sheetsState" :settings="sheetsSettings">
560
565
  <template v-if="$slots['before-sheets']" #before>
561
566
  <slot name="before-sheets" />
@@ -81,11 +81,14 @@ export function useGrid({
81
81
  text: noRowsText,
82
82
  } satisfies PlAgOverlayNoRowsParams,
83
83
  defaultCsvExportParams: {
84
- allColumns: true,
85
84
  suppressQuotes: true,
86
85
  columnSeparator: "\t",
87
86
  fileName: "table.tsv",
88
87
  },
88
+ getContextMenuItems: (params) =>
89
+ (params.defaultItems ?? []).filter(
90
+ (item) => item !== "export" && item !== "csvExport" && item !== "excelExport",
91
+ ),
89
92
  onGridReady: (event) => {
90
93
  const api = event.api;
91
94
  autoSizeRowNumberColumn(api);
@@ -396,11 +396,11 @@ function selectDisplayableIndices(
396
396
  .filter(([, spec]) => {
397
397
  switch (spec.type) {
398
398
  case "axis":
399
- return !isPartitionedAxis(spec.id);
399
+ return !isColumnHidden(spec.spec) && !isPartitionedAxis(spec.id);
400
400
  case "column":
401
401
  return (
402
- !isLabelColumnSpec(spec.spec) &&
403
402
  !isColumnHidden(spec.spec) &&
403
+ !isLabelColumnSpec(spec.spec) &&
404
404
  !isLinkerColumnSpec(spec.spec)
405
405
  );
406
406
  }
@@ -1,5 +1,5 @@
1
1
  import { inject, type Reactive } from "vue";
2
- import { pluginDataKey } from "./defineApp";
2
+ import { pluginDataKey } from "../defineApp";
3
3
  import type {
4
4
  PluginHandle,
5
5
  InferFactoryData,
@@ -0,0 +1,5 @@
1
+ import { getServices } from "../internal/getServices";
2
+
3
+ export function useServices() {
4
+ return getServices();
5
+ }
@@ -12,8 +12,9 @@ import type {
12
12
  PluginHandle,
13
13
  InferFactoryData,
14
14
  InferFactoryOutputs,
15
- InferFactoryUiServices,
16
15
  PluginFactoryLike,
16
+ UiServices as AllUiServices,
17
+ InferFactoryUiServices,
17
18
  } from "@platforma-sdk/model";
18
19
  import {
19
20
  hasAbortError,
@@ -23,7 +24,6 @@ import {
23
24
  isPluginOutputKey,
24
25
  pluginOutputPrefix,
25
26
  } from "@platforma-sdk/model";
26
- import { type UiServices as AllUiServices } from "@milaboratories/pl-model-common";
27
27
  import type { Ref } from "vue";
28
28
  import { reactive, computed, ref, markRaw } from "vue";
29
29
  import type { OutputValues, OutputErrors, AppSettings } from "../types";
@@ -32,7 +32,7 @@ import { ensureOutputHasStableFlag, MultiError } from "../utils";
32
32
  import { applyPatch } from "fast-json-patch";
33
33
  import { UpdateSerializer } from "./UpdateSerializer";
34
34
  import { watchIgnorable } from "@vueuse/core";
35
- import type { PluginState, PluginAccess } from "../usePlugin";
35
+ import type { PluginState, PluginAccess } from "../composition/usePlugin";
36
36
  import { logDebug, logError } from "./utils";
37
37
  import { getServices } from "./getServices";
38
38
 
@@ -208,7 +208,7 @@ export function createAppV3<
208
208
  .dispose()
209
209
  .then(unwrapResult)
210
210
  .catch((err) => {
211
- error("error in dispose", err);
211
+ error("platforma error in dispose", err);
212
212
  });
213
213
  });
214
214
 
@@ -19,5 +19,6 @@ export function createUiServiceRegistry(options: UiServiceOptions) {
19
19
  return new UiServiceRegistry(Services, {
20
20
  PFrameSpec: () => new SpecDriver(),
21
21
  PFrame: () => options.proxy(Services.PFrame),
22
+ Dialog: () => options.proxy(Services.Dialog),
22
23
  });
23
24
  }
package/src/lib.ts CHANGED
@@ -37,8 +37,8 @@ export * from "./components/PlDatasetSelector";
37
37
 
38
38
  export * from "./defineApp";
39
39
 
40
- export { usePlugin } from "./usePlugin";
41
- export type { PluginState } from "./usePlugin";
40
+ export { usePlugin } from "./composition/usePlugin";
41
+ export type { PluginState } from "./composition/usePlugin";
42
42
  export type {
43
43
  PluginHandle,
44
44
  PluginFactoryLike,
@@ -1 +0,0 @@
1
- {"version":3,"file":"usePlugin.d.ts","sourceRoot":"","sources":["../src/usePlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE5C,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAE9B,sEAAsE;AACtE,MAAM,WAAW,WAAW,CAC1B,IAAI,GAAG,OAAO,EACd,OAAO,GAAG,OAAO,EACjB,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAElC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;QACvB,IAAI,EAAE,IAAI,CAAC;QACX,OAAO,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5C;aAAG,CAAC,IAAI,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS;SAAE,GAChD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5B,YAAY,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjD;aAAG,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK;SAAE,GAChC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;KACvC,CAAC,CAAC;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,sBAAsB,CAAC,CAAC,SAAS,iBAAiB,EAChD,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;CACxF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,iBAAiB,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,uFAW7E"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"usePlugin.js","names":[],"sources":["../src/usePlugin.ts"],"sourcesContent":["import { inject, type Reactive } from \"vue\";\nimport { pluginDataKey } from \"./defineApp\";\nimport type {\n PluginHandle,\n InferFactoryData,\n InferFactoryOutputs,\n InferFactoryUiServices,\n PluginFactoryLike,\n} from \"@platforma-sdk/model\";\n\n/** Per-plugin reactive model exposed to consumers via usePlugin(). */\nexport interface PluginState<\n Data = unknown,\n Outputs = unknown,\n Services = Record<string, unknown>,\n> {\n readonly model: Reactive<{\n data: Data;\n outputs: Outputs extends Record<string, unknown>\n ? { [K in keyof Outputs]: Outputs[K] | undefined }\n : Record<string, unknown>;\n outputErrors: Outputs extends Record<string, unknown>\n ? { [K in keyof Outputs]?: Error }\n : Record<string, Error | undefined>;\n }>;\n readonly services: Services;\n}\n\n/** Internal interface for plugin access — provided via Vue injection to usePlugin(). */\nexport interface PluginAccess {\n getOrCreatePluginState<F extends PluginFactoryLike>(\n handle: PluginHandle<F>,\n ): PluginState<InferFactoryData<F>, InferFactoryOutputs<F>, InferFactoryUiServices<F>>;\n}\n\n/**\n * Composable for accessing a plugin's reactive model: data, outputs, and outputErrors.\n *\n * Mirrors the `app.model` access pattern — `plugin.model.data` is reactive and deep-watched,\n * mutations are automatically queued and sent to storage.\n *\n * @param handle - Opaque plugin handle obtained from `app.plugins`.\n * @typeParam F - The plugin factory type (inferred from the handle)\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { usePlugin, type InferPluginHandle } from '@platforma-sdk/ui-vue';\n * import type { CounterPlugin } from './plugins/counter';\n *\n * const props = defineProps<{ instance: InferPluginHandle<CounterPlugin> }>();\n * const plugin = usePlugin(props.instance);\n *\n * plugin.model.data.count += 1; // reactive, triggers storage update\n * plugin.model.outputs.displayText // computed, plugin's own outputs only\n * plugin.model.outputErrors.displayText // Error | undefined\n * </script>\n * ```\n */\nexport function usePlugin<F extends PluginFactoryLike>(handle: PluginHandle<F>) {\n const access = inject<PluginAccess>(pluginDataKey);\n\n if (!access) {\n throw new Error(\n \"usePlugin requires a V3 block (BlockModelV3). \" +\n \"Make sure the block uses apiVersion 3 and the plugin is installed.\",\n );\n }\n\n return access.getOrCreatePluginState<F>(handle);\n}\n"],"mappings":";;;AA2DA,SAAgB,EAAuC,GAAyB;CAC9E,IAAM,IAAS,EAAqB,EAAc;AAElD,KAAI,CAAC,EACH,OAAU,MACR,mHAED;AAGH,QAAO,EAAO,uBAA0B,EAAO"}