@morscherlab/mld-sdk 0.7.8 → 0.8.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 (41) hide show
  1. package/dist/components/ExperimentCodeBadge.vue.d.ts +7 -1
  2. package/dist/components/ExperimentCodeBadge.vue.js +41 -5
  3. package/dist/components/ExperimentCodeBadge.vue.js.map +1 -1
  4. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  5. package/dist/components/ExperimentDataViewer.vue.js +98 -44
  6. package/dist/components/ExperimentDataViewer.vue.js.map +1 -1
  7. package/dist/components/ExperimentSelectorModal.vue.d.ts +3 -1
  8. package/dist/components/ExperimentSelectorModal.vue.js +117 -63
  9. package/dist/components/ExperimentSelectorModal.vue.js.map +1 -1
  10. package/dist/composables/experiment-utils.d.ts +5 -0
  11. package/dist/composables/experiment-utils.js +34 -0
  12. package/dist/composables/experiment-utils.js.map +1 -0
  13. package/dist/composables/index.d.ts +2 -0
  14. package/dist/composables/index.js +7 -0
  15. package/dist/composables/index.js.map +1 -1
  16. package/dist/composables/useExperimentData.d.ts +17 -0
  17. package/dist/composables/useExperimentData.js +62 -0
  18. package/dist/composables/useExperimentData.js.map +1 -0
  19. package/dist/composables/useExperimentSelector.d.ts +5 -1
  20. package/dist/composables/useExperimentSelector.js +39 -9
  21. package/dist/composables/useExperimentSelector.js.map +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +7 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/styles.css +121 -10
  26. package/package.json +1 -1
  27. package/src/components/ExperimentCodeBadge.story.vue +77 -0
  28. package/src/components/ExperimentCodeBadge.vue +46 -3
  29. package/src/components/ExperimentDataViewer.story.vue +174 -0
  30. package/src/components/ExperimentDataViewer.vue +49 -12
  31. package/src/components/ExperimentSelectorModal.story.vue +244 -0
  32. package/src/components/ExperimentSelectorModal.vue +75 -37
  33. package/src/components/FitPanel.story.vue +125 -0
  34. package/src/composables/experiment-utils.ts +32 -0
  35. package/src/composables/index.ts +11 -0
  36. package/src/composables/useExperimentData.ts +85 -0
  37. package/src/composables/useExperimentSelector.ts +48 -9
  38. package/src/index.ts +9 -0
  39. package/src/styles/components/experiment-code-badge.css +20 -0
  40. package/src/styles/components/experiment-data-viewer.css +8 -1
  41. package/src/styles/components/experiment-selector-modal.css +39 -4
@@ -1,8 +1,14 @@
1
1
  interface Props {
2
2
  code: string;
3
3
  size?: 'sm' | 'md' | 'lg';
4
+ copyable?: boolean;
4
5
  }
5
- declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {
6
+ declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
7
+ copy: (code: string) => any;
8
+ }, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
9
+ onCopy?: ((code: string) => any) | undefined;
10
+ }>, {
6
11
  size: "sm" | "md" | "lg";
12
+ copyable: boolean;
7
13
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLSpanElement>;
8
14
  export default _default;
@@ -1,15 +1,51 @@
1
- import { defineComponent, openBlock, createElementBlock, normalizeClass, toDisplayString } from "vue";
1
+ import { defineComponent, ref, openBlock, createElementBlock, normalizeClass, toDisplayString } from "vue";
2
+ const _hoisted_1 = ["role", "tabindex", "title"];
2
3
  const _sfc_main = /* @__PURE__ */ defineComponent({
3
4
  __name: "ExperimentCodeBadge",
4
5
  props: {
5
6
  code: {},
6
- size: { default: "md" }
7
+ size: { default: "md" },
8
+ copyable: { type: Boolean, default: true }
7
9
  },
8
- setup(__props) {
10
+ emits: ["copy"],
11
+ setup(__props, { emit: __emit }) {
12
+ const props = __props;
13
+ const emit = __emit;
14
+ const copied = ref(false);
15
+ let copyTimeout = null;
16
+ async function handleCopy() {
17
+ if (!props.copyable) return;
18
+ try {
19
+ await navigator.clipboard.writeText(props.code);
20
+ copied.value = true;
21
+ emit("copy", props.code);
22
+ if (copyTimeout) clearTimeout(copyTimeout);
23
+ copyTimeout = setTimeout(() => {
24
+ copied.value = false;
25
+ }, 1500);
26
+ } catch {
27
+ }
28
+ }
29
+ function handleKeydown(event) {
30
+ if (!props.copyable) return;
31
+ if (event.key === "Enter" || event.key === " ") {
32
+ event.preventDefault();
33
+ handleCopy();
34
+ }
35
+ }
9
36
  return (_ctx, _cache) => {
10
37
  return openBlock(), createElementBlock("span", {
11
- class: normalizeClass(["mld-exp-code", `mld-exp-code--${__props.size}`])
12
- }, toDisplayString(__props.code), 3);
38
+ class: normalizeClass([
39
+ "mld-exp-code",
40
+ `mld-exp-code--${__props.size}`,
41
+ { "mld-exp-code--copyable": __props.copyable, "mld-exp-code--copied": copied.value }
42
+ ]),
43
+ role: __props.copyable ? "button" : void 0,
44
+ tabindex: __props.copyable ? 0 : void 0,
45
+ title: __props.copyable ? copied.value ? "Copied!" : "Click to copy" : void 0,
46
+ onClick: handleCopy,
47
+ onKeydown: handleKeydown
48
+ }, toDisplayString(copied.value ? "Copied!" : __props.code), 43, _hoisted_1);
13
49
  };
14
50
  }
15
51
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ExperimentCodeBadge.vue.js","sources":["../../src/components/ExperimentCodeBadge.vue"],"sourcesContent":["<script setup lang=\"ts\">\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<Props>(), {\n size: 'md',\n})\n</script>\n\n<template>\n <span :class=\"['mld-exp-code', `mld-exp-code--${size}`]\">\n {{ code }}\n </span>\n</template>\n\n<style>\n@import '../styles/components/experiment-code-badge.css';\n</style>\n"],"names":["_createElementBlock"],"mappings":";;;;;;;;;0BAYEA,mBAEO,QAAA;AAAA,QAFA,wDAAyC,QAAA,IAAI,EAAA,CAAA;AAAA,MAAA,mBAC/C,QAAA,IAAI,GAAA,CAAA;AAAA;;;"}
1
+ {"version":3,"file":"ExperimentCodeBadge.vue.js","sources":["../../src/components/ExperimentCodeBadge.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mld-exp-code',\n `mld-exp-code--${size}`,\n { 'mld-exp-code--copyable': copyable, 'mld-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n\n<style>\n@import '../styles/components/experiment-code-badge.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_toDisplayString"],"mappings":";;;;;;;;;;;AASA,UAAM,QAAQ;AAKd,UAAM,OAAO;AAIb,UAAM,SAAS,IAAI,KAAK;AACxB,QAAI,cAAoD;AAExD,mBAAe,aAAa;AAC1B,UAAI,CAAC,MAAM,SAAU;AACrB,UAAI;AACF,cAAM,UAAU,UAAU,UAAU,MAAM,IAAI;AAC9C,eAAO,QAAQ;AACf,aAAK,QAAQ,MAAM,IAAI;AACvB,YAAI,0BAA0B,WAAW;AACzC,sBAAc,WAAW,MAAM;AAAE,iBAAO,QAAQ;AAAA,QAAM,GAAG,IAAI;AAAA,MAC/D,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,aAAS,cAAc,OAAsB;AAC3C,UAAI,CAAC,MAAM,SAAU;AACrB,UAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,cAAM,eAAA;AACN,mBAAA;AAAA,MACF;AAAA,IACF;;0BAIEA,mBAaO,QAAA;AAAA,QAZJ,OAAKC,eAAA;AAAA;2BAAiD,QAAA,IAAI;AAAA,UAAsC,EAAA,0BAAA,QAAA,kCAAkC,OAAA,MAAA;AAAA,QAAM;QAKxI,MAAM,QAAA,WAAQ,WAAc;AAAA,QAC5B,UAAU,QAAA,WAAQ,IAAO;AAAA,QACzB,OAAO,QAAA,WAAY,OAAA,sCAAwC;AAAA,QAC3D,SAAO;AAAA,QACP,WAAS;AAAA,MAAA,GAEPC,gBAAA,OAAA,oBAAqB,QAAA,IAAI,GAAA,IAAA,UAAA;AAAA;;;"}
@@ -1,6 +1,6 @@
1
1
  import { TreeNode, DataFrameColumn, SummaryData } from '../types';
2
2
  interface Props {
3
- treeData: TreeNode[];
3
+ treeData?: TreeNode[];
4
4
  tableData?: Record<string, unknown>[];
5
5
  tableColumns?: DataFrameColumn[] | undefined;
6
6
  summaryData?: SummaryData | null;
@@ -1,12 +1,15 @@
1
- import { defineComponent, ref, watch, computed, openBlock, createElementBlock, createElementVNode, createVNode, createBlock, withCtx, createTextVNode, toDisplayString, createCommentVNode, Fragment, renderList } from "vue";
2
- import _sfc_main$4 from "./SampleHierarchyTree.vue.js";
1
+ import { defineComponent, computed, watch, ref, openBlock, createElementBlock, createElementVNode, createVNode, createBlock, withCtx, createTextVNode, toDisplayString, createCommentVNode, Fragment, renderList } from "vue";
2
+ import { useExperimentData } from "../composables/useExperimentData.js";
3
+ import _sfc_main$5 from "./SampleHierarchyTree.vue.js";
3
4
  /* empty css */
4
- import _sfc_main$3 from "./DataFrame.vue.js";
5
+ import _sfc_main$4 from "./DataFrame.vue.js";
5
6
  /* empty css */
6
7
  import _sfc_main$1 from "./SegmentedControl.vue.js";
7
8
  /* empty css */
8
9
  import _sfc_main$2 from "./BaseButton.vue.js";
9
10
  /* empty css */
11
+ import _sfc_main$3 from "./Skeleton.vue.js";
12
+ /* empty css */
10
13
  const _hoisted_1 = { class: "mld-data-viewer" };
11
14
  const _hoisted_2 = { class: "mld-data-viewer__header" };
12
15
  const _hoisted_3 = { class: "mld-data-viewer__controls" };
@@ -16,29 +19,30 @@ const _hoisted_6 = {
16
19
  key: 0,
17
20
  class: "mld-data-viewer__loading"
18
21
  };
19
- const _hoisted_7 = {
22
+ const _hoisted_7 = { class: "mld-data-viewer__skeleton" };
23
+ const _hoisted_8 = {
20
24
  key: 0,
21
25
  class: "mld-summary"
22
26
  };
23
- const _hoisted_8 = {
27
+ const _hoisted_9 = {
24
28
  key: 0,
25
29
  class: "mld-summary__metadata"
26
30
  };
27
- const _hoisted_9 = { class: "mld-summary__pill-key" };
28
- const _hoisted_10 = { class: "mld-summary__pill-value" };
29
- const _hoisted_11 = { class: "mld-summary__group-header" };
30
- const _hoisted_12 = { class: "mld-summary__group-label" };
31
- const _hoisted_13 = { class: "mld-summary__group-count" };
32
- const _hoisted_14 = {
31
+ const _hoisted_10 = { class: "mld-summary__pill-key" };
32
+ const _hoisted_11 = { class: "mld-summary__pill-value" };
33
+ const _hoisted_12 = { class: "mld-summary__group-header" };
34
+ const _hoisted_13 = { class: "mld-summary__group-label" };
35
+ const _hoisted_14 = { class: "mld-summary__group-count" };
36
+ const _hoisted_15 = {
33
37
  key: 0,
34
38
  class: "mld-summary__group-meta"
35
39
  };
36
- const _hoisted_15 = { class: "mld-summary__pill-key" };
37
- const _hoisted_16 = { class: "mld-summary__pill-value" };
38
- const _hoisted_17 = { class: "mld-summary__table-header" };
39
- const _hoisted_18 = { class: "mld-summary__section-label" };
40
- const _hoisted_19 = { class: "mld-summary__section-count" };
41
- const _hoisted_20 = {
40
+ const _hoisted_16 = { class: "mld-summary__pill-key" };
41
+ const _hoisted_17 = { class: "mld-summary__pill-value" };
42
+ const _hoisted_18 = { class: "mld-summary__table-header" };
43
+ const _hoisted_19 = { class: "mld-summary__section-label" };
44
+ const _hoisted_20 = { class: "mld-summary__section-count" };
45
+ const _hoisted_21 = {
42
46
  key: 3,
43
47
  class: "mld-data-viewer__empty"
44
48
  };
@@ -61,6 +65,37 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
61
65
  emits: ["open-plugin", "download-json", "download-csv"],
62
66
  setup(__props, { emit: __emit }) {
63
67
  const props = __props;
68
+ const expData = useExperimentData();
69
+ const hasPropData = computed(
70
+ () => props.treeData && props.treeData.length > 0 || props.summaryData
71
+ );
72
+ watch(
73
+ () => props.experimentId,
74
+ (id) => {
75
+ if (id && !hasPropData.value) {
76
+ expData.fetch(id);
77
+ }
78
+ },
79
+ { immediate: true }
80
+ );
81
+ const mergedTreeData = computed(
82
+ () => {
83
+ var _a;
84
+ return ((_a = props.treeData) == null ? void 0 : _a.length) ? props.treeData : expData.treeData.value;
85
+ }
86
+ );
87
+ const mergedTableData = computed(
88
+ () => {
89
+ var _a;
90
+ return ((_a = props.tableData) == null ? void 0 : _a.length) ? props.tableData : expData.tableData.value;
91
+ }
92
+ );
93
+ const mergedSummaryData = computed(
94
+ () => props.summaryData ?? expData.summaryData.value
95
+ );
96
+ const isLoading = computed(
97
+ () => props.loading || expData.isLoading.value
98
+ );
64
99
  const emit = __emit;
65
100
  const viewMode = ref(props.defaultView);
66
101
  watch(() => props.defaultView, (val) => {
@@ -68,25 +103,25 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
68
103
  });
69
104
  const viewOptions = computed(() => {
70
105
  const opts = [];
71
- if (props.summaryData) {
106
+ if (mergedSummaryData.value) {
72
107
  opts.push({ value: "summary", label: "Summary" });
73
108
  }
74
109
  opts.push({ value: "tree", label: "Tree" });
75
110
  opts.push({ value: "table", label: "Table" });
76
111
  return opts;
77
112
  });
78
- watch(() => props.summaryData, (val) => {
113
+ watch(mergedSummaryData, (val) => {
79
114
  if (!val && viewMode.value === "summary") {
80
115
  viewMode.value = "tree";
81
116
  }
82
117
  }, { immediate: true });
83
118
  const hasTableData = computed(
84
- () => props.tableData && props.tableData.length > 0
119
+ () => mergedTableData.value && mergedTableData.value.length > 0
85
120
  );
86
121
  const metadataEntries = computed(() => {
87
122
  var _a;
88
- if (!((_a = props.summaryData) == null ? void 0 : _a.metadata)) return [];
89
- return Object.entries(props.summaryData.metadata).filter(([, v]) => v !== null && v !== void 0 && v !== "").map(([key, value]) => ({
123
+ if (!((_a = mergedSummaryData.value) == null ? void 0 : _a.metadata)) return [];
124
+ return Object.entries(mergedSummaryData.value.metadata).filter(([, v]) => v !== null && v !== void 0 && v !== "").map(([key, value]) => ({
90
125
  key: key.replace(/_/g, " "),
91
126
  value: String(value)
92
127
  }));
@@ -167,20 +202,39 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
167
202
  ])
168
203
  ]),
169
204
  createElementVNode("div", _hoisted_5, [
170
- __props.loading ? (openBlock(), createElementBlock("div", _hoisted_6, " Loading... ")) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
171
- viewMode.value === "summary" && __props.summaryData ? (openBlock(), createElementBlock("div", _hoisted_7, [
172
- metadataEntries.value.length ? (openBlock(), createElementBlock("div", _hoisted_8, [
205
+ isLoading.value ? (openBlock(), createElementBlock("div", _hoisted_6, [
206
+ createElementVNode("div", _hoisted_7, [
207
+ createVNode(_sfc_main$3, {
208
+ width: "40%",
209
+ height: "16px"
210
+ }),
211
+ createVNode(_sfc_main$3, {
212
+ width: "100%",
213
+ height: "12px"
214
+ }),
215
+ createVNode(_sfc_main$3, {
216
+ width: "100%",
217
+ height: "12px"
218
+ }),
219
+ createVNode(_sfc_main$3, {
220
+ width: "75%",
221
+ height: "12px"
222
+ })
223
+ ])
224
+ ])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
225
+ viewMode.value === "summary" && mergedSummaryData.value ? (openBlock(), createElementBlock("div", _hoisted_8, [
226
+ metadataEntries.value.length ? (openBlock(), createElementBlock("div", _hoisted_9, [
173
227
  (openBlock(true), createElementBlock(Fragment, null, renderList(metadataEntries.value, (entry) => {
174
228
  return openBlock(), createElementBlock("span", {
175
229
  key: entry.key,
176
230
  class: "mld-summary__pill"
177
231
  }, [
178
- createElementVNode("span", _hoisted_9, toDisplayString(entry.key), 1),
179
- createElementVNode("span", _hoisted_10, toDisplayString(entry.value), 1)
232
+ createElementVNode("span", _hoisted_10, toDisplayString(entry.key), 1),
233
+ createElementVNode("span", _hoisted_11, toDisplayString(entry.value), 1)
180
234
  ]);
181
235
  }), 128))
182
236
  ])) : createCommentVNode("", true),
183
- (openBlock(true), createElementBlock(Fragment, null, renderList(__props.summaryData.sections, (section) => {
237
+ (openBlock(true), createElementBlock(Fragment, null, renderList(mergedSummaryData.value.sections, (section) => {
184
238
  var _a;
185
239
  return openBlock(), createElementBlock("div", {
186
240
  key: section.key,
@@ -191,22 +245,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
191
245
  key: idx,
192
246
  class: "mld-summary__group-card"
193
247
  }, [
194
- createElementVNode("div", _hoisted_11, [
195
- createElementVNode("span", _hoisted_12, toDisplayString(item.label), 1),
196
- createElementVNode("span", _hoisted_13, toDisplayString(item.item_count) + " " + toDisplayString(item.item_key), 1)
248
+ createElementVNode("div", _hoisted_12, [
249
+ createElementVNode("span", _hoisted_13, toDisplayString(item.label), 1),
250
+ createElementVNode("span", _hoisted_14, toDisplayString(item.item_count) + " " + toDisplayString(item.item_key), 1)
197
251
  ]),
198
- item.metadata && Object.keys(item.metadata).length ? (openBlock(), createElementBlock("div", _hoisted_14, [
252
+ item.metadata && Object.keys(item.metadata).length ? (openBlock(), createElementBlock("div", _hoisted_15, [
199
253
  (openBlock(true), createElementBlock(Fragment, null, renderList(item.metadata, (val, key) => {
200
254
  return openBlock(), createElementBlock("span", {
201
255
  key: String(key),
202
256
  class: "mld-summary__pill mld-summary__pill--sm"
203
257
  }, [
204
- createElementVNode("span", _hoisted_15, toDisplayString(String(key).replace(/_/g, " ")), 1),
205
- createElementVNode("span", _hoisted_16, toDisplayString(val), 1)
258
+ createElementVNode("span", _hoisted_16, toDisplayString(String(key).replace(/_/g, " ")), 1),
259
+ createElementVNode("span", _hoisted_17, toDisplayString(val), 1)
206
260
  ]);
207
261
  }), 128))
208
262
  ])) : createCommentVNode("", true),
209
- item.rows.length ? (openBlock(), createBlock(_sfc_main$3, {
263
+ item.rows.length ? (openBlock(), createBlock(_sfc_main$4, {
210
264
  key: 1,
211
265
  data: item.rows,
212
266
  columns: columnsForSection(item.columns),
@@ -217,11 +271,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
217
271
  }, null, 8, ["data", "columns", "searchable"])) : createCommentVNode("", true)
218
272
  ]);
219
273
  }), 128)) : section.type === "table" && section.rows ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
220
- createElementVNode("div", _hoisted_17, [
221
- createElementVNode("span", _hoisted_18, toDisplayString(section.label), 1),
222
- createElementVNode("span", _hoisted_19, toDisplayString(section.row_count) + " rows", 1)
274
+ createElementVNode("div", _hoisted_18, [
275
+ createElementVNode("span", _hoisted_19, toDisplayString(section.label), 1),
276
+ createElementVNode("span", _hoisted_20, toDisplayString(section.row_count) + " rows", 1)
223
277
  ]),
224
- createVNode(_sfc_main$3, {
278
+ createVNode(_sfc_main$4, {
225
279
  data: section.rows,
226
280
  columns: columnsForSection(section.columns || []),
227
281
  searchable: (((_a = section.rows) == null ? void 0 : _a.length) || 0) > 10,
@@ -232,20 +286,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
232
286
  ], 64)) : createCommentVNode("", true)
233
287
  ]);
234
288
  }), 128))
235
- ])) : viewMode.value === "tree" ? (openBlock(), createBlock(_sfc_main$4, {
289
+ ])) : viewMode.value === "tree" ? (openBlock(), createBlock(_sfc_main$5, {
236
290
  key: 1,
237
- nodes: __props.treeData,
291
+ nodes: mergedTreeData.value,
238
292
  "expand-all": false,
239
293
  "show-icons": true,
240
294
  "show-counts": true,
241
295
  size: "sm"
242
- }, null, 8, ["nodes"])) : viewMode.value === "table" && hasTableData.value ? (openBlock(), createBlock(_sfc_main$3, {
296
+ }, null, 8, ["nodes"])) : viewMode.value === "table" && hasTableData.value ? (openBlock(), createBlock(_sfc_main$4, {
243
297
  key: 2,
244
- data: __props.tableData,
298
+ data: mergedTableData.value,
245
299
  columns: __props.tableColumns ?? [],
246
300
  searchable: true,
247
301
  sortable: true
248
- }, null, 8, ["data", "columns"])) : viewMode.value === "table" ? (openBlock(), createElementBlock("div", _hoisted_20, " No tabular data available. Use tree view. ")) : createCommentVNode("", true)
302
+ }, null, 8, ["data", "columns"])) : viewMode.value === "table" ? (openBlock(), createElementBlock("div", _hoisted_21, " No tabular data available. Use tree view. ")) : createCommentVNode("", true)
249
303
  ], 64))
250
304
  ])
251
305
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"ExperimentDataViewer.vue.js","sources":["../../src/components/ExperimentDataViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport type { TreeNode, DataFrameColumn, SummaryData } from '../types'\nimport SampleHierarchyTree from './SampleHierarchyTree.vue'\nimport DataFrame from './DataFrame.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n treeData: TreeNode[]\n tableData?: Record<string, unknown>[]\n tableColumns?: DataFrameColumn[] | undefined\n summaryData?: SummaryData | null\n defaultView?: 'summary' | 'tree' | 'table'\n title?: string\n pluginName?: string\n pluginRoutePrefix?: string\n experimentId?: number\n loading?: boolean\n downloadJsonUrl?: string\n downloadCsvUrl?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Data',\n defaultView: 'summary',\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'open-plugin': []\n 'download-json': []\n 'download-csv': []\n}>()\n\nconst viewMode = ref<string | number>(props.defaultView)\n\n// Reset view mode when defaultView prop changes\nwatch(() => props.defaultView, (val) => { viewMode.value = val })\n\nconst viewOptions = computed(() => {\n const opts = []\n if (props.summaryData) {\n opts.push({ value: 'summary', label: 'Summary' })\n }\n opts.push({ value: 'tree', label: 'Tree' })\n opts.push({ value: 'table', label: 'Table' })\n return opts\n})\n\n// Fall back to tree if summary is selected but no summary data\nwatch(() => props.summaryData, (val) => {\n if (!val && viewMode.value === 'summary') {\n viewMode.value = 'tree'\n }\n}, { immediate: true })\n\nconst hasTableData = computed(() =>\n props.tableData && props.tableData.length > 0\n)\n\nconst metadataEntries = computed(() => {\n if (!props.summaryData?.metadata) return []\n return Object.entries(props.summaryData.metadata)\n .filter(([, v]) => v !== null && v !== undefined && v !== '')\n .map(([key, value]) => ({\n key: key.replace(/_/g, ' '),\n value: String(value),\n }))\n})\n\nfunction humanizeColumn(col: string): string {\n return col.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())\n}\n\nfunction formatCellValue(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\nfunction columnsForSection(columns: string[]): DataFrameColumn[] {\n return columns.map((col) => ({\n key: col,\n label: humanizeColumn(col),\n sortable: true,\n formatter: (value: unknown) => formatCellValue(value),\n }))\n}\n\nfunction handleDownloadJson() {\n if (props.downloadJsonUrl) {\n window.open(props.downloadJsonUrl, '_blank')\n }\n emit('download-json')\n}\n\nfunction handleDownloadCsv() {\n if (props.downloadCsvUrl) {\n window.open(props.downloadCsvUrl, '_blank')\n }\n emit('download-csv')\n}\n</script>\n\n<template>\n <div class=\"mld-data-viewer\">\n <div class=\"mld-data-viewer__header\">\n <div class=\"mld-data-viewer__controls\">\n <SegmentedControl\n v-model=\"viewMode\"\n :options=\"viewOptions\"\n variant=\"card\"\n size=\"sm\"\n :full-width=\"false\"\n />\n </div>\n <div class=\"mld-data-viewer__actions\">\n <BaseButton\n v-if=\"pluginRoutePrefix && experimentId\"\n variant=\"secondary\"\n size=\"sm\"\n @click=\"emit('open-plugin')\"\n >\n Open in {{ pluginName || 'Plugin' }}\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadJson\"\n >\n JSON\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadCsv\"\n >\n CSV\n </BaseButton>\n </div>\n </div>\n\n <div class=\"mld-data-viewer__content\">\n <div v-if=\"loading\" class=\"mld-data-viewer__loading\">\n Loading...\n </div>\n <template v-else>\n <!-- Summary View -->\n <div v-if=\"viewMode === 'summary' && summaryData\" class=\"mld-summary\">\n <!-- Metadata pills -->\n <div v-if=\"metadataEntries.length\" class=\"mld-summary__metadata\">\n <span\n v-for=\"entry in metadataEntries\"\n :key=\"entry.key\"\n class=\"mld-summary__pill\"\n >\n <span class=\"mld-summary__pill-key\">{{ entry.key }}</span>\n <span class=\"mld-summary__pill-value\">{{ entry.value }}</span>\n </span>\n </div>\n\n <!-- Sections -->\n <div\n v-for=\"section in summaryData.sections\"\n :key=\"section.key\"\n class=\"mld-summary__section\"\n >\n <!-- Group section: cards with embedded tables -->\n <template v-if=\"section.type === 'group' && section.items\">\n <div\n v-for=\"(item, idx) in section.items\"\n :key=\"idx\"\n class=\"mld-summary__group-card\"\n >\n <div class=\"mld-summary__group-header\">\n <span class=\"mld-summary__group-label\">{{ item.label }}</span>\n <span class=\"mld-summary__group-count\">\n {{ item.item_count }} {{ item.item_key }}\n </span>\n </div>\n <div v-if=\"item.metadata && Object.keys(item.metadata).length\" class=\"mld-summary__group-meta\">\n <span\n v-for=\"(val, key) in item.metadata\"\n :key=\"String(key)\"\n class=\"mld-summary__pill mld-summary__pill--sm\"\n >\n <span class=\"mld-summary__pill-key\">{{ String(key).replace(/_/g, ' ') }}</span>\n <span class=\"mld-summary__pill-value\">{{ val }}</span>\n </span>\n </div>\n <DataFrame\n v-if=\"item.rows.length\"\n :data=\"item.rows\"\n :columns=\"columnsForSection(item.columns)\"\n :searchable=\"item.rows.length > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </div>\n </template>\n\n <!-- Table section: flat table -->\n <template v-else-if=\"section.type === 'table' && section.rows\">\n <div class=\"mld-summary__table-header\">\n <span class=\"mld-summary__section-label\">{{ section.label }}</span>\n <span class=\"mld-summary__section-count\">{{ section.row_count }} rows</span>\n </div>\n <DataFrame\n :data=\"section.rows\"\n :columns=\"columnsForSection(section.columns || [])\"\n :searchable=\"(section.rows?.length || 0) > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </template>\n </div>\n </div>\n\n <!-- Tree View -->\n <SampleHierarchyTree\n v-else-if=\"viewMode === 'tree'\"\n :nodes=\"treeData\"\n :expand-all=\"false\"\n :show-icons=\"true\"\n :show-counts=\"true\"\n size=\"sm\"\n />\n\n <!-- Table View -->\n <DataFrame\n v-else-if=\"viewMode === 'table' && hasTableData\"\n :data=\"tableData!\"\n :columns=\"tableColumns ?? []\"\n :searchable=\"true\"\n :sortable=\"true\"\n />\n <div v-else-if=\"viewMode === 'table'\" class=\"mld-data-viewer__empty\">\n No tabular data available. Use tree view.\n </div>\n </template>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-data-viewer.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","SegmentedControl","_createBlock","BaseButton","_createTextVNode","_Fragment","_renderList","_toDisplayString","DataFrame","SampleHierarchyTree"],"mappings":"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"}
1
+ {"version":3,"file":"ExperimentDataViewer.vue.js","sources":["../../src/components/ExperimentDataViewer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport type { TreeNode, DataFrameColumn, SummaryData } from '../types'\nimport { useExperimentData } from '../composables/useExperimentData'\nimport SampleHierarchyTree from './SampleHierarchyTree.vue'\nimport DataFrame from './DataFrame.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport BaseButton from './BaseButton.vue'\nimport Skeleton from './Skeleton.vue'\n\ninterface Props {\n treeData?: TreeNode[]\n tableData?: Record<string, unknown>[]\n tableColumns?: DataFrameColumn[] | undefined\n summaryData?: SummaryData | null\n defaultView?: 'summary' | 'tree' | 'table'\n title?: string\n pluginName?: string\n pluginRoutePrefix?: string\n experimentId?: number\n loading?: boolean\n downloadJsonUrl?: string\n downloadCsvUrl?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Data',\n defaultView: 'summary',\n loading: false,\n})\n\n// Auto-fetch when experimentId is provided and no prop data is given\nconst expData = useExperimentData()\nconst hasPropData = computed(() =>\n (props.treeData && props.treeData.length > 0) || props.summaryData,\n)\n\nwatch(\n () => props.experimentId,\n (id) => {\n if (id && !hasPropData.value) {\n expData.fetch(id)\n }\n },\n { immediate: true },\n)\n\n// Merge: props take priority over composable data\nconst mergedTreeData = computed(() =>\n props.treeData?.length ? props.treeData : expData.treeData.value,\n)\nconst mergedTableData = computed(() =>\n props.tableData?.length ? props.tableData : expData.tableData.value,\n)\nconst mergedSummaryData = computed(() =>\n props.summaryData ?? expData.summaryData.value,\n)\nconst isLoading = computed(() =>\n props.loading || expData.isLoading.value,\n)\n\nconst emit = defineEmits<{\n 'open-plugin': []\n 'download-json': []\n 'download-csv': []\n}>()\n\nconst viewMode = ref<string | number>(props.defaultView)\n\n// Reset view mode when defaultView prop changes\nwatch(() => props.defaultView, (val) => { viewMode.value = val })\n\nconst viewOptions = computed(() => {\n const opts = []\n if (mergedSummaryData.value) {\n opts.push({ value: 'summary', label: 'Summary' })\n }\n opts.push({ value: 'tree', label: 'Tree' })\n opts.push({ value: 'table', label: 'Table' })\n return opts\n})\n\n// Fall back to tree if summary is selected but no summary data\nwatch(mergedSummaryData, (val) => {\n if (!val && viewMode.value === 'summary') {\n viewMode.value = 'tree'\n }\n}, { immediate: true })\n\nconst hasTableData = computed(() =>\n mergedTableData.value && mergedTableData.value.length > 0,\n)\n\nconst metadataEntries = computed(() => {\n if (!mergedSummaryData.value?.metadata) return []\n return Object.entries(mergedSummaryData.value.metadata)\n .filter(([, v]) => v !== null && v !== undefined && v !== '')\n .map(([key, value]) => ({\n key: key.replace(/_/g, ' '),\n value: String(value),\n }))\n})\n\nfunction humanizeColumn(col: string): string {\n return col.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())\n}\n\nfunction formatCellValue(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\nfunction columnsForSection(columns: string[]): DataFrameColumn[] {\n return columns.map((col) => ({\n key: col,\n label: humanizeColumn(col),\n sortable: true,\n formatter: (value: unknown) => formatCellValue(value),\n }))\n}\n\nfunction handleDownloadJson() {\n if (props.downloadJsonUrl) {\n window.open(props.downloadJsonUrl, '_blank')\n }\n emit('download-json')\n}\n\nfunction handleDownloadCsv() {\n if (props.downloadCsvUrl) {\n window.open(props.downloadCsvUrl, '_blank')\n }\n emit('download-csv')\n}\n</script>\n\n<template>\n <div class=\"mld-data-viewer\">\n <div class=\"mld-data-viewer__header\">\n <div class=\"mld-data-viewer__controls\">\n <SegmentedControl\n v-model=\"viewMode\"\n :options=\"viewOptions\"\n variant=\"card\"\n size=\"sm\"\n :full-width=\"false\"\n />\n </div>\n <div class=\"mld-data-viewer__actions\">\n <BaseButton\n v-if=\"pluginRoutePrefix && experimentId\"\n variant=\"secondary\"\n size=\"sm\"\n @click=\"emit('open-plugin')\"\n >\n Open in {{ pluginName || 'Plugin' }}\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadJson\"\n >\n JSON\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadCsv\"\n >\n CSV\n </BaseButton>\n </div>\n </div>\n\n <div class=\"mld-data-viewer__content\">\n <div v-if=\"isLoading\" class=\"mld-data-viewer__loading\">\n <div class=\"mld-data-viewer__skeleton\">\n <Skeleton width=\"40%\" height=\"16px\" />\n <Skeleton width=\"100%\" height=\"12px\" />\n <Skeleton width=\"100%\" height=\"12px\" />\n <Skeleton width=\"75%\" height=\"12px\" />\n </div>\n </div>\n <template v-else>\n <!-- Summary View -->\n <div v-if=\"viewMode === 'summary' && mergedSummaryData\" class=\"mld-summary\">\n <!-- Metadata pills -->\n <div v-if=\"metadataEntries.length\" class=\"mld-summary__metadata\">\n <span\n v-for=\"entry in metadataEntries\"\n :key=\"entry.key\"\n class=\"mld-summary__pill\"\n >\n <span class=\"mld-summary__pill-key\">{{ entry.key }}</span>\n <span class=\"mld-summary__pill-value\">{{ entry.value }}</span>\n </span>\n </div>\n\n <!-- Sections -->\n <div\n v-for=\"section in mergedSummaryData.sections\"\n :key=\"section.key\"\n class=\"mld-summary__section\"\n >\n <!-- Group section: cards with embedded tables -->\n <template v-if=\"section.type === 'group' && section.items\">\n <div\n v-for=\"(item, idx) in section.items\"\n :key=\"idx\"\n class=\"mld-summary__group-card\"\n >\n <div class=\"mld-summary__group-header\">\n <span class=\"mld-summary__group-label\">{{ item.label }}</span>\n <span class=\"mld-summary__group-count\">\n {{ item.item_count }} {{ item.item_key }}\n </span>\n </div>\n <div v-if=\"item.metadata && Object.keys(item.metadata).length\" class=\"mld-summary__group-meta\">\n <span\n v-for=\"(val, key) in item.metadata\"\n :key=\"String(key)\"\n class=\"mld-summary__pill mld-summary__pill--sm\"\n >\n <span class=\"mld-summary__pill-key\">{{ String(key).replace(/_/g, ' ') }}</span>\n <span class=\"mld-summary__pill-value\">{{ val }}</span>\n </span>\n </div>\n <DataFrame\n v-if=\"item.rows.length\"\n :data=\"item.rows\"\n :columns=\"columnsForSection(item.columns)\"\n :searchable=\"item.rows.length > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </div>\n </template>\n\n <!-- Table section: flat table -->\n <template v-else-if=\"section.type === 'table' && section.rows\">\n <div class=\"mld-summary__table-header\">\n <span class=\"mld-summary__section-label\">{{ section.label }}</span>\n <span class=\"mld-summary__section-count\">{{ section.row_count }} rows</span>\n </div>\n <DataFrame\n :data=\"section.rows\"\n :columns=\"columnsForSection(section.columns || [])\"\n :searchable=\"(section.rows?.length || 0) > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </template>\n </div>\n </div>\n\n <!-- Tree View -->\n <SampleHierarchyTree\n v-else-if=\"viewMode === 'tree'\"\n :nodes=\"mergedTreeData\"\n :expand-all=\"false\"\n :show-icons=\"true\"\n :show-counts=\"true\"\n size=\"sm\"\n />\n\n <!-- Table View -->\n <DataFrame\n v-else-if=\"viewMode === 'table' && hasTableData\"\n :data=\"mergedTableData!\"\n :columns=\"tableColumns ?? []\"\n :searchable=\"true\"\n :sortable=\"true\"\n />\n <div v-else-if=\"viewMode === 'table'\" class=\"mld-data-viewer__empty\">\n No tabular data available. Use tree view.\n </div>\n </template>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-data-viewer.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_createVNode","SegmentedControl","_createBlock","BaseButton","_createTextVNode","Skeleton","_Fragment","_renderList","_toDisplayString","DataFrame","SampleHierarchyTree"],"mappings":"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"}
@@ -16,5 +16,7 @@ declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, imp
16
16
  size: ModalSize;
17
17
  title: string;
18
18
  currentExperimentId: number | null;
19
- }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
19
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
20
+ listRef: HTMLDivElement;
21
+ }, any>;
20
22
  export default _default;