@morscherlab/mld-sdk 0.7.8 → 0.8.2
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.
- package/dist/components/ExperimentCodeBadge.vue.d.ts +7 -1
- package/dist/components/ExperimentCodeBadge.vue.js +41 -5
- package/dist/components/ExperimentCodeBadge.vue.js.map +1 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
- package/dist/components/ExperimentDataViewer.vue.js +98 -44
- package/dist/components/ExperimentDataViewer.vue.js.map +1 -1
- package/dist/components/ExperimentSelectorModal.vue.d.ts +3 -1
- package/dist/components/ExperimentSelectorModal.vue.js +117 -63
- package/dist/components/ExperimentSelectorModal.vue.js.map +1 -1
- package/dist/composables/experiment-utils.d.ts +5 -0
- package/dist/composables/experiment-utils.js +34 -0
- package/dist/composables/experiment-utils.js.map +1 -0
- package/dist/composables/index.d.ts +2 -0
- package/dist/composables/index.js +7 -0
- package/dist/composables/index.js.map +1 -1
- package/dist/composables/useExperimentData.d.ts +17 -0
- package/dist/composables/useExperimentData.js +62 -0
- package/dist/composables/useExperimentData.js.map +1 -0
- package/dist/composables/useExperimentSelector.d.ts +5 -1
- package/dist/composables/useExperimentSelector.js +39 -9
- package/dist/composables/useExperimentSelector.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +121 -10
- package/package.json +1 -1
- package/src/components/ExperimentCodeBadge.story.vue +77 -0
- package/src/components/ExperimentCodeBadge.vue +46 -3
- package/src/components/ExperimentDataViewer.story.vue +174 -0
- package/src/components/ExperimentDataViewer.vue +49 -12
- package/src/components/ExperimentSelectorModal.story.vue +244 -0
- package/src/components/ExperimentSelectorModal.vue +75 -37
- package/src/components/FitPanel.story.vue +125 -0
- package/src/composables/experiment-utils.ts +32 -0
- package/src/composables/index.ts +11 -0
- package/src/composables/useExperimentData.ts +85 -0
- package/src/composables/useExperimentSelector.ts +48 -9
- package/src/index.ts +9 -0
- package/src/styles/components/experiment-code-badge.css +20 -0
- package/src/styles/components/experiment-data-viewer.css +8 -1
- 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, {
|
|
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
|
-
|
|
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([
|
|
12
|
-
|
|
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\
|
|
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,12 +1,15 @@
|
|
|
1
|
-
import { defineComponent,
|
|
2
|
-
import
|
|
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$
|
|
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
|
|
27
|
+
const _hoisted_9 = {
|
|
24
28
|
key: 0,
|
|
25
29
|
class: "mld-summary__metadata"
|
|
26
30
|
};
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
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
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
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 (
|
|
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(
|
|
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
|
-
() =>
|
|
119
|
+
() => mergedTableData.value && mergedTableData.value.length > 0
|
|
85
120
|
);
|
|
86
121
|
const metadataEntries = computed(() => {
|
|
87
122
|
var _a;
|
|
88
|
-
if (!((_a =
|
|
89
|
-
return Object.entries(
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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",
|
|
179
|
-
createElementVNode("span",
|
|
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(
|
|
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",
|
|
195
|
-
createElementVNode("span",
|
|
196
|
-
createElementVNode("span",
|
|
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",
|
|
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",
|
|
205
|
-
createElementVNode("span",
|
|
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$
|
|
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",
|
|
221
|
-
createElementVNode("span",
|
|
222
|
-
createElementVNode("span",
|
|
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$
|
|
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$
|
|
289
|
+
])) : viewMode.value === "tree" ? (openBlock(), createBlock(_sfc_main$5, {
|
|
236
290
|
key: 1,
|
|
237
|
-
nodes:
|
|
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$
|
|
296
|
+
}, null, 8, ["nodes"])) : viewMode.value === "table" && hasTableData.value ? (openBlock(), createBlock(_sfc_main$4, {
|
|
243
297
|
key: 2,
|
|
244
|
-
data:
|
|
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",
|
|
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, {
|
|
19
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
20
|
+
listRef: HTMLDivElement;
|
|
21
|
+
}, any>;
|
|
20
22
|
export default _default;
|