@platforma-sdk/ui-vue 1.66.2 → 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 (75) hide show
  1. package/.turbo/turbo-build.log +25 -21
  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 +37 -0
  6. package/dist/__tests__/setup.d.ts +2 -0
  7. package/dist/__tests__/setup.d.ts.map +1 -0
  8. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.js.map +1 -1
  9. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue.d.ts +2 -0
  10. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue.d.ts.map +1 -1
  11. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue2.js +25 -17
  12. package/dist/components/PlAgCsvExporter/PlAgCsvExporter.vue2.js.map +1 -1
  13. package/dist/components/PlAgCsvExporter/export-csv.d.ts +27 -1
  14. package/dist/components/PlAgCsvExporter/export-csv.d.ts.map +1 -1
  15. package/dist/components/PlAgCsvExporter/export-csv.js +31 -33
  16. package/dist/components/PlAgCsvExporter/export-csv.js.map +1 -1
  17. package/dist/components/PlAgDataTable/PlAgDataTableV2.js.map +1 -1
  18. package/dist/components/PlAgDataTable/PlAgDataTableV2.style.js.map +1 -1
  19. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue.d.ts.map +1 -1
  20. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js +68 -64
  21. package/dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js.map +1 -1
  22. package/dist/components/PlAgDataTable/compositions/useGrid.d.ts.map +1 -1
  23. package/dist/components/PlAgDataTable/compositions/useGrid.js +1 -1
  24. package/dist/components/PlAgDataTable/compositions/useGrid.js.map +1 -1
  25. package/dist/components/PlAgDataTable/sources/table-source-v2.js +2 -2
  26. package/dist/components/PlAgDataTable/sources/table-source-v2.js.map +1 -1
  27. package/dist/components/PlDatasetSelector/PlDatasetSelector.js +9 -0
  28. package/dist/components/PlDatasetSelector/PlDatasetSelector.js.map +1 -0
  29. package/dist/components/PlDatasetSelector/PlDatasetSelector.style.css +1 -0
  30. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue.d.ts +105 -0
  31. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue.d.ts.map +1 -0
  32. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue2.js +111 -0
  33. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue2.js.map +1 -0
  34. package/dist/components/PlDatasetSelector/__tests__/PlDatasetSelector.jsdomtest.d.ts +2 -0
  35. package/dist/components/PlDatasetSelector/__tests__/PlDatasetSelector.jsdomtest.d.ts.map +1 -0
  36. package/dist/components/PlDatasetSelector/index.d.ts +2 -0
  37. package/dist/components/PlDatasetSelector/index.d.ts.map +1 -0
  38. package/dist/components/PlDatasetSelector/index.js +1 -0
  39. package/dist/composition/usePlugin.d.ts.map +1 -0
  40. package/dist/{usePlugin.js → composition/usePlugin.js} +2 -2
  41. package/dist/composition/usePlugin.js.map +1 -0
  42. package/dist/composition/useServices.d.ts +2 -0
  43. package/dist/composition/useServices.d.ts.map +1 -0
  44. package/dist/index.js +15 -14
  45. package/dist/index.js.map +1 -1
  46. package/dist/internal/createAppV3.d.ts +2 -3
  47. package/dist/internal/createAppV3.d.ts.map +1 -1
  48. package/dist/internal/createAppV3.js +1 -1
  49. package/dist/internal/createAppV3.js.map +1 -1
  50. package/dist/internal/service_factories.d.ts +1 -0
  51. package/dist/internal/service_factories.d.ts.map +1 -1
  52. package/dist/internal/service_factories.js +2 -1
  53. package/dist/internal/service_factories.js.map +1 -1
  54. package/dist/lib.d.ts +3 -2
  55. package/dist/lib.d.ts.map +1 -1
  56. package/dist/lib.js +3 -1
  57. package/package.json +9 -7
  58. package/src/__tests__/setup.ts +10 -0
  59. package/src/components/PlAgCsvExporter/PlAgCsvExporter.vue +16 -3
  60. package/src/components/PlAgCsvExporter/export-csv.ts +101 -64
  61. package/src/components/PlAgDataTable/PlAgDataTableV2.vue +6 -1
  62. package/src/components/PlAgDataTable/compositions/useGrid.ts +4 -1
  63. package/src/components/PlAgDataTable/sources/table-source-v2.ts +2 -2
  64. package/src/components/PlDatasetSelector/PlDatasetSelector.vue +164 -0
  65. package/src/components/PlDatasetSelector/__tests__/PlDatasetSelector.jsdomtest.ts +257 -0
  66. package/src/components/PlDatasetSelector/index.ts +1 -0
  67. package/src/{usePlugin.ts → composition/usePlugin.ts} +1 -1
  68. package/src/composition/useServices.ts +5 -0
  69. package/src/internal/createAppV3.ts +4 -4
  70. package/src/internal/service_factories.ts +1 -0
  71. package/src/lib.ts +4 -2
  72. package/vitest.config.mts +22 -0
  73. package/dist/usePlugin.d.ts.map +0 -1
  74. package/dist/usePlugin.js.map +0 -1
  75. /package/dist/{usePlugin.d.ts → composition/usePlugin.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ import "./PlDatasetSelector.js";
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePlugin.d.ts","sourceRoot":"","sources":["../../src/composition/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,6 +1,6 @@
1
- import { pluginDataKey as e } from "./defineApp.js";
1
+ import { pluginDataKey as e } from "../defineApp.js";
2
2
  import { inject as t } from "vue";
3
- //#region src/usePlugin.ts
3
+ //#region src/composition/usePlugin.ts
4
4
  function n(n) {
5
5
  let r = t(e);
6
6
  if (!r) throw Error("usePlugin requires a V3 block (BlockModelV3). Make sure the block uses apiVersion 3 and the plugin is installed.");
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePlugin.js","names":[],"sources":["../../src/composition/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"}
@@ -0,0 +1,2 @@
1
+ export declare function useServices(): import('@platforma-sdk/model').BlockDefaultUiServices;
2
+ //# sourceMappingURL=useServices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useServices.d.ts","sourceRoot":"","sources":["../../src/composition/useServices.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,0DAE1B"}
package/dist/index.js CHANGED
@@ -9,11 +9,11 @@ import x from "./components/PlAgGridColumnManager/PlAgGridColumnManager.js";
9
9
  import S from "./components/PlAdvancedFilter/PlAdvancedFilter.js";
10
10
  import { PlAdvancedFilterSupportedFilters as C } from "./components/PlAdvancedFilter/index.js";
11
11
  import w from "./components/PlTableFilters/PlTableFiltersV2.js";
12
- import { DeferredCircular as T, ensureNodeVisible as ee } from "./components/PlAgDataTable/sources/focus-row.js";
13
- import E from "./components/PlAgColumnHeader/PlAgColumnHeader.js";
14
- import D from "./components/PlAgTextAndButtonCell/PlAgTextAndButtonCell.js";
15
- import { defaultMainMenuItems as O } from "./components/PlAgDataTable/sources/menu-items.js";
16
- import k from "./components/PlAgDataTable/PlAgOverlayLoading.js";
12
+ import { DeferredCircular as T, ensureNodeVisible as E } from "./components/PlAgDataTable/sources/focus-row.js";
13
+ import D from "./components/PlAgColumnHeader/PlAgColumnHeader.js";
14
+ import O from "./components/PlAgTextAndButtonCell/PlAgTextAndButtonCell.js";
15
+ import { defaultMainMenuItems as k } from "./components/PlAgDataTable/sources/menu-items.js";
16
+ import ee from "./components/PlAgDataTable/PlAgOverlayLoading.js";
17
17
  import A from "./components/PlAgDataTable/PlAgOverlayNoRows.js";
18
18
  import { AgGridTheme as j } from "./AgGridVue/AgGridTheme.js";
19
19
  import M from "./components/PlAgDataTable/PlAgDataTableV2.js";
@@ -29,17 +29,18 @@ import { deselectAll as V, getSelectedRowsCount as H, getTotalRowsCount as U, is
29
29
  import K from "./components/PlAnnotations/components/PlAnnotations.js";
30
30
  import q from "./components/PlAnnotations/components/PlAnnotationsModal.js";
31
31
  import J from "./components/PlBtnExportArchive/PlBtnExportArchive.js";
32
- import { usePlugin as Y } from "./usePlugin.js";
33
- import { defineStore as X } from "./defineStore.js";
34
- import { activateAgGrid as Z } from "./composition/AgGrid/index.js";
35
- import { objectHash as Q } from "./objectHash.js";
36
- import { computedResult as $ } from "./computedResult.js";
37
- import { ReactiveFileContent as te } from "./composition/fileContent.js";
32
+ import Y from "./components/PlDatasetSelector/PlDatasetSelector.js";
33
+ import { usePlugin as X } from "./composition/usePlugin.js";
34
+ import { defineStore as Z } from "./defineStore.js";
35
+ import { activateAgGrid as Q } from "./composition/AgGrid/index.js";
36
+ import { objectHash as $ } from "./objectHash.js";
37
+ import { computedResult as te } from "./computedResult.js";
38
+ import { ReactiveFileContent as ne } from "./composition/fileContent.js";
38
39
  import "./lib.js";
39
- import { setAutoFreeze as ne } from "immer";
40
+ import { setAutoFreeze as re } from "immer";
40
41
  export * from "@milaboratories/uikit";
41
- ne(!1);
42
+ re(!1);
42
43
  //#endregion
43
- export { j as AgGridTheme, g as BlockLayout, T as DeferredCircular, e as MultiError, S as PlAdvancedFilterComponent, C as PlAdvancedFilterSupportedFilters, I as PlAgCellFile, P as PlAgCellProgress, z as PlAgCellStatusTag, R as PlAgChartHistogramCell, L as PlAgChartStackedBarCell, E as PlAgColumnHeader, _ as PlAgCsvExporter, v as PlAgDataTableRowNumberColId, M as PlAgDataTableV2, x as PlAgGridColumnManager, k as PlAgOverlayLoading, A as PlAgOverlayNoRows, D as PlAgTextAndButtonCell, K as PlAnnotations, q as PlAnnotationsModal, J as PlBtnExportArchive, M as PlDataTableV2, w as PlTableFiltersV2, te as ReactiveFileContent, t as UnresolvedError, Z as activateAgGrid, y as autoSizeRowNumberColumn, $ as computedResult, F as createAgGridColDef, u as createModel, O as defaultMainMenuItems, d as defineApp, f as defineAppV3, X as defineStore, V as deselectAll, n as ensureError, ee as ensureNodeVisible, r as ensureOutputHasStableFlag, i as formatZodError, H as getSelectedRowsCount, U as getTotalRowsCount, a as identity, o as isDefined, W as isSelectionEnabled, s as isZodError, b as makeRowNumberColDef, Q as objectHash, p as pluginDataKey, G as selectAll, c as unwrapOutput, B as useAgGridOptions, m as useFeatureFlags, N as usePlDataTableSettingsV2, Y as usePlugin, h as useSdkPlugin, l as wrapOptionalResult };
44
+ export { j as AgGridTheme, g as BlockLayout, T as DeferredCircular, e as MultiError, S as PlAdvancedFilterComponent, C as PlAdvancedFilterSupportedFilters, I as PlAgCellFile, P as PlAgCellProgress, z as PlAgCellStatusTag, R as PlAgChartHistogramCell, L as PlAgChartStackedBarCell, D as PlAgColumnHeader, _ as PlAgCsvExporter, v as PlAgDataTableRowNumberColId, M as PlAgDataTableV2, x as PlAgGridColumnManager, ee as PlAgOverlayLoading, A as PlAgOverlayNoRows, O as PlAgTextAndButtonCell, K as PlAnnotations, q as PlAnnotationsModal, J as PlBtnExportArchive, M as PlDataTableV2, Y as PlDatasetSelector, w as PlTableFiltersV2, ne as ReactiveFileContent, t as UnresolvedError, Q as activateAgGrid, y as autoSizeRowNumberColumn, te as computedResult, F as createAgGridColDef, u as createModel, k as defaultMainMenuItems, d as defineApp, f as defineAppV3, Z as defineStore, V as deselectAll, n as ensureError, E as ensureNodeVisible, r as ensureOutputHasStableFlag, i as formatZodError, H as getSelectedRowsCount, U as getTotalRowsCount, a as identity, o as isDefined, W as isSelectionEnabled, s as isZodError, b as makeRowNumberColDef, $ as objectHash, p as pluginDataKey, G as selectAll, c as unwrapOutput, B as useAgGridOptions, m as useFeatureFlags, N as usePlDataTableSettingsV2, X as usePlugin, h as useSdkPlugin, l as wrapOptionalResult };
44
45
 
45
46
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { setAutoFreeze } from \"immer\";\n\nsetAutoFreeze(false);\n\nexport * from \"./lib\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,GAAc,GAAM"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { setAutoFreeze } from \"immer\";\n\nsetAutoFreeze(false);\n\nexport * from \"./lib\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,GAAc,GAAM"}
@@ -1,8 +1,7 @@
1
1
  import { Mutable } from '@milaboratories/helpers';
2
- import { NavigationState, BlockOutputsBase, BlockStateV3, PlatformaV3, ValueWithUTag, AuthorMarker, PlatformaExtended, InferPluginHandles } from '@platforma-sdk/model';
3
- import { UiServices as AllUiServices } from '@milaboratories/pl-model-common';
2
+ import { NavigationState, BlockOutputsBase, BlockStateV3, PlatformaV3, ValueWithUTag, AuthorMarker, PlatformaExtended, InferPluginHandles, UiServices as AllUiServices } from '@platforma-sdk/model';
4
3
  import { OutputValues, OutputErrors, AppSettings } from '../types';
5
- import { PluginAccess } from '../usePlugin';
4
+ import { PluginAccess } from '../composition/usePlugin';
6
5
  export declare const patchPoolingDelay = 150;
7
6
  export declare const createNextAuthorMarker: (marker: AuthorMarker | undefined) => AuthorMarker;
8
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"createAppV3.d.ts","sourceRoot":"","sources":["../../src/internal/createAppV3.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAMnB,MAAM,sBAAsB,CAAC;AAS9B,OAAO,EAAE,KAAK,UAAU,IAAI,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGnF,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAMxE,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,cAAc,CAAC;AAI9D,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAWrC,eAAO,MAAM,sBAAsB,GAAI,QAAQ,YAAY,GAAG,SAAS,KAAG,YAGxE,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,IAAI,GAAG,OAAO,EACd,IAAI,GAAG,OAAO,EACd,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EACnD,IAAI,SAAS,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,EACxC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,EAElE,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EACvD,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,EACzF,QAAQ,EAAE,WAAW;;eAsGZ,MAAM;;;;;;;;qBAvDJ,OAAO,CAAC,OAAO,CAAC;0BACX,OAAO;6BACJ,eAAe,CAAC,IAAI,CAAC;;;;;;;;yBA+IkB,IAAI;oCAEf,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAK3E;;;;;;WAMG;uBACY,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;QAMtD;;;;;WAKG;yBACc,IAAI;;;;EA8HxB;AAED,MAAM,MAAM,SAAS,CACnB,IAAI,GAAG,OAAO,EACd,IAAI,GAAG,OAAO,EACd,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EACnD,IAAI,SAAS,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,EACxC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,IAChE,UAAU,CAAC,OAAO,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC"}
1
+ {"version":3,"file":"createAppV3.d.ts","sourceRoot":"","sources":["../../src/internal/createAppV3.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAKlB,UAAU,IAAI,aAAa,EAE5B,MAAM,sBAAsB,CAAC;AAW9B,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAMxE,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAI1E,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAWrC,eAAO,MAAM,sBAAsB,GAAI,QAAQ,YAAY,GAAG,SAAS,KAAG,YAGxE,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,IAAI,GAAG,OAAO,EACd,IAAI,GAAG,OAAO,EACd,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EACnD,IAAI,SAAS,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,EACxC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,EAElE,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EACvD,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,EACzF,QAAQ,EAAE,WAAW;;eAsGZ,MAAM;;;;;;;;qBAvDJ,OAAO,CAAC,OAAO,CAAC;0BACX,OAAO;6BACJ,eAAe,CAAC,IAAI,CAAC;;;;;;;;yBA+IkB,IAAI;oCAEf,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAK3E;;;;;;WAMG;uBACY,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;QAMtD;;;;;WAKG;yBACc,IAAI;;;;EA8HxB;AAED,MAAM,MAAM,SAAS,CACnB,IAAI,GAAG,OAAO,EACd,IAAI,GAAG,OAAO,EACd,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EACnD,IAAI,SAAS,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,EACxC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,IAChE,UAAU,CAAC,OAAO,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC"}
@@ -55,7 +55,7 @@ function C(C, w, T) {
55
55
  (async () => {
56
56
  for (window.addEventListener("beforeunload", () => {
57
57
  A.value = !0, w.dispose().then(y).catch((e) => {
58
- D("error in dispose", e);
58
+ D("platforma error in dispose", e);
59
59
  });
60
60
  }); !A.value;) try {
61
61
  let e = await w.getPatches(j.value).then(y);
@@ -1 +1 @@
1
- {"version":3,"file":"createAppV3.js","names":[],"sources":["../../src/internal/createAppV3.ts"],"sourcesContent":["import { deepClone, delay, uniqueId } from \"@milaboratories/helpers\";\nimport type { Mutable } from \"@milaboratories/helpers\";\nimport type {\n NavigationState,\n BlockOutputsBase,\n BlockStateV3,\n PlatformaV3,\n ValueWithUTag,\n AuthorMarker,\n PlatformaExtended,\n InferPluginHandles,\n PluginHandle,\n InferFactoryData,\n InferFactoryOutputs,\n InferFactoryUiServices,\n PluginFactoryLike,\n} from \"@platforma-sdk/model\";\nimport {\n hasAbortError,\n unwrapResult,\n deriveDataFromStorage,\n getPluginData,\n isPluginOutputKey,\n pluginOutputPrefix,\n} from \"@platforma-sdk/model\";\nimport { type UiServices as AllUiServices } from \"@milaboratories/pl-model-common\";\nimport type { Ref } from \"vue\";\nimport { reactive, computed, ref, markRaw } from \"vue\";\nimport type { OutputValues, OutputErrors, AppSettings } from \"../types\";\nimport { parseQuery } from \"../urls\";\nimport { ensureOutputHasStableFlag, MultiError } from \"../utils\";\nimport { applyPatch } from \"fast-json-patch\";\nimport { UpdateSerializer } from \"./UpdateSerializer\";\nimport { watchIgnorable } from \"@vueuse/core\";\nimport type { PluginState, PluginAccess } from \"../usePlugin\";\nimport { logDebug, logError } from \"./utils\";\nimport { getServices } from \"./getServices\";\n\nexport const patchPoolingDelay = 150;\n\n/** Internal per-plugin state with reconciliation support. */\ninterface InternalPluginState<\n Data = unknown,\n Outputs = unknown,\n Services = Record<string, unknown>,\n> extends PluginState<Data, Outputs, Services> {\n readonly ignoreUpdates: (fn: () => void) => void;\n}\n\nexport const createNextAuthorMarker = (marker: AuthorMarker | undefined): AuthorMarker => ({\n authorId: marker?.authorId ?? uniqueId(),\n localVersion: (marker?.localVersion ?? 0) + 1,\n});\n\n/**\n * Creates an application instance with reactive state management, outputs, and methods for state updates and navigation.\n *\n * @template Args - The type of arguments used in the application.\n * @template Outputs - The type of block outputs extending `BlockOutputsBase`.\n * @template Data - The type of the block data.\n * @template Href - The type of navigation href, defaulting to a string starting with `/`.\n *\n * @param state - Initial state of the application, including args, outputs, UI state, and navigation state.\n * @param platforma - A platform interface for interacting with block states.\n * @param settings - Application settings, such as debug flags.\n *\n * @returns A reactive application object with methods, getters, and state.\n */\nexport function createAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n>(\n state: ValueWithUTag<BlockStateV3<Data, Outputs, Href>>,\n platforma: PlatformaExtended<PlatformaV3<Data, Args, Outputs, Href, Plugins, UiServices>>,\n settings: AppSettings,\n) {\n const debug = settings.debug ? logDebug : () => {};\n const error = logError;\n\n const data = {\n isExternalSnapshot: false,\n author: {\n authorId: uniqueId(),\n localVersion: 0,\n },\n };\n\n const nextAuthorMarker = () => {\n data.author = createNextAuthorMarker(data.author);\n debug(\"nextAuthorMarker\", data.author);\n return data.author;\n };\n\n const closedRef = ref(false);\n\n const uTagRef = ref(state.uTag);\n\n const debounceSpan = settings.debounceSpan ?? 200;\n\n const setDataQueue = new UpdateSerializer({ debounceSpan });\n const pluginDataQueues = new Map<PluginHandle, UpdateSerializer>();\n const getPluginDataQueue = (handle: PluginHandle): UpdateSerializer => {\n let queue = pluginDataQueues.get(handle);\n if (!queue) {\n queue = new UpdateSerializer({ debounceSpan });\n pluginDataQueues.set(handle, queue);\n }\n return queue;\n };\n const setNavigationStateQueue = new UpdateSerializer({ debounceSpan });\n\n /** Lazily-created per-plugin reactive states. */\n const pluginStates = new Map<PluginHandle, InternalPluginState>();\n /**\n * Reactive snapshot of the application state, including args, outputs, UI state, and navigation state.\n */\n const snapshot = ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>(state.value) as Ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>;\n\n const updateData = async (value: Data) => {\n return platforma.mutateStorage({ operation: \"update-block-data\", value }, nextAuthorMarker());\n };\n\n const updatePluginData = async (handle: PluginHandle, value: unknown) => {\n return platforma.mutateStorage(\n { operation: \"update-plugin-data\", pluginId: handle, value },\n nextAuthorMarker(),\n );\n };\n\n const setNavigationState = async (state: NavigationState<Href>) => {\n return platforma.setNavigationState(state);\n };\n\n const outputs = computed<OutputValues<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, outputWithStatus]) =>\n platforma.blockModelInfo.outputs[k]?.withStatus\n ? [k, ensureOutputHasStableFlag(outputWithStatus)]\n : [\n k,\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined,\n ],\n );\n return Object.fromEntries(entries);\n });\n\n const outputErrors = computed<OutputErrors<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, vOrErr]) => [\n k,\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined,\n ]);\n return Object.fromEntries(entries);\n });\n\n const appModel = reactive({\n apiVersion: 3,\n error: \"\",\n model: {\n data: deepClone(deriveDataFromStorage<Data>(snapshot.value.blockStorage)) as Data,\n outputs,\n outputErrors,\n },\n }) as {\n error: string;\n model: {\n data: Data;\n outputs: OutputValues<Outputs>;\n outputErrors: OutputErrors<Outputs>;\n };\n };\n\n const { ignoreUpdates } = watchIgnorable(\n () => appModel.model,\n (_newData) => {\n const newData = deepClone(_newData);\n debug(\"setDataQueue appModel.model, data\", newData.data);\n setDataQueue.run(() => updateData(newData.data).then(unwrapResult));\n },\n { deep: true },\n );\n\n const updateAppModel = (newData: { data: Data }) => {\n debug(\"updateAppModel\", newData);\n appModel.model.data = deepClone(newData.data) as Data;\n };\n\n (async () => {\n window.addEventListener(\"beforeunload\", () => {\n closedRef.value = true;\n platforma\n .dispose()\n .then(unwrapResult)\n .catch((err) => {\n error(\"error in dispose\", err);\n });\n });\n\n while (!closedRef.value) {\n try {\n const patches = await platforma.getPatches(uTagRef.value).then(unwrapResult);\n\n debug(\"patches.length\", patches.value.length);\n debug(\"uTagRef.value\", uTagRef.value);\n debug(\"patches.uTag\", patches.uTag);\n debug(\"patches.author\", patches.author);\n debug(\"data.author\", data.author);\n\n uTagRef.value = patches.uTag;\n\n if (patches.value.length === 0) {\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n continue;\n }\n\n const isAuthorChanged = data.author?.authorId !== patches.author?.authorId;\n\n // Immutable behavior, apply external changes to the snapshot\n if (isAuthorChanged || data.isExternalSnapshot) {\n debug(\"got external changes, applying them to the snapshot\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value, false, false).newDocument;\n updateAppModel({ data: deriveDataFromStorage<Data>(snapshot.value.blockStorage) });\n // Reconcile plugin data from external source\n for (const [handle, pluginState] of pluginStates) {\n pluginState.ignoreUpdates(() => {\n pluginState.model.data = deepClone(\n getPluginData(snapshot.value.blockStorage, handle),\n );\n });\n }\n data.isExternalSnapshot = isAuthorChanged;\n });\n } else {\n // Mutable behavior\n debug(\"outputs changed\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value).newDocument;\n });\n }\n\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n } catch (err) {\n if (hasAbortError(err)) {\n debug(\"patches loop aborted\");\n closedRef.value = true;\n } else {\n error(\"error in patches loop\", err);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n })();\n\n const cloneData = () => deepClone(appModel.model.data) as Data;\n const cloneNavigationState = () =>\n deepClone(snapshot.value.navigationState) as Mutable<NavigationState<Href>>;\n\n const methods = {\n cloneData,\n cloneNavigationState,\n /**\n * Updates the UI state by applying a callback.\n *\n * @param cb - Callback to modify the current UI state.\n * @returns A promise resolving after the update is applied.\n * @todo Make it mutable since there is already an initial one\n */\n updateData(cb: (data: Data) => Data): Promise<boolean> {\n const newData = cb(cloneData());\n debug(\"updateData\", newData);\n appModel.model.data = newData;\n return setDataQueue.run(() => updateData(newData).then(unwrapResult));\n },\n /**\n * Navigates to a specific href by updating the navigation state.\n *\n * @param href - The target href to navigate to.\n * @returns A promise resolving after the navigation state is updated.\n */\n navigateTo(href: Href) {\n const newState = cloneNavigationState();\n newState.href = href;\n return setNavigationStateQueue.run(() => setNavigationState(newState).then(unwrapResult));\n },\n async allSettled() {\n await delay(0);\n const allQueues = [\n setDataQueue.allSettled(),\n ...Array.from(pluginDataQueues.values()).map((q) => q.allSettled()),\n ];\n await Promise.all(allQueues);\n },\n };\n\n const services = getServices<UiServices>({ platforma });\n\n /** Creates a lazily-cached per-plugin reactive state. */\n const createPluginState = <F extends PluginFactoryLike>(\n handle: PluginHandle<F>,\n ): InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>> => {\n const prefix = pluginOutputPrefix(handle);\n\n const pluginOutputs = computed(() => {\n const result: Record<string, unknown> = {};\n for (const [key, outputWithStatus] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n const outputKey = key.slice(prefix.length);\n if (platforma.blockModelInfo.outputs[key]?.withStatus) {\n result[outputKey] = outputWithStatus\n ? ensureOutputHasStableFlag(outputWithStatus)\n : undefined;\n } else {\n result[outputKey] =\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined;\n }\n }\n return result;\n });\n\n const pluginOutputErrors = computed(() => {\n const result: Record<string, Error | undefined> = {};\n for (const [key, vOrErr] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n result[key.slice(prefix.length)] =\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined;\n }\n return result;\n });\n\n const pluginModel = reactive({\n data: deepClone(getPluginData(snapshot.value.blockStorage, handle)),\n outputs: pluginOutputs,\n outputErrors: pluginOutputErrors,\n }) as InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>>[\"model\"];\n\n const { ignoreUpdates } = watchIgnorable(\n () => pluginModel.data,\n (newData) => {\n if (newData === undefined) return;\n debug(\"plugin setData\", handle, newData);\n getPluginDataQueue(handle).run(() =>\n updatePluginData(handle, deepClone(newData)).then(unwrapResult),\n );\n },\n { deep: true },\n );\n\n return {\n model: pluginModel,\n services: markRaw(services),\n ignoreUpdates,\n };\n };\n\n /** Plugin internals — provided via separate injection key, not exposed on useApp(). */\n const pluginAccess: PluginAccess = {\n getOrCreatePluginState<F extends PluginFactoryLike>(handle: PluginHandle<F>) {\n const existing = pluginStates.get(handle);\n if (existing) {\n return existing as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n }\n const state = createPluginState(handle);\n pluginStates.set(handle, state);\n return state as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n },\n };\n\n const plugins = Object.fromEntries(\n platforma.blockModelInfo.pluginIds.map((id) => [id, { handle: id }]),\n ) as InferPluginHandles<Plugins>;\n\n const getters = {\n closedRef,\n snapshot,\n plugins,\n services: markRaw(services),\n queryParams: computed(() => parseQuery<Href>(snapshot.value.navigationState.href as Href)),\n href: computed(() => snapshot.value.navigationState.href),\n hasErrors: computed(() =>\n Object.values(snapshot.value.outputs as Partial<Readonly<Outputs>>).some((v) => !v?.ok),\n ),\n };\n\n const app = Object.assign(reactive(Object.assign(appModel, getters)), methods);\n\n if (settings.debug) {\n // @ts-expect-error (to inspect in console in debug mode)\n globalThis.__block_app__ = app;\n }\n\n return { app, pluginAccess };\n}\n\nexport type BaseAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n> = ReturnType<typeof createAppV3<Data, Args, Outputs, Href, Plugins, UiServices>>[\"app\"];\n"],"mappings":"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"}
1
+ {"version":3,"file":"createAppV3.js","names":[],"sources":["../../src/internal/createAppV3.ts"],"sourcesContent":["import { deepClone, delay, uniqueId } from \"@milaboratories/helpers\";\nimport type { Mutable } from \"@milaboratories/helpers\";\nimport type {\n NavigationState,\n BlockOutputsBase,\n BlockStateV3,\n PlatformaV3,\n ValueWithUTag,\n AuthorMarker,\n PlatformaExtended,\n InferPluginHandles,\n PluginHandle,\n InferFactoryData,\n InferFactoryOutputs,\n PluginFactoryLike,\n UiServices as AllUiServices,\n InferFactoryUiServices,\n} from \"@platforma-sdk/model\";\nimport {\n hasAbortError,\n unwrapResult,\n deriveDataFromStorage,\n getPluginData,\n isPluginOutputKey,\n pluginOutputPrefix,\n} from \"@platforma-sdk/model\";\nimport type { Ref } from \"vue\";\nimport { reactive, computed, ref, markRaw } from \"vue\";\nimport type { OutputValues, OutputErrors, AppSettings } from \"../types\";\nimport { parseQuery } from \"../urls\";\nimport { ensureOutputHasStableFlag, MultiError } from \"../utils\";\nimport { applyPatch } from \"fast-json-patch\";\nimport { UpdateSerializer } from \"./UpdateSerializer\";\nimport { watchIgnorable } from \"@vueuse/core\";\nimport type { PluginState, PluginAccess } from \"../composition/usePlugin\";\nimport { logDebug, logError } from \"./utils\";\nimport { getServices } from \"./getServices\";\n\nexport const patchPoolingDelay = 150;\n\n/** Internal per-plugin state with reconciliation support. */\ninterface InternalPluginState<\n Data = unknown,\n Outputs = unknown,\n Services = Record<string, unknown>,\n> extends PluginState<Data, Outputs, Services> {\n readonly ignoreUpdates: (fn: () => void) => void;\n}\n\nexport const createNextAuthorMarker = (marker: AuthorMarker | undefined): AuthorMarker => ({\n authorId: marker?.authorId ?? uniqueId(),\n localVersion: (marker?.localVersion ?? 0) + 1,\n});\n\n/**\n * Creates an application instance with reactive state management, outputs, and methods for state updates and navigation.\n *\n * @template Args - The type of arguments used in the application.\n * @template Outputs - The type of block outputs extending `BlockOutputsBase`.\n * @template Data - The type of the block data.\n * @template Href - The type of navigation href, defaulting to a string starting with `/`.\n *\n * @param state - Initial state of the application, including args, outputs, UI state, and navigation state.\n * @param platforma - A platform interface for interacting with block states.\n * @param settings - Application settings, such as debug flags.\n *\n * @returns A reactive application object with methods, getters, and state.\n */\nexport function createAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n>(\n state: ValueWithUTag<BlockStateV3<Data, Outputs, Href>>,\n platforma: PlatformaExtended<PlatformaV3<Data, Args, Outputs, Href, Plugins, UiServices>>,\n settings: AppSettings,\n) {\n const debug = settings.debug ? logDebug : () => {};\n const error = logError;\n\n const data = {\n isExternalSnapshot: false,\n author: {\n authorId: uniqueId(),\n localVersion: 0,\n },\n };\n\n const nextAuthorMarker = () => {\n data.author = createNextAuthorMarker(data.author);\n debug(\"nextAuthorMarker\", data.author);\n return data.author;\n };\n\n const closedRef = ref(false);\n\n const uTagRef = ref(state.uTag);\n\n const debounceSpan = settings.debounceSpan ?? 200;\n\n const setDataQueue = new UpdateSerializer({ debounceSpan });\n const pluginDataQueues = new Map<PluginHandle, UpdateSerializer>();\n const getPluginDataQueue = (handle: PluginHandle): UpdateSerializer => {\n let queue = pluginDataQueues.get(handle);\n if (!queue) {\n queue = new UpdateSerializer({ debounceSpan });\n pluginDataQueues.set(handle, queue);\n }\n return queue;\n };\n const setNavigationStateQueue = new UpdateSerializer({ debounceSpan });\n\n /** Lazily-created per-plugin reactive states. */\n const pluginStates = new Map<PluginHandle, InternalPluginState>();\n /**\n * Reactive snapshot of the application state, including args, outputs, UI state, and navigation state.\n */\n const snapshot = ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>(state.value) as Ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>;\n\n const updateData = async (value: Data) => {\n return platforma.mutateStorage({ operation: \"update-block-data\", value }, nextAuthorMarker());\n };\n\n const updatePluginData = async (handle: PluginHandle, value: unknown) => {\n return platforma.mutateStorage(\n { operation: \"update-plugin-data\", pluginId: handle, value },\n nextAuthorMarker(),\n );\n };\n\n const setNavigationState = async (state: NavigationState<Href>) => {\n return platforma.setNavigationState(state);\n };\n\n const outputs = computed<OutputValues<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, outputWithStatus]) =>\n platforma.blockModelInfo.outputs[k]?.withStatus\n ? [k, ensureOutputHasStableFlag(outputWithStatus)]\n : [\n k,\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined,\n ],\n );\n return Object.fromEntries(entries);\n });\n\n const outputErrors = computed<OutputErrors<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, vOrErr]) => [\n k,\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined,\n ]);\n return Object.fromEntries(entries);\n });\n\n const appModel = reactive({\n apiVersion: 3,\n error: \"\",\n model: {\n data: deepClone(deriveDataFromStorage<Data>(snapshot.value.blockStorage)) as Data,\n outputs,\n outputErrors,\n },\n }) as {\n error: string;\n model: {\n data: Data;\n outputs: OutputValues<Outputs>;\n outputErrors: OutputErrors<Outputs>;\n };\n };\n\n const { ignoreUpdates } = watchIgnorable(\n () => appModel.model,\n (_newData) => {\n const newData = deepClone(_newData);\n debug(\"setDataQueue appModel.model, data\", newData.data);\n setDataQueue.run(() => updateData(newData.data).then(unwrapResult));\n },\n { deep: true },\n );\n\n const updateAppModel = (newData: { data: Data }) => {\n debug(\"updateAppModel\", newData);\n appModel.model.data = deepClone(newData.data) as Data;\n };\n\n (async () => {\n window.addEventListener(\"beforeunload\", () => {\n closedRef.value = true;\n platforma\n .dispose()\n .then(unwrapResult)\n .catch((err) => {\n error(\"platforma error in dispose\", err);\n });\n });\n\n while (!closedRef.value) {\n try {\n const patches = await platforma.getPatches(uTagRef.value).then(unwrapResult);\n\n debug(\"patches.length\", patches.value.length);\n debug(\"uTagRef.value\", uTagRef.value);\n debug(\"patches.uTag\", patches.uTag);\n debug(\"patches.author\", patches.author);\n debug(\"data.author\", data.author);\n\n uTagRef.value = patches.uTag;\n\n if (patches.value.length === 0) {\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n continue;\n }\n\n const isAuthorChanged = data.author?.authorId !== patches.author?.authorId;\n\n // Immutable behavior, apply external changes to the snapshot\n if (isAuthorChanged || data.isExternalSnapshot) {\n debug(\"got external changes, applying them to the snapshot\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value, false, false).newDocument;\n updateAppModel({ data: deriveDataFromStorage<Data>(snapshot.value.blockStorage) });\n // Reconcile plugin data from external source\n for (const [handle, pluginState] of pluginStates) {\n pluginState.ignoreUpdates(() => {\n pluginState.model.data = deepClone(\n getPluginData(snapshot.value.blockStorage, handle),\n );\n });\n }\n data.isExternalSnapshot = isAuthorChanged;\n });\n } else {\n // Mutable behavior\n debug(\"outputs changed\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value).newDocument;\n });\n }\n\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n } catch (err) {\n if (hasAbortError(err)) {\n debug(\"patches loop aborted\");\n closedRef.value = true;\n } else {\n error(\"error in patches loop\", err);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n })();\n\n const cloneData = () => deepClone(appModel.model.data) as Data;\n const cloneNavigationState = () =>\n deepClone(snapshot.value.navigationState) as Mutable<NavigationState<Href>>;\n\n const methods = {\n cloneData,\n cloneNavigationState,\n /**\n * Updates the UI state by applying a callback.\n *\n * @param cb - Callback to modify the current UI state.\n * @returns A promise resolving after the update is applied.\n * @todo Make it mutable since there is already an initial one\n */\n updateData(cb: (data: Data) => Data): Promise<boolean> {\n const newData = cb(cloneData());\n debug(\"updateData\", newData);\n appModel.model.data = newData;\n return setDataQueue.run(() => updateData(newData).then(unwrapResult));\n },\n /**\n * Navigates to a specific href by updating the navigation state.\n *\n * @param href - The target href to navigate to.\n * @returns A promise resolving after the navigation state is updated.\n */\n navigateTo(href: Href) {\n const newState = cloneNavigationState();\n newState.href = href;\n return setNavigationStateQueue.run(() => setNavigationState(newState).then(unwrapResult));\n },\n async allSettled() {\n await delay(0);\n const allQueues = [\n setDataQueue.allSettled(),\n ...Array.from(pluginDataQueues.values()).map((q) => q.allSettled()),\n ];\n await Promise.all(allQueues);\n },\n };\n\n const services = getServices<UiServices>({ platforma });\n\n /** Creates a lazily-cached per-plugin reactive state. */\n const createPluginState = <F extends PluginFactoryLike>(\n handle: PluginHandle<F>,\n ): InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>> => {\n const prefix = pluginOutputPrefix(handle);\n\n const pluginOutputs = computed(() => {\n const result: Record<string, unknown> = {};\n for (const [key, outputWithStatus] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n const outputKey = key.slice(prefix.length);\n if (platforma.blockModelInfo.outputs[key]?.withStatus) {\n result[outputKey] = outputWithStatus\n ? ensureOutputHasStableFlag(outputWithStatus)\n : undefined;\n } else {\n result[outputKey] =\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined;\n }\n }\n return result;\n });\n\n const pluginOutputErrors = computed(() => {\n const result: Record<string, Error | undefined> = {};\n for (const [key, vOrErr] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n result[key.slice(prefix.length)] =\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined;\n }\n return result;\n });\n\n const pluginModel = reactive({\n data: deepClone(getPluginData(snapshot.value.blockStorage, handle)),\n outputs: pluginOutputs,\n outputErrors: pluginOutputErrors,\n }) as InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>>[\"model\"];\n\n const { ignoreUpdates } = watchIgnorable(\n () => pluginModel.data,\n (newData) => {\n if (newData === undefined) return;\n debug(\"plugin setData\", handle, newData);\n getPluginDataQueue(handle).run(() =>\n updatePluginData(handle, deepClone(newData)).then(unwrapResult),\n );\n },\n { deep: true },\n );\n\n return {\n model: pluginModel,\n services: markRaw(services),\n ignoreUpdates,\n };\n };\n\n /** Plugin internals — provided via separate injection key, not exposed on useApp(). */\n const pluginAccess: PluginAccess = {\n getOrCreatePluginState<F extends PluginFactoryLike>(handle: PluginHandle<F>) {\n const existing = pluginStates.get(handle);\n if (existing) {\n return existing as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n }\n const state = createPluginState(handle);\n pluginStates.set(handle, state);\n return state as unknown as PluginState<\n InferFactoryData<F>,\n InferFactoryOutputs<F>,\n InferFactoryUiServices<F>\n >;\n },\n };\n\n const plugins = Object.fromEntries(\n platforma.blockModelInfo.pluginIds.map((id) => [id, { handle: id }]),\n ) as InferPluginHandles<Plugins>;\n\n const getters = {\n closedRef,\n snapshot,\n plugins,\n services: markRaw(services),\n queryParams: computed(() => parseQuery<Href>(snapshot.value.navigationState.href as Href)),\n href: computed(() => snapshot.value.navigationState.href),\n hasErrors: computed(() =>\n Object.values(snapshot.value.outputs as Partial<Readonly<Outputs>>).some((v) => !v?.ok),\n ),\n };\n\n const app = Object.assign(reactive(Object.assign(appModel, getters)), methods);\n\n if (settings.debug) {\n // @ts-expect-error (to inspect in console in debug mode)\n globalThis.__block_app__ = app;\n }\n\n return { app, pluginAccess };\n}\n\nexport type BaseAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n UiServices extends Partial<AllUiServices> = Partial<AllUiServices>,\n> = ReturnType<typeof createAppV3<Data, Args, Outputs, Href, Plugins, UiServices>>[\"app\"];\n"],"mappings":"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"}
@@ -6,5 +6,6 @@ export type UiServiceOptions = {
6
6
  export declare function createUiServiceRegistry(options: UiServiceOptions): UiServiceRegistry<{
7
7
  PFrameSpec: import('@milaboratories/pl-model-common').Branded<"pframeSpec", import('@milaboratories/pl-model-common').ServiceTypesLike<import('@milaboratories/pl-model-common').PFrameSpecDriver, import('@milaboratories/pl-model-common').PFrameSpecDriver, "wasm">>;
8
8
  PFrame: import('@milaboratories/pl-model-common').Branded<"pframe", import('@milaboratories/pl-model-common').ServiceTypesLike<import('@milaboratories/pl-model-common').PFrameModelDriver<import('@milaboratories/pl-model-common').PColumn<string | import('@milaboratories/pl-model-common').PColumnValues | import('@milaboratories/pl-model-common').DataInfo<string>>>, import('@milaboratories/pl-model-common').PFrameDriver, "node">>;
9
+ Dialog: import('@milaboratories/pl-model-common').Branded<"dialog", import('@milaboratories/pl-model-common').ServiceTypesLike<Record<string, never>, import('@milaboratories/pl-model-common').DialogService, "main">>;
9
10
  }>;
10
11
  //# sourceMappingURL=service_factories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service_factories.d.ts","sourceRoot":"","sources":["../../src/internal/service_factories.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;;;GAKhE"}
1
+ {"version":3,"file":"service_factories.d.ts","sourceRoot":"","sources":["../../src/internal/service_factories.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,gBAAgB;;;;GAMhE"}
@@ -4,7 +4,8 @@ import { SpecDriver as n } from "@milaboratories/pf-spec-driver";
4
4
  function r(r) {
5
5
  return new t(e, {
6
6
  PFrameSpec: () => new n(),
7
- PFrame: () => r.proxy(e.PFrame)
7
+ PFrame: () => r.proxy(e.PFrame),
8
+ Dialog: () => r.proxy(e.Dialog)
8
9
  });
9
10
  }
10
11
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"service_factories.js","names":[],"sources":["../../src/internal/service_factories.ts"],"sourcesContent":["/**\n * UI service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * UI-side driver instance:\n * - WASM services: instantiated directly (e.g. SpecDriver)\n * - Node services: proxied via IPC using NodeServiceProxy\n */\n\nimport { Services, UiServiceRegistry } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport type { ServiceProxy } from \"@platforma-sdk/model\";\n\nexport type UiServiceOptions = {\n proxy: ServiceProxy;\n};\n\nexport function createUiServiceRegistry(options: UiServiceOptions) {\n return new UiServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver(),\n PFrame: () => options.proxy(Services.PFrame),\n });\n}\n"],"mappings":";;;AAiBA,SAAgB,EAAwB,GAA2B;AACjE,QAAO,IAAI,EAAkB,GAAU;EACrC,kBAAkB,IAAI,GAAY;EAClC,cAAc,EAAQ,MAAM,EAAS,OAAO;EAC7C,CAAC"}
1
+ {"version":3,"file":"service_factories.js","names":[],"sources":["../../src/internal/service_factories.ts"],"sourcesContent":["/**\n * UI service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * UI-side driver instance:\n * - WASM services: instantiated directly (e.g. SpecDriver)\n * - Node services: proxied via IPC using NodeServiceProxy\n */\n\nimport { Services, UiServiceRegistry } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport type { ServiceProxy } from \"@platforma-sdk/model\";\n\nexport type UiServiceOptions = {\n proxy: ServiceProxy;\n};\n\nexport function createUiServiceRegistry(options: UiServiceOptions) {\n return new UiServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver(),\n PFrame: () => options.proxy(Services.PFrame),\n Dialog: () => options.proxy(Services.Dialog),\n });\n}\n"],"mappings":";;;AAiBA,SAAgB,EAAwB,GAA2B;AACjE,QAAO,IAAI,EAAkB,GAAU;EACrC,kBAAkB,IAAI,GAAY;EAClC,cAAc,EAAQ,MAAM,EAAS,OAAO;EAC5C,cAAc,EAAQ,MAAM,EAAS,OAAO;EAC7C,CAAC"}
package/dist/lib.d.ts CHANGED
@@ -18,9 +18,10 @@ export * from './components/PlTableFilters';
18
18
  export * from './components/PlAnnotations';
19
19
  export * from './components/PlBtnExportArchive';
20
20
  export * from './components/PlAdvancedFilter';
21
+ export * from './components/PlDatasetSelector';
21
22
  export * from './defineApp';
22
- export { usePlugin } from './usePlugin';
23
- export type { PluginState } from './usePlugin';
23
+ export { usePlugin } from './composition/usePlugin';
24
+ export type { PluginState } from './composition/usePlugin';
24
25
  export type { PluginHandle, PluginFactoryLike, InferPluginHandle, InferFactoryData, InferFactoryOutputs, } from '@platforma-sdk/model';
25
26
  export * from './createModel';
26
27
  export * from './types';
package/dist/lib.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AAClG,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAEhG,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,cAAc,aAAa,CAAC;AAE5B,cAAc,+BAA+B,CAAC;AAE9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AAErD,cAAc,4BAA4B,CAAC;AAE3C,cAAc,8BAA8B,CAAC;AAE7C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,oCAAoC,CAAC;AAEnD,cAAc,6BAA6B,CAAC;AAE5C,cAAc,4BAA4B,CAAC;AAE3C,cAAc,iCAAiC,CAAC;AAEhD,cAAc,+BAA+B,CAAC;AAE9C,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,cAAc,eAAe,CAAC;AAE9B,cAAc,SAAS,CAAC;AAExB,cAAc,eAAe,CAAC;AAE9B,cAAc,sBAAsB,CAAC;AAErC,cAAc,SAAS,CAAC;AAExB,cAAc,cAAc,CAAC;AAE7B,cAAc,kBAAkB,CAAC;AAEjC,cAAc,2BAA2B,CAAC;AAE1C,cAAc,uBAAuB,CAAC;AAEtC,mBAAmB,uBAAuB,CAAC"}
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AAClG,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAEhG,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,cAAc,aAAa,CAAC;AAE5B,cAAc,+BAA+B,CAAC;AAE9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AAErD,cAAc,4BAA4B,CAAC;AAE3C,cAAc,8BAA8B,CAAC;AAE7C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,oCAAoC,CAAC;AAEnD,cAAc,6BAA6B,CAAC;AAE5C,cAAc,4BAA4B,CAAC;AAE3C,cAAc,iCAAiC,CAAC;AAEhD,cAAc,+BAA+B,CAAC;AAE9C,cAAc,gCAAgC,CAAC;AAE/C,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,cAAc,eAAe,CAAC;AAE9B,cAAc,SAAS,CAAC;AAExB,cAAc,eAAe,CAAC;AAE9B,cAAc,sBAAsB,CAAC;AAErC,cAAc,SAAS,CAAC;AAExB,cAAc,cAAc,CAAC;AAE7B,cAAc,kBAAkB,CAAC;AAEjC,cAAc,2BAA2B,CAAC;AAE1C,cAAc,uBAAuB,CAAC;AAEtC,mBAAmB,uBAAuB,CAAC"}
package/dist/lib.js CHANGED
@@ -44,7 +44,9 @@ import "./components/PlAnnotations/components/PlAnnotationsModal.js";
44
44
  import "./components/PlAnnotations/index.js";
45
45
  import "./components/PlBtnExportArchive/PlBtnExportArchive.js";
46
46
  import "./components/PlBtnExportArchive/index.js";
47
- import "./usePlugin.js";
47
+ import "./components/PlDatasetSelector/PlDatasetSelector.js";
48
+ import "./components/PlDatasetSelector/index.js";
49
+ import "./composition/usePlugin.js";
48
50
  import "./defineStore.js";
49
51
  import "./composition/AgGrid/index.js";
50
52
  import "./objectHash.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.66.2",
3
+ "version": "1.68.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -26,10 +26,10 @@
26
26
  "lru-cache": "^11.2.2",
27
27
  "vue": "^3.5.24",
28
28
  "zod": "~3.25.76",
29
- "@milaboratories/pl-model-common": "1.34.1",
30
- "@milaboratories/pf-spec-driver": "1.3.2",
31
- "@milaboratories/uikit": "2.12.8",
32
- "@platforma-sdk/model": "1.66.2"
29
+ "@milaboratories/pl-model-common": "1.36.0",
30
+ "@milaboratories/uikit": "2.12.10",
31
+ "@platforma-sdk/model": "1.68.0",
32
+ "@milaboratories/pf-spec-driver": "1.3.4"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@faker-js/faker": "^9.2.0",
@@ -37,6 +37,8 @@
37
37
  "@vitest/coverage-istanbul": "^4.1.3",
38
38
  "@vue/test-utils": "^2.4.6",
39
39
  "fast-json-patch": "^3.1.1",
40
+ "jsdom": "^25.0.1",
41
+ "resize-observer-polyfill": "^1.5.1",
40
42
  "rollup-plugin-sourcemaps2": "^0.5.2",
41
43
  "sass": "~1.83.4",
42
44
  "semver": "^7.7.2",
@@ -44,9 +46,9 @@
44
46
  "vite": "^8.0.6",
45
47
  "vitest": "^4.1.3",
46
48
  "@milaboratories/ts-builder": "1.3.2",
49
+ "@milaboratories/build-configs": "2.0.0",
47
50
  "@milaboratories/helpers": "1.14.1",
48
- "@milaboratories/ts-configs": "1.2.3",
49
- "@milaboratories/build-configs": "2.0.0"
51
+ "@milaboratories/ts-configs": "1.2.3"
50
52
  },
51
53
  "scripts": {
52
54
  "dev": "ts-builder serve --target browser-lib",
@@ -0,0 +1,10 @@
1
+ import ResizeObserver from "resize-observer-polyfill";
2
+ globalThis.ResizeObserver = ResizeObserver;
3
+
4
+ // CSS.paintWorklet is not available in jsdom — uikit's PlPlaceholder
5
+ // accesses it at module load time.
6
+ if (typeof globalThis.CSS === "undefined") {
7
+ (globalThis as Record<string, unknown>).CSS = { paintWorklet: { addModule: () => {} } };
8
+ } else if (!("paintWorklet" in globalThis.CSS)) {
9
+ (globalThis.CSS as Record<string, unknown>).paintWorklet = { addModule: () => {} };
10
+ }
@@ -1,25 +1,38 @@
1
1
  <script setup lang="ts">
2
2
  import type { GridApi } from "ag-grid-enterprise";
3
+ import type { PTableHandle } from "@platforma-sdk/model";
3
4
  import { PlBtnGhost, usePlBlockPageTitleTeleportTarget } from "@milaboratories/uikit";
4
5
  import { shallowRef, toRefs } from "vue";
5
- import { exportCsv } from "./export-csv";
6
+ import { isNil } from "es-toolkit";
7
+ import { exportCsv, isCsvExportAvailable } from "./export-csv";
8
+ import type { ExportOptions } from "./export-csv";
6
9
 
7
10
  const props = defineProps<{
8
11
  api: GridApi;
12
+ tableHandle?: PTableHandle;
9
13
  }>();
10
14
  const { api: gridApi } = toRefs(props);
15
+ const csvExportAvailable = isCsvExportAvailable();
11
16
 
12
17
  const exporting = shallowRef(false);
13
18
  const initiateExport = () => {
19
+ const nativeOptions: ExportOptions | undefined = !isNil(props.tableHandle)
20
+ ? { tableHandle: props.tableHandle, format: "csv" }
21
+ : undefined;
22
+
23
+ if (isNil(nativeOptions)) {
24
+ return;
25
+ }
26
+
14
27
  exporting.value = true;
15
- exportCsv(gridApi.value, () => (exporting.value = false));
28
+ exportCsv(gridApi.value, nativeOptions).finally(() => (exporting.value = false));
16
29
  };
17
30
 
18
31
  const teleportTarget = usePlBlockPageTitleTeleportTarget("PlAgCsvExporter");
19
32
  </script>
20
33
 
21
34
  <template>
22
- <Teleport v-if="teleportTarget" :to="teleportTarget">
35
+ <Teleport v-if="teleportTarget && csvExportAvailable" :to="teleportTarget">
23
36
  <PlBtnGhost :loading="exporting" icon="export" @click.stop="initiateExport">
24
37
  Export
25
38
  </PlBtnGhost>
@@ -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);