@platforma-sdk/ui-vue 1.69.0 → 1.71.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 (26) hide show
  1. package/.turbo/turbo-build.log +12 -12
  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 +24 -0
  6. package/dist/components/PlAgDataTable/sources/common.d.ts +1 -2
  7. package/dist/components/PlAgDataTable/sources/common.d.ts.map +1 -1
  8. package/dist/components/PlAgDataTable/sources/common.js +1 -1
  9. package/dist/components/PlAgDataTable/sources/common.js.map +1 -1
  10. package/dist/components/PlAgDataTable/sources/value-rendering.d.ts +1 -1
  11. package/dist/components/PlAgDataTable/sources/value-rendering.d.ts.map +1 -1
  12. package/dist/components/PlAgDataTable/sources/value-rendering.js +10 -8
  13. package/dist/components/PlAgDataTable/sources/value-rendering.js.map +1 -1
  14. package/dist/components/PlAnnotations/components/PlAnnotations.vue2.js.map +1 -1
  15. package/dist/components/PlDatasetSelector/PlDatasetSelector.js +1 -1
  16. package/dist/components/PlDatasetSelector/PlDatasetSelector.js.map +1 -1
  17. package/dist/components/PlDatasetSelector/PlDatasetSelector.style.css +1 -1
  18. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue.d.ts +9 -19
  19. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue.d.ts.map +1 -1
  20. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue2.js +22 -23
  21. package/dist/components/PlDatasetSelector/PlDatasetSelector.vue2.js.map +1 -1
  22. package/package.json +6 -6
  23. package/src/components/PlAgDataTable/sources/common.ts +2 -4
  24. package/src/components/PlAgDataTable/sources/value-rendering.ts +16 -7
  25. package/src/components/PlDatasetSelector/PlDatasetSelector.vue +24 -23
  26. package/src/components/PlDatasetSelector/__tests__/PlDatasetSelector.jsdomtest.ts +70 -72
@@ -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.69.0 build /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.71.0 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...
@@ -16,7 +16,7 @@ Building browser-lib project...
16
16
  rendering chunks...
17
17
 
18
18
  [vite:dts] Start generate declaration files...
19
- [vite:dts] Declaration files built in 4968ms.
19
+ [vite:dts] Declaration files built in 4672ms.
20
20
 
21
21
  computing gzip size...
22
22
  dist/components/PlAnnotations/components/PlAnnotations.vue?vue&type=style&index=0&lang.css 0.04 kB │ gzip: 0.06 kB
@@ -24,7 +24,7 @@ dist/components/PlAgCellStatusTag/PlAgCellStatusTag.vue_vue_type_style_index_0_l
24
24
  dist/components/BlockLayout.vue?vue&type=style&index=0&lang.css 0.05 kB │ gzip: 0.07 kB
25
25
  dist/components/PlAgDataTable/PlAgDataTableSheets.vue?vue&type=style&index=0&lang.css 0.06 kB │ gzip: 0.08 kB
26
26
  dist/components/PlAnnotations/components/PlAnnotationsModal.vue?vue&type=style&index=0&lang.css 0.07 kB │ gzip: 0.08 kB
27
- dist/components/PlDatasetSelector/PlDatasetSelector.vue_vue_type_style_index_0_scoped_b52c3499_lang.css 0.08 kB │ gzip: 0.09 kB
27
+ dist/components/PlDatasetSelector/PlDatasetSelector.vue_vue_type_style_index_0_scoped_95f2adf4_lang.css 0.08 kB │ gzip: 0.09 kB
28
28
  dist/components/PlAgDataTable/PlAgDataTableV2.vue?vue&type=style&index=0&lang.css 0.09 kB │ gzip: 0.10 kB
29
29
  dist/components/PlAnnotations/components/FilterSidebar.vue?vue&type=style&index=0&lang.css 0.11 kB │ gzip: 0.09 kB
30
30
  dist/components/PlAgChartHistogramCell/PlAgChartHistogramCell.vue_vue_type_style_index_0_lang.css 0.16 kB │ gzip: 0.13 kB
@@ -100,10 +100,10 @@ dist/components/PlAdvancedFilter/index.js
100
100
  dist/components/PlAgDataTable/PlAgDataTableV2.vue_vue_type_style_index_0_lang.module.js 0.25 kB │ gzip: 0.20 kB │ map: 0.45 kB
101
101
  dist/components/PlAnnotations/components/PlAnnotationsModal.vue_vue_type_style_index_0_lang.module.js 0.25 kB │ gzip: 0.20 kB │ map: 0.46 kB
102
102
  dist/lib/util/helpers/dist/functions.js 0.26 kB │ gzip: 0.20 kB │ map: 5.18 kB
103
- dist/components/PlAgDataTable/sources/common.js 0.26 kB │ gzip: 0.20 kB │ map: 0.60 kB
104
103
  dist/components/PlAnnotations/utils.js 0.26 kB │ gzip: 0.23 kB │ map: 1.02 kB
105
104
  dist/components/PlBtnExportArchive/Summary.vue_vue_type_style_index_0_lang.module.js 0.26 kB │ gzip: 0.20 kB │ map: 0.50 kB
106
105
  dist/components/PlAppErrorNotificationAlert/PlAppErrorNotificationAlert.js 0.27 kB │ gzip: 0.17 kB │ map: 2.36 kB
106
+ dist/components/PlAgDataTable/sources/common.js 0.27 kB │ gzip: 0.21 kB │ map: 0.55 kB
107
107
  dist/components/PlAdvancedFilter/OperandButton.vue_vue_type_style_index_0_lang.module.js 0.27 kB │ gzip: 0.21 kB │ map: 0.50 kB
108
108
  dist/components/PlBtnExportArchive/Item.vue_vue_type_style_index_0_lang.module.js 0.28 kB │ gzip: 0.21 kB │ map: 0.54 kB
109
109
  dist/components/PlAgDataTable/pl-ag-overlay-loading.module.js 0.29 kB │ gzip: 0.20 kB │ map: 0.52 kB
@@ -169,12 +169,12 @@ dist/components/PlAgDataTable/compositions/useFilterableColumns.js
169
169
  dist/components/PlAgDataTable/sources/focus-row.js 0.95 kB │ gzip: 0.51 kB │ map: 2.22 kB
170
170
  dist/plugins/Monetization/validation.js 0.99 kB │ gzip: 0.49 kB │ map: 2.58 kB
171
171
  dist/AgGridVue/selection.js 1.00 kB │ gzip: 0.42 kB │ map: 3.18 kB
172
- dist/components/PlAgDataTable/sources/value-rendering.js 1.06 kB │ gzip: 0.52 kB │ map: 2.99 kB
173
172
  dist/components/PlAgChartHistogramCell/PlAgChartHistogramCell.vue_vue_type_script_setup_true_lang.js 1.10 kB │ gzip: 0.61 kB │ map: 1.88 kB
174
173
  dist/components/PlAgCellFile/PlAgCellFile.vue_vue_type_script_setup_true_lang.js 1.14 kB │ gzip: 0.64 kB │ map: 2.93 kB
175
174
  dist/plugins/Monetization/RunStatus.vue_vue_type_script_setup_true_lang.js 1.16 kB │ gzip: 0.62 kB │ map: 3.04 kB
176
175
  dist/components/PlAgGridColumnManager/useFilteredItems.js 1.18 kB │ gzip: 0.61 kB │ map: 3.48 kB
177
176
  dist/lib/util/helpers/dist/objects.js 1.20 kB │ gzip: 0.58 kB │ map: 9.83 kB
177
+ dist/components/PlAgDataTable/sources/value-rendering.js 1.21 kB │ gzip: 0.59 kB │ map: 3.54 kB
178
178
  dist/createModel.js 1.22 kB │ gzip: 0.61 kB │ map: 3.11 kB
179
179
  dist/utils.js 1.26 kB │ gzip: 0.62 kB │ map: 3.40 kB
180
180
  dist/lib/util/helpers/dist/prettyBytes.js 1.29 kB │ gzip: 0.67 kB │ map: 3.79 kB
@@ -207,7 +207,7 @@ dist/components/PlAgDataTable/compositions/useGrid.js
207
207
  dist/components/PlAgColumnHeader/PlAgColumnHeader.vue_vue_type_script_setup_true_lang.js 2.93 kB │ gzip: 1.26 kB │ map: 7.97 kB
208
208
  dist/components/PlAgDataTable/sources/row-number.js 2.99 kB │ gzip: 1.31 kB │ map: 7.42 kB
209
209
  dist/defineApp.js 3.01 kB │ gzip: 1.05 kB │ map: 14.38 kB
210
- dist/components/PlDatasetSelector/PlDatasetSelector.vue_vue_type_script_setup_true_lang.js 3.09 kB │ gzip: 1.21 kB │ map: 6.90 kB
210
+ dist/components/PlDatasetSelector/PlDatasetSelector.vue_vue_type_script_setup_true_lang.js 3.18 kB │ gzip: 1.24 kB │ map: 7.33 kB
211
211
  dist/plugins/Monetization/LimitCard.vue_vue_type_script_setup_true_lang.js 3.31 kB │ gzip: 1.15 kB │ map: 9.08 kB
212
212
  dist/composition/fileContent.js 3.78 kB │ gzip: 1.45 kB │ map: 12.95 kB
213
213
  dist/components/PlAgGridColumnManager/PlAgGridColumnManager.vue_vue_type_script_setup_true_lang.js 3.83 kB │ gzip: 1.64 kB │ map: 6.89 kB
@@ -228,12 +228,12 @@ dist/components/PlAdvancedFilter/FilterEditor.vue_vue_type_script_setup_true_lan
228
228
  dist/components/PlAgDataTable/PlAgDataTableV2.vue_vue_type_script_setup_true_lang.js 12.31 kB │ gzip: 3.90 kB │ map: 29.22 kB
229
229
 
230
230
  [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugins. Here is a breakdown:
231
- - vite:dts (30%)
232
- - sourcemaps (27%)
233
- - vite:vue (12%)
234
- - vite:css-post (10%)
235
- - vite:css (8%)
231
+ - sourcemaps (30%)
232
+ - vite:dts (26%)
233
+ - vite:css-post (12%)
234
+ - vite:vue (11%)
235
+ - vite:css (7%)
236
236
  See https://rolldown.rs/options/checks#plugintimings for more details.
237
237
  
238
- ✓ built in 5.79s
238
+ ✓ built in 5.47s
239
239
  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.69.0 formatter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.71.0 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 1533ms on 137 files using 8 threads.
11
+ Finished in 5486ms on 137 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.69.0 linter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.71.0 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 29ms on 120 files with 98 rules using 8 threads.
9
+ Finished in 53ms on 120 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.69.0 types:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.71.0 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,29 @@
1
1
  # @platforma-sdk/ui-vue
2
2
 
3
+ ## 1.71.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6369956: Show table with partial data
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [6369956]
12
+ - @milaboratories/pl-model-common@1.38.0
13
+ - @platforma-sdk/model@1.71.0
14
+ - @milaboratories/pf-spec-driver@1.3.8
15
+ - @milaboratories/uikit@2.13.2
16
+
17
+ ## 1.70.0
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [a40505e]
22
+ - @milaboratories/pl-model-common@1.37.0
23
+ - @platforma-sdk/model@1.70.0
24
+ - @milaboratories/pf-spec-driver@1.3.7
25
+ - @milaboratories/uikit@2.13.1
26
+
3
27
  ## 1.69.0
4
28
 
5
29
  ### Minor Changes
@@ -1,7 +1,6 @@
1
- import { PTableValue } from '@platforma-sdk/model';
2
1
  export declare const PTableHidden: {
3
2
  readonly type: "hidden";
4
3
  };
5
4
  export type PTableHidden = typeof PTableHidden;
6
- export declare function isPTableHidden(value: PTableValue | PTableHidden): value is PTableHidden;
5
+ export declare function isPTableHidden(value: unknown): value is PTableHidden;
7
6
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../src/components/PlAgDataTable/sources/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,eAAO,MAAM,YAAY;;CAA8B,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC;AAE/C,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,GAAG,KAAK,IAAI,YAAY,CAEvF"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../src/components/PlAgDataTable/sources/common.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY;;CAA8B,CAAC;AACxD,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC;AAE/C,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE"}
@@ -1,7 +1,7 @@
1
1
  //#region src/components/PlAgDataTable/sources/common.ts
2
2
  var e = { type: "hidden" };
3
3
  function t(e) {
4
- return typeof e == "object" && !!e && e.type === "hidden";
4
+ return typeof e == "object" && !!e && "type" in e && e.type === "hidden";
5
5
  }
6
6
  //#endregion
7
7
  export { e as PTableHidden, t as isPTableHidden };
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","names":[],"sources":["../../../../src/components/PlAgDataTable/sources/common.ts"],"sourcesContent":["import type { PTableValue } from \"@platforma-sdk/model\";\n\nexport const PTableHidden = { type: \"hidden\" } as const;\nexport type PTableHidden = typeof PTableHidden;\n\nexport function isPTableHidden(value: PTableValue | PTableHidden): value is PTableHidden {\n return typeof value === \"object\" && value !== null && value.type === \"hidden\";\n}\n"],"mappings":";AAEA,IAAa,IAAe,EAAE,MAAM,UAAU;AAG9C,SAAgB,EAAe,GAA0D;AACvF,QAAO,OAAO,KAAU,cAAY,KAAkB,EAAM,SAAS"}
1
+ {"version":3,"file":"common.js","names":[],"sources":["../../../../src/components/PlAgDataTable/sources/common.ts"],"sourcesContent":["export const PTableHidden = { type: \"hidden\" } as const;\nexport type PTableHidden = typeof PTableHidden;\n\nexport function isPTableHidden(value: unknown): value is PTableHidden {\n return typeof value === \"object\" && value !== null && \"type\" in value && value.type === \"hidden\";\n}\n"],"mappings":";AAAA,IAAa,IAAe,EAAE,MAAM,UAAU;AAG9C,SAAgB,EAAe,GAAuC;AACpE,QAAO,OAAO,KAAU,cAAY,KAAkB,UAAU,KAAS,EAAM,SAAS"}
@@ -2,7 +2,7 @@ import { PTableColumnSpec, PTableValue } from '@platforma-sdk/model';
2
2
  import { ValueFormatterFunc } from 'ag-grid-enterprise';
3
3
  import { PTableHidden } from './common';
4
4
  import { PlAgDataTableV2Row } from '../types';
5
- export declare function formatSpecialValues(value: PTableValue | PTableHidden | undefined): string | undefined;
5
+ export declare function formatSpecialValues(value: undefined | PTableValue | PTableHidden, dataStatus: undefined | "absent" | "error" | "computing" | "ready"): string | undefined;
6
6
  export type ColumnRenderingSpec = {
7
7
  valueFormatter: ValueFormatterFunc<PlAgDataTableV2Row, PTableValue | PTableHidden>;
8
8
  fontFamily?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"value-rendering.d.ts","sourceRoot":"","sources":["../../../../src/components/PlAgDataTable/sources/value-rendering.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,WAAW,GAAG,YAAY,GAAG,SAAS,GAC5C,MAAM,GAAG,SAAS,CAUpB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,kBAAkB,CAAC,kBAAkB,EAAE,WAAW,GAAG,YAAY,CAAC,CAAC;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,mBAAmB,CAgClF"}
1
+ {"version":3,"file":"value-rendering.d.ts","sourceRoot":"","sources":["../../../../src/components/PlAgDataTable/sources/value-rendering.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,YAAY,EAC7C,UAAU,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,GACjE,MAAM,GAAG,SAAS,CAcpB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,kBAAkB,CAAC,kBAAkB,EAAE,WAAW,GAAG,YAAY,CAAC,CAAC;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,GAAG,mBAAmB,CAoClF"}
@@ -2,8 +2,10 @@ import { isPTableHidden as e } from "./common.js";
2
2
  import { Annotation as t, PTableNA as n, ValueType as r, readAnnotation as i } from "@platforma-sdk/model";
3
3
  import * as a from "d3-format";
4
4
  //#region src/components/PlAgDataTable/sources/value-rendering.ts
5
- function o(t) {
6
- if (t === void 0) return "undefined";
5
+ function o(t, r) {
6
+ if (r === "absent") return "absent";
7
+ if (r === "error") return "error";
8
+ if (r === "computing") return "computing...";
7
9
  if (e(t)) return "loading...";
8
10
  if (t === n) return "";
9
11
  }
@@ -14,17 +16,17 @@ function s(e) {
14
16
  case r.Long:
15
17
  case r.Float:
16
18
  case r.Double: {
17
- let n = i(e.spec, t.Format), r = n ? a.format(n) : void 0;
19
+ let n = i(e.spec, t.Format), r = i(e.spec, t.DataStatus), c = n ? a.format(n) : void 0;
18
20
  s = { valueFormatter: (e) => {
19
- let t = o(e.value);
20
- return t === void 0 ? r ? r(Number(e.value)) : e.value.toString() : t;
21
+ let t = o(e.value, r);
22
+ return t === void 0 ? c ? c(Number(e.value)) : e.value?.toString() ?? "" : t;
21
23
  } };
22
24
  break;
23
25
  }
24
26
  default:
25
- s = { valueFormatter: (e) => {
26
- let t = o(e.value);
27
- return t === void 0 ? e.value.toString() : t;
27
+ s = { valueFormatter: (n) => {
28
+ let r = o(n.value, i(e.spec, t.DataStatus));
29
+ return r === void 0 ? n.value?.toString() ?? "" : r;
28
30
  } };
29
31
  break;
30
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"value-rendering.js","names":[],"sources":["../../../../src/components/PlAgDataTable/sources/value-rendering.ts"],"sourcesContent":["import {\n Annotation,\n PTableNA,\n readAnnotation,\n ValueType,\n type PTableColumnSpec,\n type PTableValue,\n} from \"@platforma-sdk/model\";\nimport type { ValueFormatterFunc } from \"ag-grid-enterprise\";\nimport type { PTableHidden } from \"./common\";\nimport { isPTableHidden } from \"./common\";\nimport * as d3 from \"d3-format\";\nimport type { PlAgDataTableV2Row } from \"../types\";\n\nexport function formatSpecialValues(\n value: PTableValue | PTableHidden | undefined,\n): string | undefined {\n if (value === undefined) {\n return \"undefined\";\n } else if (isPTableHidden(value)) {\n return \"loading...\";\n } else if (value === PTableNA) {\n return \"\";\n } else {\n return undefined;\n }\n}\n\nexport type ColumnRenderingSpec = {\n valueFormatter: ValueFormatterFunc<PlAgDataTableV2Row, PTableValue | PTableHidden>;\n fontFamily?: string;\n};\n\nexport function getColumnRenderingSpec(spec: PTableColumnSpec): ColumnRenderingSpec {\n const valueType = spec.type === \"axis\" ? spec.spec.type : spec.spec.valueType;\n let renderSpec: ColumnRenderingSpec;\n switch (valueType) {\n case ValueType.Int:\n case ValueType.Long:\n case ValueType.Float:\n case ValueType.Double: {\n const format = readAnnotation(spec.spec, Annotation.Format);\n const formatFn = format ? d3.format(format) : undefined;\n renderSpec = {\n valueFormatter: (params) => {\n const formatted = formatSpecialValues(params.value);\n if (formatted !== undefined) return formatted;\n return formatFn ? formatFn(Number(params.value)) : params.value!.toString();\n },\n };\n break;\n }\n default:\n renderSpec = {\n valueFormatter: (params) => {\n const formatted = formatSpecialValues(params.value);\n if (formatted !== undefined) return formatted;\n return params.value!.toString();\n },\n };\n break;\n }\n const fontFamily = readAnnotation(spec.spec, Annotation.Table.FontFamily);\n if (fontFamily) renderSpec.fontFamily = fontFamily;\n return renderSpec;\n}\n"],"mappings":";;;;AAcA,SAAgB,EACd,GACoB;AACpB,KAAI,MAAU,KAAA,EACZ,QAAO;KACE,EAAe,EAAM,CAC9B,QAAO;KACE,MAAU,EACnB,QAAO;;AAWX,SAAgB,EAAuB,GAA6C;CAClF,IAAM,IAAY,EAAK,SAAS,SAAS,EAAK,KAAK,OAAO,EAAK,KAAK,WAChE;AACJ,SAAQ,GAAR;EACE,KAAK,EAAU;EACf,KAAK,EAAU;EACf,KAAK,EAAU;EACf,KAAK,EAAU,QAAQ;GACrB,IAAM,IAAS,EAAe,EAAK,MAAM,EAAW,OAAO,EACrD,IAAW,IAAS,EAAG,OAAO,EAAO,GAAG,KAAA;AAC9C,OAAa,EACX,iBAAiB,MAAW;IAC1B,IAAM,IAAY,EAAoB,EAAO,MAAM;AAEnD,WADI,MAAc,KAAA,IACX,IAAW,EAAS,OAAO,EAAO,MAAM,CAAC,GAAG,EAAO,MAAO,UAAU,GADvC;MAGvC;AACD;;EAEF;AACE,OAAa,EACX,iBAAiB,MAAW;IAC1B,IAAM,IAAY,EAAoB,EAAO,MAAM;AAEnD,WADI,MAAc,KAAA,IACX,EAAO,MAAO,UAAU,GADK;MAGvC;AACD;;CAEJ,IAAM,IAAa,EAAe,EAAK,MAAM,EAAW,MAAM,WAAW;AAEzE,QADI,MAAY,EAAW,aAAa,IACjC"}
1
+ {"version":3,"file":"value-rendering.js","names":[],"sources":["../../../../src/components/PlAgDataTable/sources/value-rendering.ts"],"sourcesContent":["import {\n Annotation,\n PTableNA,\n readAnnotation,\n ValueType,\n type PTableColumnSpec,\n type PTableValue,\n} from \"@platforma-sdk/model\";\nimport type { ValueFormatterFunc } from \"ag-grid-enterprise\";\nimport type { PTableHidden } from \"./common\";\nimport { isPTableHidden } from \"./common\";\nimport * as d3 from \"d3-format\";\nimport type { PlAgDataTableV2Row } from \"../types\";\n\nexport function formatSpecialValues(\n value: undefined | PTableValue | PTableHidden,\n dataStatus: undefined | \"absent\" | \"error\" | \"computing\" | \"ready\",\n): string | undefined {\n if (dataStatus === \"absent\") {\n return \"absent\";\n } else if (dataStatus === \"error\") {\n return \"error\";\n } else if (dataStatus === \"computing\") {\n return \"computing...\";\n } else if (isPTableHidden(value)) {\n return \"loading...\";\n } else if (value === PTableNA) {\n return \"\";\n } else {\n return undefined;\n }\n}\n\nexport type ColumnRenderingSpec = {\n valueFormatter: ValueFormatterFunc<PlAgDataTableV2Row, PTableValue | PTableHidden>;\n fontFamily?: string;\n};\n\nexport function getColumnRenderingSpec(spec: PTableColumnSpec): ColumnRenderingSpec {\n const valueType = spec.type === \"axis\" ? spec.spec.type : spec.spec.valueType;\n let renderSpec: ColumnRenderingSpec;\n switch (valueType) {\n case ValueType.Int:\n case ValueType.Long:\n case ValueType.Float:\n case ValueType.Double: {\n const format = readAnnotation(spec.spec, Annotation.Format);\n const dataStatus = readAnnotation(spec.spec, Annotation.DataStatus);\n const formatFn = format ? d3.format(format) : undefined;\n renderSpec = {\n valueFormatter: (params) => {\n const formatted = formatSpecialValues(params.value, dataStatus);\n if (formatted !== undefined) return formatted;\n return formatFn ? formatFn(Number(params.value)) : (params.value?.toString() ?? \"\");\n },\n };\n break;\n }\n default:\n renderSpec = {\n valueFormatter: (params) => {\n const formatted = formatSpecialValues(\n params.value,\n readAnnotation(spec.spec, Annotation.DataStatus),\n );\n if (formatted !== undefined) return formatted;\n return params.value?.toString() ?? \"\";\n },\n };\n break;\n }\n const fontFamily = readAnnotation(spec.spec, Annotation.Table.FontFamily);\n if (fontFamily) renderSpec.fontFamily = fontFamily;\n return renderSpec;\n}\n"],"mappings":";;;;AAcA,SAAgB,EACd,GACA,GACoB;AACpB,KAAI,MAAe,SACjB,QAAO;KACE,MAAe,QACxB,QAAO;KACE,MAAe,YACxB,QAAO;KACE,EAAe,EAAM,CAC9B,QAAO;KACE,MAAU,EACnB,QAAO;;AAWX,SAAgB,EAAuB,GAA6C;CAClF,IAAM,IAAY,EAAK,SAAS,SAAS,EAAK,KAAK,OAAO,EAAK,KAAK,WAChE;AACJ,SAAQ,GAAR;EACE,KAAK,EAAU;EACf,KAAK,EAAU;EACf,KAAK,EAAU;EACf,KAAK,EAAU,QAAQ;GACrB,IAAM,IAAS,EAAe,EAAK,MAAM,EAAW,OAAO,EACrD,IAAa,EAAe,EAAK,MAAM,EAAW,WAAW,EAC7D,IAAW,IAAS,EAAG,OAAO,EAAO,GAAG,KAAA;AAC9C,OAAa,EACX,iBAAiB,MAAW;IAC1B,IAAM,IAAY,EAAoB,EAAO,OAAO,EAAW;AAE/D,WADI,MAAc,KAAA,IACX,IAAW,EAAS,OAAO,EAAO,MAAM,CAAC,GAAI,EAAO,OAAO,UAAU,IAAI,KAD5C;MAGvC;AACD;;EAEF;AACE,OAAa,EACX,iBAAiB,MAAW;IAC1B,IAAM,IAAY,EAChB,EAAO,OACP,EAAe,EAAK,MAAM,EAAW,WAAW,CACjD;AAED,WADI,MAAc,KAAA,IACX,EAAO,OAAO,UAAU,IAAI,KADC;MAGvC;AACD;;CAEJ,IAAM,IAAa,EAAe,EAAK,MAAM,EAAW,MAAM,WAAW;AAEzE,QADI,MAAY,EAAW,aAAa,IACjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"PlAnnotations.vue_vue_type_script_setup_true_lang.js","names":["$style"],"sources":["../../../../src/components/PlAnnotations/components/PlAnnotations.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { Props as BaseProps } from \"./FilterSidebar.vue\";\nexport type Props = Omit<BaseProps, \"step\" | \"onUpdateStep\"> & {\n annotation: Annotation;\n onUpdateAnnotation: (annotation: Annotation) => void;\n onDeleteSchema?: () => void;\n};\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, effect, shallowRef } from \"vue\";\n\nimport { isNil } from \"@milaboratories/helpers\";\nimport { produce } from \"immer\";\nimport { PlSidebarGroup, useConfirm } from \"@milaboratories/uikit\";\n\nimport type { Annotation, Filter } from \"../types\";\nimport AnnotationsSidebar from \"./AnnotationsSidebar.vue\";\nimport FilterSidebar from \"./FilterSidebar.vue\";\n\nconst props = defineProps<Props>();\n\nconst selectedStepId = shallowRef<undefined | number>(undefined);\n\nconst selectedStep = computed(() => {\n return isNil(selectedStepId.value) || isNil(props.annotation)\n ? undefined\n : props.annotation.steps.find((step) => step.id === selectedStepId.value);\n});\n\neffect(function setDefaultStepId() {\n if (selectedStepId.value === undefined && props.annotation.steps.length > 0) {\n selectedStepId.value = props.annotation.steps[0].id;\n }\n});\n\nconst confirmResetSchema = useConfirm({\n title: \"Reset Schema\",\n message: \"Are you sure you want to reset the schema? This action cannot be undone.\",\n confirmLabel: \"Yes, reset\",\n cancelLabel: \"No, cancel\",\n});\n\nasync function handleDeleteSchema() {\n if (await confirmResetSchema()) {\n selectedStepId.value = undefined;\n props.onDeleteSchema?.();\n }\n}\n\nfunction updateSelectedStepId(id: undefined | number) {\n selectedStepId.value = id;\n}\n\nfunction updateSelectedStep(step: Filter) {\n props.onUpdateAnnotation(\n produce(props.annotation, (draft) => {\n const idx = draft.steps.findIndex((s) => s.id === step.id);\n if (idx !== -1) {\n draft.steps[idx] = step;\n }\n }),\n );\n}\n</script>\n\n<template>\n <PlSidebarGroup>\n <template #item-0>\n <AnnotationsSidebar\n :annotation=\"props.annotation\"\n :selected-step-id=\"selectedStepId\"\n :on-update-annotation=\"props.onUpdateAnnotation\"\n :on-update-selected-step-id=\"updateSelectedStepId\"\n :class=\"$style.sidebarItem\"\n @delete-schema=\"handleDeleteSchema\"\n />\n </template>\n <template #item-1>\n <FilterSidebar\n v-if=\"selectedStep\"\n :step=\"selectedStep\"\n :on-update-step=\"updateSelectedStep\"\n :class=\"$style.sidebarItem\"\n :columns=\"props.columns\"\n :get-suggest-options=\"props.getSuggestOptions\"\n :hasSelectedColumns=\"props.hasSelectedColumns\"\n :getValuesForSelectedColumns=\"props.getValuesForSelectedColumns\"\n />\n </template>\n </PlSidebarGroup>\n</template>\n\n<style module>\n.sidebarItem {\n width: 100%;\n height: 100%;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAoBA,IAAM,IAAQ,GAER,IAAiB,EAA+B,KAAA,EAAU,EAE1D,IAAe,QACZ,EAAM,EAAe,MAAM,IAAI,EAAM,EAAM,WAAU,GACxD,KAAA,IACA,EAAM,WAAW,MAAM,MAAM,MAAS,EAAK,OAAO,EAAe,MAAM,CAC3E;AAEF,IAAO,WAA4B;AACjC,GAAI,EAAe,UAAU,KAAA,KAAa,EAAM,WAAW,MAAM,SAAS,MACxE,EAAe,QAAQ,EAAM,WAAW,MAAM,GAAG;IAEnD;EAEF,IAAM,IAAqB,EAAW;GACpC,OAAO;GACP,SAAS;GACT,cAAc;GACd,aAAa;GACd,CAAC;EAEF,eAAe,IAAqB;AAClC,GAAI,MAAM,GAAoB,KAC5B,EAAe,QAAQ,KAAA,GACvB,EAAM,kBAAkB;;EAI5B,SAAS,EAAqB,GAAwB;AACpD,KAAe,QAAQ;;EAGzB,SAAS,EAAmB,GAAc;AACxC,KAAM,mBACJ,EAAQ,EAAM,aAAa,MAAU;IACnC,IAAM,IAAM,EAAM,MAAM,WAAW,MAAM,EAAE,OAAO,EAAK,GAAG;AAC1D,IAAI,MAAQ,OACV,EAAM,MAAM,KAAO;KAErB,CACH;;yBAKD,EAuBiB,EAAA,EAAA,EAAA,MAAA;GAtBJ,UAAM,QAQb,CAPF,EAOE,GAAA;IANC,YAAY,EAAM;IAClB,oBAAkB,EAAA;IAClB,wBAAsB,EAAM;IAC5B,8BAA4B;IAC5B,OAAK,EAAEA,EAAAA,OAAO,YAAW;IACzB,gBAAe;;;;;;;GAGT,UAAM,QAiBZ,CAfK,EAAA,SAAA,GAAA,EADR,EASE,GAAA;;IAPC,MAAM,EAAA;IACN,kBAAgB;IAChB,OAAK,EAAEA,EAAAA,OAAO,YAAW;IACzB,SAAS,EAAM;IACf,uBAAqB,EAAM;IAC3B,oBAAoB,EAAM;IAC1B,6BAA6B,EAAM"}
1
+ {"version":3,"file":"PlAnnotations.vue_vue_type_script_setup_true_lang.js","names":["$style"],"sources":["../../../../src/components/PlAnnotations/components/PlAnnotations.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { Props as BaseProps } from \"./FilterSidebar.vue\";\nexport type Props = Omit<BaseProps, \"step\" | \"onUpdateStep\"> & {\n annotation: Annotation;\n onUpdateAnnotation: (annotation: Annotation) => void;\n onDeleteSchema?: () => void;\n};\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, effect, shallowRef } from \"vue\";\n\nimport { isNil } from \"@milaboratories/helpers\";\nimport { produce } from \"immer\";\nimport { PlSidebarGroup, useConfirm } from \"@milaboratories/uikit\";\n\nimport type { Annotation, Filter } from \"../types\";\nimport AnnotationsSidebar from \"./AnnotationsSidebar.vue\";\nimport FilterSidebar from \"./FilterSidebar.vue\";\n\nconst props = defineProps<Props>();\n\nconst selectedStepId = shallowRef<undefined | number>(undefined);\n\nconst selectedStep = computed(() => {\n return isNil(selectedStepId.value) || isNil(props.annotation)\n ? undefined\n : props.annotation.steps.find((step) => step.id === selectedStepId.value);\n});\n\neffect(function setDefaultStepId() {\n if (selectedStepId.value === undefined && props.annotation.steps.length > 0) {\n selectedStepId.value = props.annotation.steps[0].id;\n }\n});\n\nconst confirmResetSchema = useConfirm({\n title: \"Reset Schema\",\n message: \"Are you sure you want to reset the schema? This action cannot be undone.\",\n confirmLabel: \"Yes, reset\",\n cancelLabel: \"No, cancel\",\n});\n\nasync function handleDeleteSchema() {\n if (await confirmResetSchema()) {\n selectedStepId.value = undefined;\n props.onDeleteSchema?.();\n }\n}\n\nfunction updateSelectedStepId(id: undefined | number) {\n selectedStepId.value = id;\n}\n\nfunction updateSelectedStep(step: Filter) {\n props.onUpdateAnnotation(\n produce(props.annotation, (draft) => {\n const idx = draft.steps.findIndex((s) => s.id === step.id);\n if (idx !== -1) {\n draft.steps[idx] = step;\n }\n }),\n );\n}\n</script>\n\n<template>\n <PlSidebarGroup>\n <template #item-0>\n <AnnotationsSidebar\n :annotation=\"props.annotation\"\n :selected-step-id=\"selectedStepId\"\n :on-update-annotation=\"props.onUpdateAnnotation\"\n :on-update-selected-step-id=\"updateSelectedStepId\"\n :class=\"$style.sidebarItem\"\n @delete-schema=\"handleDeleteSchema\"\n />\n </template>\n <template #item-1>\n <FilterSidebar\n v-if=\"selectedStep\"\n :step=\"selectedStep\"\n :on-update-step=\"updateSelectedStep\"\n :class=\"$style.sidebarItem\"\n :columns=\"props.columns\"\n :get-suggest-options=\"props.getSuggestOptions\"\n :hasSelectedColumns=\"props.hasSelectedColumns\"\n :getValuesForSelectedColumns=\"props.getValuesForSelectedColumns\"\n />\n </template>\n </PlSidebarGroup>\n</template>\n\n<style module>\n.sidebarItem {\n width: 100%;\n height: 100%;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAoBA,IAAM,IAAQ,GAER,IAAiB,EAA+B,KAAA,EAAU,EAE1D,IAAe,QACZ,EAAM,EAAe,MAAM,IAAI,EAAM,EAAM,WAAU,GACxD,KAAA,IACA,EAAM,WAAW,MAAM,MAAM,MAAS,EAAK,OAAO,EAAe,MAAM,CAC3E;AAEF,IAAO,WAA4B;AACjC,GAAI,EAAe,UAAU,KAAA,KAAa,EAAM,WAAW,MAAM,SAAS,MACxE,EAAe,QAAQ,EAAM,WAAW,MAAM,GAAG;IAEnD;EAEF,IAAM,IAAqB,EAAW;GACpC,OAAO;GACP,SAAS;GACT,cAAc;GACd,aAAa;GACd,CAAC;EAEF,eAAe,IAAqB;AAClC,GAAI,MAAM,GAAoB,KAC5B,EAAe,QAAQ,KAAA,GACvB,EAAM,kBAAkB;;EAI5B,SAAS,EAAqB,GAAwB;AACpD,KAAe,QAAQ;;EAGzB,SAAS,EAAmB,GAAc;AACxC,KAAM,mBACJ,EAAQ,EAAM,aAAa,MAAU;IACnC,IAAM,IAAM,EAAM,MAAM,WAAW,MAAM,EAAE,OAAO,EAAK,GAAG;AAC1D,IAAI,MAAQ,OACV,EAAM,MAAM,KAAO;KAErB,CACH;;yBAKD,EAuBiB,EAAA,EAAA,EAAA,MAAA;GAtBJ,UAAM,QAQb,CAPF,EAOE,GAAA;IANC,YAAY,EAAM;IAClB,oBAAkB,EAAA;IAClB,wBAAsB,EAAM;IAC5B,8BAA4B;IAC5B,OAAK,EAAEA,EAAAA,OAAO,YAAW;IACzB,gBAAe;;;;;;;GAGT,UAAM,QAUb,CARM,EAAA,SAAA,GAAA,EADR,EASE,GAAA;;IAPC,MAAM,EAAA;IACN,kBAAgB;IAChB,OAAK,EAAEA,EAAAA,OAAO,YAAW;IACzB,SAAS,EAAM;IACf,uBAAqB,EAAM;IAC3B,oBAAoB,EAAM;IAC1B,6BAA6B,EAAM"}
@@ -2,7 +2,7 @@ import e from "../../_virtual/_plugin-vue_export-helper.js";
2
2
  import t from "./PlDatasetSelector.vue2.js";
3
3
  import './PlDatasetSelector.style.css';/* empty css */
4
4
  //#region src/components/PlDatasetSelector/PlDatasetSelector.vue
5
- var n = /* @__PURE__ */ e(t, [["__scopeId", "data-v-b52c3499"]]);
5
+ var n = /* @__PURE__ */ e(t, [["__scopeId", "data-v-95f2adf4"]]);
6
6
  //#endregion
7
7
  export { n as default };
8
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"PlDatasetSelector.js","names":[],"sources":["../../../src/components/PlDatasetSelector/PlDatasetSelector.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Select a dataset and (optionally) a filter column, emitting a `PrimaryRef`.\n *\n * Behaves like {@link PlDropdownRef} when none of the offered datasets carry\n * filter options. When the selected dataset has compatible filters, a second\n * dropdown appears with the filters plus a \"No filter\" entry.\n *\n * Accepts `PrimaryRef | PlRef | undefined` as `modelValue` (a plain `PlRef`\n * is treated as an unfiltered dataset), but always emits `PrimaryRef`\n * (or `undefined` when cleared).\n */\nexport default {\n name: \"PlDatasetSelector\",\n};\n</script>\n\n<script lang=\"ts\" setup>\nimport type { DatasetOption, PlRef, PrimaryRef } from \"@platforma-sdk/model\";\nimport { createPrimaryRef, isPrimaryRef, plRefsEqual } from \"@platforma-sdk/model\";\nimport type { ListOption } from \"@milaboratories/uikit\";\nimport { PlDropdown, PlDropdownRef } from \"@milaboratories/uikit\";\nimport { computed } from \"vue\";\n\nconst slots = defineSlots<{\n tooltip?: () => unknown;\n}>();\n\n/**\n * v-model value. Accepts `PrimaryRef`, plain `PlRef` (treated as unfiltered),\n * or `undefined`. Writes always emit `PrimaryRef` or `undefined`.\n */\nconst model = defineModel<PrimaryRef | PlRef | undefined>();\n\nconst props = withDefaults(\n defineProps<{\n /** Available datasets, each optionally carrying compatible filter choices. */\n options?: Readonly<DatasetOption[]>;\n /** Label above the dataset dropdown. */\n label?: string;\n /** Helper text below the dataset dropdown (shown when there is no error). */\n helper?: string;\n /** Helper text shown while `options` is undefined (loading). */\n loadingOptionsHelper?: string;\n /** Error message displayed below the dataset dropdown. */\n error?: unknown;\n /** Placeholder when no dataset is selected. */\n placeholder?: string;\n /** Label above the filter dropdown. */\n filterLabel?: string;\n /** Placeholder for the filter dropdown. */\n filterPlaceholder?: string;\n /** Label of the \"no filter\" entry prepended to the filter options. */\n noFilterLabel?: string;\n /** Show a clear button on the dataset dropdown. */\n clearable?: boolean;\n /** Mark the dataset dropdown as required. */\n required?: boolean;\n /** Disable all interaction. */\n disabled?: boolean;\n }>(),\n {\n options: undefined,\n label: undefined,\n helper: undefined,\n loadingOptionsHelper: undefined,\n error: undefined,\n placeholder: \"...\",\n filterLabel: \"\",\n filterPlaceholder: \"...\",\n noFilterLabel: \"No filter\",\n clearable: false,\n required: false,\n disabled: false,\n },\n);\n\nconst selectedDataset = computed<PlRef | undefined>(() => {\n const v = model.value;\n if (v === undefined) return undefined;\n return isPrimaryRef(v) ? v.column : v;\n});\n\nconst selectedFilter = computed<PlRef | undefined>(() => {\n const v = model.value;\n return isPrimaryRef(v) ? v.filter : undefined;\n});\n\nconst currentDatasetOption = computed<DatasetOption | undefined>(() => {\n const dataset = selectedDataset.value;\n if (!dataset) return undefined;\n return props.options?.find((o) => plRefsEqual(o.ref, dataset, true));\n});\n\nconst hasFilters = computed(() => (currentDatasetOption.value?.filters?.length ?? 0) > 0);\n\n/**\n * Filter dropdown options. The first entry (`null`) is the \"No filter\" choice —\n * null distinguishes it from `undefined` (dropdown clear button, disabled here).\n */\nconst filterOptions = computed<ListOption<PlRef | null>[]>(() => {\n const filters = currentDatasetOption.value?.filters;\n if (!filters) return [];\n return [\n { label: props.noFilterLabel, value: null } as ListOption<PlRef | null>,\n ...filters.map((f) => ({ label: f.label, value: f.ref }) as ListOption<PlRef | null>),\n ];\n});\n\nconst filterValue = computed<PlRef | null>(() => selectedFilter.value ?? null);\n\nfunction emitValue(dataset: PlRef | undefined, filter: PlRef | undefined) {\n model.value = dataset === undefined ? undefined : createPrimaryRef(dataset, filter);\n}\n\nfunction onDatasetChange(dataset: PlRef | undefined) {\n emitValue(dataset, undefined);\n}\n\nfunction onFilterChange(value: PlRef | null | undefined) {\n const dataset = selectedDataset.value;\n if (!dataset) return;\n emitValue(dataset, value ?? undefined);\n}\n</script>\n\n<template>\n <div class=\"pl-dataset-selector\">\n <PlDropdownRef\n :model-value=\"selectedDataset\"\n :options=\"options\"\n :label=\"label\"\n :helper=\"helper\"\n :loading-options-helper=\"loadingOptionsHelper\"\n :error=\"error\"\n :placeholder=\"placeholder\"\n :clearable=\"clearable\"\n :required=\"required\"\n :disabled=\"disabled\"\n @update:model-value=\"onDatasetChange\"\n >\n <template v-if=\"slots.tooltip\" #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlDropdownRef>\n <PlDropdown\n v-if=\"hasFilters\"\n :model-value=\"filterValue\"\n :options=\"filterOptions\"\n :label=\"filterLabel\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n @update:model-value=\"onFilterChange\"\n />\n </div>\n</template>\n\n<style scoped>\n.pl-dataset-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"PlDatasetSelector.js","names":[],"sources":["../../../src/components/PlDatasetSelector/PlDatasetSelector.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Select a dataset and (optionally) a filter column, emitting a {@link DatasetSelection}.\n *\n * Behaves like {@link PlDropdownRef} when none of the offered datasets carry\n * filter options. When the selected dataset has compatible filters, a second\n * dropdown appears with the filters plus a \"No filter\" entry.\n *\n * The emitted value bundles the user's pick (`primary`) with the auto-attached\n * `enrichments` payload from the matching `DatasetOption`. Enrichments are\n * opaque to the UI block authors unbundle them inside their args resolver.\n */\nexport default {\n name: \"PlDatasetSelector\",\n};\n</script>\n\n<script lang=\"ts\" setup>\nimport type { DatasetOption, DatasetSelection, PlRef } from \"@platforma-sdk/model\";\nimport { createDatasetSelection, createPrimaryRef, plRefsEqual } from \"@platforma-sdk/model\";\nimport type { ListOption } from \"@milaboratories/uikit\";\nimport { PlDropdown, PlDropdownRef } from \"@milaboratories/uikit\";\nimport { computed } from \"vue\";\n\nconst slots = defineSlots<{\n tooltip?: () => unknown;\n}>();\n\nconst model = defineModel<DatasetSelection | undefined>();\n\nconst props = withDefaults(\n defineProps<{\n /** Available datasets, each optionally carrying compatible filter choices. */\n options?: Readonly<DatasetOption[]>;\n /** Label above the dataset dropdown. */\n label?: string;\n /** Helper text below the dataset dropdown (shown when there is no error). */\n helper?: string;\n /** Helper text shown while `options` is undefined (loading). */\n loadingOptionsHelper?: string;\n /** Error message displayed below the dataset dropdown. */\n error?: unknown;\n /** Placeholder when no dataset is selected. */\n placeholder?: string;\n /** Label above the filter dropdown. */\n filterLabel?: string;\n /** Placeholder for the filter dropdown. */\n filterPlaceholder?: string;\n /** Label of the \"no filter\" entry prepended to the filter options. */\n noFilterLabel?: string;\n /** Show a clear button on the dataset dropdown. */\n clearable?: boolean;\n /** Mark the dataset dropdown as required. */\n required?: boolean;\n /** Disable all interaction. */\n disabled?: boolean;\n }>(),\n {\n options: undefined,\n label: undefined,\n helper: undefined,\n loadingOptionsHelper: undefined,\n error: undefined,\n placeholder: \"...\",\n filterLabel: \"\",\n filterPlaceholder: \"...\",\n noFilterLabel: \"No filter\",\n clearable: false,\n required: false,\n disabled: false,\n },\n);\n\nconst selectedDataset = computed<PlRef | undefined>(() => model.value?.primary.column);\n\nconst selectedFilter = computed<PlRef | undefined>(() => model.value?.primary.filter);\n\nconst currentDatasetOption = computed<DatasetOption | undefined>(() => {\n const dataset = selectedDataset.value;\n if (!dataset) return undefined;\n return props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));\n});\n\n// PlDropdownRef expects `Option[]`; project the primary out of each entry.\nconst primaryOptions = computed<readonly { ref: PlRef; label: string }[] | undefined>(() =>\n props.options?.map((o) => o.primary),\n);\n\nconst hasFilters = computed(() => (currentDatasetOption.value?.filters?.length ?? 0) > 0);\n\n/**\n * Filter dropdown options. The first entry (`null`) is the \"No filter\" choice —\n * null distinguishes it from `undefined` (dropdown clear button, disabled here).\n */\nconst filterOptions = computed<ListOption<PlRef | null>[]>(() => {\n const filters = currentDatasetOption.value?.filters;\n if (!filters) return [];\n return [\n { label: props.noFilterLabel, value: null } as ListOption<PlRef | null>,\n ...filters.map((f) => ({ label: f.label, value: f.ref }) as ListOption<PlRef | null>),\n ];\n});\n\nconst filterValue = computed<PlRef | null>(() => selectedFilter.value ?? null);\n\nfunction emitValue(dataset: PlRef | undefined, filter: PlRef | undefined) {\n if (dataset === undefined) {\n model.value = undefined;\n return;\n }\n // Resolve from `props.options` directly `currentDatasetOption` may not\n // have recomputed yet when this runs synchronously inside a change handler.\n const option = props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));\n model.value = createDatasetSelection(createPrimaryRef(dataset, filter), option?.enrichments);\n}\n\nfunction onDatasetChange(dataset: PlRef | undefined) {\n emitValue(dataset, undefined);\n}\n\nfunction onFilterChange(value: PlRef | null | undefined) {\n const dataset = selectedDataset.value;\n if (!dataset) return;\n emitValue(dataset, value ?? undefined);\n}\n</script>\n\n<template>\n <div class=\"pl-dataset-selector\">\n <PlDropdownRef\n :model-value=\"selectedDataset\"\n :options=\"primaryOptions\"\n :label=\"label\"\n :helper=\"helper\"\n :loading-options-helper=\"loadingOptionsHelper\"\n :error=\"error\"\n :placeholder=\"placeholder\"\n :clearable=\"clearable\"\n :required=\"required\"\n :disabled=\"disabled\"\n @update:model-value=\"onDatasetChange\"\n >\n <template v-if=\"slots.tooltip\" #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlDropdownRef>\n <PlDropdown\n v-if=\"hasFilters\"\n :model-value=\"filterValue\"\n :options=\"filterOptions\"\n :label=\"filterLabel\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n @update:model-value=\"onFilterChange\"\n />\n </div>\n</template>\n\n<style scoped>\n.pl-dataset-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n</style>\n"],"mappings":""}
@@ -1 +1 @@
1
- .pl-dataset-selector[data-v-b52c3499]{flex-direction:column;gap:12px;display:flex}
1
+ .pl-dataset-selector[data-v-95f2adf4]{flex-direction:column;gap:12px;display:flex}
@@ -1,17 +1,17 @@
1
- import { DatasetOption, PlRef, PrimaryRef } from '@platforma-sdk/model';
1
+ import { DatasetOption, DatasetSelection } from '@platforma-sdk/model';
2
2
  /**
3
- * Select a dataset and (optionally) a filter column, emitting a `PrimaryRef`.
3
+ * Select a dataset and (optionally) a filter column, emitting a {@link DatasetSelection}.
4
4
  *
5
5
  * Behaves like {@link PlDropdownRef} when none of the offered datasets carry
6
6
  * filter options. When the selected dataset has compatible filters, a second
7
7
  * dropdown appears with the filters plus a "No filter" entry.
8
8
  *
9
- * Accepts `PrimaryRef | PlRef | undefined` as `modelValue` (a plain `PlRef`
10
- * is treated as an unfiltered dataset), but always emits `PrimaryRef`
11
- * (or `undefined` when cleared).
9
+ * The emitted value bundles the user's pick (`primary`) with the auto-attached
10
+ * `enrichments` payload from the matching `DatasetOption`. Enrichments are
11
+ * opaque to the UI — block authors unbundle them inside their args resolver.
12
12
  */
13
13
  declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
14
- modelValue?: PrimaryRef | PlRef | undefined;
14
+ modelValue?: DatasetSelection | undefined;
15
15
  } & {
16
16
  /** Available datasets, each optionally carrying compatible filter choices. */
17
17
  options?: Readonly<DatasetOption[]>;
@@ -38,14 +38,9 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
38
38
  /** Disable all interaction. */
39
39
  disabled?: boolean;
40
40
  }, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
41
- "update:modelValue": (value: PrimaryRef | Readonly<{
42
- __isRef: true;
43
- blockId: string;
44
- name: string;
45
- requireEnrichments?: true | undefined;
46
- }> | undefined) => any;
41
+ "update:modelValue": (value: DatasetSelection | undefined) => any;
47
42
  }, string, import('vue').PublicProps, Readonly<{
48
- modelValue?: PrimaryRef | PlRef | undefined;
43
+ modelValue?: DatasetSelection | undefined;
49
44
  } & {
50
45
  /** Available datasets, each optionally carrying compatible filter choices. */
51
46
  options?: Readonly<DatasetOption[]>;
@@ -72,12 +67,7 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
72
67
  /** Disable all interaction. */
73
68
  disabled?: boolean;
74
69
  }> & Readonly<{
75
- "onUpdate:modelValue"?: ((value: PrimaryRef | Readonly<{
76
- __isRef: true;
77
- blockId: string;
78
- name: string;
79
- requireEnrichments?: true | undefined;
80
- }> | undefined) => any) | undefined;
70
+ "onUpdate:modelValue"?: ((value: DatasetSelection | undefined) => any) | undefined;
81
71
  }>, {
82
72
  error: undefined;
83
73
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"PlDatasetSelector.vue.d.ts","sourceRoot":"","sources":["../../../src/components/PlDatasetSelector/PlDatasetSelector.vue"],"names":[],"mappings":"AAsKA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAO7E;;;;;;;;;;GAUG;;iBA0HU,UAAU,GAAG,KAAK,GAAG,SAAS;;IA7GvC,8EAA8E;cACpE,QAAQ,CAAC,aAAa,EAAE,CAAC;IACnC,wCAAwC;YAChC,MAAM;IACd,6EAA6E;aACpE,MAAM;IACf,gEAAgE;2BACzC,MAAM;IAC7B,0DAA0D;YAClD,OAAO;IACf,+CAA+C;kBACjC,MAAM;IACpB,uCAAuC;kBACzB,MAAM;IACpB,2CAA2C;wBACvB,MAAM;IAC1B,sEAAsE;oBACtD,MAAM;IACtB,mDAAmD;gBACvC,OAAO;IACnB,6CAA6C;eAClC,OAAO;IAClB,+BAA+B;eACpB,OAAO;;;;;;;;;iBAsFT,UAAU,GAAG,KAAK,GAAG,SAAS;;IA7GvC,8EAA8E;cACpE,QAAQ,CAAC,aAAa,EAAE,CAAC;IACnC,wCAAwC;YAChC,MAAM;IACd,6EAA6E;aACpE,MAAM;IACf,gEAAgE;2BACzC,MAAM;IAC7B,0DAA0D;YAClD,OAAO;IACf,+CAA+C;kBACjC,MAAM;IACpB,uCAAuC;kBACzB,MAAM;IACpB,2CAA2C;wBACvB,MAAM;IAC1B,sEAAsE;oBACtD,MAAM;IACtB,mDAAmD;gBACvC,OAAO;IACnB,6CAA6C;eAClC,OAAO;IAClB,+BAA+B;eACpB,OAAO;;;;;;;;;;WApBV,MAAM;iBAQA,MAAM;aAVV,QAAQ,CAAC,aAAa,EAAE,CAAC;YAI1B,MAAM;0BAEQ,MAAM;eAYjB,OAAO;cAER,OAAO;cAEP,OAAO;iBAVJ,MAAM;uBAEA,MAAM;mBAEV,MAAM;;cA3Bd,MAAM,OAAO;;cAAb,MAAM,OAAO;;AAFzB,wBAmQK;AAcL,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"PlDatasetSelector.vue.d.ts","sourceRoot":"","sources":["../../../src/components/PlDatasetSelector/PlDatasetSelector.vue"],"names":[],"mappings":"AAuKA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAS,MAAM,sBAAsB,CAAC;AAOnF;;;;;;;;;;GAUG;;iBA2HU,gBAAgB,GAAG,SAAS;;IAlHrC,8EAA8E;cACpE,QAAQ,CAAC,aAAa,EAAE,CAAC;IACnC,wCAAwC;YAChC,MAAM;IACd,6EAA6E;aACpE,MAAM;IACf,gEAAgE;2BACzC,MAAM;IAC7B,0DAA0D;YAClD,OAAO;IACf,+CAA+C;kBACjC,MAAM;IACpB,uCAAuC;kBACzB,MAAM;IACpB,2CAA2C;wBACvB,MAAM;IAC1B,sEAAsE;oBACtD,MAAM;IACtB,mDAAmD;gBACvC,OAAO;IACnB,6CAA6C;eAClC,OAAO;IAClB,+BAA+B;eACpB,OAAO;;;;iBA2FT,gBAAgB,GAAG,SAAS;;IAlHrC,8EAA8E;cACpE,QAAQ,CAAC,aAAa,EAAE,CAAC;IACnC,wCAAwC;YAChC,MAAM;IACd,6EAA6E;aACpE,MAAM;IACf,gEAAgE;2BACzC,MAAM;IAC7B,0DAA0D;YAClD,OAAO;IACf,+CAA+C;kBACjC,MAAM;IACpB,uCAAuC;kBACzB,MAAM;IACpB,2CAA2C;wBACvB,MAAM;IAC1B,sEAAsE;oBACtD,MAAM;IACtB,mDAAmD;gBACvC,OAAO;IACnB,6CAA6C;eAClC,OAAO;IAClB,+BAA+B;eACpB,OAAO;;;;;WApBV,MAAM;iBAQA,MAAM;aAVV,QAAQ,CAAC,aAAa,EAAE,CAAC;YAI1B,MAAM;0BAEQ,MAAM;eAYjB,OAAO;cAER,OAAO;cAEP,OAAO;iBAVJ,MAAM;uBAEA,MAAM;mBAEV,MAAM;;cAvBd,MAAM,OAAO;;cAAb,MAAM,OAAO;;AAFzB,wBAqQK;AAcL,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { computed as e, createBlock as t, createCommentVNode as n, createElementBlock as r, createSlots as i, createVNode as a, defineComponent as o, mergeModels as s, openBlock as c, renderSlot as l, unref as u, useModel as d, useSlots as f, withCtx as p } from "vue";
2
- import { createPrimaryRef as m, isPrimaryRef as h, plRefsEqual as g } from "@platforma-sdk/model";
2
+ import { createDatasetSelection as m, createPrimaryRef as h, plRefsEqual as g } from "@platforma-sdk/model";
3
3
  import { PlDropdown as _, PlDropdownRef as v } from "@milaboratories/uikit";
4
4
  //#region src/components/PlDatasetSelector/PlDatasetSelector.vue?vue&type=script&setup=true&lang.ts
5
5
  var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
@@ -32,16 +32,10 @@ var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
32
32
  }),
33
33
  emits: ["update:modelValue"],
34
34
  setup(o) {
35
- let s = f(), b = d(o, "modelValue"), x = o, S = e(() => {
36
- let e = b.value;
37
- if (e !== void 0) return h(e) ? e.column : e;
38
- }), C = e(() => {
39
- let e = b.value;
40
- return h(e) ? e.filter : void 0;
41
- }), w = e(() => {
35
+ let s = f(), b = d(o, "modelValue"), x = o, S = e(() => b.value?.primary.column), C = e(() => b.value?.primary.filter), w = e(() => {
42
36
  let e = S.value;
43
- if (e) return x.options?.find((t) => g(t.ref, e, !0));
44
- }), T = e(() => (w.value?.filters?.length ?? 0) > 0), E = e(() => {
37
+ if (e) return x.options?.find((t) => g(t.primary.ref, e, !0));
38
+ }), T = e(() => x.options?.map((e) => e.primary)), E = e(() => (w.value?.filters?.length ?? 0) > 0), D = e(() => {
45
39
  let e = w.value?.filters;
46
40
  return e ? [{
47
41
  label: x.noFilterLabel,
@@ -50,20 +44,25 @@ var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
50
44
  label: e.label,
51
45
  value: e.ref
52
46
  }))] : [];
53
- }), D = e(() => C.value ?? null);
54
- function O(e, t) {
55
- b.value = e === void 0 ? void 0 : m(e, t);
56
- }
57
- function k(e) {
58
- O(e, void 0);
47
+ }), O = e(() => C.value ?? null);
48
+ function k(e, t) {
49
+ if (e === void 0) {
50
+ b.value = void 0;
51
+ return;
52
+ }
53
+ let n = x.options?.find((t) => g(t.primary.ref, e, !0));
54
+ b.value = m(h(e, t), n?.enrichments);
59
55
  }
60
56
  function A(e) {
57
+ k(e, void 0);
58
+ }
59
+ function j(e) {
61
60
  let t = S.value;
62
- t && O(t, e ?? void 0);
61
+ t && k(t, e ?? void 0);
63
62
  }
64
63
  return (e, d) => (c(), r("div", y, [a(u(v), {
65
64
  "model-value": S.value,
66
- options: o.options,
65
+ options: T.value,
67
66
  label: o.label,
68
67
  helper: o.helper,
69
68
  "loading-options-helper": o.loadingOptionsHelper,
@@ -72,7 +71,7 @@ var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
72
71
  clearable: o.clearable,
73
72
  required: o.required,
74
73
  disabled: o.disabled,
75
- "onUpdate:modelValue": k
74
+ "onUpdate:modelValue": A
76
75
  }, i({ _: 2 }, [s.tooltip ? {
77
76
  name: "tooltip",
78
77
  fn: p(() => [l(e.$slots, "tooltip", {}, void 0, !0)]),
@@ -88,14 +87,14 @@ var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
88
87
  "clearable",
89
88
  "required",
90
89
  "disabled"
91
- ]), T.value ? (c(), t(u(_), {
90
+ ]), E.value ? (c(), t(u(_), {
92
91
  key: 0,
93
- "model-value": D.value,
94
- options: E.value,
92
+ "model-value": O.value,
93
+ options: D.value,
95
94
  label: o.filterLabel,
96
95
  placeholder: o.filterPlaceholder,
97
96
  disabled: o.disabled,
98
- "onUpdate:modelValue": A
97
+ "onUpdate:modelValue": j
99
98
  }, null, 8, [
100
99
  "model-value",
101
100
  "options",
@@ -1 +1 @@
1
- {"version":3,"file":"PlDatasetSelector.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/PlDatasetSelector/PlDatasetSelector.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Select a dataset and (optionally) a filter column, emitting a `PrimaryRef`.\n *\n * Behaves like {@link PlDropdownRef} when none of the offered datasets carry\n * filter options. When the selected dataset has compatible filters, a second\n * dropdown appears with the filters plus a \"No filter\" entry.\n *\n * Accepts `PrimaryRef | PlRef | undefined` as `modelValue` (a plain `PlRef`\n * is treated as an unfiltered dataset), but always emits `PrimaryRef`\n * (or `undefined` when cleared).\n */\nexport default {\n name: \"PlDatasetSelector\",\n};\n</script>\n\n<script lang=\"ts\" setup>\nimport type { DatasetOption, PlRef, PrimaryRef } from \"@platforma-sdk/model\";\nimport { createPrimaryRef, isPrimaryRef, plRefsEqual } from \"@platforma-sdk/model\";\nimport type { ListOption } from \"@milaboratories/uikit\";\nimport { PlDropdown, PlDropdownRef } from \"@milaboratories/uikit\";\nimport { computed } from \"vue\";\n\nconst slots = defineSlots<{\n tooltip?: () => unknown;\n}>();\n\n/**\n * v-model value. Accepts `PrimaryRef`, plain `PlRef` (treated as unfiltered),\n * or `undefined`. Writes always emit `PrimaryRef` or `undefined`.\n */\nconst model = defineModel<PrimaryRef | PlRef | undefined>();\n\nconst props = withDefaults(\n defineProps<{\n /** Available datasets, each optionally carrying compatible filter choices. */\n options?: Readonly<DatasetOption[]>;\n /** Label above the dataset dropdown. */\n label?: string;\n /** Helper text below the dataset dropdown (shown when there is no error). */\n helper?: string;\n /** Helper text shown while `options` is undefined (loading). */\n loadingOptionsHelper?: string;\n /** Error message displayed below the dataset dropdown. */\n error?: unknown;\n /** Placeholder when no dataset is selected. */\n placeholder?: string;\n /** Label above the filter dropdown. */\n filterLabel?: string;\n /** Placeholder for the filter dropdown. */\n filterPlaceholder?: string;\n /** Label of the \"no filter\" entry prepended to the filter options. */\n noFilterLabel?: string;\n /** Show a clear button on the dataset dropdown. */\n clearable?: boolean;\n /** Mark the dataset dropdown as required. */\n required?: boolean;\n /** Disable all interaction. */\n disabled?: boolean;\n }>(),\n {\n options: undefined,\n label: undefined,\n helper: undefined,\n loadingOptionsHelper: undefined,\n error: undefined,\n placeholder: \"...\",\n filterLabel: \"\",\n filterPlaceholder: \"...\",\n noFilterLabel: \"No filter\",\n clearable: false,\n required: false,\n disabled: false,\n },\n);\n\nconst selectedDataset = computed<PlRef | undefined>(() => {\n const v = model.value;\n if (v === undefined) return undefined;\n return isPrimaryRef(v) ? v.column : v;\n});\n\nconst selectedFilter = computed<PlRef | undefined>(() => {\n const v = model.value;\n return isPrimaryRef(v) ? v.filter : undefined;\n});\n\nconst currentDatasetOption = computed<DatasetOption | undefined>(() => {\n const dataset = selectedDataset.value;\n if (!dataset) return undefined;\n return props.options?.find((o) => plRefsEqual(o.ref, dataset, true));\n});\n\nconst hasFilters = computed(() => (currentDatasetOption.value?.filters?.length ?? 0) > 0);\n\n/**\n * Filter dropdown options. The first entry (`null`) is the \"No filter\" choice —\n * null distinguishes it from `undefined` (dropdown clear button, disabled here).\n */\nconst filterOptions = computed<ListOption<PlRef | null>[]>(() => {\n const filters = currentDatasetOption.value?.filters;\n if (!filters) return [];\n return [\n { label: props.noFilterLabel, value: null } as ListOption<PlRef | null>,\n ...filters.map((f) => ({ label: f.label, value: f.ref }) as ListOption<PlRef | null>),\n ];\n});\n\nconst filterValue = computed<PlRef | null>(() => selectedFilter.value ?? null);\n\nfunction emitValue(dataset: PlRef | undefined, filter: PlRef | undefined) {\n model.value = dataset === undefined ? undefined : createPrimaryRef(dataset, filter);\n}\n\nfunction onDatasetChange(dataset: PlRef | undefined) {\n emitValue(dataset, undefined);\n}\n\nfunction onFilterChange(value: PlRef | null | undefined) {\n const dataset = selectedDataset.value;\n if (!dataset) return;\n emitValue(dataset, value ?? undefined);\n}\n</script>\n\n<template>\n <div class=\"pl-dataset-selector\">\n <PlDropdownRef\n :model-value=\"selectedDataset\"\n :options=\"options\"\n :label=\"label\"\n :helper=\"helper\"\n :loading-options-helper=\"loadingOptionsHelper\"\n :error=\"error\"\n :placeholder=\"placeholder\"\n :clearable=\"clearable\"\n :required=\"required\"\n :disabled=\"disabled\"\n @update:model-value=\"onDatasetChange\"\n >\n <template v-if=\"slots.tooltip\" #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlDropdownRef>\n <PlDropdown\n v-if=\"hasFilters\"\n :model-value=\"filterValue\"\n :options=\"filterOptions\"\n :label=\"filterLabel\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n @update:model-value=\"onFilterChange\"\n />\n </div>\n</template>\n\n<style scoped>\n.pl-dataset-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n</style>\n"],"mappings":";;;;;CAaE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWR,IAAM,IAAQ,GAEV,EAME,IAAQ,EAA2C,GAAA,aAAE,EAErD,IAAQ,GA2CR,IAAkB,QAAkC;GACxD,IAAM,IAAI,EAAM;AACZ,aAAM,KAAA,EACV,QAAO,EAAa,EAAE,GAAG,EAAE,SAAS;IACpC,EAEI,IAAiB,QAAkC;GACvD,IAAM,IAAI,EAAM;AAChB,UAAO,EAAa,EAAE,GAAG,EAAE,SAAS,KAAA;IACpC,EAEI,IAAuB,QAA0C;GACrE,IAAM,IAAU,EAAgB;AAC3B,SACL,QAAO,EAAM,SAAS,MAAM,MAAM,EAAY,EAAE,KAAK,GAAS,GAAK,CAAC;IACpE,EAEI,IAAa,SAAgB,EAAqB,OAAO,SAAS,UAAU,KAAK,EAAE,EAMnF,IAAgB,QAA2C;GAC/D,IAAM,IAAU,EAAqB,OAAO;AAE5C,UADK,IACE,CACL;IAAE,OAAO,EAAM;IAAe,OAAO;IAAM,EAC3C,GAAG,EAAQ,KAAK,OAAO;IAAE,OAAO,EAAE;IAAO,OAAO,EAAE;IAAK,EAA8B,CACtF,GAJoB,EAAE;IAKvB,EAEI,IAAc,QAA6B,EAAe,SAAS,KAAK;EAE9E,SAAS,EAAU,GAA4B,GAA2B;AACxE,KAAM,QAAQ,MAAY,KAAA,IAAY,KAAA,IAAY,EAAiB,GAAS,EAAO;;EAGrF,SAAS,EAAgB,GAA4B;AACnD,KAAU,GAAS,KAAA,EAAU;;EAG/B,SAAS,EAAe,GAAiC;GACvD,IAAM,IAAU,EAAgB;AAC3B,QACL,EAAU,GAAS,KAAS,KAAA,EAAU;;yBAKtC,EA2BM,OA3BN,GA2BM,CA1BJ,EAgBgB,EAAA,EAAA,EAAA;GAfb,eAAa,EAAA;GACb,SAAS,EAAA;GACT,OAAO,EAAA;GACP,QAAQ,EAAA;GACR,0BAAwB,EAAA;GACxB,OAAO,EAAA;GACP,aAAa,EAAA;GACb,WAAW,EAAA;GACX,UAAU,EAAA;GACV,UAAU,EAAA;GACV,uBAAoB;kBAEL,EAAM,UAAA;SAAU;eACP,CAAvB,EAAuB,EAAA,QAAA,WAAA,EAAA,EAAA,KAAA,GAAA,GAAA,CAAA,CAAA;;;;;;;;;;;;;MAInB,EAAA,SAAA,GAAA,EADR,EAQE,EAAA,EAAA,EAAA;;GANC,eAAa,EAAA;GACb,SAAS,EAAA;GACT,OAAO,EAAA;GACP,aAAa,EAAA;GACb,UAAU,EAAA;GACV,uBAAoB"}
1
+ {"version":3,"file":"PlDatasetSelector.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/PlDatasetSelector/PlDatasetSelector.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Select a dataset and (optionally) a filter column, emitting a {@link DatasetSelection}.\n *\n * Behaves like {@link PlDropdownRef} when none of the offered datasets carry\n * filter options. When the selected dataset has compatible filters, a second\n * dropdown appears with the filters plus a \"No filter\" entry.\n *\n * The emitted value bundles the user's pick (`primary`) with the auto-attached\n * `enrichments` payload from the matching `DatasetOption`. Enrichments are\n * opaque to the UI block authors unbundle them inside their args resolver.\n */\nexport default {\n name: \"PlDatasetSelector\",\n};\n</script>\n\n<script lang=\"ts\" setup>\nimport type { DatasetOption, DatasetSelection, PlRef } from \"@platforma-sdk/model\";\nimport { createDatasetSelection, createPrimaryRef, plRefsEqual } from \"@platforma-sdk/model\";\nimport type { ListOption } from \"@milaboratories/uikit\";\nimport { PlDropdown, PlDropdownRef } from \"@milaboratories/uikit\";\nimport { computed } from \"vue\";\n\nconst slots = defineSlots<{\n tooltip?: () => unknown;\n}>();\n\nconst model = defineModel<DatasetSelection | undefined>();\n\nconst props = withDefaults(\n defineProps<{\n /** Available datasets, each optionally carrying compatible filter choices. */\n options?: Readonly<DatasetOption[]>;\n /** Label above the dataset dropdown. */\n label?: string;\n /** Helper text below the dataset dropdown (shown when there is no error). */\n helper?: string;\n /** Helper text shown while `options` is undefined (loading). */\n loadingOptionsHelper?: string;\n /** Error message displayed below the dataset dropdown. */\n error?: unknown;\n /** Placeholder when no dataset is selected. */\n placeholder?: string;\n /** Label above the filter dropdown. */\n filterLabel?: string;\n /** Placeholder for the filter dropdown. */\n filterPlaceholder?: string;\n /** Label of the \"no filter\" entry prepended to the filter options. */\n noFilterLabel?: string;\n /** Show a clear button on the dataset dropdown. */\n clearable?: boolean;\n /** Mark the dataset dropdown as required. */\n required?: boolean;\n /** Disable all interaction. */\n disabled?: boolean;\n }>(),\n {\n options: undefined,\n label: undefined,\n helper: undefined,\n loadingOptionsHelper: undefined,\n error: undefined,\n placeholder: \"...\",\n filterLabel: \"\",\n filterPlaceholder: \"...\",\n noFilterLabel: \"No filter\",\n clearable: false,\n required: false,\n disabled: false,\n },\n);\n\nconst selectedDataset = computed<PlRef | undefined>(() => model.value?.primary.column);\n\nconst selectedFilter = computed<PlRef | undefined>(() => model.value?.primary.filter);\n\nconst currentDatasetOption = computed<DatasetOption | undefined>(() => {\n const dataset = selectedDataset.value;\n if (!dataset) return undefined;\n return props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));\n});\n\n// PlDropdownRef expects `Option[]`; project the primary out of each entry.\nconst primaryOptions = computed<readonly { ref: PlRef; label: string }[] | undefined>(() =>\n props.options?.map((o) => o.primary),\n);\n\nconst hasFilters = computed(() => (currentDatasetOption.value?.filters?.length ?? 0) > 0);\n\n/**\n * Filter dropdown options. The first entry (`null`) is the \"No filter\" choice —\n * null distinguishes it from `undefined` (dropdown clear button, disabled here).\n */\nconst filterOptions = computed<ListOption<PlRef | null>[]>(() => {\n const filters = currentDatasetOption.value?.filters;\n if (!filters) return [];\n return [\n { label: props.noFilterLabel, value: null } as ListOption<PlRef | null>,\n ...filters.map((f) => ({ label: f.label, value: f.ref }) as ListOption<PlRef | null>),\n ];\n});\n\nconst filterValue = computed<PlRef | null>(() => selectedFilter.value ?? null);\n\nfunction emitValue(dataset: PlRef | undefined, filter: PlRef | undefined) {\n if (dataset === undefined) {\n model.value = undefined;\n return;\n }\n // Resolve from `props.options` directly `currentDatasetOption` may not\n // have recomputed yet when this runs synchronously inside a change handler.\n const option = props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));\n model.value = createDatasetSelection(createPrimaryRef(dataset, filter), option?.enrichments);\n}\n\nfunction onDatasetChange(dataset: PlRef | undefined) {\n emitValue(dataset, undefined);\n}\n\nfunction onFilterChange(value: PlRef | null | undefined) {\n const dataset = selectedDataset.value;\n if (!dataset) return;\n emitValue(dataset, value ?? undefined);\n}\n</script>\n\n<template>\n <div class=\"pl-dataset-selector\">\n <PlDropdownRef\n :model-value=\"selectedDataset\"\n :options=\"primaryOptions\"\n :label=\"label\"\n :helper=\"helper\"\n :loading-options-helper=\"loadingOptionsHelper\"\n :error=\"error\"\n :placeholder=\"placeholder\"\n :clearable=\"clearable\"\n :required=\"required\"\n :disabled=\"disabled\"\n @update:model-value=\"onDatasetChange\"\n >\n <template v-if=\"slots.tooltip\" #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlDropdownRef>\n <PlDropdown\n v-if=\"hasFilters\"\n :model-value=\"filterValue\"\n :options=\"filterOptions\"\n :label=\"filterLabel\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n @update:model-value=\"onFilterChange\"\n />\n </div>\n</template>\n\n<style scoped>\n.pl-dataset-selector {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n</style>\n"],"mappings":";;;;;CAaE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWR,IAAM,IAAQ,GAEV,EAEE,IAAQ,EAAyC,GAAA,aAAE,EAEnD,IAAQ,GA2CR,IAAkB,QAAkC,EAAM,OAAO,QAAQ,OAAO,EAEhF,IAAiB,QAAkC,EAAM,OAAO,QAAQ,OAAO,EAE/E,IAAuB,QAA0C;GACrE,IAAM,IAAU,EAAgB;AAC3B,SACL,QAAO,EAAM,SAAS,MAAM,MAAM,EAAY,EAAE,QAAQ,KAAK,GAAS,GAAK,CAAC;IAC5E,EAGI,IAAiB,QACrB,EAAM,SAAS,KAAK,MAAM,EAAE,QAAQ,CACrC,EAEK,IAAa,SAAgB,EAAqB,OAAO,SAAS,UAAU,KAAK,EAAE,EAMnF,IAAgB,QAA2C;GAC/D,IAAM,IAAU,EAAqB,OAAO;AAE5C,UADK,IACE,CACL;IAAE,OAAO,EAAM;IAAe,OAAO;IAAM,EAC3C,GAAG,EAAQ,KAAK,OAAO;IAAE,OAAO,EAAE;IAAO,OAAO,EAAE;IAAK,EAA8B,CACtF,GAJoB,EAAE;IAKvB,EAEI,IAAc,QAA6B,EAAe,SAAS,KAAK;EAE9E,SAAS,EAAU,GAA4B,GAA2B;AACxE,OAAI,MAAY,KAAA,GAAW;AACzB,MAAM,QAAQ,KAAA;AACd;;GAIF,IAAM,IAAS,EAAM,SAAS,MAAM,MAAM,EAAY,EAAE,QAAQ,KAAK,GAAS,GAAK,CAAC;AACpF,KAAM,QAAQ,EAAuB,EAAiB,GAAS,EAAO,EAAE,GAAQ,YAAY;;EAG9F,SAAS,EAAgB,GAA4B;AACnD,KAAU,GAAS,KAAA,EAAU;;EAG/B,SAAS,EAAe,GAAiC;GACvD,IAAM,IAAU,EAAgB;AAC3B,QACL,EAAU,GAAS,KAAS,KAAA,EAAU;;yBAKtC,EA2BM,OA3BN,GA2BM,CA1BJ,EAgBgB,EAAA,EAAA,EAAA;GAfb,eAAa,EAAA;GACb,SAAS,EAAA;GACT,OAAO,EAAA;GACP,QAAQ,EAAA;GACR,0BAAwB,EAAA;GACxB,OAAO,EAAA;GACP,aAAa,EAAA;GACb,WAAW,EAAA;GACX,UAAU,EAAA;GACV,UAAU,EAAA;GACV,uBAAoB;kBAEL,EAAM,UAAA;SAAU;eACP,CAAvB,EAAuB,EAAA,QAAA,WAAA,EAAA,EAAA,KAAA,GAAA,GAAA,CAAA,CAAA;;;;;;;;;;;;;MAInB,EAAA,SAAA,GAAA,EADR,EAQE,EAAA,EAAA,EAAA;;GANC,eAAa,EAAA;GACb,SAAS,EAAA;GACT,OAAO,EAAA;GACP,aAAa,EAAA;GACb,UAAU,EAAA;GACV,uBAAoB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.69.0",
3
+ "version": "1.71.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/pf-spec-driver": "1.3.6",
30
- "@milaboratories/uikit": "2.13.0",
31
- "@platforma-sdk/model": "1.69.0",
32
- "@milaboratories/pl-model-common": "1.36.2"
29
+ "@milaboratories/pf-spec-driver": "1.3.8",
30
+ "@milaboratories/pl-model-common": "1.38.0",
31
+ "@milaboratories/uikit": "2.13.2",
32
+ "@platforma-sdk/model": "1.71.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@faker-js/faker": "^9.2.0",
@@ -45,9 +45,9 @@
45
45
  "typescript": "~5.9.3",
46
46
  "vite": "^8.0.6",
47
47
  "vitest": "^4.1.3",
48
+ "@milaboratories/helpers": "1.14.1",
48
49
  "@milaboratories/build-configs": "2.0.0",
49
50
  "@milaboratories/ts-configs": "1.2.3",
50
- "@milaboratories/helpers": "1.14.1",
51
51
  "@milaboratories/ts-builder": "1.3.2"
52
52
  },
53
53
  "scripts": {
@@ -1,8 +1,6 @@
1
- import type { PTableValue } from "@platforma-sdk/model";
2
-
3
1
  export const PTableHidden = { type: "hidden" } as const;
4
2
  export type PTableHidden = typeof PTableHidden;
5
3
 
6
- export function isPTableHidden(value: PTableValue | PTableHidden): value is PTableHidden {
7
- return typeof value === "object" && value !== null && value.type === "hidden";
4
+ export function isPTableHidden(value: unknown): value is PTableHidden {
5
+ return typeof value === "object" && value !== null && "type" in value && value.type === "hidden";
8
6
  }
@@ -13,10 +13,15 @@ import * as d3 from "d3-format";
13
13
  import type { PlAgDataTableV2Row } from "../types";
14
14
 
15
15
  export function formatSpecialValues(
16
- value: PTableValue | PTableHidden | undefined,
16
+ value: undefined | PTableValue | PTableHidden,
17
+ dataStatus: undefined | "absent" | "error" | "computing" | "ready",
17
18
  ): string | undefined {
18
- if (value === undefined) {
19
- return "undefined";
19
+ if (dataStatus === "absent") {
20
+ return "absent";
21
+ } else if (dataStatus === "error") {
22
+ return "error";
23
+ } else if (dataStatus === "computing") {
24
+ return "computing...";
20
25
  } else if (isPTableHidden(value)) {
21
26
  return "loading...";
22
27
  } else if (value === PTableNA) {
@@ -40,12 +45,13 @@ export function getColumnRenderingSpec(spec: PTableColumnSpec): ColumnRenderingS
40
45
  case ValueType.Float:
41
46
  case ValueType.Double: {
42
47
  const format = readAnnotation(spec.spec, Annotation.Format);
48
+ const dataStatus = readAnnotation(spec.spec, Annotation.DataStatus);
43
49
  const formatFn = format ? d3.format(format) : undefined;
44
50
  renderSpec = {
45
51
  valueFormatter: (params) => {
46
- const formatted = formatSpecialValues(params.value);
52
+ const formatted = formatSpecialValues(params.value, dataStatus);
47
53
  if (formatted !== undefined) return formatted;
48
- return formatFn ? formatFn(Number(params.value)) : params.value!.toString();
54
+ return formatFn ? formatFn(Number(params.value)) : (params.value?.toString() ?? "");
49
55
  },
50
56
  };
51
57
  break;
@@ -53,9 +59,12 @@ export function getColumnRenderingSpec(spec: PTableColumnSpec): ColumnRenderingS
53
59
  default:
54
60
  renderSpec = {
55
61
  valueFormatter: (params) => {
56
- const formatted = formatSpecialValues(params.value);
62
+ const formatted = formatSpecialValues(
63
+ params.value,
64
+ readAnnotation(spec.spec, Annotation.DataStatus),
65
+ );
57
66
  if (formatted !== undefined) return formatted;
58
- return params.value!.toString();
67
+ return params.value?.toString() ?? "";
59
68
  },
60
69
  };
61
70
  break;
@@ -1,14 +1,14 @@
1
1
  <script lang="ts">
2
2
  /**
3
- * Select a dataset and (optionally) a filter column, emitting a `PrimaryRef`.
3
+ * Select a dataset and (optionally) a filter column, emitting a {@link DatasetSelection}.
4
4
  *
5
5
  * Behaves like {@link PlDropdownRef} when none of the offered datasets carry
6
6
  * filter options. When the selected dataset has compatible filters, a second
7
7
  * dropdown appears with the filters plus a "No filter" entry.
8
8
  *
9
- * Accepts `PrimaryRef | PlRef | undefined` as `modelValue` (a plain `PlRef`
10
- * is treated as an unfiltered dataset), but always emits `PrimaryRef`
11
- * (or `undefined` when cleared).
9
+ * The emitted value bundles the user's pick (`primary`) with the auto-attached
10
+ * `enrichments` payload from the matching `DatasetOption`. Enrichments are
11
+ * opaque to the UI — block authors unbundle them inside their args resolver.
12
12
  */
13
13
  export default {
14
14
  name: "PlDatasetSelector",
@@ -16,8 +16,8 @@ export default {
16
16
  </script>
17
17
 
18
18
  <script lang="ts" setup>
19
- import type { DatasetOption, PlRef, PrimaryRef } from "@platforma-sdk/model";
20
- import { createPrimaryRef, isPrimaryRef, plRefsEqual } from "@platforma-sdk/model";
19
+ import type { DatasetOption, DatasetSelection, PlRef } from "@platforma-sdk/model";
20
+ import { createDatasetSelection, createPrimaryRef, plRefsEqual } from "@platforma-sdk/model";
21
21
  import type { ListOption } from "@milaboratories/uikit";
22
22
  import { PlDropdown, PlDropdownRef } from "@milaboratories/uikit";
23
23
  import { computed } from "vue";
@@ -26,11 +26,7 @@ const slots = defineSlots<{
26
26
  tooltip?: () => unknown;
27
27
  }>();
28
28
 
29
- /**
30
- * v-model value. Accepts `PrimaryRef`, plain `PlRef` (treated as unfiltered),
31
- * or `undefined`. Writes always emit `PrimaryRef` or `undefined`.
32
- */
33
- const model = defineModel<PrimaryRef | PlRef | undefined>();
29
+ const model = defineModel<DatasetSelection | undefined>();
34
30
 
35
31
  const props = withDefaults(
36
32
  defineProps<{
@@ -75,23 +71,21 @@ const props = withDefaults(
75
71
  },
76
72
  );
77
73
 
78
- const selectedDataset = computed<PlRef | undefined>(() => {
79
- const v = model.value;
80
- if (v === undefined) return undefined;
81
- return isPrimaryRef(v) ? v.column : v;
82
- });
74
+ const selectedDataset = computed<PlRef | undefined>(() => model.value?.primary.column);
83
75
 
84
- const selectedFilter = computed<PlRef | undefined>(() => {
85
- const v = model.value;
86
- return isPrimaryRef(v) ? v.filter : undefined;
87
- });
76
+ const selectedFilter = computed<PlRef | undefined>(() => model.value?.primary.filter);
88
77
 
89
78
  const currentDatasetOption = computed<DatasetOption | undefined>(() => {
90
79
  const dataset = selectedDataset.value;
91
80
  if (!dataset) return undefined;
92
- return props.options?.find((o) => plRefsEqual(o.ref, dataset, true));
81
+ return props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));
93
82
  });
94
83
 
84
+ // PlDropdownRef expects `Option[]`; project the primary out of each entry.
85
+ const primaryOptions = computed<readonly { ref: PlRef; label: string }[] | undefined>(() =>
86
+ props.options?.map((o) => o.primary),
87
+ );
88
+
95
89
  const hasFilters = computed(() => (currentDatasetOption.value?.filters?.length ?? 0) > 0);
96
90
 
97
91
  /**
@@ -110,7 +104,14 @@ const filterOptions = computed<ListOption<PlRef | null>[]>(() => {
110
104
  const filterValue = computed<PlRef | null>(() => selectedFilter.value ?? null);
111
105
 
112
106
  function emitValue(dataset: PlRef | undefined, filter: PlRef | undefined) {
113
- model.value = dataset === undefined ? undefined : createPrimaryRef(dataset, filter);
107
+ if (dataset === undefined) {
108
+ model.value = undefined;
109
+ return;
110
+ }
111
+ // Resolve from `props.options` directly — `currentDatasetOption` may not
112
+ // have recomputed yet when this runs synchronously inside a change handler.
113
+ const option = props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));
114
+ model.value = createDatasetSelection(createPrimaryRef(dataset, filter), option?.enrichments);
114
115
  }
115
116
 
116
117
  function onDatasetChange(dataset: PlRef | undefined) {
@@ -128,7 +129,7 @@ function onFilterChange(value: PlRef | null | undefined) {
128
129
  <div class="pl-dataset-selector">
129
130
  <PlDropdownRef
130
131
  :model-value="selectedDataset"
131
- :options="options"
132
+ :options="primaryOptions"
132
133
  :label="label"
133
134
  :helper="helper"
134
135
  :loading-options-helper="loadingOptionsHelper"
@@ -1,6 +1,6 @@
1
1
  import { flushPromises, mount } from "@vue/test-utils";
2
- import type { DatasetOption, PrimaryRef } from "@platforma-sdk/model";
3
- import { createPlRef, createPrimaryRef } from "@platforma-sdk/model";
2
+ import type { DatasetOption, DatasetSelection } from "@platforma-sdk/model";
3
+ import { createDatasetSelection, createPlRef, createPrimaryRef } from "@platforma-sdk/model";
4
4
  import { describe, expect, it } from "vitest";
5
5
  import PlDatasetSelector from "../PlDatasetSelector.vue";
6
6
 
@@ -9,27 +9,41 @@ const datasetB = createPlRef("2", "out-b", true);
9
9
  const filterA1 = createPlRef("1", "filter-a1");
10
10
  const filterA2 = createPlRef("1", "filter-a2");
11
11
 
12
+ import type { PObjectId } from "@platforma-sdk/model";
13
+ const enrichmentA = "enrichment-a" as PObjectId;
14
+ const enrichmentsA = [
15
+ { ref: { __isEnrichment: "v1" as const, hit: enrichmentA }, label: "Enrichment A" },
16
+ ];
17
+
12
18
  const optionsWithFilters: DatasetOption[] = [
13
19
  {
14
- label: "Dataset A",
15
- ref: datasetA,
20
+ primary: { label: "Dataset A", ref: datasetA },
16
21
  filters: [
17
22
  { label: "Top 1000", ref: filterA1 },
18
23
  { label: "High quality", ref: filterA2 },
19
24
  ],
25
+ enrichments: enrichmentsA,
20
26
  },
21
27
  // Dataset B has no filters — filter dropdown must stay hidden.
22
- { label: "Dataset B", ref: datasetB },
28
+ { primary: { label: "Dataset B", ref: datasetB } },
23
29
  ];
24
30
 
25
31
  const datasetC = createPlRef("3", "out-c", true);
26
32
 
27
- const optionsNoFilters: DatasetOption[] = [{ label: "Dataset B", ref: datasetB }];
33
+ const optionsNoFilters: DatasetOption[] = [{ primary: { label: "Dataset B", ref: datasetB } }];
28
34
 
29
35
  const optionsWithEmptyFilters: DatasetOption[] = [
30
- { label: "Dataset C", ref: datasetC, filters: [] },
36
+ { primary: { label: "Dataset C", ref: datasetC }, filters: [] },
31
37
  ];
32
38
 
39
+ function selection(
40
+ ref: typeof datasetA,
41
+ filter?: typeof filterA1,
42
+ enrichments?: typeof enrichmentsA,
43
+ ): DatasetSelection {
44
+ return createDatasetSelection(createPrimaryRef(ref, filter), enrichments);
45
+ }
46
+
33
47
  async function pickOption(index: number) {
34
48
  const options = [...document.body.querySelectorAll(".dropdown-list-item")] as HTMLElement[];
35
49
  options[index].click();
@@ -53,7 +67,7 @@ describe("PlDatasetSelector", () => {
53
67
 
54
68
  it("shows the filter dropdown when the selected dataset has filters", async () => {
55
69
  const wrapper = mount(PlDatasetSelector, {
56
- props: { modelValue: createPrimaryRef(datasetA), options: optionsWithFilters },
70
+ props: { modelValue: selection(datasetA), options: optionsWithFilters },
57
71
  attachTo: document.body,
58
72
  });
59
73
  await flushPromises();
@@ -64,7 +78,7 @@ describe("PlDatasetSelector", () => {
64
78
 
65
79
  it("hides the filter dropdown when the selected dataset has no filters", async () => {
66
80
  const wrapper = mount(PlDatasetSelector, {
67
- props: { modelValue: createPrimaryRef(datasetB), options: optionsWithFilters },
81
+ props: { modelValue: selection(datasetB), options: optionsWithFilters },
68
82
  attachTo: document.body,
69
83
  });
70
84
  await flushPromises();
@@ -73,63 +87,69 @@ describe("PlDatasetSelector", () => {
73
87
  wrapper.unmount();
74
88
  });
75
89
 
76
- it("emits PrimaryRef with filter: undefined when dataset changes", async () => {
90
+ it("emits DatasetSelection bundling primary + enrichments when dataset changes", async () => {
77
91
  const wrapper = mount(PlDatasetSelector, {
78
92
  props: {
79
- modelValue: createPrimaryRef(datasetA, filterA1),
93
+ modelValue: selection(datasetA, filterA1, enrichmentsA),
80
94
  options: optionsWithFilters,
81
- "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
95
+ "onUpdate:modelValue": (e: DatasetSelection | undefined) =>
96
+ wrapper.setProps({ modelValue: e }),
82
97
  },
83
98
  attachTo: document.body,
84
99
  });
85
100
  await flushPromises();
86
101
 
87
- // Open the dataset dropdown (the first input — dataset comes first).
88
102
  const inputs = wrapper.findAll("input");
89
103
  await inputs[0].trigger("focus");
90
-
91
- // Dataset A is already selected (index 0); pick Dataset B (index 1).
104
+ // Dataset A is index 0; pick Dataset B (index 1) — has no enrichments.
92
105
  await pickOption(1);
93
106
 
94
107
  const emitted = wrapper.emitted("update:modelValue");
95
108
  expect(emitted).toBeDefined();
96
- const last = emitted![emitted!.length - 1][0] as PrimaryRef;
97
- expect(last).toEqual({ __isPrimaryRef: "v1", column: datasetB });
109
+ const last = emitted![emitted!.length - 1][0] as DatasetSelection;
110
+ expect(last).toEqual({
111
+ __isDatasetSelection: "v1",
112
+ primary: { __isPrimaryRef: "v1", column: datasetB },
113
+ });
98
114
  wrapper.unmount();
99
115
  });
100
116
 
101
- it("emits PrimaryRef with filter set when a filter is picked", async () => {
117
+ it("emits DatasetSelection with filter set when a filter is picked", async () => {
102
118
  const wrapper = mount(PlDatasetSelector, {
103
119
  props: {
104
- modelValue: createPrimaryRef(datasetA),
120
+ modelValue: selection(datasetA, undefined, enrichmentsA),
105
121
  options: optionsWithFilters,
106
- "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
122
+ "onUpdate:modelValue": (e: DatasetSelection | undefined) =>
123
+ wrapper.setProps({ modelValue: e }),
107
124
  },
108
125
  attachTo: document.body,
109
126
  });
110
127
  await flushPromises();
111
128
 
112
- // Open the filter dropdown — it's the second input in the component.
113
129
  const inputs = wrapper.findAll("input");
114
130
  expect(inputs.length).toBe(2);
115
131
  await inputs[1].trigger("focus");
116
-
117
- // Options are: [No filter, Top 1000, High quality]. Pick "Top 1000" (index 1).
132
+ // Options: [No filter, Top 1000, High quality]. Pick "Top 1000".
118
133
  await pickOption(1);
119
134
 
120
135
  const emitted = wrapper.emitted("update:modelValue");
121
136
  expect(emitted).toBeDefined();
122
- const last = emitted![emitted!.length - 1][0] as PrimaryRef;
123
- expect(last).toEqual({ __isPrimaryRef: "v1", column: datasetA, filter: filterA1 });
137
+ const last = emitted![emitted!.length - 1][0] as DatasetSelection;
138
+ expect(last).toEqual({
139
+ __isDatasetSelection: "v1",
140
+ primary: { __isPrimaryRef: "v1", column: datasetA, filter: filterA1 },
141
+ enrichments: enrichmentsA,
142
+ });
124
143
  wrapper.unmount();
125
144
  });
126
145
 
127
- it("emits PrimaryRef with filter: undefined when 'No filter' is picked", async () => {
146
+ it("emits DatasetSelection with no filter key when 'No filter' is picked", async () => {
128
147
  const wrapper = mount(PlDatasetSelector, {
129
148
  props: {
130
- modelValue: createPrimaryRef(datasetA, filterA1),
149
+ modelValue: selection(datasetA, filterA1, enrichmentsA),
131
150
  options: optionsWithFilters,
132
- "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
151
+ "onUpdate:modelValue": (e: DatasetSelection | undefined) =>
152
+ wrapper.setProps({ modelValue: e }),
133
153
  },
134
154
  attachTo: document.body,
135
155
  });
@@ -137,43 +157,20 @@ describe("PlDatasetSelector", () => {
137
157
 
138
158
  const inputs = wrapper.findAll("input");
139
159
  await inputs[1].trigger("focus");
140
- // Pick "No filter" (index 0).
141
- await pickOption(0);
160
+ await pickOption(0); // "No filter"
142
161
 
143
162
  const emitted = wrapper.emitted("update:modelValue");
144
163
  expect(emitted).toBeDefined();
145
- const last = emitted![emitted!.length - 1][0] as PrimaryRef;
146
- expect(last).toEqual({ __isPrimaryRef: "v1", column: datasetA });
147
- expect("filter" in last).toBe(false);
148
- wrapper.unmount();
149
- });
150
-
151
- it("accepts plain PlRef as modelValue for backward compat (filterless dataset)", async () => {
152
- const wrapper = mount(PlDatasetSelector, {
153
- props: { modelValue: datasetB, options: optionsWithFilters },
154
- attachTo: document.body,
155
- });
156
- await flushPromises();
157
-
158
- expect(wrapper.find(".pl-dataset-selector").element.children.length).toBe(1);
159
- wrapper.unmount();
160
- });
161
-
162
- it("accepts plain PlRef as modelValue for backward compat (dataset with filters)", async () => {
163
- const wrapper = mount(PlDatasetSelector, {
164
- props: { modelValue: datasetA, options: optionsWithFilters },
165
- attachTo: document.body,
166
- });
167
- await flushPromises();
168
-
169
- // PlRef matching dataset A — filter dropdown should appear since A has filters.
170
- expect(wrapper.find(".pl-dataset-selector").element.children.length).toBe(2);
164
+ const last = emitted![emitted!.length - 1][0] as DatasetSelection;
165
+ expect(last.primary).toEqual({ __isPrimaryRef: "v1", column: datasetA });
166
+ expect("filter" in last.primary).toBe(false);
167
+ expect(last.enrichments).toEqual(enrichmentsA);
171
168
  wrapper.unmount();
172
169
  });
173
170
 
174
171
  it("hides filter dropdown when dataset has filters: [] (empty array)", async () => {
175
172
  const wrapper = mount(PlDatasetSelector, {
176
- props: { modelValue: createPrimaryRef(datasetC), options: optionsWithEmptyFilters },
173
+ props: { modelValue: selection(datasetC), options: optionsWithEmptyFilters },
177
174
  attachTo: document.body,
178
175
  });
179
176
  await flushPromises();
@@ -182,25 +179,22 @@ describe("PlDatasetSelector", () => {
182
179
  wrapper.unmount();
183
180
  });
184
181
 
185
- it("filter dropdown defaults to 'No filter' when dataset has filters but none selected", async () => {
182
+ it("does not emit on mount when no filter is selected", async () => {
186
183
  const wrapper = mount(PlDatasetSelector, {
187
184
  props: {
188
- modelValue: createPrimaryRef(datasetA),
185
+ modelValue: selection(datasetA),
189
186
  options: optionsWithFilters,
190
- "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
187
+ "onUpdate:modelValue": (e: DatasetSelection | undefined) =>
188
+ wrapper.setProps({ modelValue: e }),
191
189
  },
192
190
  attachTo: document.body,
193
191
  });
194
192
  await flushPromises();
195
193
 
196
- // No emission on mount — the component does not auto-select a filter.
197
194
  expect(wrapper.emitted("update:modelValue")).toBeUndefined();
198
195
 
199
- // Filter dropdown is visible.
200
196
  const inputs = wrapper.findAll("input");
201
197
  expect(inputs.length).toBe(2);
202
-
203
- // Open filter dropdown and verify "No filter" is the first option.
204
198
  await inputs[1].trigger("focus");
205
199
  const items = document.body.querySelectorAll(".dropdown-list-item");
206
200
  expect(items.length).toBe(3); // No filter, Top 1000, High quality
@@ -208,12 +202,13 @@ describe("PlDatasetSelector", () => {
208
202
  wrapper.unmount();
209
203
  });
210
204
 
211
- it("emits PrimaryRef without filter key when selecting a filterless dataset", async () => {
205
+ it("emits DatasetSelection without enrichments when the option carries none", async () => {
212
206
  const wrapper = mount(PlDatasetSelector, {
213
207
  props: {
214
208
  modelValue: undefined,
215
209
  options: optionsNoFilters,
216
- "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
210
+ "onUpdate:modelValue": (e: DatasetSelection | undefined) =>
211
+ wrapper.setProps({ modelValue: e }),
217
212
  },
218
213
  attachTo: document.body,
219
214
  });
@@ -225,25 +220,28 @@ describe("PlDatasetSelector", () => {
225
220
 
226
221
  const emitted = wrapper.emitted("update:modelValue");
227
222
  expect(emitted).toBeDefined();
228
- const last = emitted![emitted!.length - 1][0] as PrimaryRef;
229
- expect(last).toEqual({ __isPrimaryRef: "v1", column: datasetB });
230
- expect("filter" in last).toBe(false);
223
+ const last = emitted![emitted!.length - 1][0] as DatasetSelection;
224
+ expect(last).toEqual({
225
+ __isDatasetSelection: "v1",
226
+ primary: { __isPrimaryRef: "v1", column: datasetB },
227
+ });
228
+ expect("enrichments" in last).toBe(false);
231
229
  wrapper.unmount();
232
230
  });
233
231
 
234
232
  it("emits undefined when cleared via the dataset dropdown", async () => {
235
233
  const wrapper = mount(PlDatasetSelector, {
236
234
  props: {
237
- modelValue: createPrimaryRef(datasetA, filterA1),
235
+ modelValue: selection(datasetA, filterA1, enrichmentsA),
238
236
  options: optionsWithFilters,
239
237
  clearable: true,
240
- "onUpdate:modelValue": (e) => wrapper.setProps({ modelValue: e }),
238
+ "onUpdate:modelValue": (e: DatasetSelection | undefined) =>
239
+ wrapper.setProps({ modelValue: e }),
241
240
  },
242
241
  attachTo: document.body,
243
242
  });
244
243
  await flushPromises();
245
244
 
246
- // PlDropdown's clear button carries the ".clear" class.
247
245
  const clearBtn = wrapper.find(".clear");
248
246
  expect(clearBtn.exists()).toBe(true);
249
247
  await clearBtn.trigger("click");