@morscherlab/mld-sdk 0.7.7 → 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.
- 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 +22 -0
- package/dist/components/ExperimentSelectorModal.vue.js +218 -0
- package/dist/components/ExperimentSelectorModal.vue.js.map +1 -0
- package/dist/components/ExperimentSelectorModal.vue3.js +6 -0
- package/dist/components/ExperimentSelectorModal.vue3.js.map +1 -0
- package/dist/components/FitPanel.vue.d.ts +46 -0
- package/dist/components/FitPanel.vue.js +118 -0
- package/dist/components/FitPanel.vue.js.map +1 -0
- package/dist/components/FitPanel.vue3.js +6 -0
- package/dist/components/FitPanel.vue3.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +6 -0
- package/dist/components/index.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 +3 -0
- package/dist/composables/index.js +9 -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 +24 -0
- package/dist/composables/useExperimentSelector.js +112 -0
- package/dist/composables/useExperimentSelector.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +407 -2
- package/dist/types/components.d.ts +30 -0
- package/dist/types/index.d.ts +1 -1
- 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 +195 -0
- package/src/components/FitPanel.story.vue +125 -0
- package/src/components/FitPanel.vue +119 -0
- package/src/components/index.ts +4 -0
- package/src/composables/experiment-utils.ts +32 -0
- package/src/composables/index.ts +16 -0
- package/src/composables/useExperimentData.ts +85 -0
- package/src/composables/useExperimentSelector.ts +152 -0
- package/src/index.ts +24 -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 +136 -0
- package/src/styles/components/fit-panel.css +67 -0
- package/src/styles/index.css +2 -0
- package/src/types/components.ts +38 -0
- package/src/types/index.ts +8 -0
|
@@ -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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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ModalSize, ExperimentSummary } from '../types';
|
|
2
|
+
interface Props {
|
|
3
|
+
modelValue: boolean;
|
|
4
|
+
experimentType?: string;
|
|
5
|
+
currentExperimentId?: number | null;
|
|
6
|
+
title?: string;
|
|
7
|
+
size?: ModalSize;
|
|
8
|
+
}
|
|
9
|
+
declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
10
|
+
select: (experiment: ExperimentSummary) => any;
|
|
11
|
+
"update:modelValue": (value: boolean) => any;
|
|
12
|
+
}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
|
|
13
|
+
onSelect?: ((experiment: ExperimentSummary) => any) | undefined;
|
|
14
|
+
"onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
|
|
15
|
+
}>, {
|
|
16
|
+
size: ModalSize;
|
|
17
|
+
title: string;
|
|
18
|
+
currentExperimentId: number | null;
|
|
19
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
20
|
+
listRef: HTMLDivElement;
|
|
21
|
+
}, any>;
|
|
22
|
+
export default _default;
|