@platforma-sdk/ui-vue 1.72.0 → 1.73.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.
@@ -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.72.0 build /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.73.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 5393ms.
19
+ [vite:dts] Declaration files built in 5519ms.
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_95f2adf4_lang.css 0.08 kB │ gzip: 0.09 kB
27
+ dist/components/PlDatasetSelector/PlDatasetSelector.vue_vue_type_style_index_0_scoped_cb8d3a14_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
@@ -203,11 +203,11 @@ dist/components/PlAppErrorNotificationAlert/PlAppErrorNotificationAlert.vue_vue_
203
203
  dist/components/PlAgDataTable/PlAgDataTableSheets.vue_vue_type_script_setup_true_lang.js 2.45 kB │ gzip: 1.17 kB │ map: 5.01 kB
204
204
  dist/components/PlAnnotations/components/PlAnnotations.vue_vue_type_script_setup_true_lang.js 2.69 kB │ gzip: 1.12 kB │ map: 4.16 kB
205
205
  dist/lib.js 2.73 kB │ gzip: 0.57 kB │ map: 3.98 kB
206
+ dist/components/PlDatasetSelector/PlDatasetSelector.vue_vue_type_script_setup_true_lang.js 2.90 kB │ gzip: 1.14 kB │ map: 6.46 kB
206
207
  dist/components/PlAgDataTable/compositions/useGrid.js 2.91 kB │ gzip: 1.29 kB │ map: 6.51 kB
207
208
  dist/components/PlAgColumnHeader/PlAgColumnHeader.vue_vue_type_script_setup_true_lang.js 2.93 kB │ gzip: 1.26 kB │ map: 7.97 kB
208
209
  dist/components/PlAgDataTable/sources/row-number.js 2.99 kB │ gzip: 1.31 kB │ map: 7.42 kB
209
210
  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.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
- - sourcemaps (46%)
232
- - vite:dts (21%)
233
- - vite:vue (9%)
234
- - vite:css-post (7%)
235
- - vite:css (6%)
231
+ - sourcemaps (34%)
232
+ - vite:dts (24%)
233
+ - vite:vue (11%)
234
+ - vite:css-post (11%)
235
+ - vite:css (8%)
236
236
  See https://rolldown.rs/options/checks#plugintimings for more details.
237
237
  
238
- ✓ built in 6.25s
238
+ ✓ built in 6.42s
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.72.0 formatter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.73.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 2617ms on 137 files using 8 threads.
11
+ Finished in 3528ms 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.72.0 linter:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.73.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 56ms on 120 files with 98 rules using 8 threads.
9
+ Finished in 30ms 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.72.0 types:check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.73.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,19 @@
1
1
  # @platforma-sdk/ui-vue
2
2
 
3
+ ## 1.73.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 2df0aff: `buildDatasetOptions`: scope filter discovery to the result pool only (block outputs and prerun no longer pollute the filter dropdown), add a `filter` predicate to restrict eligible filter columns, and skip filter matches without a PlRef instead of throwing. Removes the need for callers to wrap `buildDatasetOptions` in try/catch.
8
+
9
+ `tableBuilder.addPrimary`: drop a `PrimaryRef.filter` whose `resolveColumn` produced no spec, so a stale or malformed filter ref no longer causes `pframes.build-table` to panic with `field "spec" not found and inputs locked`.
10
+
11
+ `PlDatasetSelector`: always render the filter dropdown, regardless of whether the selected dataset has compatible filters; the dropdown is clearable and uses an empty selection (instead of a synthetic "No filter" entry) to mean "no filter applied". Removed the `noFilterLabel` prop.
12
+
13
+ - Updated dependencies [2df0aff]
14
+ - @platforma-sdk/model@1.73.0
15
+ - @milaboratories/uikit@2.13.4
16
+
3
17
  ## 1.72.0
4
18
 
5
19
  ### Patch Changes
@@ -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,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"}
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"}
@@ -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-95f2adf4"]]);
5
+ var n = /* @__PURE__ */ e(t, [["__scopeId", "data-v-cb8d3a14"]]);
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 {@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
+ {"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 * The filter dropdown is always rendered and is clearable leaving it empty\n * means \"no filter\". When the selected dataset has no compatible filters, the\n * dropdown opens to an empty option list.\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 /** 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 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 filterOptions = computed<ListOption<PlRef>[]>(\n () => currentDatasetOption.value?.filters?.map((f) => ({ label: f.label, value: f.ref })) ?? [],\n);\n\n// Resolve from `props.options` directly — `currentDatasetOption` may not have\n// recomputed yet when this runs synchronously inside a change handler.\nfunction findOption(dataset: PlRef): DatasetOption | undefined {\n return props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));\n}\n\nfunction onDatasetChange(dataset: PlRef | undefined) {\n if (dataset === undefined) {\n model.value = undefined;\n return;\n }\n model.value = createDatasetSelection(createPrimaryRef(dataset), findOption(dataset)?.enrichments);\n}\n\nfunction onFilterChange(filter: PlRef | undefined) {\n const dataset = selectedDataset.value;\n if (!dataset) return;\n model.value = createDatasetSelection(\n createPrimaryRef(dataset, filter),\n findOption(dataset)?.enrichments,\n );\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 :model-value=\"selectedFilter\"\n :options=\"filterOptions\"\n :label=\"filterLabel\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n clearable\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-95f2adf4]{flex-direction:column;gap:12px;display:flex}
1
+ .pl-dataset-selector[data-v-cb8d3a14]{flex-direction:column;gap:12px;display:flex}
@@ -2,9 +2,9 @@ import { DatasetOption, DatasetSelection } from '@platforma-sdk/model';
2
2
  /**
3
3
  * Select a dataset and (optionally) a filter column, emitting a {@link DatasetSelection}.
4
4
  *
5
- * Behaves like {@link PlDropdownRef} when none of the offered datasets carry
6
- * filter options. When the selected dataset has compatible filters, a second
7
- * dropdown appears with the filters plus a "No filter" entry.
5
+ * The filter dropdown is always rendered and is clearable leaving it empty
6
+ * means "no filter". When the selected dataset has no compatible filters, the
7
+ * dropdown opens to an empty option list.
8
8
  *
9
9
  * The emitted value bundles the user's pick (`primary`) with the auto-attached
10
10
  * `enrichments` payload from the matching `DatasetOption`. Enrichments are
@@ -29,8 +29,6 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
29
29
  filterLabel?: string;
30
30
  /** Placeholder for the filter dropdown. */
31
31
  filterPlaceholder?: string;
32
- /** Label of the "no filter" entry prepended to the filter options. */
33
- noFilterLabel?: string;
34
32
  /** Show a clear button on the dataset dropdown. */
35
33
  clearable?: boolean;
36
34
  /** Mark the dataset dropdown as required. */
@@ -58,8 +56,6 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
58
56
  filterLabel?: string;
59
57
  /** Placeholder for the filter dropdown. */
60
58
  filterPlaceholder?: string;
61
- /** Label of the "no filter" entry prepended to the filter options. */
62
- noFilterLabel?: string;
63
59
  /** Show a clear button on the dataset dropdown. */
64
60
  clearable?: boolean;
65
61
  /** Mark the dataset dropdown as required. */
@@ -80,7 +76,6 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<{
80
76
  disabled: boolean;
81
77
  filterLabel: string;
82
78
  filterPlaceholder: string;
83
- noFilterLabel: string;
84
79
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>, Readonly<{
85
80
  tooltip?: () => unknown;
86
81
  }> & {
@@ -1 +1 @@
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
+ {"version":3,"file":"PlDatasetSelector.vue.d.ts","sourceRoot":"","sources":["../../../src/components/PlDatasetSelector/PlDatasetSelector.vue"],"names":[],"mappings":"AAyJA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAS,MAAM,sBAAsB,CAAC;AAOnF;;;;;;;;;;GAUG;;iBA4GU,gBAAgB,GAAG,SAAS;;IAnGrC,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,mDAAmD;gBACvC,OAAO;IACnB,6CAA6C;eAClC,OAAO;IAClB,+BAA+B;eACpB,OAAO;;;;iBA8ET,gBAAgB,GAAG,SAAS;;IAnGrC,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,mDAAmD;gBACvC,OAAO;IACnB,6CAA6C;eAClC,OAAO;IAClB,+BAA+B;eACpB,OAAO;;;;;WAlBV,MAAM;iBAQA,MAAM;aAVV,QAAQ,CAAC,aAAa,EAAE,CAAC;YAI1B,MAAM;0BAEQ,MAAM;eAUjB,OAAO;cAER,OAAO;cAEP,OAAO;iBARJ,MAAM;uBAEA,MAAM;;cArBlB,MAAM,OAAO;;cAAb,MAAM,OAAO;;AAFzB,wBAqPK;AAcL,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -1,10 +1,10 @@
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 { createDatasetSelection as m, createPrimaryRef as h, plRefsEqual as g } from "@platforma-sdk/model";
3
- import { PlDropdown as _, PlDropdownRef as v } from "@milaboratories/uikit";
1
+ import { computed as e, createElementBlock as t, createSlots as n, createVNode as r, defineComponent as i, mergeModels as a, openBlock as o, renderSlot as s, unref as c, useModel as l, useSlots as u, withCtx as d } from "vue";
2
+ import { createDatasetSelection as f, createPrimaryRef as p, plRefsEqual as m } from "@platforma-sdk/model";
3
+ import { PlDropdown as h, PlDropdownRef as g } from "@milaboratories/uikit";
4
4
  //#region src/components/PlDatasetSelector/PlDatasetSelector.vue?vue&type=script&setup=true&lang.ts
5
- var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
5
+ var _ = { class: "pl-dataset-selector" }, v = /* @__PURE__ */ i({
6
6
  name: "PlDatasetSelector",
7
- props: /* @__PURE__ */ s({
7
+ props: /* @__PURE__ */ a({
8
8
  options: { default: void 0 },
9
9
  label: { default: void 0 },
10
10
  helper: { default: void 0 },
@@ -13,7 +13,6 @@ var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
13
13
  placeholder: { default: "..." },
14
14
  filterLabel: { default: "" },
15
15
  filterPlaceholder: { default: "..." },
16
- noFilterLabel: { default: "No filter" },
17
16
  clearable: {
18
17
  type: Boolean,
19
18
  default: !1
@@ -31,50 +30,43 @@ var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
31
30
  modelModifiers: {}
32
31
  }),
33
32
  emits: ["update:modelValue"],
34
- setup(o) {
35
- let s = f(), b = d(o, "modelValue"), x = o, S = e(() => b.value?.primary.column), C = e(() => b.value?.primary.filter), w = e(() => {
36
- let e = S.value;
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(() => {
39
- let e = w.value?.filters;
40
- return e ? [{
41
- label: x.noFilterLabel,
42
- value: null
43
- }, ...e.map((e) => ({
44
- label: e.label,
45
- value: e.ref
46
- }))] : [];
47
- }), O = e(() => C.value ?? null);
48
- function k(e, t) {
33
+ setup(i) {
34
+ let a = u(), v = l(i, "modelValue"), y = i, b = e(() => v.value?.primary.column), x = e(() => v.value?.primary.filter), S = e(() => {
35
+ let e = b.value;
36
+ if (e) return y.options?.find((t) => m(t.primary.ref, e, !0));
37
+ }), C = e(() => y.options?.map((e) => e.primary)), w = e(() => S.value?.filters?.map((e) => ({
38
+ label: e.label,
39
+ value: e.ref
40
+ })) ?? []);
41
+ function T(e) {
42
+ return y.options?.find((t) => m(t.primary.ref, e, !0));
43
+ }
44
+ function E(e) {
49
45
  if (e === void 0) {
50
- b.value = void 0;
46
+ v.value = void 0;
51
47
  return;
52
48
  }
53
- let n = x.options?.find((t) => g(t.primary.ref, e, !0));
54
- b.value = m(h(e, t), n?.enrichments);
55
- }
56
- function A(e) {
57
- k(e, void 0);
49
+ v.value = f(p(e), T(e)?.enrichments);
58
50
  }
59
- function j(e) {
60
- let t = S.value;
61
- t && k(t, e ?? void 0);
51
+ function D(e) {
52
+ let t = b.value;
53
+ t && (v.value = f(p(t, e), T(t)?.enrichments));
62
54
  }
63
- return (e, d) => (c(), r("div", y, [a(u(v), {
64
- "model-value": S.value,
65
- options: T.value,
66
- label: o.label,
67
- helper: o.helper,
68
- "loading-options-helper": o.loadingOptionsHelper,
69
- error: o.error,
70
- placeholder: o.placeholder,
71
- clearable: o.clearable,
72
- required: o.required,
73
- disabled: o.disabled,
74
- "onUpdate:modelValue": A
75
- }, i({ _: 2 }, [s.tooltip ? {
55
+ return (e, l) => (o(), t("div", _, [r(c(g), {
56
+ "model-value": b.value,
57
+ options: C.value,
58
+ label: i.label,
59
+ helper: i.helper,
60
+ "loading-options-helper": i.loadingOptionsHelper,
61
+ error: i.error,
62
+ placeholder: i.placeholder,
63
+ clearable: i.clearable,
64
+ required: i.required,
65
+ disabled: i.disabled,
66
+ "onUpdate:modelValue": E
67
+ }, n({ _: 2 }, [a.tooltip ? {
76
68
  name: "tooltip",
77
- fn: p(() => [l(e.$slots, "tooltip", {}, void 0, !0)]),
69
+ fn: d(() => [s(e.$slots, "tooltip", {}, void 0, !0)]),
78
70
  key: "0"
79
71
  } : void 0]), 1032, [
80
72
  "model-value",
@@ -87,24 +79,24 @@ var y = { class: "pl-dataset-selector" }, b = /* @__PURE__ */ o({
87
79
  "clearable",
88
80
  "required",
89
81
  "disabled"
90
- ]), E.value ? (c(), t(u(_), {
91
- key: 0,
92
- "model-value": O.value,
93
- options: D.value,
94
- label: o.filterLabel,
95
- placeholder: o.filterPlaceholder,
96
- disabled: o.disabled,
97
- "onUpdate:modelValue": j
82
+ ]), r(c(h), {
83
+ "model-value": x.value,
84
+ options: w.value,
85
+ label: i.filterLabel,
86
+ placeholder: i.filterPlaceholder,
87
+ disabled: i.disabled,
88
+ clearable: "",
89
+ "onUpdate:modelValue": D
98
90
  }, null, 8, [
99
91
  "model-value",
100
92
  "options",
101
93
  "label",
102
94
  "placeholder",
103
95
  "disabled"
104
- ])) : n("", !0)]));
96
+ ])]));
105
97
  }
106
98
  });
107
99
  //#endregion
108
- export { b as default };
100
+ export { v as default };
109
101
 
110
102
  //# sourceMappingURL=PlDatasetSelector.vue2.js.map
@@ -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 {@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"}
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 * The filter dropdown is always rendered and is clearable leaving it empty\n * means \"no filter\". When the selected dataset has no compatible filters, the\n * dropdown opens to an empty option list.\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 /** 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 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 filterOptions = computed<ListOption<PlRef>[]>(\n () => currentDatasetOption.value?.filters?.map((f) => ({ label: f.label, value: f.ref })) ?? [],\n);\n\n// Resolve from `props.options` directly — `currentDatasetOption` may not have\n// recomputed yet when this runs synchronously inside a change handler.\nfunction findOption(dataset: PlRef): DatasetOption | undefined {\n return props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));\n}\n\nfunction onDatasetChange(dataset: PlRef | undefined) {\n if (dataset === undefined) {\n model.value = undefined;\n return;\n }\n model.value = createDatasetSelection(createPrimaryRef(dataset), findOption(dataset)?.enrichments);\n}\n\nfunction onFilterChange(filter: PlRef | undefined) {\n const dataset = selectedDataset.value;\n if (!dataset) return;\n model.value = createDatasetSelection(\n createPrimaryRef(dataset, filter),\n findOption(dataset)?.enrichments,\n );\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 :model-value=\"selectedFilter\"\n :options=\"filterOptions\"\n :label=\"filterLabel\"\n :placeholder=\"filterPlaceholder\"\n :disabled=\"disabled\"\n clearable\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,GAwCR,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,IAAgB,QACd,EAAqB,OAAO,SAAS,KAAK,OAAO;GAAE,OAAO,EAAE;GAAO,OAAO,EAAE;GAAK,EAAE,IAAI,EAAE,CAChG;EAID,SAAS,EAAW,GAA2C;AAC7D,UAAO,EAAM,SAAS,MAAM,MAAM,EAAY,EAAE,QAAQ,KAAK,GAAS,GAAK,CAAC;;EAG9E,SAAS,EAAgB,GAA4B;AACnD,OAAI,MAAY,KAAA,GAAW;AACzB,MAAM,QAAQ,KAAA;AACd;;AAEF,KAAM,QAAQ,EAAuB,EAAiB,EAAQ,EAAE,EAAW,EAAQ,EAAE,YAAY;;EAGnG,SAAS,EAAe,GAA2B;GACjD,IAAM,IAAU,EAAgB;AAC3B,SACL,EAAM,QAAQ,EACZ,EAAiB,GAAS,EAAO,EACjC,EAAW,EAAQ,EAAE,YACtB;;yBAKD,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;;;;;;;;;;;;;MAG3B,EAQE,EAAA,EAAA,EAAA;GAPC,eAAa,EAAA;GACb,SAAS,EAAA;GACT,OAAO,EAAA;GACP,aAAa,EAAA;GACb,UAAU,EAAA;GACX,WAAA;GACC,uBAAoB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/ui-vue",
3
- "version": "1.72.0",
3
+ "version": "1.73.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -27,8 +27,8 @@
27
27
  "vue": "^3.5.24",
28
28
  "zod": "~3.25.76",
29
29
  "@milaboratories/pf-spec-driver": "1.3.9",
30
- "@milaboratories/uikit": "2.13.3",
31
- "@platforma-sdk/model": "1.72.0",
30
+ "@platforma-sdk/model": "1.73.0",
31
+ "@milaboratories/uikit": "2.13.4",
32
32
  "@milaboratories/pl-model-common": "1.39.0"
33
33
  },
34
34
  "devDependencies": {
@@ -45,10 +45,10 @@
45
45
  "typescript": "~5.9.3",
46
46
  "vite": "^8.0.6",
47
47
  "vitest": "^4.1.3",
48
+ "@milaboratories/build-configs": "2.0.0",
48
49
  "@milaboratories/helpers": "1.14.1",
49
50
  "@milaboratories/ts-builder": "1.3.2",
50
- "@milaboratories/ts-configs": "1.2.3",
51
- "@milaboratories/build-configs": "2.0.0"
51
+ "@milaboratories/ts-configs": "1.2.3"
52
52
  },
53
53
  "scripts": {
54
54
  "dev": "ts-builder serve --target browser-lib",
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * Select a dataset and (optionally) a filter column, emitting a {@link DatasetSelection}.
4
4
  *
5
- * Behaves like {@link PlDropdownRef} when none of the offered datasets carry
6
- * filter options. When the selected dataset has compatible filters, a second
7
- * dropdown appears with the filters plus a "No filter" entry.
5
+ * The filter dropdown is always rendered and is clearable leaving it empty
6
+ * means "no filter". When the selected dataset has no compatible filters, the
7
+ * dropdown opens to an empty option list.
8
8
  *
9
9
  * The emitted value bundles the user's pick (`primary`) with the auto-attached
10
10
  * `enrichments` payload from the matching `DatasetOption`. Enrichments are
@@ -46,8 +46,6 @@ const props = withDefaults(
46
46
  filterLabel?: string;
47
47
  /** Placeholder for the filter dropdown. */
48
48
  filterPlaceholder?: string;
49
- /** Label of the "no filter" entry prepended to the filter options. */
50
- noFilterLabel?: string;
51
49
  /** Show a clear button on the dataset dropdown. */
52
50
  clearable?: boolean;
53
51
  /** Mark the dataset dropdown as required. */
@@ -64,7 +62,6 @@ const props = withDefaults(
64
62
  placeholder: "...",
65
63
  filterLabel: "",
66
64
  filterPlaceholder: "...",
67
- noFilterLabel: "No filter",
68
65
  clearable: false,
69
66
  required: false,
70
67
  disabled: false,
@@ -86,42 +83,31 @@ const primaryOptions = computed<readonly { ref: PlRef; label: string }[] | undef
86
83
  props.options?.map((o) => o.primary),
87
84
  );
88
85
 
89
- const hasFilters = computed(() => (currentDatasetOption.value?.filters?.length ?? 0) > 0);
90
-
91
- /**
92
- * Filter dropdown options. The first entry (`null`) is the "No filter" choice —
93
- * null distinguishes it from `undefined` (dropdown clear button, disabled here).
94
- */
95
- const filterOptions = computed<ListOption<PlRef | null>[]>(() => {
96
- const filters = currentDatasetOption.value?.filters;
97
- if (!filters) return [];
98
- return [
99
- { label: props.noFilterLabel, value: null } as ListOption<PlRef | null>,
100
- ...filters.map((f) => ({ label: f.label, value: f.ref }) as ListOption<PlRef | null>),
101
- ];
102
- });
86
+ const filterOptions = computed<ListOption<PlRef>[]>(
87
+ () => currentDatasetOption.value?.filters?.map((f) => ({ label: f.label, value: f.ref })) ?? [],
88
+ );
103
89
 
104
- const filterValue = computed<PlRef | null>(() => selectedFilter.value ?? null);
90
+ // Resolve from `props.options` directly `currentDatasetOption` may not have
91
+ // recomputed yet when this runs synchronously inside a change handler.
92
+ function findOption(dataset: PlRef): DatasetOption | undefined {
93
+ return props.options?.find((o) => plRefsEqual(o.primary.ref, dataset, true));
94
+ }
105
95
 
106
- function emitValue(dataset: PlRef | undefined, filter: PlRef | undefined) {
96
+ function onDatasetChange(dataset: PlRef | undefined) {
107
97
  if (dataset === undefined) {
108
98
  model.value = undefined;
109
99
  return;
110
100
  }
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);
115
- }
116
-
117
- function onDatasetChange(dataset: PlRef | undefined) {
118
- emitValue(dataset, undefined);
101
+ model.value = createDatasetSelection(createPrimaryRef(dataset), findOption(dataset)?.enrichments);
119
102
  }
120
103
 
121
- function onFilterChange(value: PlRef | null | undefined) {
104
+ function onFilterChange(filter: PlRef | undefined) {
122
105
  const dataset = selectedDataset.value;
123
106
  if (!dataset) return;
124
- emitValue(dataset, value ?? undefined);
107
+ model.value = createDatasetSelection(
108
+ createPrimaryRef(dataset, filter),
109
+ findOption(dataset)?.enrichments,
110
+ );
125
111
  }
126
112
  </script>
127
113
 
@@ -145,12 +131,12 @@ function onFilterChange(value: PlRef | null | undefined) {
145
131
  </template>
146
132
  </PlDropdownRef>
147
133
  <PlDropdown
148
- v-if="hasFilters"
149
- :model-value="filterValue"
134
+ :model-value="selectedFilter"
150
135
  :options="filterOptions"
151
136
  :label="filterLabel"
152
137
  :placeholder="filterPlaceholder"
153
138
  :disabled="disabled"
139
+ clearable
154
140
  @update:model-value="onFilterChange"
155
141
  />
156
142
  </div>
@@ -24,7 +24,7 @@ const optionsWithFilters: DatasetOption[] = [
24
24
  ],
25
25
  enrichments: enrichmentsA,
26
26
  },
27
- // Dataset B has no filters — filter dropdown must stay hidden.
27
+ // Dataset B has no filters — filter dropdown is rendered but with no options.
28
28
  { primary: { label: "Dataset B", ref: datasetB } },
29
29
  ];
30
30
 
@@ -51,7 +51,7 @@ async function pickOption(index: number) {
51
51
  }
52
52
 
53
53
  describe("PlDatasetSelector", () => {
54
- it("renders a single dropdown when no dataset has filters", async () => {
54
+ it("always renders both dataset and filter dropdowns", async () => {
55
55
  const wrapper = mount(PlDatasetSelector, {
56
56
  props: { modelValue: undefined, options: optionsNoFilters },
57
57
  attachTo: document.body,
@@ -60,12 +60,11 @@ describe("PlDatasetSelector", () => {
60
60
 
61
61
  const selector = wrapper.find(".pl-dataset-selector");
62
62
  expect(selector.exists()).toBe(true);
63
- // Only PlDropdownRef is rendered — no filter dropdown.
64
- expect(selector.element.children.length).toBe(1);
63
+ expect(selector.element.children.length).toBe(2);
65
64
  wrapper.unmount();
66
65
  });
67
66
 
68
- it("shows the filter dropdown when the selected dataset has filters", async () => {
67
+ it("filter dropdown is rendered when the selected dataset has filters", async () => {
69
68
  const wrapper = mount(PlDatasetSelector, {
70
69
  props: { modelValue: selection(datasetA), options: optionsWithFilters },
71
70
  attachTo: document.body,
@@ -76,14 +75,20 @@ describe("PlDatasetSelector", () => {
76
75
  wrapper.unmount();
77
76
  });
78
77
 
79
- it("hides the filter dropdown when the selected dataset has no filters", async () => {
78
+ it("filter dropdown is still rendered (with no options) when the selected dataset has no filters", async () => {
80
79
  const wrapper = mount(PlDatasetSelector, {
81
80
  props: { modelValue: selection(datasetB), options: optionsWithFilters },
82
81
  attachTo: document.body,
83
82
  });
84
83
  await flushPromises();
85
84
 
86
- expect(wrapper.find(".pl-dataset-selector").element.children.length).toBe(1);
85
+ expect(wrapper.find(".pl-dataset-selector").element.children.length).toBe(2);
86
+
87
+ // Opening the filter dropdown shows no options.
88
+ const inputs = wrapper.findAll("input");
89
+ expect(inputs.length).toBe(2);
90
+ await inputs[1].trigger("focus");
91
+ expect(document.body.querySelectorAll(".dropdown-list-item").length).toBe(0);
87
92
  wrapper.unmount();
88
93
  });
89
94
 
@@ -129,8 +134,8 @@ describe("PlDatasetSelector", () => {
129
134
  const inputs = wrapper.findAll("input");
130
135
  expect(inputs.length).toBe(2);
131
136
  await inputs[1].trigger("focus");
132
- // Options: [No filter, Top 1000, High quality]. Pick "Top 1000".
133
- await pickOption(1);
137
+ // Options: [Top 1000, High quality]. Pick "Top 1000".
138
+ await pickOption(0);
134
139
 
135
140
  const emitted = wrapper.emitted("update:modelValue");
136
141
  expect(emitted).toBeDefined();
@@ -143,7 +148,7 @@ describe("PlDatasetSelector", () => {
143
148
  wrapper.unmount();
144
149
  });
145
150
 
146
- it("emits DatasetSelection with no filter key when 'No filter' is picked", async () => {
151
+ it("emits DatasetSelection with no filter key when the filter dropdown is cleared", async () => {
147
152
  const wrapper = mount(PlDatasetSelector, {
148
153
  props: {
149
154
  modelValue: selection(datasetA, filterA1, enrichmentsA),
@@ -155,9 +160,12 @@ describe("PlDatasetSelector", () => {
155
160
  });
156
161
  await flushPromises();
157
162
 
158
- const inputs = wrapper.findAll("input");
159
- await inputs[1].trigger("focus");
160
- await pickOption(0); // "No filter"
163
+ // The filter dropdown is the second .clear button (the first belongs to
164
+ // the dataset dropdown, but it's only present when the dataset selector
165
+ // itself is clearable; here only the filter is clearable by default).
166
+ const clearBtns = wrapper.findAll(".clear");
167
+ expect(clearBtns.length).toBeGreaterThan(0);
168
+ await clearBtns[clearBtns.length - 1].trigger("click");
161
169
 
162
170
  const emitted = wrapper.emitted("update:modelValue");
163
171
  expect(emitted).toBeDefined();
@@ -168,14 +176,18 @@ describe("PlDatasetSelector", () => {
168
176
  wrapper.unmount();
169
177
  });
170
178
 
171
- it("hides filter dropdown when dataset has filters: [] (empty array)", async () => {
179
+ it("filter dropdown is still rendered (with no options) when dataset has filters: [] (empty array)", async () => {
172
180
  const wrapper = mount(PlDatasetSelector, {
173
181
  props: { modelValue: selection(datasetC), options: optionsWithEmptyFilters },
174
182
  attachTo: document.body,
175
183
  });
176
184
  await flushPromises();
177
185
 
178
- expect(wrapper.find(".pl-dataset-selector").element.children.length).toBe(1);
186
+ expect(wrapper.find(".pl-dataset-selector").element.children.length).toBe(2);
187
+
188
+ const inputs = wrapper.findAll("input");
189
+ await inputs[1].trigger("focus");
190
+ expect(document.body.querySelectorAll(".dropdown-list-item").length).toBe(0);
179
191
  wrapper.unmount();
180
192
  });
181
193
 
@@ -197,8 +209,7 @@ describe("PlDatasetSelector", () => {
197
209
  expect(inputs.length).toBe(2);
198
210
  await inputs[1].trigger("focus");
199
211
  const items = document.body.querySelectorAll(".dropdown-list-item");
200
- expect(items.length).toBe(3); // No filter, Top 1000, High quality
201
- expect(items[0].textContent).toContain("No filter");
212
+ expect(items.length).toBe(2); // Top 1000, High quality — no synthetic "No filter"
202
213
  wrapper.unmount();
203
214
  });
204
215
 
@@ -242,9 +253,10 @@ describe("PlDatasetSelector", () => {
242
253
  });
243
254
  await flushPromises();
244
255
 
245
- const clearBtn = wrapper.find(".clear");
246
- expect(clearBtn.exists()).toBe(true);
247
- await clearBtn.trigger("click");
256
+ // First .clear button is on the dataset dropdown (clearable: true).
257
+ const clearBtns = wrapper.findAll(".clear");
258
+ expect(clearBtns.length).toBeGreaterThan(0);
259
+ await clearBtns[0].trigger("click");
248
260
 
249
261
  const emitted = wrapper.emitted("update:modelValue");
250
262
  expect(emitted).toBeDefined();