@morscherlab/mint-sdk 1.3.0 → 1.3.1
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/__tests__/components/AutoGroupModal.anatomy.test.d.ts +1 -0
- package/dist/__tests__/components/AutoGroupModal.wiring.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/fieldEdits.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/golden.test.d.ts +1 -0
- package/dist/components/AutoGroupModal.adapter.d.ts +27 -4
- package/dist/components/SmartGroup.types.d.ts +22 -0
- package/dist/components/SmartGroupFieldRecipe.groups.d.ts +10 -2
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +3 -0
- package/dist/components/index.js +1 -1
- package/dist/{components-vHfxV2PB.js → components-bbanLd1u.js} +499 -263
- package/dist/components-bbanLd1u.js.map +1 -0
- package/dist/composables/autoGroup/classify.d.ts +7 -1
- package/dist/composables/autoGroup/columns.d.ts +16 -1
- package/dist/composables/autoGroup/index.d.ts +5 -7
- package/dist/composables/autoGroup/tokenize.d.ts +36 -1
- package/dist/composables/index.js +2 -2
- package/dist/composables/useAutoGroup.d.ts +13 -2
- package/dist/{composables-D6uqp0UF.js → composables-BbHozlA7.js} +2 -2
- package/dist/{composables-D6uqp0UF.js.map → composables-BbHozlA7.js.map} +1 -1
- package/dist/index.js +3 -3
- package/dist/install.js +1 -1
- package/dist/permissions.js +2 -2
- package/dist/permissions.js.map +1 -1
- package/dist/styles.css +71 -82
- package/dist/types/auto-group.d.ts +1 -1
- package/dist/{validation-Dmp9Q5W0.js → validation-CukfLjy-.js} +323 -187
- package/dist/validation-CukfLjy-.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/components/AutoGroupModal.anatomy.test.ts +86 -0
- package/src/__tests__/components/AutoGroupModal.wiring.test.ts +53 -0
- package/src/__tests__/components/SampleSelector.test.ts +14 -16
- package/src/__tests__/components/SmartGroupFieldRecipe.groups.test.ts +23 -0
- package/src/__tests__/components/SmartGroupFieldRecipe.test.ts +176 -0
- package/src/__tests__/composables/autoGroup/columns.test.ts +100 -11
- package/src/__tests__/composables/autoGroup/fieldEdits.test.ts +74 -0
- package/src/__tests__/composables/autoGroup/fingerprint.test.ts +9 -0
- package/src/__tests__/composables/autoGroup/golden.test.ts +187 -0
- package/src/__tests__/composables/autoGroup/groupByOrder.test.ts +7 -7
- package/src/__tests__/composables/autoGroup/tokenLength.test.ts +7 -0
- package/src/__tests__/composables/autoGroup/tokenize.test.ts +78 -1
- package/src/__tests__/composables/useAutoGroup.test.ts +2 -1
- package/src/__tests__/fixtures/auto-group/share-exp021.txt +103 -0
- package/src/__tests__/fixtures/auto-group/share-exp031.txt +39 -0
- package/src/__tests__/fixtures/auto-group/share-exp114.txt +123 -0
- package/src/__tests__/fixtures/auto-group/share-exp214.txt +63 -0
- package/src/__tests__/utils/permissions.test.ts +15 -0
- package/src/components/AutoGroupModal.adapter.ts +80 -9
- package/src/components/AutoGroupModal.vue +41 -8
- package/src/components/SmartGroup.types.ts +23 -0
- package/src/components/SmartGroupFieldRecipe.groups.ts +14 -2
- package/src/components/SmartGroupFieldRecipe.vue +289 -169
- package/src/composables/autoGroup/classify.ts +26 -7
- package/src/composables/autoGroup/columns.ts +144 -36
- package/src/composables/autoGroup/compose.ts +8 -10
- package/src/composables/autoGroup/fingerprint.ts +1 -1
- package/src/composables/autoGroup/index.ts +5 -12
- package/src/composables/autoGroup/tokenLength.ts +8 -0
- package/src/composables/autoGroup/tokenize.ts +148 -1
- package/src/composables/autoGroup/vocab.json +5 -11
- package/src/composables/useAutoGroup.ts +92 -79
- package/src/permissions.ts +3 -2
- package/src/styles/components/smart-group.css +71 -82
- package/src/types/auto-group.ts +1 -0
- package/dist/components-vHfxV2PB.js.map +0 -1
- package/dist/composables/autoGroup/replicatePreGroup.d.ts +0 -36
- package/dist/validation-Dmp9Q5W0.js.map +0 -1
- package/src/composables/autoGroup/replicatePreGroup.ts +0 -124
|
@@ -2,7 +2,7 @@ import { a as tryUseSettingsStore, c as mintApiErrorFromResponse, i as tableDens
|
|
|
2
2
|
import { a as SORT_OPTIONS, c as formatExperimentDate, f as BaseModal_default, h as BaseButton_default, l as formatExperimentStatus, m as useEventListener, p as useFocusTrap, r as EXPERIMENT_STATUS_OPTIONS, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "./experiment-utils-DTANyVKV.js";
|
|
3
3
|
import { a as normalizeOptionInput, i as BaseSelect_default, n as Skeleton_default, o as BaseInput_default, r as EmptyState_default, t as ResourcePickerListInternal_default } from "./ResourcePickerListInternal-CFAw4vTZ.js";
|
|
4
4
|
import { $n as FormulaInput_default, An as getControlDefaults, B as toBioTemplateComponentProps, Cn as SAMPLE_GROUP_TOKENS, Cr as FileUploader_default, F as getBioTemplateComponentProps, Fn as controlsToSettingsSchema, Gn as getFieldRegistryEntry, H as toBioTemplateComponentPropsById, Hr as durationMinutes, In as controlsToSidebarPanels, Jn as UnitInput_default, Kr as formatTime, L as toBioTemplateComponentBindings, Mn as controlsToFormSchema, Pn as controlsToSectionFormSchemas, R as toBioTemplateComponentBindingsById, Rr as TagsInput_default, Sn as SAMPLE_COLOR_SCALE, Sr as _plugin_vue_export_helper_default, Tr as SecretInput_default, V as toBioTemplateComponentPropsByComponent, Vr as compareTime, Xn as MoleculeInput_default, Yn as useConcentrationUnits, Zn as SequenceInput_default, _n as createPlateRowLabels, ai as useListSelection, ar as getInjectedPlatformContext, br as isRecord, ci as BaseRadioGroup_default, di as Tooltip_default, fi as BaseTextarea_default, fn as extractTemplateCollection, gn as createPlateColumnLabels, hn as WELL_PLATE_SIZE_CONFIGS, hr as normalizeJobPercent, ii as MultiSelect_default, li as BaseToggle_default, lr as createGeneratedAnalysisTransport, mn as PLATE_CONFIGS, mr as jobStatusLabel, ni as DatePicker_default, nr as GeneratedPathInput_default, qn as ConcentrationInput_default, ri as useDropdownState, rr as GENERATED_JOBS_API_KEY, si as BaseSlider_default, sr as resolveCurrentExperimentId, tr as useChemicalFormula, ui as BaseCheckbox_default, ur as useGeneratedAnalysis, vn as createWellGrid, wn as SLOT_ORDER, wr as NumberInput_default, xr as useMenuKeyboard, yn as getRectangleWellIds, yr as DateTimePicker_default, zr as TimePicker_default } from "./templates-BU1pet28.js";
|
|
5
|
-
import { B as
|
|
5
|
+
import { $ as downloadBlob, At as useAppExperiment, B as GROUPING_FIELD_ROLES, Bt as useTextSearch, C as useExperimentSamples, D as getSampleGroupMajorPrefix, E as useSampleGroups, F as useAutoGroup, Ft as usePlatformContext, G as reorderList, H as classKey, Ht as useSortedItems, It as useTheme, J as useDoseCalculator, K as useListReorder, Lt as useToast, Mt as useRuntimeAlignment, O as getSampleGroupParentPath, Ot as mergeControlWorkspaceOptions, Pt as usePresenceHeartbeat, S as useScheduleDrag, St as useControlWorkspace, T as useExpansionSet, U as useRackEditor, Ut as useRovingFocus, V as isExperimentalKind, Z as usePluginJobCenter, a as useExperimentSelector, c as DEFAULT_PRESETS, d as useReagentSeries, dt as usePluginWorkspace, f as useGroupAssignment, ft as DEFAULT_MOBILE_VIEWPORT_QUERY, i as useAnalysisArtifacts, j as readFileAsText, jt as getInjectedFrontendRevision, kt as APP_EXPERIMENT_KEY, l as DEFAULT_UNITS, m as useBioTemplatePackWorkspace, mt as useFormBuilder, n as pickerFormat, p as useBioTemplatePresetWorkspace, pt as useMobileSupportGate, q as useReorderAnnouncer, r as pickerReason, s as useProtocolTemplates, t as pickerExclusion, ut as useRequestSyncState, vt as defineDoseDesignControlModel, w as useExperimentData, wt as resolveControlModel, z as DEFAULT_COLORS } from "./validation-CukfLjy-.js";
|
|
6
6
|
import { canAccessByPolicy, hasAllPermissions } from "./permissions.js";
|
|
7
7
|
import { a as useApi, o as useAuthStore, t as useExperimentStore } from "./experiment-DgllIc3I.js";
|
|
8
8
|
import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "./analysisArtifactTypes-Cy3D0Ipg.js";
|
|
@@ -10714,6 +10714,13 @@ function appendQcGroup(groups, route, qcColor, qcCount = 3) {
|
|
|
10714
10714
|
function sampleTotalFor(route, realCount = 9, qcCount = 3) {
|
|
10715
10715
|
return route === "Exclude" ? realCount : realCount + qcCount;
|
|
10716
10716
|
}
|
|
10717
|
+
/**
|
|
10718
|
+
* True when half or more of the groups have one sample: the design has no
|
|
10719
|
+
* replicate in the names, or a field that is on is not part of the condition.
|
|
10720
|
+
*/
|
|
10721
|
+
function mostlySingletons(groups) {
|
|
10722
|
+
return groups.length > 1 && groups.filter((group) => group.count === 1).length * 2 >= groups.length;
|
|
10723
|
+
}
|
|
10717
10724
|
/** Decide which inline summary callout the Field Recipe should show. */
|
|
10718
10725
|
function fieldRecipeSummary(input) {
|
|
10719
10726
|
const groupCount = input.baseGroups.length;
|
|
@@ -10725,6 +10732,10 @@ function fieldRecipeSummary(input) {
|
|
|
10725
10732
|
kind: "warn",
|
|
10726
10733
|
groupCount
|
|
10727
10734
|
};
|
|
10735
|
+
if (mostlySingletons(input.baseGroups)) return {
|
|
10736
|
+
kind: "singletons",
|
|
10737
|
+
groupCount
|
|
10738
|
+
};
|
|
10728
10739
|
return {
|
|
10729
10740
|
kind: "info",
|
|
10730
10741
|
groupCount
|
|
@@ -10732,97 +10743,110 @@ function fieldRecipeSummary(input) {
|
|
|
10732
10743
|
}
|
|
10733
10744
|
//#endregion
|
|
10734
10745
|
//#region src/components/SmartGroupFieldRecipe.vue?vue&type=script&setup=true&lang.ts
|
|
10735
|
-
var _hoisted_1$42 = { class: "sg-
|
|
10736
|
-
var _hoisted_2$38 = { class: "sg-
|
|
10737
|
-
var _hoisted_3$31 =
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
};
|
|
10742
|
-
var _hoisted_5$28 = ["aria-expanded"];
|
|
10743
|
-
var _hoisted_6$26 = {
|
|
10744
|
-
key: 0,
|
|
10745
|
-
class: "fr-csv-menu",
|
|
10746
|
-
role: "menu"
|
|
10747
|
-
};
|
|
10748
|
-
var _hoisted_7$24 = [
|
|
10749
|
-
"disabled",
|
|
10750
|
-
"aria-disabled",
|
|
10751
|
-
"title"
|
|
10752
|
-
];
|
|
10753
|
-
var _hoisted_8$22 = [
|
|
10754
|
-
"disabled",
|
|
10755
|
-
"aria-disabled",
|
|
10756
|
-
"title"
|
|
10746
|
+
var _hoisted_1$42 = { class: "sg-head" };
|
|
10747
|
+
var _hoisted_2$38 = { class: "sg-head__sub" };
|
|
10748
|
+
var _hoisted_3$31 = [
|
|
10749
|
+
"aria-expanded",
|
|
10750
|
+
"onClick",
|
|
10751
|
+
"onKeydown"
|
|
10757
10752
|
];
|
|
10758
|
-
var
|
|
10753
|
+
var _hoisted_4$30 = {
|
|
10759
10754
|
class: "sg-mode",
|
|
10760
10755
|
role: "tablist",
|
|
10761
10756
|
"aria-label": "Grouping mode"
|
|
10762
10757
|
};
|
|
10763
|
-
var
|
|
10764
|
-
var
|
|
10758
|
+
var _hoisted_5$28 = { class: "sg-body" };
|
|
10759
|
+
var _hoisted_6$26 = {
|
|
10765
10760
|
key: 0,
|
|
10766
10761
|
class: "fr-dropzone"
|
|
10767
10762
|
};
|
|
10768
|
-
var
|
|
10763
|
+
var _hoisted_7$24 = {
|
|
10769
10764
|
key: 1,
|
|
10770
10765
|
class: "fr-loading"
|
|
10771
10766
|
};
|
|
10767
|
+
var _hoisted_8$22 = {
|
|
10768
|
+
key: 2,
|
|
10769
|
+
class: "fr-blank",
|
|
10770
|
+
"data-test": "recipe-empty"
|
|
10771
|
+
};
|
|
10772
|
+
var _hoisted_9$20 = {
|
|
10773
|
+
key: 0,
|
|
10774
|
+
class: "fr-anatomy"
|
|
10775
|
+
};
|
|
10776
|
+
var _hoisted_10$19 = { class: "fr-anatomy__tokens" };
|
|
10777
|
+
var _hoisted_11$17 = { class: "fr-token__text" };
|
|
10778
|
+
var _hoisted_12$16 = { class: "fr-token__caption" };
|
|
10772
10779
|
var _hoisted_13$16 = { class: "sg-recipe" };
|
|
10773
|
-
var _hoisted_14$16 = {
|
|
10774
|
-
var _hoisted_15$16 = { class: "sg-field__name" };
|
|
10775
|
-
var _hoisted_16$13 = [
|
|
10776
|
-
"aria-checked",
|
|
10777
|
-
"aria-label",
|
|
10778
|
-
"onClick"
|
|
10779
|
-
];
|
|
10780
|
-
var _hoisted_17$13 = {
|
|
10780
|
+
var _hoisted_14$16 = {
|
|
10781
10781
|
key: 0,
|
|
10782
|
-
class: "
|
|
10782
|
+
class: "fr-empty"
|
|
10783
10783
|
};
|
|
10784
|
-
var
|
|
10785
|
-
|
|
10786
|
-
"
|
|
10787
|
-
"
|
|
10788
|
-
"
|
|
10784
|
+
var _hoisted_15$16 = [
|
|
10785
|
+
"data-field",
|
|
10786
|
+
"draggable",
|
|
10787
|
+
"onDragstart",
|
|
10788
|
+
"onDragover",
|
|
10789
|
+
"onDrop"
|
|
10789
10790
|
];
|
|
10790
|
-
var
|
|
10791
|
-
|
|
10791
|
+
var _hoisted_16$13 = ["aria-label", "onKeydown"];
|
|
10792
|
+
var _hoisted_17$13 = { class: "fr-row__layer" };
|
|
10793
|
+
var _hoisted_18$13 = [
|
|
10792
10794
|
"aria-label",
|
|
10793
|
-
"
|
|
10795
|
+
"aria-invalid",
|
|
10796
|
+
"onKeydown",
|
|
10797
|
+
"onBlur"
|
|
10794
10798
|
];
|
|
10795
|
-
var
|
|
10796
|
-
|
|
10799
|
+
var _hoisted_19$13 = {
|
|
10800
|
+
key: 1,
|
|
10801
|
+
class: "fr-row__error",
|
|
10802
|
+
role: "alert"
|
|
10803
|
+
};
|
|
10804
|
+
var _hoisted_20$12 = { class: "sg-field__name" };
|
|
10805
|
+
var _hoisted_21$9 = ["aria-label", "onClick"];
|
|
10806
|
+
var _hoisted_22$9 = { class: "sg-field__vals" };
|
|
10807
|
+
var _hoisted_23$7 = {
|
|
10808
|
+
key: 3,
|
|
10809
|
+
class: "fr-row__hint"
|
|
10810
|
+
};
|
|
10811
|
+
var _hoisted_24$7 = { class: "sg-recipe" };
|
|
10812
|
+
var _hoisted_25$7 = ["data-field"];
|
|
10813
|
+
var _hoisted_26$5 = { class: "sg-field__name" };
|
|
10814
|
+
var _hoisted_27$5 = { class: "sg-field__vals" };
|
|
10815
|
+
var _hoisted_28$4 = {
|
|
10797
10816
|
key: 2,
|
|
10798
10817
|
class: "sg-summary fr-summary--mute"
|
|
10799
10818
|
};
|
|
10800
|
-
var
|
|
10801
|
-
var
|
|
10819
|
+
var _hoisted_29$4 = { class: "sg-summary__icon" };
|
|
10820
|
+
var _hoisted_30$3 = {
|
|
10802
10821
|
key: 3,
|
|
10803
10822
|
class: "sg-summary sg-summary--warn"
|
|
10804
10823
|
};
|
|
10805
|
-
var
|
|
10806
|
-
var
|
|
10824
|
+
var _hoisted_31$3 = { class: "sg-summary__icon" };
|
|
10825
|
+
var _hoisted_32$3 = {
|
|
10807
10826
|
key: 4,
|
|
10827
|
+
class: "sg-summary sg-summary--warn"
|
|
10828
|
+
};
|
|
10829
|
+
var _hoisted_33$3 = { class: "sg-summary__icon" };
|
|
10830
|
+
var _hoisted_34$2 = {
|
|
10831
|
+
key: 5,
|
|
10808
10832
|
class: "sg-summary"
|
|
10809
10833
|
};
|
|
10810
|
-
var
|
|
10811
|
-
var
|
|
10812
|
-
var
|
|
10813
|
-
var
|
|
10814
|
-
var
|
|
10815
|
-
var
|
|
10834
|
+
var _hoisted_35$2 = { class: "sg-summary__icon" };
|
|
10835
|
+
var _hoisted_36$2 = { class: "sg-body__side" };
|
|
10836
|
+
var _hoisted_37$2 = { class: "sg-preview__head" };
|
|
10837
|
+
var _hoisted_38$2 = { class: "sg-preview__count" };
|
|
10838
|
+
var _hoisted_39$2 = { class: "sg-grouplist" };
|
|
10839
|
+
var _hoisted_40$2 = {
|
|
10816
10840
|
key: 0,
|
|
10817
10841
|
class: "fr-empty"
|
|
10818
10842
|
};
|
|
10819
|
-
var
|
|
10820
|
-
var
|
|
10821
|
-
var
|
|
10822
|
-
var
|
|
10823
|
-
var
|
|
10824
|
-
var
|
|
10825
|
-
var
|
|
10843
|
+
var _hoisted_41$2 = { class: "sg-group__name" };
|
|
10844
|
+
var _hoisted_42$1 = { class: "sg-group__count" };
|
|
10845
|
+
var _hoisted_43$1 = { class: "sg-qc" };
|
|
10846
|
+
var _hoisted_44$1 = { class: "sg-qc__head" };
|
|
10847
|
+
var _hoisted_45$1 = { class: "fr-qc__chips" };
|
|
10848
|
+
var _hoisted_46$1 = { class: "sg-foot" };
|
|
10849
|
+
var _hoisted_47$1 = { class: "sg-foot__hint" };
|
|
10826
10850
|
var DEFAULT_SAMPLES = [
|
|
10827
10851
|
{
|
|
10828
10852
|
Condition: "Control",
|
|
@@ -10891,7 +10915,15 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
10891
10915
|
loading: {
|
|
10892
10916
|
type: Boolean,
|
|
10893
10917
|
default: false
|
|
10894
|
-
}
|
|
10918
|
+
},
|
|
10919
|
+
anatomy: {},
|
|
10920
|
+
hints: {},
|
|
10921
|
+
qcChips: {},
|
|
10922
|
+
renamable: {
|
|
10923
|
+
type: Boolean,
|
|
10924
|
+
default: false
|
|
10925
|
+
},
|
|
10926
|
+
reservedNames: {}
|
|
10895
10927
|
},
|
|
10896
10928
|
emits: [
|
|
10897
10929
|
"apply",
|
|
@@ -10900,6 +10932,7 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
10900
10932
|
"close",
|
|
10901
10933
|
"toggle",
|
|
10902
10934
|
"move",
|
|
10935
|
+
"rename",
|
|
10903
10936
|
"update:route",
|
|
10904
10937
|
"csv-file",
|
|
10905
10938
|
"download-template",
|
|
@@ -10926,9 +10959,9 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
10926
10959
|
check: "M20 6L9 17l-5-5",
|
|
10927
10960
|
close: "M6 18L18 6M6 6l12 12",
|
|
10928
10961
|
upload: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12",
|
|
10929
|
-
download: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3",
|
|
10930
10962
|
chev: "M6 9l6 6 6-6",
|
|
10931
|
-
|
|
10963
|
+
grip: "M9 6h.01M9 12h.01M9 18h.01M15 6h.01M15 12h.01M15 18h.01",
|
|
10964
|
+
pencil: "M12 20h9M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4L16.5 3.5z"
|
|
10932
10965
|
};
|
|
10933
10966
|
const MAX_BAR = 44;
|
|
10934
10967
|
/** QC reads the same everywhere: neutral solid ring, never a group color (3a). */
|
|
@@ -10948,7 +10981,8 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
10948
10981
|
if (file) emit("csv-file", file);
|
|
10949
10982
|
input.value = "";
|
|
10950
10983
|
}
|
|
10951
|
-
function onDragEnter() {
|
|
10984
|
+
function onDragEnter(event) {
|
|
10985
|
+
if (!event.dataTransfer?.types.includes("Files")) return;
|
|
10952
10986
|
dragDepth += 1;
|
|
10953
10987
|
dragOver.value = true;
|
|
10954
10988
|
}
|
|
@@ -10962,25 +10996,43 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
10962
10996
|
const file = event.dataTransfer?.files?.[0];
|
|
10963
10997
|
if (file && isCsvLike(file)) emit("csv-file", file);
|
|
10964
10998
|
}
|
|
10965
|
-
const
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10999
|
+
const csvMenuItems = computed(() => [
|
|
11000
|
+
{
|
|
11001
|
+
id: "upload",
|
|
11002
|
+
label: "Upload CSV"
|
|
11003
|
+
},
|
|
11004
|
+
{
|
|
11005
|
+
id: "template",
|
|
11006
|
+
label: "Download template",
|
|
11007
|
+
disabled: !props.canDownloadTemplate
|
|
11008
|
+
},
|
|
11009
|
+
{
|
|
11010
|
+
id: "prefilled",
|
|
11011
|
+
label: "Download pre-filled",
|
|
11012
|
+
disabled: !props.canDownloadTemplate
|
|
11013
|
+
}
|
|
11014
|
+
]);
|
|
11015
|
+
function onCsvMenuSelect(item) {
|
|
11016
|
+
if (item.id === "upload") pickCsv();
|
|
11017
|
+
else if (item.id === "template") emit("download-template");
|
|
11018
|
+
else emit("download-prefilled");
|
|
10982
11019
|
}
|
|
10983
|
-
const
|
|
11020
|
+
const QC_ROUTE_OPTIONS = [
|
|
11021
|
+
{
|
|
11022
|
+
value: "Overlay only",
|
|
11023
|
+
label: "Overlay"
|
|
11024
|
+
},
|
|
11025
|
+
{
|
|
11026
|
+
value: "Exclude",
|
|
11027
|
+
label: "Exclude"
|
|
11028
|
+
},
|
|
11029
|
+
{
|
|
11030
|
+
value: "Mix into groups",
|
|
11031
|
+
label: "Mix in"
|
|
11032
|
+
}
|
|
11033
|
+
];
|
|
11034
|
+
const startLayers = props.enabled === void 0 ? props.layers : void 0;
|
|
11035
|
+
const onInner = ref(props.initialOn ? [...props.initialOn] : startLayers?.map((layer) => layer !== null) ?? props.fields.map((field) => !field.rep));
|
|
10984
11036
|
const orderInner = ref(onInner.value.flatMap((isOn, i) => isOn ? [i] : []));
|
|
10985
11037
|
const routeInner = ref(props.initialRoute);
|
|
10986
11038
|
const controlledEnabled = computed(() => props.enabled !== void 0);
|
|
@@ -10988,7 +11040,11 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
10988
11040
|
const on = computed(() => props.enabled ?? onInner.value);
|
|
10989
11041
|
const currentRoute = computed(() => props.route ?? routeInner.value);
|
|
10990
11042
|
const layers = computed(() => {
|
|
10991
|
-
if (props.layers !== void 0) return props.layers;
|
|
11043
|
+
if (props.layers !== void 0 && controlledEnabled.value) return props.layers;
|
|
11044
|
+
if (props.layers !== void 0) {
|
|
11045
|
+
let last = Math.max(0, ...props.layers.map((layer) => layer ?? 0));
|
|
11046
|
+
return props.layers.map((layer, i) => on.value[i] ? layer ?? ++last : null);
|
|
11047
|
+
}
|
|
10992
11048
|
if (controlledEnabled.value) {
|
|
10993
11049
|
let layer = 0;
|
|
10994
11050
|
return props.fields.map((_, i) => on.value[i] ? ++layer : null);
|
|
@@ -11002,19 +11058,22 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11002
11058
|
layer,
|
|
11003
11059
|
i
|
|
11004
11060
|
}]).sort((a, b) => a.layer - b.layer).map((entry) => entry.i));
|
|
11061
|
+
const onCards = computed(() => layerOrder.value.map((index) => ({
|
|
11062
|
+
index,
|
|
11063
|
+
field: props.fields[index]
|
|
11064
|
+
})));
|
|
11065
|
+
const offCards = computed(() => props.fields.flatMap((field, index) => layers.value[index] === null ? [{
|
|
11066
|
+
index,
|
|
11067
|
+
field
|
|
11068
|
+
}] : []));
|
|
11005
11069
|
const computedBase = computed(() => computeFieldGroups(props.samples, props.fields, on.value, props.palette, layerOrder.value));
|
|
11006
11070
|
const baseGroups = computed(() => props.groups ?? computedBase.value);
|
|
11007
11071
|
const groups = computed(() => props.groups ?? appendQcGroup(computedBase.value, currentRoute.value, props.qcColor));
|
|
11008
11072
|
const activeFactors = computed(() => layerOrder.value.map((i) => props.fields[i].key));
|
|
11009
11073
|
const counts = computed(() => baseGroups.value.map((group) => group.count));
|
|
11010
11074
|
const uniform = computed(() => counts.value.length > 0 && counts.value.every((count) => count === counts.value[0]));
|
|
11011
|
-
const
|
|
11012
|
-
const repOn = computed(() =>
|
|
11013
|
-
const repRange = computed(() => {
|
|
11014
|
-
const parts = (repField.value?.vals ?? "").split(",").map((part) => part.trim()).filter(Boolean);
|
|
11015
|
-
if (parts.length === 0) return "";
|
|
11016
|
-
return parts.length > 1 ? `${parts[0]}–${parts[parts.length - 1]}` : parts[0];
|
|
11017
|
-
});
|
|
11075
|
+
const repOnField = computed(() => props.fields.find((field, i) => field.rep && on.value[i]));
|
|
11076
|
+
const repOn = computed(() => repOnField.value !== void 0);
|
|
11018
11077
|
const summary = computed(() => fieldRecipeSummary({
|
|
11019
11078
|
activeFactors: activeFactors.value,
|
|
11020
11079
|
repOn: repOn.value,
|
|
@@ -11025,12 +11084,23 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11025
11084
|
const qcCount = computed(() => props.qcCount ?? Math.max(0, props.headerCount - props.samples.length));
|
|
11026
11085
|
const realCount = computed(() => Math.max(0, props.headerCount - qcCount.value));
|
|
11027
11086
|
const sampleTotal = computed(() => sampleTotalFor(currentRoute.value, realCount.value, qcCount.value));
|
|
11028
|
-
const
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11087
|
+
const qcChips = computed(() => {
|
|
11088
|
+
if (props.qcChips !== void 0) return props.qcChips;
|
|
11089
|
+
if (props.qcChip !== void 0) return props.qcChip ? [props.qcChip] : [];
|
|
11090
|
+
return currentRoute.value !== "Mix into groups" ? [{
|
|
11091
|
+
name: "QC_Pool",
|
|
11092
|
+
count: qcCount.value
|
|
11093
|
+
}] : [];
|
|
11094
|
+
});
|
|
11032
11095
|
const footHint = computed(() => currentRoute.value === "Exclude" ? ` · ${qcCount.value} QC excluded` : currentRoute.value === "Mix into groups" ? " · QC mixed in" : ` · ${qcCount.value} QC overlaid`);
|
|
11096
|
+
const root = ref(null);
|
|
11097
|
+
const controlOf = (index, selector) => root.value?.querySelector(`[data-field="${index}"] ${selector}`) ?? null;
|
|
11098
|
+
async function refocus(index, selector) {
|
|
11099
|
+
await nextTick();
|
|
11100
|
+
controlOf(index, selector)?.focus();
|
|
11101
|
+
}
|
|
11033
11102
|
function toggle(index) {
|
|
11103
|
+
refocus(index, "[role=\"switch\"]");
|
|
11034
11104
|
if (controlledEnabled.value) {
|
|
11035
11105
|
emit("toggle", index);
|
|
11036
11106
|
return;
|
|
@@ -11040,7 +11110,7 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11040
11110
|
orderInner.value = wasOn ? orderInner.value.filter((i) => i !== index) : [...orderInner.value, index];
|
|
11041
11111
|
}
|
|
11042
11112
|
function moveLayer(index, direction) {
|
|
11043
|
-
if (controlledEnabled.value) {
|
|
11113
|
+
if (controlledEnabled.value || props.layers !== void 0) {
|
|
11044
11114
|
emit("move", index, direction);
|
|
11045
11115
|
return;
|
|
11046
11116
|
}
|
|
@@ -11052,6 +11122,48 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11052
11122
|
[next[from], next[to]] = [next[to], next[from]];
|
|
11053
11123
|
orderInner.value = next;
|
|
11054
11124
|
}
|
|
11125
|
+
const reorder = useListReorder({
|
|
11126
|
+
items: () => onCards.value,
|
|
11127
|
+
getId: (card) => card.field.key,
|
|
11128
|
+
getLabel: (card) => card.field.key,
|
|
11129
|
+
itemNoun: "layer",
|
|
11130
|
+
getElement: (id) => {
|
|
11131
|
+
const card = onCards.value.find((candidate) => candidate.field.key === id);
|
|
11132
|
+
return card ? controlOf(card.index, ".fr-row__grip") : null;
|
|
11133
|
+
},
|
|
11134
|
+
onReorder: ({ fromIndex, toIndex }) => {
|
|
11135
|
+
const { index } = onCards.value[fromIndex];
|
|
11136
|
+
const direction = toIndex < fromIndex ? "up" : "down";
|
|
11137
|
+
for (let step = 0; step < Math.abs(toIndex - fromIndex); step++) moveLayer(index, direction);
|
|
11138
|
+
}
|
|
11139
|
+
});
|
|
11140
|
+
const renaming = ref(null);
|
|
11141
|
+
const draftName = ref("");
|
|
11142
|
+
const renameTaken = ref(false);
|
|
11143
|
+
const renameInput = ref([]);
|
|
11144
|
+
async function startRename(card) {
|
|
11145
|
+
renaming.value = card.index;
|
|
11146
|
+
renameTaken.value = false;
|
|
11147
|
+
draftName.value = card.field.key;
|
|
11148
|
+
await nextTick();
|
|
11149
|
+
renameInput.value[0]?.select();
|
|
11150
|
+
}
|
|
11151
|
+
function endRename(card, restoreFocus) {
|
|
11152
|
+
renaming.value = null;
|
|
11153
|
+
renameTaken.value = false;
|
|
11154
|
+
if (restoreFocus) refocus(card.index, ".fr-row__rename");
|
|
11155
|
+
}
|
|
11156
|
+
function commitRename(card, fromBlur = false) {
|
|
11157
|
+
if (renaming.value !== card.index) return;
|
|
11158
|
+
const name = draftName.value.trim();
|
|
11159
|
+
const taken = name !== card.field.key && (props.fields.some((field) => field.key === name) || (props.reservedNames?.includes(name) ?? false));
|
|
11160
|
+
if (taken && !fromBlur) {
|
|
11161
|
+
renameTaken.value = true;
|
|
11162
|
+
return;
|
|
11163
|
+
}
|
|
11164
|
+
endRename(card, !fromBlur);
|
|
11165
|
+
if (name && name !== card.field.key && !taken) emit("rename", card.index, name);
|
|
11166
|
+
}
|
|
11055
11167
|
function onRouteChange(value) {
|
|
11056
11168
|
if (controlledRoute.value) {
|
|
11057
11169
|
emit("update:route", value);
|
|
@@ -11069,79 +11181,53 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11069
11181
|
});
|
|
11070
11182
|
}
|
|
11071
11183
|
return (_ctx, _cache) => {
|
|
11072
|
-
return openBlock(), createElementBlock("div",
|
|
11073
|
-
|
|
11184
|
+
return openBlock(), createElementBlock("div", {
|
|
11185
|
+
ref_key: "root",
|
|
11186
|
+
ref: root,
|
|
11187
|
+
class: "sg-modal fr"
|
|
11188
|
+
}, [
|
|
11189
|
+
createElementVNode("div", _hoisted_1$42, [
|
|
11074
11190
|
createVNode(Icon, {
|
|
11075
11191
|
d: ICON.bolt,
|
|
11076
11192
|
size: 18
|
|
11077
11193
|
}, null, 8, ["d"]),
|
|
11078
11194
|
_cache[11] || (_cache[11] = createElementVNode("span", { class: "sg-head__title" }, "Group samples", -1)),
|
|
11079
|
-
createElementVNode("span",
|
|
11195
|
+
createElementVNode("span", _hoisted_2$38, [createTextVNode("· " + toDisplayString(__props.headerCount) + " samples", 1), __props.experimentLabel ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" from " + toDisplayString(__props.experimentLabel), 1)], 64)) : createCommentVNode("", true)]),
|
|
11080
11196
|
_cache[12] || (_cache[12] = createElementVNode("div", { class: "sg-head__spacer" }, null, -1)),
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11197
|
+
createVNode(ActionMenu_default, {
|
|
11198
|
+
label: "CSV",
|
|
11199
|
+
items: csvMenuItems.value,
|
|
11200
|
+
onSelect: onCsvMenuSelect
|
|
11201
|
+
}, {
|
|
11202
|
+
trigger: withCtx(({ open, toggle: toggleMenu, keydown }) => [createElementVNode("button", {
|
|
11084
11203
|
type: "button",
|
|
11085
11204
|
class: "fr-upload",
|
|
11086
11205
|
"aria-haspopup": "menu",
|
|
11087
|
-
"aria-expanded":
|
|
11088
|
-
onClick:
|
|
11206
|
+
"aria-expanded": open,
|
|
11207
|
+
onClick: toggleMenu,
|
|
11208
|
+
onKeydown: keydown
|
|
11089
11209
|
}, [
|
|
11090
11210
|
createVNode(Icon, {
|
|
11091
11211
|
d: ICON.upload,
|
|
11092
11212
|
size: 14
|
|
11093
11213
|
}, null, 8, ["d"]),
|
|
11094
|
-
_cache[
|
|
11214
|
+
_cache[9] || (_cache[9] = createTextVNode(" CSV ", -1)),
|
|
11095
11215
|
createVNode(Icon, {
|
|
11096
11216
|
d: ICON.chev,
|
|
11097
11217
|
size: 13
|
|
11098
11218
|
}, null, 8, ["d"])
|
|
11099
|
-
],
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
type: "button",
|
|
11112
|
-
class: "fr-csv-item",
|
|
11113
|
-
role: "menuitem",
|
|
11114
|
-
disabled: !__props.canDownloadTemplate,
|
|
11115
|
-
"aria-disabled": !__props.canDownloadTemplate,
|
|
11116
|
-
title: __props.canDownloadTemplate ? void 0 : "Load samples first",
|
|
11117
|
-
onClick: chooseDownloadTemplate
|
|
11118
|
-
}, [createVNode(Icon, {
|
|
11119
|
-
d: ICON.download,
|
|
11120
|
-
size: 14
|
|
11121
|
-
}, null, 8, ["d"]), _cache[8] || (_cache[8] = createTextVNode(" Download template ", -1))], 8, _hoisted_7$24),
|
|
11122
|
-
createElementVNode("button", {
|
|
11123
|
-
type: "button",
|
|
11124
|
-
class: "fr-csv-item",
|
|
11125
|
-
role: "menuitem",
|
|
11126
|
-
disabled: !__props.canDownloadTemplate,
|
|
11127
|
-
"aria-disabled": !__props.canDownloadTemplate,
|
|
11128
|
-
title: __props.canDownloadTemplate ? void 0 : "Load samples first",
|
|
11129
|
-
onClick: chooseDownloadPrefilled
|
|
11130
|
-
}, [createVNode(Icon, {
|
|
11131
|
-
d: ICON.download,
|
|
11132
|
-
size: 14
|
|
11133
|
-
}, null, 8, ["d"]), _cache[9] || (_cache[9] = createTextVNode(" Download pre-filled ", -1))], 8, _hoisted_8$22)
|
|
11134
|
-
])) : createCommentVNode("", true),
|
|
11135
|
-
createElementVNode("input", {
|
|
11136
|
-
ref_key: "fileInput",
|
|
11137
|
-
ref: fileInput,
|
|
11138
|
-
type: "file",
|
|
11139
|
-
accept: ".csv,.tsv,text/csv,text/tab-separated-values",
|
|
11140
|
-
hidden: "",
|
|
11141
|
-
onChange: onFileChange
|
|
11142
|
-
}, null, 544)
|
|
11143
|
-
], 512),
|
|
11144
|
-
createElementVNode("div", _hoisted_9$20, [_cache[10] || (_cache[10] = createElementVNode("button", {
|
|
11219
|
+
], 40, _hoisted_3$31)]),
|
|
11220
|
+
_: 1
|
|
11221
|
+
}, 8, ["items"]),
|
|
11222
|
+
createElementVNode("input", {
|
|
11223
|
+
ref_key: "fileInput",
|
|
11224
|
+
ref: fileInput,
|
|
11225
|
+
type: "file",
|
|
11226
|
+
accept: ".csv,.tsv,text/csv,text/tab-separated-values",
|
|
11227
|
+
hidden: "",
|
|
11228
|
+
onChange: onFileChange
|
|
11229
|
+
}, null, 544),
|
|
11230
|
+
createElementVNode("div", _hoisted_4$30, [_cache[10] || (_cache[10] = createElementVNode("button", {
|
|
11145
11231
|
type: "button",
|
|
11146
11232
|
class: "sg-mode__btn sg-mode__btn--on",
|
|
11147
11233
|
role: "tab",
|
|
@@ -11151,104 +11237,166 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11151
11237
|
class: "sg-mode__btn",
|
|
11152
11238
|
role: "tab",
|
|
11153
11239
|
"aria-selected": false,
|
|
11154
|
-
onClick: _cache[
|
|
11240
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("manual"))
|
|
11155
11241
|
}, "Manual")]),
|
|
11156
11242
|
createElementVNode("button", {
|
|
11157
11243
|
class: "sg-head__close",
|
|
11158
11244
|
"aria-label": "Close",
|
|
11159
|
-
onClick: _cache[
|
|
11245
|
+
onClick: _cache[1] || (_cache[1] = ($event) => emit("close"))
|
|
11160
11246
|
}, [createVNode(Icon, {
|
|
11161
11247
|
d: ICON.close,
|
|
11162
11248
|
size: 16
|
|
11163
11249
|
}, null, 8, ["d"])])
|
|
11164
11250
|
]),
|
|
11165
|
-
createElementVNode("div",
|
|
11251
|
+
createElementVNode("div", _hoisted_5$28, [createElementVNode("div", {
|
|
11166
11252
|
class: "sg-body__main",
|
|
11167
11253
|
onDragenter: withModifiers(onDragEnter, ["prevent"]),
|
|
11168
|
-
onDragover: _cache[
|
|
11254
|
+
onDragover: _cache[6] || (_cache[6] = withModifiers(() => {}, ["prevent"])),
|
|
11169
11255
|
onDragleave: withModifiers(onDragLeave, ["prevent"]),
|
|
11170
11256
|
onDrop: withModifiers(onDrop, ["prevent"])
|
|
11171
11257
|
}, [
|
|
11172
|
-
dragOver.value ? (openBlock(), createElementBlock("div",
|
|
11258
|
+
dragOver.value ? (openBlock(), createElementBlock("div", _hoisted_6$26, [createVNode(Icon, {
|
|
11173
11259
|
d: ICON.upload,
|
|
11174
11260
|
size: 22
|
|
11175
11261
|
}, null, 8, ["d"]), _cache[13] || (_cache[13] = createElementVNode("span", null, "Drop CSV to load samples", -1))])) : createCommentVNode("", true),
|
|
11176
|
-
__props.loading ? (openBlock(), createElementBlock("div",
|
|
11262
|
+
__props.loading ? (openBlock(), createElementBlock("div", _hoisted_7$24, [createVNode(LoadingSpinner_default, {
|
|
11177
11263
|
size: "md",
|
|
11178
11264
|
label: "Loading experiment metadata"
|
|
11179
11265
|
}), _cache[14] || (_cache[14] = createElementVNode("span", null, "Loading experiment metadata…", -1))])) : createCommentVNode("", true),
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11266
|
+
__props.fields.length === 0 && __props.headerCount === 0 && !__props.loading ? (openBlock(), createElementBlock("div", _hoisted_8$22, [
|
|
11267
|
+
_cache[16] || (_cache[16] = createElementVNode("span", { class: "sg-lead__q" }, "No samples to group", -1)),
|
|
11268
|
+
_cache[17] || (_cache[17] = createElementVNode("span", { class: "sg-lead__hint" }, " Upload a CSV with one sample column and one column for each field, or close this dialog and select samples first. ", -1)),
|
|
11269
|
+
createVNode(BaseButton_default, {
|
|
11270
|
+
variant: "secondary",
|
|
11271
|
+
size: "sm",
|
|
11272
|
+
onClick: pickCsv
|
|
11273
|
+
}, {
|
|
11274
|
+
"icon-left": withCtx(() => [createVNode(Icon, {
|
|
11275
|
+
d: ICON.upload,
|
|
11276
|
+
size: 15
|
|
11277
|
+
}, null, 8, ["d"])]),
|
|
11278
|
+
default: withCtx(() => [_cache[15] || (_cache[15] = createTextVNode(" Upload CSV ", -1))]),
|
|
11279
|
+
_: 1
|
|
11280
|
+
})
|
|
11281
|
+
])) : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
|
|
11282
|
+
__props.anatomy && __props.anatomy.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_9$20, [_cache[18] || (_cache[18] = createElementVNode("span", { class: "fr-label" }, "How MINT read your names", -1)), createElementVNode("div", _hoisted_10$19, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.anatomy, (token, t) => {
|
|
11283
|
+
return openBlock(), createElementBlock("span", {
|
|
11284
|
+
key: t,
|
|
11285
|
+
class: normalizeClass(["fr-token", "fr-token--" + token.state])
|
|
11286
|
+
}, [createElementVNode("span", _hoisted_11$17, toDisplayString(token.text), 1), createElementVNode("span", _hoisted_12$16, toDisplayString(token.caption), 1)], 2);
|
|
11287
|
+
}), 128))])])) : createCommentVNode("", true),
|
|
11288
|
+
_cache[28] || (_cache[28] = createElementVNode("span", { class: "fr-label" }, "Group by", -1)),
|
|
11289
|
+
createElementVNode("div", _hoisted_13$16, [onCards.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_14$16, toDisplayString(__props.fields.length === 0 ? "These sample names have no field to group by." : "No field is on. Switch on a field below."), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(onCards.value, (card, position) => {
|
|
11290
|
+
return openBlock(), createElementBlock("div", {
|
|
11291
|
+
key: card.field.key,
|
|
11292
|
+
class: normalizeClass(["sg-field sg-field--on fr-row", {
|
|
11293
|
+
"fr-field--warn": card.field.rep,
|
|
11294
|
+
"fr-row--dragging": unref(reorder).isDragging(card.field.key),
|
|
11295
|
+
"fr-row--over": unref(reorder).isDragOver(card.field.key)
|
|
11296
|
+
}]),
|
|
11297
|
+
"data-test": "field-on",
|
|
11298
|
+
"data-field": card.index,
|
|
11299
|
+
draggable: renaming.value !== card.index,
|
|
11300
|
+
onDragstart: ($event) => unref(reorder).onDragStart($event, card, position),
|
|
11301
|
+
onDragover: ($event) => unref(reorder).onDragOver($event, card, position),
|
|
11302
|
+
onDragleave: _cache[4] || (_cache[4] = (...args) => unref(reorder).onDragLeave && unref(reorder).onDragLeave(...args)),
|
|
11303
|
+
onDrop: ($event) => unref(reorder).onDrop($event, card, position),
|
|
11304
|
+
onDragend: _cache[5] || (_cache[5] = (...args) => unref(reorder).onDragEnd && unref(reorder).onDragEnd(...args))
|
|
11305
|
+
}, [
|
|
11199
11306
|
createElementVNode("button", {
|
|
11200
11307
|
type: "button",
|
|
11201
|
-
class: "
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
title: "Move up a layer",
|
|
11205
|
-
onClick: withModifiers(($event) => moveLayer(i, "up"), ["stop"])
|
|
11308
|
+
class: "fr-row__grip",
|
|
11309
|
+
"aria-label": `Move ${card.field.key}, layer ${position + 1} of ${onCards.value.length}. Press Alt and an arrow key.`,
|
|
11310
|
+
onKeydown: ($event) => unref(reorder).onKeydown($event, card, position)
|
|
11206
11311
|
}, [createVNode(Icon, {
|
|
11207
|
-
d: ICON.
|
|
11208
|
-
size:
|
|
11209
|
-
}, null, 8, ["d"])],
|
|
11210
|
-
createElementVNode("
|
|
11312
|
+
d: ICON.grip,
|
|
11313
|
+
size: 14
|
|
11314
|
+
}, null, 8, ["d"])], 40, _hoisted_16$13),
|
|
11315
|
+
createElementVNode("span", _hoisted_17$13, toDisplayString(position + 1), 1),
|
|
11316
|
+
renaming.value === card.index ? withDirectives((openBlock(), createElementBlock("input", {
|
|
11317
|
+
key: 0,
|
|
11318
|
+
ref_for: true,
|
|
11319
|
+
ref_key: "renameInput",
|
|
11320
|
+
ref: renameInput,
|
|
11321
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => draftName.value = $event),
|
|
11322
|
+
class: "fr-row__name-input",
|
|
11323
|
+
"aria-label": "New name for " + card.field.key,
|
|
11324
|
+
"aria-invalid": renameTaken.value || void 0,
|
|
11325
|
+
onInput: _cache[3] || (_cache[3] = ($event) => renameTaken.value = false),
|
|
11326
|
+
onKeydown: [withKeys(withModifiers(($event) => commitRename(card), ["prevent"]), ["enter"]), withKeys(withModifiers(($event) => endRename(card, true), ["stop"]), ["esc"])],
|
|
11327
|
+
onBlur: ($event) => commitRename(card, true)
|
|
11328
|
+
}, null, 40, _hoisted_18$13)), [[vModelText, draftName.value]]) : createCommentVNode("", true),
|
|
11329
|
+
renaming.value === card.index && renameTaken.value ? (openBlock(), createElementBlock("span", _hoisted_19$13, " Another field has this name ")) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [createElementVNode("span", _hoisted_20$12, toDisplayString(card.field.key), 1), __props.renamable ? (openBlock(), createElementBlock("button", {
|
|
11330
|
+
key: 0,
|
|
11211
11331
|
type: "button",
|
|
11212
|
-
class: "
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
title: "Move down a layer",
|
|
11216
|
-
onClick: withModifiers(($event) => moveLayer(i, "down"), ["stop"])
|
|
11332
|
+
class: "fr-row__rename",
|
|
11333
|
+
"aria-label": "Rename " + card.field.key,
|
|
11334
|
+
onClick: ($event) => startRename(card)
|
|
11217
11335
|
}, [createVNode(Icon, {
|
|
11218
|
-
d: ICON.
|
|
11336
|
+
d: ICON.pencil,
|
|
11219
11337
|
size: 12
|
|
11220
|
-
}, null, 8, ["d"])], 8,
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11338
|
+
}, null, 8, ["d"])], 8, _hoisted_21$9)) : createCommentVNode("", true)], 64)),
|
|
11339
|
+
createElementVNode("span", _hoisted_22$9, toDisplayString(card.field.vals), 1),
|
|
11340
|
+
__props.hints?.[card.index] ? (openBlock(), createElementBlock("span", _hoisted_23$7, toDisplayString(__props.hints[card.index]), 1)) : createCommentVNode("", true),
|
|
11341
|
+
createVNode(BaseToggle_default, {
|
|
11342
|
+
size: "sm",
|
|
11343
|
+
"model-value": true,
|
|
11344
|
+
"aria-label": "Group by " + card.field.key,
|
|
11345
|
+
"onUpdate:modelValue": ($event) => toggle(card.index)
|
|
11346
|
+
}, null, 8, ["aria-label", "onUpdate:modelValue"])
|
|
11347
|
+
], 42, _hoisted_15$16);
|
|
11348
|
+
}), 128))]),
|
|
11349
|
+
createElementVNode("span", {
|
|
11350
|
+
style: normalizeStyle(unref(reorder).announcerStyle),
|
|
11351
|
+
"aria-live": "polite"
|
|
11352
|
+
}, toDisplayString(unref(reorder).announcement.value), 5),
|
|
11353
|
+
offCards.value.length > 0 ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [_cache[19] || (_cache[19] = createElementVNode("span", { class: "fr-label" }, "Left off", -1)), createElementVNode("div", _hoisted_24$7, [(openBlock(true), createElementBlock(Fragment, null, renderList(offCards.value, (card) => {
|
|
11354
|
+
return openBlock(), createElementBlock("div", {
|
|
11355
|
+
key: card.field.key,
|
|
11356
|
+
class: "sg-field fr-row",
|
|
11357
|
+
"data-test": "field-off",
|
|
11358
|
+
"data-field": card.index
|
|
11359
|
+
}, [
|
|
11360
|
+
createElementVNode("span", _hoisted_26$5, toDisplayString(card.field.key), 1),
|
|
11361
|
+
createElementVNode("span", { class: normalizeClass(["sg-field__role", { "sg-field__role--rep": card.field.rep }]) }, toDisplayString(card.field.role), 3),
|
|
11362
|
+
createElementVNode("span", _hoisted_27$5, toDisplayString(card.field.vals), 1),
|
|
11363
|
+
createVNode(BaseToggle_default, {
|
|
11364
|
+
size: "sm",
|
|
11365
|
+
"model-value": false,
|
|
11366
|
+
"aria-label": "Group by " + card.field.key,
|
|
11367
|
+
"onUpdate:modelValue": ($event) => toggle(card.index)
|
|
11368
|
+
}, null, 8, ["aria-label", "onUpdate:modelValue"])
|
|
11369
|
+
], 8, _hoisted_25$7);
|
|
11370
|
+
}), 128))])], 64)) : createCommentVNode("", true),
|
|
11371
|
+
summary.value.kind === "mute" ? (openBlock(), createElementBlock("div", _hoisted_28$4, [createElementVNode("span", _hoisted_29$4, [createVNode(Icon, {
|
|
11372
|
+
d: ICON.info,
|
|
11373
|
+
size: 16
|
|
11374
|
+
}, null, 8, ["d"])]), _cache[20] || (_cache[20] = createElementVNode("span", null, "Turn on at least one field to form groups.", -1))])) : summary.value.kind === "warn" ? (openBlock(), createElementBlock("div", _hoisted_30$3, [createElementVNode("span", _hoisted_31$3, [createVNode(Icon, {
|
|
11375
|
+
d: ICON.warn,
|
|
11376
|
+
size: 16
|
|
11377
|
+
}, null, 8, ["d"])]), createElementVNode("span", null, [
|
|
11378
|
+
_cache[21] || (_cache[21] = createTextVNode(" Including ", -1)),
|
|
11379
|
+
createElementVNode("b", null, toDisplayString(repOnField.value?.key), 1),
|
|
11380
|
+
_cache[22] || (_cache[22] = createTextVNode(" makes every sample unique — ", -1)),
|
|
11381
|
+
createElementVNode("b", null, toDisplayString(baseGroups.value.length) + " groups of 1", 1),
|
|
11382
|
+
_cache[23] || (_cache[23] = createTextVNode(". Switch it off to keep replicates together. ", -1))
|
|
11383
|
+
])])) : summary.value.kind === "singletons" ? (openBlock(), createElementBlock("div", _hoisted_32$3, [createElementVNode("span", _hoisted_33$3, [createVNode(Icon, {
|
|
11384
|
+
d: ICON.warn,
|
|
11385
|
+
size: 16
|
|
11386
|
+
}, null, 8, ["d"])]), createElementVNode("span", null, [createElementVNode("b", null, toDisplayString(counts.value.filter((count) => count === 1).length) + " of " + toDisplayString(baseGroups.value.length) + " groups", 1), _cache[24] || (_cache[24] = createTextVNode(" have one sample. Switch off a field to form larger groups. ", -1))])])) : (openBlock(), createElementBlock("div", _hoisted_34$2, [createElementVNode("span", _hoisted_35$2, [createVNode(Icon, {
|
|
11387
|
+
d: ICON.info,
|
|
11388
|
+
size: 16
|
|
11389
|
+
}, null, 8, ["d"])]), createElementVNode("span", null, [
|
|
11390
|
+
_cache[25] || (_cache[25] = createTextVNode(" Grouping by ", -1)),
|
|
11391
|
+
createElementVNode("b", null, toDisplayString(activeFactors.value.join(" × ")), 1),
|
|
11392
|
+
_cache[26] || (_cache[26] = createTextVNode(" → ", -1)),
|
|
11393
|
+
createElementVNode("b", null, [createTextVNode(toDisplayString(baseGroups.value.length) + " groups", 1), uniform.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(" of " + toDisplayString(counts.value[0]), 1)], 64)) : createCommentVNode("", true)]),
|
|
11394
|
+
_cache[27] || (_cache[27] = createTextVNode(". ", -1))
|
|
11395
|
+
])]))
|
|
11396
|
+
], 64))
|
|
11397
|
+
], 32), createElementVNode("div", _hoisted_36$2, [
|
|
11398
|
+
createElementVNode("div", _hoisted_37$2, [_cache[29] || (_cache[29] = createTextVNode(" Resulting groups ", -1)), createElementVNode("span", _hoisted_38$2, toDisplayString(groups.value.length) + " groups · " + toDisplayString(groupedSampleTotal.value) + " samples", 1)]),
|
|
11399
|
+
createElementVNode("div", _hoisted_39$2, [groups.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_40$2, "No groups yet — pick a field to start.")) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(groups.value, (group) => {
|
|
11252
11400
|
return openBlock(), createElementBlock("div", {
|
|
11253
11401
|
key: group.name,
|
|
11254
11402
|
class: "sg-group"
|
|
@@ -11257,7 +11405,7 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11257
11405
|
class: "sg-dot",
|
|
11258
11406
|
style: normalizeStyle(group.name === unref("QC_Pool") ? unref(QC_DOT_STYLE) : { background: group.color })
|
|
11259
11407
|
}, null, 4),
|
|
11260
|
-
createElementVNode("span",
|
|
11408
|
+
createElementVNode("span", _hoisted_41$2, toDisplayString(group.name), 1),
|
|
11261
11409
|
createElementVNode("span", {
|
|
11262
11410
|
class: "sg-group__bar",
|
|
11263
11411
|
style: normalizeStyle({
|
|
@@ -11265,34 +11413,36 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11265
11413
|
"--g": group.color
|
|
11266
11414
|
})
|
|
11267
11415
|
}, null, 4),
|
|
11268
|
-
createElementVNode("span",
|
|
11416
|
+
createElementVNode("span", _hoisted_42$1, toDisplayString(group.count), 1)
|
|
11269
11417
|
]);
|
|
11270
11418
|
}), 128))]),
|
|
11271
|
-
createElementVNode("div",
|
|
11272
|
-
class: "
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11419
|
+
createElementVNode("div", _hoisted_43$1, [createElementVNode("div", _hoisted_44$1, [_cache[30] || (_cache[30] = createElementVNode("span", { class: "sg-qc__title" }, "Quality control", -1)), createVNode(SegmentedControl_default, {
|
|
11420
|
+
class: "fr-qc__route",
|
|
11421
|
+
size: "sm",
|
|
11422
|
+
variant: "card",
|
|
11423
|
+
"aria-label": "Quality control routing",
|
|
11424
|
+
"model-value": currentRoute.value,
|
|
11425
|
+
options: QC_ROUTE_OPTIONS,
|
|
11426
|
+
"onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => onRouteChange($event))
|
|
11427
|
+
}, null, 8, ["model-value"])]), createElementVNode("div", _hoisted_45$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(qcChips.value, (chip) => {
|
|
11428
|
+
return openBlock(), createElementBlock("span", {
|
|
11429
|
+
key: chip.name,
|
|
11430
|
+
class: normalizeClass(["sg-qc__chip", { "fr-qc__chip--off": currentRoute.value === "Exclude" }])
|
|
11431
|
+
}, [createElementVNode("span", {
|
|
11432
|
+
class: "sg-dot",
|
|
11433
|
+
style: normalizeStyle(unref(QC_DOT_STYLE))
|
|
11434
|
+
}, null, 4), createTextVNode(toDisplayString(chip.name) + " ×" + toDisplayString(chip.count), 1)], 2);
|
|
11435
|
+
}), 128))])])
|
|
11286
11436
|
])]),
|
|
11287
|
-
createElementVNode("div",
|
|
11288
|
-
createElementVNode("span",
|
|
11289
|
-
_cache[
|
|
11437
|
+
createElementVNode("div", _hoisted_46$1, [
|
|
11438
|
+
createElementVNode("span", _hoisted_47$1, "Applies to " + toDisplayString(sampleTotal.value) + " samples" + toDisplayString(footHint.value), 1),
|
|
11439
|
+
_cache[33] || (_cache[33] = createElementVNode("div", { class: "sg-foot__spacer" }, null, -1)),
|
|
11290
11440
|
createVNode(BaseButton_default, {
|
|
11291
11441
|
variant: "ghost",
|
|
11292
11442
|
size: "sm",
|
|
11293
|
-
onClick: _cache[
|
|
11443
|
+
onClick: _cache[8] || (_cache[8] = ($event) => emit("cancel"))
|
|
11294
11444
|
}, {
|
|
11295
|
-
default: withCtx(() => [..._cache[
|
|
11445
|
+
default: withCtx(() => [..._cache[31] || (_cache[31] = [createTextVNode("Cancel", -1)])]),
|
|
11296
11446
|
_: 1
|
|
11297
11447
|
}),
|
|
11298
11448
|
createVNode(BaseButton_default, {
|
|
@@ -11305,11 +11455,11 @@ var SmartGroupFieldRecipe_default = /* @__PURE__ */ defineComponent({
|
|
|
11305
11455
|
d: ICON.check,
|
|
11306
11456
|
size: 15
|
|
11307
11457
|
}, null, 8, ["d"])]),
|
|
11308
|
-
default: withCtx(() => [_cache[
|
|
11458
|
+
default: withCtx(() => [_cache[32] || (_cache[32] = createTextVNode(" Apply grouping ", -1))]),
|
|
11309
11459
|
_: 1
|
|
11310
11460
|
}, 8, ["disabled"])
|
|
11311
11461
|
])
|
|
11312
|
-
]);
|
|
11462
|
+
], 512);
|
|
11313
11463
|
};
|
|
11314
11464
|
}
|
|
11315
11465
|
});
|
|
@@ -11928,11 +12078,73 @@ function parseCSV(text) {
|
|
|
11928
12078
|
}
|
|
11929
12079
|
//#endregion
|
|
11930
12080
|
//#region src/components/AutoGroupModal.adapter.ts
|
|
12081
|
+
var PREVIEW_VALUES = 3;
|
|
12082
|
+
/** Value preview of a field card: the first three values, then the count of the rest. */
|
|
12083
|
+
function previewValues(values) {
|
|
12084
|
+
const shown = values.slice(0, PREVIEW_VALUES).join(", ");
|
|
12085
|
+
return values.length > PREVIEW_VALUES ? `${shown} … +${values.length - PREVIEW_VALUES}` : shown;
|
|
12086
|
+
}
|
|
12087
|
+
/** Experiment line of the modal header: `exp157 · 2025-10-13 · LT`. */
|
|
12088
|
+
function experimentLabel(header) {
|
|
12089
|
+
return [
|
|
12090
|
+
header.code,
|
|
12091
|
+
header.date,
|
|
12092
|
+
header.operator
|
|
12093
|
+
].filter(Boolean).join(" · ");
|
|
12094
|
+
}
|
|
12095
|
+
/**
|
|
12096
|
+
* Names of all columns of the experimental classes. A field cannot take one
|
|
12097
|
+
* of them: the engine merges fields by name.
|
|
12098
|
+
*/
|
|
12099
|
+
function engineColumnNames(schemas) {
|
|
12100
|
+
const names = Object.values(schemas).filter((schema) => isExperimentalKind(schema.classKind)).flatMap((schema) => schema.columns.map((column) => column.displayName ?? column.name));
|
|
12101
|
+
return [...new Set(names)];
|
|
12102
|
+
}
|
|
12103
|
+
/** One chip for each QC-type class, with its sample count. */
|
|
12104
|
+
function engineQcChips(classes) {
|
|
12105
|
+
return classes.filter((c) => !isExperimentalKind(c.kind)).map((c) => ({
|
|
12106
|
+
name: c.label,
|
|
12107
|
+
count: c.members.length
|
|
12108
|
+
}));
|
|
12109
|
+
}
|
|
12110
|
+
/**
|
|
12111
|
+
* The tokens of one study sample with the role the engine gave each of them,
|
|
12112
|
+
* so the user sees how the names were read. The sample is the first member of
|
|
12113
|
+
* the largest experimental class. `cards` supplies the on/off state and the
|
|
12114
|
+
* layer of the tokens that are grouping fields.
|
|
12115
|
+
*/
|
|
12116
|
+
function engineAnatomy(classes, schemas, tokenized, cards) {
|
|
12117
|
+
const cls = classes.filter((c) => isExperimentalKind(c.kind) && schemas[classKey(c)]).sort((a, b) => b.members.length - a.members.length)[0];
|
|
12118
|
+
if (!cls) return [];
|
|
12119
|
+
const tokens = tokenized[cls.members[0]] ?? [];
|
|
12120
|
+
return schemas[classKey(cls)].columns.flatMap((column) => {
|
|
12121
|
+
const text = column.sourceIndices.map((index) => tokens[index] ?? "").join("_");
|
|
12122
|
+
if (!text) return [];
|
|
12123
|
+
const name = column.displayName ?? column.name;
|
|
12124
|
+
const card = cards.fields.findIndex((field) => field.key === name);
|
|
12125
|
+
if (card === -1) return [{
|
|
12126
|
+
text,
|
|
12127
|
+
caption: /^(Field \d+|Username.*|.* \d+)$/.test(name) ? roleLabel(column.role).toLowerCase() : name,
|
|
12128
|
+
state: "fixed"
|
|
12129
|
+
}];
|
|
12130
|
+
const layer = cards.layers[card];
|
|
12131
|
+
return [layer === null ? {
|
|
12132
|
+
text,
|
|
12133
|
+
caption: name,
|
|
12134
|
+
state: "off"
|
|
12135
|
+
} : {
|
|
12136
|
+
text,
|
|
12137
|
+
caption: String(layer),
|
|
12138
|
+
state: "on"
|
|
12139
|
+
}];
|
|
12140
|
+
});
|
|
12141
|
+
}
|
|
11931
12142
|
var ROLE_LABELS = {
|
|
11932
12143
|
"constant": "Constant",
|
|
11933
12144
|
"factor": "Factor",
|
|
11934
12145
|
"replicate": "Replicate",
|
|
11935
12146
|
"run-order": "Run",
|
|
12147
|
+
"id": "Sample ID",
|
|
11936
12148
|
"numeric": "Numeric",
|
|
11937
12149
|
"class-tag": "Class tag",
|
|
11938
12150
|
"ignore": "Ignore"
|
|
@@ -11942,14 +12154,15 @@ function roleLabel(role) {
|
|
|
11942
12154
|
return role ? ROLE_LABELS[role] : "Factor";
|
|
11943
12155
|
}
|
|
11944
12156
|
/**
|
|
11945
|
-
* Columns worth offering as grouping toggles: factors
|
|
11946
|
-
*
|
|
12157
|
+
* Columns worth offering as grouping toggles: factors, numerics, replicates and
|
|
12158
|
+
* sample IDs (the last two are off by default), plus any column already enabled
|
|
12159
|
+
* in `groupBy` (so a manual group-by choice still shows).
|
|
11947
12160
|
* Preserves the engine's column order. `groupBy` is the schema's ORDERED layer
|
|
11948
12161
|
* list — entry N is hierarchy layer N of the composed group names.
|
|
11949
12162
|
*/
|
|
11950
12163
|
function candidateColumns(columns, groupBy) {
|
|
11951
12164
|
const enabled = new Set(groupBy);
|
|
11952
|
-
return columns.filter((c) =>
|
|
12165
|
+
return columns.filter((c) => GROUPING_FIELD_ROLES.has(c.role) || enabled.has(c.index));
|
|
11953
12166
|
}
|
|
11954
12167
|
/**
|
|
11955
12168
|
* Build the batch-wide field recipe from every class schema. The active schema
|
|
@@ -11965,6 +12178,7 @@ function engineSchemasToCards(schemas, activeClassKey) {
|
|
|
11965
12178
|
const enabledOrder = [];
|
|
11966
12179
|
const enabled = /* @__PURE__ */ new Set();
|
|
11967
12180
|
for (const [, schema] of ordered) {
|
|
12181
|
+
if (!isExperimentalKind(schema.classKind)) continue;
|
|
11968
12182
|
for (const index of schema.groupBy) {
|
|
11969
12183
|
const column = schema.columns.find((candidate) => candidate.index === index);
|
|
11970
12184
|
if (!column) continue;
|
|
@@ -11984,8 +12198,8 @@ function engineSchemasToCards(schemas, activeClassKey) {
|
|
|
11984
12198
|
fields: cards.map(([key, column]) => ({
|
|
11985
12199
|
key,
|
|
11986
12200
|
role: roleLabel(column.role),
|
|
11987
|
-
vals: column.uniqueValues
|
|
11988
|
-
rep: column.role === "replicate"
|
|
12201
|
+
vals: previewValues(column.uniqueValues),
|
|
12202
|
+
rep: column.role === "replicate" || column.role === "id"
|
|
11989
12203
|
})),
|
|
11990
12204
|
enabled: cards.map(([key]) => enabled.has(key)),
|
|
11991
12205
|
layers: cards.map(([key]) => {
|
|
@@ -12169,12 +12383,20 @@ var AutoGroupModal_default = /* @__PURE__ */ defineComponent({
|
|
|
12169
12383
|
const fieldCards = computed(() => engineSchemasToCards(autoGroup.schemas.value, autoGroup.activeClassKey.value));
|
|
12170
12384
|
const railGroups = computed(() => engineGroupsToRail(autoGroup.result.value.experimentalGroups ?? autoGroup.groups.value));
|
|
12171
12385
|
const qcSampleCount = computed(() => autoGroup.classes.value.filter((c) => c.kind !== "biological" && c.kind !== "unknown").reduce((acc, c) => acc + c.members.length, 0));
|
|
12172
|
-
const
|
|
12173
|
-
name: "QC_Pool",
|
|
12174
|
-
count: qcSampleCount.value
|
|
12175
|
-
} : null);
|
|
12386
|
+
const qcChips = computed(() => qcRouting.value === "mix" ? [] : engineQcChips(autoGroup.classes.value));
|
|
12176
12387
|
const fieldRecipeRoute = computed(() => engineToQcRoute(qcRouting.value));
|
|
12177
12388
|
const headerCount = computed(() => autoGroup.samples.value.length);
|
|
12389
|
+
const headerLabel = computed(() => experimentLabel(autoGroup.experiment.value));
|
|
12390
|
+
const reservedNames = computed(() => engineColumnNames(autoGroup.schemas.value));
|
|
12391
|
+
const anatomy = computed(() => autoGroup.inputMode.value === "paste" ? engineAnatomy(autoGroup.classes.value, autoGroup.schemas.value, autoGroup.tokenized.value, fieldCards.value) : []);
|
|
12392
|
+
const fieldHints = computed(() => {
|
|
12393
|
+
const needsHelp = mostlySingletons(railGroups.value);
|
|
12394
|
+
return fieldCards.value.fields.map((field, index) => {
|
|
12395
|
+
if (!needsHelp || !fieldCards.value.enabled[index]) return null;
|
|
12396
|
+
const remaining = autoGroup.groupCountWithout(field.key);
|
|
12397
|
+
return `off → ${remaining} ${remaining === 1 ? "group" : "groups"}`;
|
|
12398
|
+
});
|
|
12399
|
+
});
|
|
12178
12400
|
function onToggleField(cardIndex) {
|
|
12179
12401
|
const field = fieldCards.value.fields[cardIndex];
|
|
12180
12402
|
if (!field) return;
|
|
@@ -12185,6 +12407,11 @@ var AutoGroupModal_default = /* @__PURE__ */ defineComponent({
|
|
|
12185
12407
|
if (!field) return;
|
|
12186
12408
|
autoGroup.moveGroupByField(field.key, direction);
|
|
12187
12409
|
}
|
|
12410
|
+
function onRenameField(cardIndex, name) {
|
|
12411
|
+
const field = fieldCards.value.fields[cardIndex];
|
|
12412
|
+
if (!field) return;
|
|
12413
|
+
autoGroup.renameGroupByField(field.key, name);
|
|
12414
|
+
}
|
|
12188
12415
|
function onRouteChange(route) {
|
|
12189
12416
|
setQcRouting(qcRouteToEngine(route));
|
|
12190
12417
|
}
|
|
@@ -12293,15 +12520,20 @@ var AutoGroupModal_default = /* @__PURE__ */ defineComponent({
|
|
|
12293
12520
|
enabled: fieldCards.value.enabled,
|
|
12294
12521
|
layers: fieldCards.value.layers,
|
|
12295
12522
|
groups: railGroups.value,
|
|
12296
|
-
"qc-
|
|
12523
|
+
"qc-chips": qcChips.value,
|
|
12297
12524
|
route: fieldRecipeRoute.value,
|
|
12298
12525
|
"header-count": headerCount.value,
|
|
12299
12526
|
"qc-count": qcSampleCount.value,
|
|
12300
12527
|
"can-download-template": unref(autoGroup).canDownloadTemplate.value,
|
|
12301
12528
|
loading: experimentLoading.value,
|
|
12302
|
-
"experiment-label":
|
|
12529
|
+
"experiment-label": headerLabel.value,
|
|
12530
|
+
anatomy: anatomy.value,
|
|
12531
|
+
hints: fieldHints.value,
|
|
12532
|
+
"reserved-names": reservedNames.value,
|
|
12533
|
+
renamable: "",
|
|
12303
12534
|
onToggle: onToggleField,
|
|
12304
12535
|
onMove: onMoveField,
|
|
12536
|
+
onRename: onRenameField,
|
|
12305
12537
|
"onUpdate:route": onRouteChange,
|
|
12306
12538
|
onCsvFile,
|
|
12307
12539
|
onDownloadTemplate,
|
|
@@ -12315,12 +12547,16 @@ var AutoGroupModal_default = /* @__PURE__ */ defineComponent({
|
|
|
12315
12547
|
"enabled",
|
|
12316
12548
|
"layers",
|
|
12317
12549
|
"groups",
|
|
12318
|
-
"qc-
|
|
12550
|
+
"qc-chips",
|
|
12319
12551
|
"route",
|
|
12320
12552
|
"header-count",
|
|
12321
12553
|
"qc-count",
|
|
12322
12554
|
"can-download-template",
|
|
12323
|
-
"loading"
|
|
12555
|
+
"loading",
|
|
12556
|
+
"experiment-label",
|
|
12557
|
+
"anatomy",
|
|
12558
|
+
"hints",
|
|
12559
|
+
"reserved-names"
|
|
12324
12560
|
])) : (openBlock(), createBlock(SmartGroupManual_default, {
|
|
12325
12561
|
key: manualKey.value,
|
|
12326
12562
|
"sample-names": manualSampleNames.value,
|
|
@@ -22896,4 +23132,4 @@ var components_exports = /* @__PURE__ */ __exportAll({
|
|
|
22896
23132
|
//#endregion
|
|
22897
23133
|
export { PlateEditor_default as $, SampleLegend_default as A, AppToastContainer_default as At, StatusIndicator_default as B, ChemicalFormula_default as C, AppAvatarMenu_default as Ct, ReagentEditor_default as D, ActionMenu_default as Dt, SampleHierarchyTree_default as E, ThemeToggle_default as Et, SettingsModal_default as F, Calendar_default as Ft, ControlWorkspaceView_default as G, LayoutResizeHandle_default as H, AdductText_default as I, BasePill_default as It, SampleSelector_default as J, ComponentBindingRenderer_default as K, Breadcrumb_default as L, SegmentedControl_default as Lt, BioTemplatePackWorkspaceView_default as M, FormField_default as Mt, BioTemplateExperimentWorkspaceView_default as N, DataFrame_default as Nt, SmartGroupModal_default as O, IconButton_default as Ot, BioTemplateRenderer_default as P, LoadingSpinner_default as Pt, ReagentList_default as Q, Avatar_default as R, BaseTabs_default as Rt, sequenceSamplesRemaining as S, AppTopBar_default as St, ProtocolStepEditor_default as T, CollapsibleCard_default as Tt, AppContainer_default as U, Divider_default as V, DoseDesignWorkspaceView_default as W, SmartGroupManual_default as X, AutoGroupModal_default as Y, SmartGroupFieldRecipe_default as Z, estimateSequenceFinishDate as _, AppLayout_default as _t, ArtifactSaveDialog_default as a, GeneratedPluginView_default as at, formatSequenceRemaining as b, FormActions_default as bt, SectionCard_default as c, GeneratedPlotlyResult_default as ct, ExperimentCodeBadge_default as d, ChartContainer_default as dt, ConfirmDialog_default as et, ExperimentDataViewer_default as f, generatedJobDefaults as ft, SequenceProgressBar_default as g, MobileSupportGate_default as gt, InstrumentStateBadge_default as h, PluginWorkspaceView_default as ht, ExperimentPopover_default as i, DoseCalculator_default as it, BioTemplatePresetWorkspaceView_default as j, AlertBox_default as jt, GroupAssigner_default as k, RuntimeUpdateNotice_default as kt, ResourceCard_default as l, PlotlyChart_default as lt, AuditTrail_default as m, normalizeGeneratedJobInput as mt, FilePicker_default as n, sampleTypeMarker as nt, ArtifactSelectorModal_default as o, JobsStatusTray_default as ot, BatchProgressList_default as p, generatedJobFormSchema as pt, ScheduleCalendar_default as q, FitPanel_default as r, ExperimentTimeline_default as rt, ExperimentSelectorModal_default as s, GeneratedResultRenderer_default as st, components_exports as t, WellPlate_default as tt, TimeRangeInput_default as u, mintPlotlyTemplate as ut, estimateSequenceRemainingSeconds as v, AppSidebar_default as vt, ScientificNumber_default as w, PluginIcon_default as wt, sequenceProgressPercent as x, StepWizard_default as xt, formatSequenceEta as y, FormBuilder_default as yt, ProgressBar_default as z, SearchableSelect_default as zt };
|
|
22898
23134
|
|
|
22899
|
-
//# sourceMappingURL=components-
|
|
23135
|
+
//# sourceMappingURL=components-bbanLd1u.js.map
|