@morscherlab/mld-sdk 0.7.6 → 0.7.7
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/AutoGroupModal.vue.js +2 -2
- package/dist/components/AutoGroupModal.vue.js.map +1 -1
- package/dist/components/NumberInput.vue.js +53 -50
- package/dist/components/NumberInput.vue.js.map +1 -1
- package/dist/composables/useAutoGroup.d.ts +1 -0
- package/dist/composables/useAutoGroup.js +24 -4
- package/dist/composables/useAutoGroup.js.map +1 -1
- package/dist/styles.css +46 -18
- package/package.json +1 -1
- package/src/__tests__/composables/useAutoGroup.test.ts +133 -0
- package/src/components/AutoGroupModal.story.vue +100 -32
- package/src/components/AutoGroupModal.vue +2 -2
- package/src/components/NumberInput.vue +33 -31
- package/src/composables/useAutoGroup.ts +34 -5
- package/src/styles/components/app-sidebar.css +1 -1
- package/src/styles/components/auto-group-modal.css +5 -4
- package/src/styles/components/modal.css +6 -0
- package/src/styles/components/number-input.css +7 -5
- package/src/styles/components/step-wizard.css +5 -0
|
@@ -101,7 +101,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
101
101
|
autoGroup.rawText.value = props.samples.join("\n");
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
});
|
|
104
|
+
}, { immediate: true });
|
|
105
105
|
const allSteps = [
|
|
106
106
|
{ id: "input", label: "Input" },
|
|
107
107
|
{ id: "outliers", label: "Outliers" },
|
|
@@ -297,7 +297,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
297
297
|
"step-outliers": withCtx(() => [
|
|
298
298
|
createElementVNode("div", _hoisted_10, [
|
|
299
299
|
createElementVNode("div", _hoisted_11, [
|
|
300
|
-
createTextVNode(toDisplayString(unref(autoGroup).outliers.value.length) + " of " + toDisplayString(unref(autoGroup).samples.value.length) + " samples have irregular structure (fewer than " + toDisplayString(unref(autoGroup).
|
|
300
|
+
createTextVNode(toDisplayString(unref(autoGroup).outliers.value.length) + " of " + toDisplayString(unref(autoGroup).samples.value.length) + " samples have irregular structure (fewer than " + toDisplayString(unref(autoGroup).dominantFieldCount.value) + " fields, delimiter ", 1),
|
|
301
301
|
createElementVNode("code", null, toDisplayString(unref(autoGroup).delimiter.value), 1),
|
|
302
302
|
_cache[14] || (_cache[14] = createTextVNode(") ", -1))
|
|
303
303
|
]),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoGroupModal.vue.js","sources":["../../src/components/AutoGroupModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport StepWizard from './StepWizard.vue'\nimport { useAutoGroup, parseCSV } from '../composables/useAutoGroup'\nimport type { WizardStep } from '../types/components'\nimport type { AutoGroupResult } from '../types/auto-group'\n\ninterface Props {\n modelValue: boolean\n samples?: string[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n samples: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'apply': [result: AutoGroupResult]\n}>()\n\nconst autoGroup = useAutoGroup()\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\nconst currentStep = ref(0)\nconst isDragOver = ref(false)\nconst csvFileName = ref('')\n\n// Pre-fill from props\nwatch(() => props.modelValue, (open) => {\n if (open) {\n autoGroup.reset()\n currentStep.value = 0\n csvFileName.value = ''\n if (props.samples.length > 0) {\n autoGroup.rawText.value = props.samples.join('\\n')\n }\n }\n})\n\n// Dynamic steps: skip outlier step when no outliers\nconst allSteps: WizardStep[] = [\n { id: 'input', label: 'Input' },\n { id: 'outliers', label: 'Outliers' },\n { id: 'fields', label: 'Fields' },\n { id: 'preview', label: 'Preview' },\n]\n\nconst dynamicSteps = computed<WizardStep[]>(() => {\n if (autoGroup.hasOutliers.value) {\n return allSteps\n }\n return allSteps.filter(s => s.id !== 'outliers')\n})\n\n// Map current step index to step ID\nconst currentStepId = computed(() => {\n const step = dynamicSteps.value[currentStep.value]\n return step?.id ?? 'input'\n})\n\n// Clamp step when dynamic steps change\nwatch(() => dynamicSteps.value.length, (newLen) => {\n if (currentStep.value >= newLen) {\n currentStep.value = newLen - 1\n }\n})\n\n// Step validity\nconst inputValid = computed(() => autoGroup.samples.value.length > 0)\nconst fieldsValid = computed(() => autoGroup.enabledFields.value.size > 0)\n\nwatch([inputValid, fieldsValid, dynamicSteps], () => {\n if (!wizardRef.value) return\n const steps = dynamicSteps.value\n for (let i = 0; i < steps.length; i++) {\n const id = steps[i].id\n if (id === 'input') wizardRef.value.setStepValid(i, inputValid.value)\n else if (id === 'outliers') wizardRef.value.setStepValid(i, true)\n else if (id === 'fields') wizardRef.value.setStepValid(i, fieldsValid.value)\n else if (id === 'preview') wizardRef.value.setStepValid(i, true)\n }\n}, { immediate: true })\n\n// Navigation\nfunction handleNext() {\n if (currentStepId.value === 'input') {\n autoGroup.parseInput()\n }\n // StepWizard handles the actual navigation via v-model\n}\n\nfunction handleApply() {\n emit('apply', autoGroup.result.value)\n emit('update:modelValue', false)\n}\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n}\n\n// CSV file handling\nfunction handleFileDrop(event: DragEvent) {\n event.preventDefault()\n isDragOver.value = false\n const files = event.dataTransfer?.files\n if (files && files.length > 0) {\n processFile(files[0])\n }\n}\n\nfunction handleFileInput(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.files && target.files.length > 0) {\n processFile(target.files[0])\n }\n}\n\nasync function processFile(file: File) {\n if (!file.name.endsWith('.csv') && !file.name.endsWith('.tsv')) return\n\n try {\n const text = await file.text()\n const parsed = parseCSV(text)\n autoGroup.inputMode.value = 'csv'\n autoGroup.csvData.value = parsed\n csvFileName.value = file.name\n } catch {\n // CSV parse error — stay in current state\n }\n}\n\nfunction clearCsvFile() {\n autoGroup.csvData.value = null\n autoGroup.inputMode.value = 'paste'\n csvFileName.value = ''\n}\n\n// Last step check\nconst isLastStep = computed(() => currentStep.value === dynamicSteps.value.length - 1)\nconst isFirstStep = computed(() => currentStep.value === 0)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n title=\"Smart Group\"\n size=\"lg\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleCancel\"\n >\n <div class=\"mld-auto-group\">\n <StepWizard\n ref=\"wizardRef\"\n v-model=\"currentStep\"\n :steps=\"dynamicSteps\"\n :linear=\"true\"\n size=\"sm\"\n >\n <!-- Step: Input -->\n <template #step-input>\n <div class=\"mld-auto-group__input-step\">\n <!-- Mode toggle -->\n <div class=\"mld-auto-group__mode-toggle\">\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'paste' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'paste'\"\n >\n Paste\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'csv' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'csv'\"\n >\n CSV\n </BaseButton>\n </div>\n\n <!-- Paste mode -->\n <div v-if=\"autoGroup.inputMode.value === 'paste'\" class=\"mld-auto-group__paste\">\n <textarea\n v-model=\"autoGroup.rawText.value\"\n class=\"mld-auto-group__textarea\"\n rows=\"12\"\n placeholder=\"Paste sample names, one per line...\"\n />\n <div v-if=\"autoGroup.samples.value.length > 0\" class=\"mld-auto-group__sample-count\">\n {{ autoGroup.samples.value.length }} samples\n </div>\n </div>\n\n <!-- CSV mode -->\n <div v-if=\"autoGroup.inputMode.value === 'csv'\" class=\"mld-auto-group__csv\">\n <div v-if=\"!autoGroup.csvData.value\"\n :class=\"[\n 'mld-auto-group__dropzone',\n isDragOver ? 'mld-auto-group__dropzone--dragover' : '',\n ]\"\n @drop=\"handleFileDrop\"\n @dragover.prevent=\"isDragOver = true\"\n @dragleave=\"isDragOver = false\"\n >\n <input\n type=\"file\"\n accept=\".csv,.tsv\"\n class=\"mld-auto-group__file-input\"\n @change=\"handleFileInput\"\n />\n <svg class=\"mld-auto-group__upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <p class=\"mld-auto-group__upload-text\">\n <span class=\"mld-auto-group__upload-highlight\">Click to upload</span>\n or drag and drop a CSV file\n </p>\n </div>\n\n <div v-else class=\"mld-auto-group__file-info\">\n <svg class=\"mld-auto-group__file-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <span class=\"mld-auto-group__file-name\">{{ csvFileName }}</span>\n <span class=\"mld-auto-group__file-rows\">{{ autoGroup.csvData.value.rows.length }} rows</span>\n <button type=\"button\" class=\"mld-auto-group__file-clear\" @click=\"clearCsvFile\">\n Change file\n </button>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Outliers -->\n <template #step-outliers>\n <div class=\"mld-auto-group__outlier-step\">\n <div class=\"mld-auto-group__outlier-banner\">\n {{ autoGroup.outliers.value.length }} of {{ autoGroup.samples.value.length }}\n samples have irregular structure\n (fewer than {{ autoGroup.minFieldCount.value }} fields, delimiter\n <code>{{ autoGroup.delimiter.value }}</code>)\n </div>\n\n <div class=\"mld-auto-group__outlier-batch\">\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('include')\">\n Include All\n </BaseButton>\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('exclude')\">\n Exclude All\n </BaseButton>\n </div>\n\n <div class=\"mld-auto-group__outlier-list\">\n <div\n v-for=\"outlier in autoGroup.outliers.value\"\n :key=\"outlier.index\"\n class=\"mld-auto-group__outlier-item\"\n >\n <div class=\"mld-auto-group__outlier-info\">\n <code class=\"mld-auto-group__outlier-name\">{{ outlier.sample }}</code>\n <span class=\"mld-auto-group__outlier-badge\">{{ outlier.fieldCount }} fields</span>\n </div>\n <div class=\"mld-auto-group__outlier-actions\">\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'include' ? 'mld-auto-group__action-btn--active' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'include')\"\n >Include</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'exclude' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--exclude' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'exclude')\"\n >Exclude</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'qc' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--qc' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'qc')\"\n >QC</button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Fields -->\n <template #step-fields>\n <div class=\"mld-auto-group__field-step\">\n <div class=\"mld-auto-group__field-grid\">\n <div\n v-for=\"col in autoGroup.effectiveColumns.value\"\n :key=\"col.index\"\n :class=\"[\n 'mld-auto-group__field-card',\n autoGroup.enabledFields.value.has(col.index) ? 'mld-auto-group__field-card--enabled' : '',\n ]\"\n >\n <div class=\"mld-auto-group__field-header\">\n <label class=\"mld-auto-group__field-toggle\">\n <input\n type=\"checkbox\"\n :checked=\"autoGroup.enabledFields.value.has(col.index)\"\n @change=\"autoGroup.toggleField(col.index)\"\n />\n <span class=\"mld-auto-group__field-toggle-label\">Group by</span>\n </label>\n <span class=\"mld-auto-group__field-cardinality\">{{ col.cardinality }} unique</span>\n </div>\n\n <BaseInput\n :model-value=\"col.name\"\n placeholder=\"Field name...\"\n class=\"mld-auto-group__field-name-input\"\n @update:model-value=\"autoGroup.renameField(col.index, String($event ?? ''))\"\n />\n\n <div class=\"mld-auto-group__field-values\">\n <span\n v-for=\"val in col.uniqueValues.slice(0, 5)\"\n :key=\"val\"\n class=\"mld-auto-group__field-value\"\n >{{ val }}</span>\n <span\n v-if=\"col.uniqueValues.length > 5\"\n class=\"mld-auto-group__field-more\"\n >+{{ col.uniqueValues.length - 5 }} more</span>\n </div>\n </div>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size > 0\" class=\"mld-auto-group__field-summary\">\n Grouping by:\n <strong>{{\n autoGroup.effectiveColumns.value\n .filter(c => autoGroup.enabledFields.value.has(c.index))\n .map(c => c.name)\n .join(' / ')\n }}</strong>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size === 0\" class=\"mld-auto-group__field-warning\">\n Select at least one field for grouping\n </div>\n </div>\n </template>\n\n <!-- Step: Preview -->\n <template #step-preview>\n <div class=\"mld-auto-group__preview-step\">\n <div class=\"mld-auto-group__preview-summary\">\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.length }}</strong> groups\n </span>\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.reduce((sum, g) => sum + g.samples.length, 0) }}</strong> samples\n </span>\n <span v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-stat mld-auto-group__preview-stat--excluded\">\n <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded\n </span>\n </div>\n\n <div class=\"mld-auto-group__preview-groups\">\n <details\n v-for=\"group in autoGroup.groups.value\"\n :key=\"group.name\"\n class=\"mld-auto-group__preview-group\"\n >\n <summary class=\"mld-auto-group__preview-group-header\">\n <span\n class=\"mld-auto-group__preview-dot\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mld-auto-group__preview-group-name\">{{ group.name }}</span>\n <span\n class=\"mld-auto-group__preview-group-count\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >{{ group.samples.length }}</span>\n </summary>\n <div class=\"mld-auto-group__preview-samples\">\n <span\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample\"\n >{{ sample }}</span>\n </div>\n </details>\n </div>\n\n <div v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-excluded\">\n <div class=\"mld-auto-group__preview-excluded-title\">Excluded Samples</div>\n <div class=\"mld-auto-group__preview-excluded-list\">\n <span\n v-for=\"sample in autoGroup.excludedSamples.value\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample mld-auto-group__preview-sample--excluded\"\n >{{ sample }}</span>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Custom navigation -->\n <template #navigation=\"{ goBack, goNext, canProceed }\">\n <div class=\"mld-auto-group__nav\">\n <BaseButton variant=\"secondary\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <div style=\"flex: 1\" />\n <BaseButton\n v-if=\"!isFirstStep\"\n variant=\"secondary\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"!isLastStep\"\n variant=\"primary\"\n :disabled=\"!canProceed\"\n @click=\"() => { handleNext(); goNext() }\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-if=\"isLastStep\"\n variant=\"primary\"\n @click=\"handleApply\"\n >\n Apply\n </BaseButton>\n </div>\n </template>\n </StepWizard>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/auto-group-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_createVNode","StepWizard","BaseButton","_unref","_openBlock","_createElementBlock","_toDisplayString","_normalizeClass","_Fragment","BaseInput","_normalizeStyle","_renderList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AutoGroupModal.vue.js","sources":["../../src/components/AutoGroupModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport StepWizard from './StepWizard.vue'\nimport { useAutoGroup, parseCSV } from '../composables/useAutoGroup'\nimport type { WizardStep } from '../types/components'\nimport type { AutoGroupResult } from '../types/auto-group'\n\ninterface Props {\n modelValue: boolean\n samples?: string[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n samples: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'apply': [result: AutoGroupResult]\n}>()\n\nconst autoGroup = useAutoGroup()\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\nconst currentStep = ref(0)\nconst isDragOver = ref(false)\nconst csvFileName = ref('')\n\n// Pre-fill from props\nwatch(() => props.modelValue, (open) => {\n if (open) {\n autoGroup.reset()\n currentStep.value = 0\n csvFileName.value = ''\n if (props.samples.length > 0) {\n autoGroup.rawText.value = props.samples.join('\\n')\n }\n }\n}, { immediate: true })\n\n// Dynamic steps: skip outlier step when no outliers\nconst allSteps: WizardStep[] = [\n { id: 'input', label: 'Input' },\n { id: 'outliers', label: 'Outliers' },\n { id: 'fields', label: 'Fields' },\n { id: 'preview', label: 'Preview' },\n]\n\nconst dynamicSteps = computed<WizardStep[]>(() => {\n if (autoGroup.hasOutliers.value) {\n return allSteps\n }\n return allSteps.filter(s => s.id !== 'outliers')\n})\n\n// Map current step index to step ID\nconst currentStepId = computed(() => {\n const step = dynamicSteps.value[currentStep.value]\n return step?.id ?? 'input'\n})\n\n// Clamp step when dynamic steps change\nwatch(() => dynamicSteps.value.length, (newLen) => {\n if (currentStep.value >= newLen) {\n currentStep.value = newLen - 1\n }\n})\n\n// Step validity\nconst inputValid = computed(() => autoGroup.samples.value.length > 0)\nconst fieldsValid = computed(() => autoGroup.enabledFields.value.size > 0)\n\nwatch([inputValid, fieldsValid, dynamicSteps], () => {\n if (!wizardRef.value) return\n const steps = dynamicSteps.value\n for (let i = 0; i < steps.length; i++) {\n const id = steps[i].id\n if (id === 'input') wizardRef.value.setStepValid(i, inputValid.value)\n else if (id === 'outliers') wizardRef.value.setStepValid(i, true)\n else if (id === 'fields') wizardRef.value.setStepValid(i, fieldsValid.value)\n else if (id === 'preview') wizardRef.value.setStepValid(i, true)\n }\n}, { immediate: true })\n\n// Navigation\nfunction handleNext() {\n if (currentStepId.value === 'input') {\n autoGroup.parseInput()\n }\n // StepWizard handles the actual navigation via v-model\n}\n\nfunction handleApply() {\n emit('apply', autoGroup.result.value)\n emit('update:modelValue', false)\n}\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n}\n\n// CSV file handling\nfunction handleFileDrop(event: DragEvent) {\n event.preventDefault()\n isDragOver.value = false\n const files = event.dataTransfer?.files\n if (files && files.length > 0) {\n processFile(files[0])\n }\n}\n\nfunction handleFileInput(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.files && target.files.length > 0) {\n processFile(target.files[0])\n }\n}\n\nasync function processFile(file: File) {\n if (!file.name.endsWith('.csv') && !file.name.endsWith('.tsv')) return\n\n try {\n const text = await file.text()\n const parsed = parseCSV(text)\n autoGroup.inputMode.value = 'csv'\n autoGroup.csvData.value = parsed\n csvFileName.value = file.name\n } catch {\n // CSV parse error — stay in current state\n }\n}\n\nfunction clearCsvFile() {\n autoGroup.csvData.value = null\n autoGroup.inputMode.value = 'paste'\n csvFileName.value = ''\n}\n\n// Last step check\nconst isLastStep = computed(() => currentStep.value === dynamicSteps.value.length - 1)\nconst isFirstStep = computed(() => currentStep.value === 0)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n title=\"Smart Group\"\n size=\"lg\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleCancel\"\n >\n <div class=\"mld-auto-group\">\n <StepWizard\n ref=\"wizardRef\"\n v-model=\"currentStep\"\n :steps=\"dynamicSteps\"\n :linear=\"true\"\n size=\"sm\"\n >\n <!-- Step: Input -->\n <template #step-input>\n <div class=\"mld-auto-group__input-step\">\n <!-- Mode toggle -->\n <div class=\"mld-auto-group__mode-toggle\">\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'paste' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'paste'\"\n >\n Paste\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'csv' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'csv'\"\n >\n CSV\n </BaseButton>\n </div>\n\n <!-- Paste mode -->\n <div v-if=\"autoGroup.inputMode.value === 'paste'\" class=\"mld-auto-group__paste\">\n <textarea\n v-model=\"autoGroup.rawText.value\"\n class=\"mld-auto-group__textarea\"\n rows=\"12\"\n placeholder=\"Paste sample names, one per line...\"\n />\n <div v-if=\"autoGroup.samples.value.length > 0\" class=\"mld-auto-group__sample-count\">\n {{ autoGroup.samples.value.length }} samples\n </div>\n </div>\n\n <!-- CSV mode -->\n <div v-if=\"autoGroup.inputMode.value === 'csv'\" class=\"mld-auto-group__csv\">\n <div v-if=\"!autoGroup.csvData.value\"\n :class=\"[\n 'mld-auto-group__dropzone',\n isDragOver ? 'mld-auto-group__dropzone--dragover' : '',\n ]\"\n @drop=\"handleFileDrop\"\n @dragover.prevent=\"isDragOver = true\"\n @dragleave=\"isDragOver = false\"\n >\n <input\n type=\"file\"\n accept=\".csv,.tsv\"\n class=\"mld-auto-group__file-input\"\n @change=\"handleFileInput\"\n />\n <svg class=\"mld-auto-group__upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <p class=\"mld-auto-group__upload-text\">\n <span class=\"mld-auto-group__upload-highlight\">Click to upload</span>\n or drag and drop a CSV file\n </p>\n </div>\n\n <div v-else class=\"mld-auto-group__file-info\">\n <svg class=\"mld-auto-group__file-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <span class=\"mld-auto-group__file-name\">{{ csvFileName }}</span>\n <span class=\"mld-auto-group__file-rows\">{{ autoGroup.csvData.value.rows.length }} rows</span>\n <button type=\"button\" class=\"mld-auto-group__file-clear\" @click=\"clearCsvFile\">\n Change file\n </button>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Outliers -->\n <template #step-outliers>\n <div class=\"mld-auto-group__outlier-step\">\n <div class=\"mld-auto-group__outlier-banner\">\n {{ autoGroup.outliers.value.length }} of {{ autoGroup.samples.value.length }}\n samples have irregular structure\n (fewer than {{ autoGroup.dominantFieldCount.value }} fields, delimiter\n <code>{{ autoGroup.delimiter.value }}</code>)\n </div>\n\n <div class=\"mld-auto-group__outlier-batch\">\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('include')\">\n Include All\n </BaseButton>\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('exclude')\">\n Exclude All\n </BaseButton>\n </div>\n\n <div class=\"mld-auto-group__outlier-list\">\n <div\n v-for=\"outlier in autoGroup.outliers.value\"\n :key=\"outlier.index\"\n class=\"mld-auto-group__outlier-item\"\n >\n <div class=\"mld-auto-group__outlier-info\">\n <code class=\"mld-auto-group__outlier-name\">{{ outlier.sample }}</code>\n <span class=\"mld-auto-group__outlier-badge\">{{ outlier.fieldCount }} fields</span>\n </div>\n <div class=\"mld-auto-group__outlier-actions\">\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'include' ? 'mld-auto-group__action-btn--active' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'include')\"\n >Include</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'exclude' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--exclude' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'exclude')\"\n >Exclude</button>\n <button\n type=\"button\"\n :class=\"['mld-auto-group__action-btn', outlier.action === 'qc' ? 'mld-auto-group__action-btn--active mld-auto-group__action-btn--qc' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'qc')\"\n >QC</button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Fields -->\n <template #step-fields>\n <div class=\"mld-auto-group__field-step\">\n <div class=\"mld-auto-group__field-grid\">\n <div\n v-for=\"col in autoGroup.effectiveColumns.value\"\n :key=\"col.index\"\n :class=\"[\n 'mld-auto-group__field-card',\n autoGroup.enabledFields.value.has(col.index) ? 'mld-auto-group__field-card--enabled' : '',\n ]\"\n >\n <div class=\"mld-auto-group__field-header\">\n <label class=\"mld-auto-group__field-toggle\">\n <input\n type=\"checkbox\"\n :checked=\"autoGroup.enabledFields.value.has(col.index)\"\n @change=\"autoGroup.toggleField(col.index)\"\n />\n <span class=\"mld-auto-group__field-toggle-label\">Group by</span>\n </label>\n <span class=\"mld-auto-group__field-cardinality\">{{ col.cardinality }} unique</span>\n </div>\n\n <BaseInput\n :model-value=\"col.name\"\n placeholder=\"Field name...\"\n class=\"mld-auto-group__field-name-input\"\n @update:model-value=\"autoGroup.renameField(col.index, String($event ?? ''))\"\n />\n\n <div class=\"mld-auto-group__field-values\">\n <span\n v-for=\"val in col.uniqueValues.slice(0, 5)\"\n :key=\"val\"\n class=\"mld-auto-group__field-value\"\n >{{ val }}</span>\n <span\n v-if=\"col.uniqueValues.length > 5\"\n class=\"mld-auto-group__field-more\"\n >+{{ col.uniqueValues.length - 5 }} more</span>\n </div>\n </div>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size > 0\" class=\"mld-auto-group__field-summary\">\n Grouping by:\n <strong>{{\n autoGroup.effectiveColumns.value\n .filter(c => autoGroup.enabledFields.value.has(c.index))\n .map(c => c.name)\n .join(' / ')\n }}</strong>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size === 0\" class=\"mld-auto-group__field-warning\">\n Select at least one field for grouping\n </div>\n </div>\n </template>\n\n <!-- Step: Preview -->\n <template #step-preview>\n <div class=\"mld-auto-group__preview-step\">\n <div class=\"mld-auto-group__preview-summary\">\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.length }}</strong> groups\n </span>\n <span class=\"mld-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.reduce((sum, g) => sum + g.samples.length, 0) }}</strong> samples\n </span>\n <span v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-stat mld-auto-group__preview-stat--excluded\">\n <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded\n </span>\n </div>\n\n <div class=\"mld-auto-group__preview-groups\">\n <details\n v-for=\"group in autoGroup.groups.value\"\n :key=\"group.name\"\n class=\"mld-auto-group__preview-group\"\n >\n <summary class=\"mld-auto-group__preview-group-header\">\n <span\n class=\"mld-auto-group__preview-dot\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mld-auto-group__preview-group-name\">{{ group.name }}</span>\n <span\n class=\"mld-auto-group__preview-group-count\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >{{ group.samples.length }}</span>\n </summary>\n <div class=\"mld-auto-group__preview-samples\">\n <span\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample\"\n >{{ sample }}</span>\n </div>\n </details>\n </div>\n\n <div v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mld-auto-group__preview-excluded\">\n <div class=\"mld-auto-group__preview-excluded-title\">Excluded Samples</div>\n <div class=\"mld-auto-group__preview-excluded-list\">\n <span\n v-for=\"sample in autoGroup.excludedSamples.value\"\n :key=\"sample\"\n class=\"mld-auto-group__preview-sample mld-auto-group__preview-sample--excluded\"\n >{{ sample }}</span>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Custom navigation -->\n <template #navigation=\"{ goBack, goNext, canProceed }\">\n <div class=\"mld-auto-group__nav\">\n <BaseButton variant=\"secondary\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <div style=\"flex: 1\" />\n <BaseButton\n v-if=\"!isFirstStep\"\n variant=\"secondary\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"!isLastStep\"\n variant=\"primary\"\n :disabled=\"!canProceed\"\n @click=\"() => { handleNext(); goNext() }\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-if=\"isLastStep\"\n variant=\"primary\"\n @click=\"handleApply\"\n >\n Apply\n </BaseButton>\n </div>\n </template>\n </StepWizard>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/auto-group-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_createVNode","StepWizard","BaseButton","_unref","_openBlock","_createElementBlock","_toDisplayString","_normalizeClass","_Fragment","BaseInput","_normalizeStyle","_renderList"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { defineComponent, computed, openBlock, createElementBlock, normalizeClass, createElementVNode } from "vue";
|
|
2
|
-
const _hoisted_1 = ["disabled"];
|
|
3
|
-
const _hoisted_2 =
|
|
2
|
+
const _hoisted_1 = ["value", "min", "max", "step", "disabled", "placeholder"];
|
|
3
|
+
const _hoisted_2 = { class: "mld-number-input__buttons" };
|
|
4
4
|
const _hoisted_3 = ["disabled"];
|
|
5
|
+
const _hoisted_4 = ["disabled"];
|
|
5
6
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
6
7
|
__name: "NumberInput",
|
|
7
8
|
props: {
|
|
@@ -62,29 +63,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
62
63
|
__props.disabled ? "mld-number-input--disabled" : ""
|
|
63
64
|
])
|
|
64
65
|
}, [
|
|
65
|
-
createElementVNode("button", {
|
|
66
|
-
type: "button",
|
|
67
|
-
"aria-label": "Decrease value",
|
|
68
|
-
disabled: __props.disabled || !canDecrement.value,
|
|
69
|
-
class: normalizeClass([
|
|
70
|
-
"mld-number-input__button",
|
|
71
|
-
"mld-number-input__button--decrement",
|
|
72
|
-
`mld-number-input__button--${__props.size}`
|
|
73
|
-
]),
|
|
74
|
-
onClick: decrement
|
|
75
|
-
}, [..._cache[0] || (_cache[0] = [
|
|
76
|
-
createElementVNode("svg", {
|
|
77
|
-
class: "mld-number-input__button-icon",
|
|
78
|
-
fill: "none",
|
|
79
|
-
stroke: "currentColor",
|
|
80
|
-
"stroke-width": "2",
|
|
81
|
-
"stroke-linecap": "round",
|
|
82
|
-
"stroke-linejoin": "round",
|
|
83
|
-
viewBox: "0 0 24 24"
|
|
84
|
-
}, [
|
|
85
|
-
createElementVNode("path", { d: "M5 12h14" })
|
|
86
|
-
], -1)
|
|
87
|
-
])], 10, _hoisted_1),
|
|
88
66
|
createElementVNode("input", {
|
|
89
67
|
type: "number",
|
|
90
68
|
value: __props.modelValue,
|
|
@@ -99,31 +77,56 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
99
77
|
__props.disabled ? "mld-number-input__input--disabled" : ""
|
|
100
78
|
]),
|
|
101
79
|
onInput: handleInput
|
|
102
|
-
}, null, 42,
|
|
103
|
-
createElementVNode("
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
80
|
+
}, null, 42, _hoisted_1),
|
|
81
|
+
createElementVNode("div", _hoisted_2, [
|
|
82
|
+
createElementVNode("button", {
|
|
83
|
+
type: "button",
|
|
84
|
+
"aria-label": "Decrease value",
|
|
85
|
+
disabled: __props.disabled || !canDecrement.value,
|
|
86
|
+
class: normalizeClass([
|
|
87
|
+
"mld-number-input__button",
|
|
88
|
+
"mld-number-input__button--decrement",
|
|
89
|
+
`mld-number-input__button--${__props.size}`
|
|
90
|
+
]),
|
|
91
|
+
onClick: decrement
|
|
92
|
+
}, [..._cache[0] || (_cache[0] = [
|
|
93
|
+
createElementVNode("svg", {
|
|
94
|
+
class: "mld-number-input__button-icon",
|
|
95
|
+
fill: "none",
|
|
96
|
+
stroke: "currentColor",
|
|
97
|
+
"stroke-width": "2",
|
|
98
|
+
"stroke-linecap": "round",
|
|
99
|
+
"stroke-linejoin": "round",
|
|
100
|
+
viewBox: "0 0 24 24"
|
|
101
|
+
}, [
|
|
102
|
+
createElementVNode("path", { d: "M5 12h14" })
|
|
103
|
+
], -1)
|
|
104
|
+
])], 10, _hoisted_3),
|
|
105
|
+
createElementVNode("button", {
|
|
106
|
+
type: "button",
|
|
107
|
+
"aria-label": "Increase value",
|
|
108
|
+
disabled: __props.disabled || !canIncrement.value,
|
|
109
|
+
class: normalizeClass([
|
|
110
|
+
"mld-number-input__button",
|
|
111
|
+
"mld-number-input__button--increment",
|
|
112
|
+
`mld-number-input__button--${__props.size}`
|
|
113
|
+
]),
|
|
114
|
+
onClick: increment
|
|
115
|
+
}, [..._cache[1] || (_cache[1] = [
|
|
116
|
+
createElementVNode("svg", {
|
|
117
|
+
class: "mld-number-input__button-icon",
|
|
118
|
+
fill: "none",
|
|
119
|
+
stroke: "currentColor",
|
|
120
|
+
"stroke-width": "2",
|
|
121
|
+
"stroke-linecap": "round",
|
|
122
|
+
"stroke-linejoin": "round",
|
|
123
|
+
viewBox: "0 0 24 24"
|
|
124
|
+
}, [
|
|
125
|
+
createElementVNode("path", { d: "M5 12h14" }),
|
|
126
|
+
createElementVNode("path", { d: "M12 5v14" })
|
|
127
|
+
], -1)
|
|
128
|
+
])], 10, _hoisted_4)
|
|
129
|
+
])
|
|
127
130
|
], 2);
|
|
128
131
|
};
|
|
129
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberInput.vue.js","sources":["../../src/components/NumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n step: 1,\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | undefined]\n}>()\n\nconst canDecrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n if (value !== undefined && !isNaN(value)) {\n emit('update:modelValue', clamp(value))\n } else {\n emit('update:modelValue', undefined)\n }\n}\n\nfunction decrement() {\n if (props.disabled || !canDecrement.value) return\n const current = props.modelValue ?? (props.max ?? 0)\n emit('update:modelValue', clamp(current - props.step))\n}\n\nfunction increment() {\n if (props.disabled || !canIncrement.value) return\n const current = props.modelValue ?? (props.min ?? 0)\n emit('update:modelValue', clamp(current + props.step))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-number-input',\n `mld-number-input--${size}`,\n error ? 'mld-number-input--error' : '',\n disabled ? 'mld-number-input--disabled' : '',\n ]\"\n >\n <
|
|
1
|
+
{"version":3,"file":"NumberInput.vue.js","sources":["../../src/components/NumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n step: 1,\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | undefined]\n}>()\n\nconst canDecrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue === undefined) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? undefined : Number(target.value)\n if (value !== undefined && !isNaN(value)) {\n emit('update:modelValue', clamp(value))\n } else {\n emit('update:modelValue', undefined)\n }\n}\n\nfunction decrement() {\n if (props.disabled || !canDecrement.value) return\n const current = props.modelValue ?? (props.max ?? 0)\n emit('update:modelValue', clamp(current - props.step))\n}\n\nfunction increment() {\n if (props.disabled || !canIncrement.value) return\n const current = props.modelValue ?? (props.min ?? 0)\n emit('update:modelValue', clamp(current + props.step))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-number-input',\n `mld-number-input--${size}`,\n error ? 'mld-number-input--error' : '',\n disabled ? 'mld-number-input--disabled' : '',\n ]\"\n >\n <input\n type=\"number\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :class=\"[\n 'mld-number-input__input',\n `mld-number-input__input--${size}`,\n disabled ? 'mld-number-input__input--disabled' : '',\n ]\"\n @input=\"handleInput\"\n />\n\n <div class=\"mld-number-input__buttons\">\n <button\n type=\"button\"\n aria-label=\"Decrease value\"\n :disabled=\"disabled || !canDecrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--decrement',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"decrement\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n aria-label=\"Increase value\"\n :disabled=\"disabled || !canIncrement\"\n :class=\"[\n 'mld-number-input__button',\n 'mld-number-input__button--increment',\n `mld-number-input__button--${size}`,\n ]\"\n @click=\"increment\"\n >\n <svg class=\"mld-number-input__button-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/number-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode"],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ export declare function analyzeDelimiter(lines: string[]): {
|
|
|
8
8
|
consistency: number;
|
|
9
9
|
};
|
|
10
10
|
export declare function detectOutliers(lines: string[], delimiter: string, minFieldCount: number): OutlierInfo[];
|
|
11
|
+
export declare function classifyOutlierAction(sample: string, delimiter: string): OutlierAction;
|
|
11
12
|
export declare function extractColumns(samples: string[], delimiter: string, minFieldCount: number): ColumnInfo[];
|
|
12
13
|
export declare function parseCSVLine(line: string): string[];
|
|
13
14
|
export declare function parseCSV(text: string): ParsedCsvData;
|
|
@@ -66,6 +66,19 @@ function detectOutliers(lines, delimiter, minFieldCount) {
|
|
|
66
66
|
}
|
|
67
67
|
return outliers;
|
|
68
68
|
}
|
|
69
|
+
const QC_KEYWORDS = /* @__PURE__ */ new Set([
|
|
70
|
+
"eqc",
|
|
71
|
+
"iqc",
|
|
72
|
+
"qc",
|
|
73
|
+
"blank",
|
|
74
|
+
"std",
|
|
75
|
+
"standard",
|
|
76
|
+
"test"
|
|
77
|
+
]);
|
|
78
|
+
function classifyOutlierAction(sample, delimiter) {
|
|
79
|
+
const segments = sample.split(delimiter);
|
|
80
|
+
return segments.some((seg) => QC_KEYWORDS.has(seg.toLowerCase())) ? "qc" : "include";
|
|
81
|
+
}
|
|
69
82
|
function extractColumns(samples, delimiter, minFieldCount) {
|
|
70
83
|
if (samples.length === 0) return [];
|
|
71
84
|
const suffixCount = minFieldCount - 1;
|
|
@@ -286,14 +299,20 @@ function useAutoGroup() {
|
|
|
286
299
|
const analysis = analyzeDelimiter(lines);
|
|
287
300
|
delimiter.value = analysis.delimiter;
|
|
288
301
|
dominantFieldCount.value = analysis.dominantFieldCount;
|
|
289
|
-
|
|
290
|
-
|
|
302
|
+
outliers.value = detectOutliers(lines, analysis.delimiter, analysis.dominantFieldCount);
|
|
303
|
+
for (const outlier of outliers.value) {
|
|
304
|
+
outlier.action = classifyOutlierAction(outlier.sample, analysis.delimiter);
|
|
305
|
+
}
|
|
291
306
|
const conforming = lines.filter(
|
|
292
307
|
(_, i) => !outliers.value.some((o) => o.index === i)
|
|
293
308
|
);
|
|
294
|
-
|
|
309
|
+
const conformingFieldCounts = conforming.map((s) => s.split(analysis.delimiter).length);
|
|
310
|
+
minFieldCount.value = conformingFieldCounts.length > 0 ? Math.min(...conformingFieldCounts) : analysis.dominantFieldCount;
|
|
311
|
+
fields.value = extractColumns(conforming, analysis.delimiter, minFieldCount.value);
|
|
295
312
|
fieldNames.value = {};
|
|
296
|
-
enabledFields.value = new Set(
|
|
313
|
+
enabledFields.value = new Set(
|
|
314
|
+
fields.value.filter((f) => f.cardinality > 1).map((f) => f.index)
|
|
315
|
+
);
|
|
297
316
|
}
|
|
298
317
|
function parseCsvInput() {
|
|
299
318
|
if (!csvData.value) return;
|
|
@@ -387,6 +406,7 @@ function useAutoGroup() {
|
|
|
387
406
|
export {
|
|
388
407
|
DEFAULT_COLORS,
|
|
389
408
|
analyzeDelimiter,
|
|
409
|
+
classifyOutlierAction,
|
|
390
410
|
computeGroups,
|
|
391
411
|
detectOutliers,
|
|
392
412
|
extractColumns,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAutoGroup.js","sources":["../../src/composables/useAutoGroup.ts"],"sourcesContent":["import { ref, computed } from 'vue'\nimport type {\n InputMode,\n OutlierAction,\n OutlierInfo,\n ColumnInfo,\n MetadataRow,\n AutoGroupResult,\n ParsedCsvData,\n} from '../types/auto-group'\nimport type { SampleGroup } from '../types/components'\n\nexport const DEFAULT_COLORS = [\n '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6',\n '#EC4899', '#06B6D4', '#84CC16', '#F97316', '#6366F1',\n]\n\nconst DELIMITER_CANDIDATES = ['_', '-', '.'] as const\n\n// --- Pure functions (exported for testing) ---\n\nexport function analyzeDelimiter(lines: string[]): {\n delimiter: string\n dominantFieldCount: number\n minFieldCount: number\n consistency: number\n} {\n if (lines.length === 0) {\n return { delimiter: '_', dominantFieldCount: 1, minFieldCount: 1, consistency: 0 }\n }\n\n let bestDelimiter = '_'\n let bestConsistency = -1\n let bestFieldCount = 1\n\n for (const candidate of DELIMITER_CANDIDATES) {\n const fieldCounts = lines.map(line => line.split(candidate).length)\n const countFrequency = new Map<number, number>()\n\n for (const count of fieldCounts) {\n countFrequency.set(count, (countFrequency.get(count) ?? 0) + 1)\n }\n\n // Find mode (most frequent field count)\n let modeCount = 1\n let modeFrequency = 0\n for (const [count, freq] of countFrequency) {\n if (freq > modeFrequency || (freq === modeFrequency && count > modeCount)) {\n modeCount = count\n modeFrequency = freq\n }\n }\n\n const rawConsistency = modeFrequency / lines.length\n // A delimiter that produces field count 1 didn't actually split anything\n const consistency = modeCount > 1 ? rawConsistency : 0\n\n if (\n consistency > bestConsistency ||\n (consistency === bestConsistency &&\n DELIMITER_CANDIDATES.indexOf(candidate) < DELIMITER_CANDIDATES.indexOf(bestDelimiter as typeof candidate))\n ) {\n bestDelimiter = candidate\n bestConsistency = consistency\n bestFieldCount = modeCount\n }\n }\n\n const bestFieldCounts = lines.map(line => line.split(bestDelimiter).length)\n const multiFieldCounts = bestFieldCounts.filter(c => c >= 2)\n const minFieldCount = multiFieldCounts.length > 0 ? Math.min(...multiFieldCounts) : 1\n\n return {\n delimiter: bestDelimiter,\n dominantFieldCount: bestFieldCount,\n minFieldCount,\n consistency: bestConsistency,\n }\n}\n\nexport function detectOutliers(\n lines: string[],\n delimiter: string,\n minFieldCount: number,\n): OutlierInfo[] {\n const outliers: OutlierInfo[] = []\n\n for (let i = 0; i < lines.length; i++) {\n const fieldCount = lines[i].split(delimiter).length\n if (fieldCount < minFieldCount) {\n outliers.push({\n sample: lines[i],\n index: i,\n fieldCount,\n action: 'include',\n })\n }\n }\n\n return outliers\n}\n\nexport function extractColumns(\n samples: string[],\n delimiter: string,\n minFieldCount: number,\n): ColumnInfo[] {\n if (samples.length === 0) return []\n\n const suffixCount = minFieldCount - 1\n const rows = samples.map(s => {\n const parts = s.split(delimiter)\n const splitAt = parts.length - suffixCount\n return [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n })\n\n const columnCount = minFieldCount\n const columns: ColumnInfo[] = []\n for (let col = 0; col < columnCount; col++) {\n const values = rows.map(row => row[col])\n const unique = [...new Set(values)]\n columns.push({\n index: col,\n name: col === 0 ? 'Condition' : `Field ${col + 1}`,\n uniqueValues: unique,\n cardinality: unique.length,\n type: col === 0 ? 'prefix' : 'suffix',\n })\n }\n\n return columns\n}\n\nexport function parseCSVLine(line: string): string[] {\n const result: string[] = []\n let current = ''\n let inQuotes = false\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i]\n if (char === '\"') {\n inQuotes = !inQuotes\n } else if (char === ',' && !inQuotes) {\n result.push(current.trim())\n current = ''\n } else {\n current += char\n }\n }\n result.push(current.trim())\n\n return result\n}\n\nexport function parseCSV(text: string): ParsedCsvData {\n const lines = text.trim().split('\\n')\n if (lines.length < 2) {\n throw new Error('CSV must have at least a header and one data row')\n }\n\n const headers = parseCSVLine(lines[0])\n const rows: Record<string, string>[] = []\n\n for (let i = 1; i < lines.length; i++) {\n const values = parseCSVLine(lines[i])\n if (values.length !== headers.length) continue\n const row: Record<string, string> = {}\n headers.forEach((header, idx) => {\n row[header] = values[idx]\n })\n rows.push(row)\n }\n\n // Auto-detect sample column\n const sampleKeywords = ['sample', 'name', 'id', 'sample_name', 'samplename']\n const sampleColumn =\n headers.find(h => sampleKeywords.includes(h.toLowerCase())) ?? headers[0]\n\n return { columns: headers, rows, sampleColumn }\n}\n\nexport function computeGroups(\n allSamples: string[],\n columns: ColumnInfo[],\n enabledFields: Set<number>,\n outlierActions: Map<number, OutlierAction>,\n delimiter: string,\n minFieldCount: number,\n): { groups: SampleGroup[]; metadata: MetadataRow[]; excludedSamples: string[] } {\n const excludedSamples: string[] = []\n const qcSamples: string[] = []\n const conformingSamples: string[] = []\n\n for (let i = 0; i < allSamples.length; i++) {\n const action = outlierActions.get(i)\n if (action === 'exclude') {\n excludedSamples.push(allSamples[i])\n } else if (action === 'qc') {\n qcSamples.push(allSamples[i])\n } else {\n conformingSamples.push(allSamples[i])\n }\n }\n\n // Build group map\n const groupMap = new Map<string, string[]>()\n const metadata: MetadataRow[] = []\n const enabledIndices = [...enabledFields].sort((a, b) => a - b)\n\n const suffixCount = minFieldCount - 1\n\n for (const sample of conformingSamples) {\n const parts = sample.split(delimiter)\n const splitAt = Math.max(1, parts.length - suffixCount)\n const row = [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n\n // Build group key from enabled columns\n const keyParts: string[] = []\n for (const idx of enabledIndices) {\n if (idx < row.length && idx < columns.length) {\n keyParts.push(row[idx])\n }\n }\n const groupKey = keyParts.join(' / ')\n\n if (!groupMap.has(groupKey)) {\n groupMap.set(groupKey, [])\n }\n groupMap.get(groupKey)!.push(sample)\n\n // Build metadata row with ALL columns\n const fields: Record<string, string> = {}\n for (const col of columns) {\n if (col.index < row.length) {\n fields[col.name] = row[col.index]\n }\n }\n metadata.push({ sampleName: sample, fields, group: groupKey })\n }\n\n // Convert to SampleGroup[]\n const groups: SampleGroup[] = []\n let colorIdx = 0\n for (const [name, samples] of groupMap) {\n groups.push({\n name,\n color: DEFAULT_COLORS[colorIdx % DEFAULT_COLORS.length],\n samples,\n })\n colorIdx++\n }\n\n // QC group\n if (qcSamples.length > 0) {\n groups.push({\n name: 'QC',\n color: '#6B7280',\n samples: qcSamples,\n })\n for (const sample of qcSamples) {\n metadata.push({ sampleName: sample, fields: {}, group: 'QC' })\n }\n }\n\n return { groups, metadata, excludedSamples }\n}\n\n// --- Reactive composable ---\n\nexport function useAutoGroup() {\n const inputMode = ref<InputMode>('paste')\n const rawText = ref('')\n const csvData = ref<ParsedCsvData | null>(null)\n const delimiter = ref('_')\n const dominantFieldCount = ref(1)\n const minFieldCount = ref(1)\n const outliers = ref<OutlierInfo[]>([])\n const fields = ref<ColumnInfo[]>([])\n const fieldNames = ref<Record<number, string>>({})\n const enabledFields = ref(new Set<number>())\n\n const samples = computed(() => {\n if (inputMode.value === 'csv' && csvData.value) {\n return csvData.value.rows.map(r => r[csvData.value!.sampleColumn])\n }\n return rawText.value\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0)\n })\n\n const hasOutliers = computed(() => outliers.value.length > 0)\n\n const conformingSamples = computed(() => {\n const outlierIndices = new Set(outliers.value.map(o => o.index))\n return samples.value.filter((_, i) => !outlierIndices.has(i))\n })\n\n const outlierActions = computed(() => {\n const map = new Map<number, OutlierAction>()\n for (const o of outliers.value) {\n map.set(o.index, o.action)\n }\n return map\n })\n\n const effectiveColumns = computed(() => {\n return fields.value.map(col => ({\n ...col,\n name: fieldNames.value[col.index] ?? col.name,\n }))\n })\n\n const groups = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n const result = computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n )\n return result.groups\n })\n\n const metadata = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).metadata\n })\n\n const excludedSamples = computed(() => {\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).excludedSamples\n })\n\n const result = computed<AutoGroupResult>(() => ({\n groups: groups.value,\n metadata: metadata.value,\n excludedSamples: excludedSamples.value,\n }))\n\n function parseInput() {\n if (inputMode.value === 'csv' && csvData.value) {\n parseCsvInput()\n } else {\n parsePasteInput()\n }\n }\n\n function parsePasteInput() {\n const lines = samples.value\n if (lines.length === 0) return\n\n const analysis = analyzeDelimiter(lines)\n delimiter.value = analysis.delimiter\n dominantFieldCount.value = analysis.dominantFieldCount\n minFieldCount.value = analysis.minFieldCount\n\n outliers.value = detectOutliers(lines, analysis.delimiter, analysis.minFieldCount)\n\n const conforming = lines.filter(\n (_, i) => !outliers.value.some(o => o.index === i)\n )\n fields.value = extractColumns(conforming, analysis.delimiter, analysis.minFieldCount)\n\n // Reset field names and enable all by default\n fieldNames.value = {}\n enabledFields.value = new Set(fields.value.map(f => f.index))\n }\n\n function parseCsvInput() {\n if (!csvData.value) return\n\n const csv = csvData.value\n const nonSampleCols = csv.columns.filter(c => c !== csv.sampleColumn)\n\n fields.value = nonSampleCols.map((col, i) => {\n const values = csv.rows.map(r => r[col])\n const unique = [...new Set(values)]\n return {\n index: i,\n name: col,\n uniqueValues: unique,\n cardinality: unique.length,\n }\n })\n\n // For CSV, no outliers\n outliers.value = []\n delimiter.value = ','\n dominantFieldCount.value = csv.columns.length\n\n fieldNames.value = {}\n for (const f of fields.value) {\n fieldNames.value[f.index] = f.name\n }\n enabledFields.value = new Set(fields.value.map(f => f.index))\n }\n\n function setOutlierAction(index: number, action: OutlierAction) {\n const outlier = outliers.value.find(o => o.index === index)\n if (outlier) {\n outlier.action = action\n // Trigger reactivity\n outliers.value = [...outliers.value]\n }\n }\n\n function setAllOutlierActions(action: OutlierAction) {\n for (const outlier of outliers.value) {\n outlier.action = action\n }\n outliers.value = [...outliers.value]\n }\n\n function toggleField(index: number) {\n const newSet = new Set(enabledFields.value)\n if (newSet.has(index)) {\n newSet.delete(index)\n } else {\n newSet.add(index)\n }\n enabledFields.value = newSet\n }\n\n function renameField(index: number, name: string) {\n fieldNames.value = { ...fieldNames.value, [index]: name }\n }\n\n function reset() {\n rawText.value = ''\n csvData.value = null\n delimiter.value = '_'\n dominantFieldCount.value = 1\n minFieldCount.value = 1\n outliers.value = []\n fields.value = []\n fieldNames.value = {}\n enabledFields.value = new Set()\n }\n\n return {\n // State\n inputMode,\n rawText,\n csvData,\n delimiter,\n dominantFieldCount,\n minFieldCount,\n outliers,\n fields,\n fieldNames,\n enabledFields,\n // Computed\n samples,\n hasOutliers,\n conformingSamples,\n groups,\n metadata,\n excludedSamples,\n result,\n effectiveColumns,\n // Actions\n parseInput,\n setOutlierAction,\n setAllOutlierActions,\n toggleField,\n renameField,\n reset,\n }\n}\n"],"names":["result"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAutoGroup.js","sources":["../../src/composables/useAutoGroup.ts"],"sourcesContent":["import { ref, computed } from 'vue'\nimport type {\n InputMode,\n OutlierAction,\n OutlierInfo,\n ColumnInfo,\n MetadataRow,\n AutoGroupResult,\n ParsedCsvData,\n} from '../types/auto-group'\nimport type { SampleGroup } from '../types/components'\n\nexport const DEFAULT_COLORS = [\n '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6',\n '#EC4899', '#06B6D4', '#84CC16', '#F97316', '#6366F1',\n]\n\nconst DELIMITER_CANDIDATES = ['_', '-', '.'] as const\n\n// --- Pure functions (exported for testing) ---\n\nexport function analyzeDelimiter(lines: string[]): {\n delimiter: string\n dominantFieldCount: number\n minFieldCount: number\n consistency: number\n} {\n if (lines.length === 0) {\n return { delimiter: '_', dominantFieldCount: 1, minFieldCount: 1, consistency: 0 }\n }\n\n let bestDelimiter = '_'\n let bestConsistency = -1\n let bestFieldCount = 1\n\n for (const candidate of DELIMITER_CANDIDATES) {\n const fieldCounts = lines.map(line => line.split(candidate).length)\n const countFrequency = new Map<number, number>()\n\n for (const count of fieldCounts) {\n countFrequency.set(count, (countFrequency.get(count) ?? 0) + 1)\n }\n\n // Find mode (most frequent field count)\n let modeCount = 1\n let modeFrequency = 0\n for (const [count, freq] of countFrequency) {\n if (freq > modeFrequency || (freq === modeFrequency && count > modeCount)) {\n modeCount = count\n modeFrequency = freq\n }\n }\n\n const rawConsistency = modeFrequency / lines.length\n // A delimiter that produces field count 1 didn't actually split anything\n const consistency = modeCount > 1 ? rawConsistency : 0\n\n if (\n consistency > bestConsistency ||\n (consistency === bestConsistency &&\n DELIMITER_CANDIDATES.indexOf(candidate) < DELIMITER_CANDIDATES.indexOf(bestDelimiter as typeof candidate))\n ) {\n bestDelimiter = candidate\n bestConsistency = consistency\n bestFieldCount = modeCount\n }\n }\n\n const bestFieldCounts = lines.map(line => line.split(bestDelimiter).length)\n const multiFieldCounts = bestFieldCounts.filter(c => c >= 2)\n const minFieldCount = multiFieldCounts.length > 0 ? Math.min(...multiFieldCounts) : 1\n\n return {\n delimiter: bestDelimiter,\n dominantFieldCount: bestFieldCount,\n minFieldCount,\n consistency: bestConsistency,\n }\n}\n\nexport function detectOutliers(\n lines: string[],\n delimiter: string,\n minFieldCount: number,\n): OutlierInfo[] {\n const outliers: OutlierInfo[] = []\n\n for (let i = 0; i < lines.length; i++) {\n const fieldCount = lines[i].split(delimiter).length\n if (fieldCount < minFieldCount) {\n outliers.push({\n sample: lines[i],\n index: i,\n fieldCount,\n action: 'include',\n })\n }\n }\n\n return outliers\n}\n\nconst QC_KEYWORDS = new Set([\n 'eqc', 'iqc', 'qc', 'blank', 'std', 'standard', 'test',\n])\n\nexport function classifyOutlierAction(\n sample: string,\n delimiter: string,\n): OutlierAction {\n const segments = sample.split(delimiter)\n return segments.some(seg => QC_KEYWORDS.has(seg.toLowerCase()))\n ? 'qc'\n : 'include'\n}\n\nexport function extractColumns(\n samples: string[],\n delimiter: string,\n minFieldCount: number,\n): ColumnInfo[] {\n if (samples.length === 0) return []\n\n const suffixCount = minFieldCount - 1\n const rows = samples.map(s => {\n const parts = s.split(delimiter)\n const splitAt = parts.length - suffixCount\n return [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n })\n\n const columnCount = minFieldCount\n const columns: ColumnInfo[] = []\n for (let col = 0; col < columnCount; col++) {\n const values = rows.map(row => row[col])\n const unique = [...new Set(values)]\n columns.push({\n index: col,\n name: col === 0 ? 'Condition' : `Field ${col + 1}`,\n uniqueValues: unique,\n cardinality: unique.length,\n type: col === 0 ? 'prefix' : 'suffix',\n })\n }\n\n return columns\n}\n\nexport function parseCSVLine(line: string): string[] {\n const result: string[] = []\n let current = ''\n let inQuotes = false\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i]\n if (char === '\"') {\n inQuotes = !inQuotes\n } else if (char === ',' && !inQuotes) {\n result.push(current.trim())\n current = ''\n } else {\n current += char\n }\n }\n result.push(current.trim())\n\n return result\n}\n\nexport function parseCSV(text: string): ParsedCsvData {\n const lines = text.trim().split('\\n')\n if (lines.length < 2) {\n throw new Error('CSV must have at least a header and one data row')\n }\n\n const headers = parseCSVLine(lines[0])\n const rows: Record<string, string>[] = []\n\n for (let i = 1; i < lines.length; i++) {\n const values = parseCSVLine(lines[i])\n if (values.length !== headers.length) continue\n const row: Record<string, string> = {}\n headers.forEach((header, idx) => {\n row[header] = values[idx]\n })\n rows.push(row)\n }\n\n // Auto-detect sample column\n const sampleKeywords = ['sample', 'name', 'id', 'sample_name', 'samplename']\n const sampleColumn =\n headers.find(h => sampleKeywords.includes(h.toLowerCase())) ?? headers[0]\n\n return { columns: headers, rows, sampleColumn }\n}\n\nexport function computeGroups(\n allSamples: string[],\n columns: ColumnInfo[],\n enabledFields: Set<number>,\n outlierActions: Map<number, OutlierAction>,\n delimiter: string,\n minFieldCount: number,\n): { groups: SampleGroup[]; metadata: MetadataRow[]; excludedSamples: string[] } {\n const excludedSamples: string[] = []\n const qcSamples: string[] = []\n const conformingSamples: string[] = []\n\n for (let i = 0; i < allSamples.length; i++) {\n const action = outlierActions.get(i)\n if (action === 'exclude') {\n excludedSamples.push(allSamples[i])\n } else if (action === 'qc') {\n qcSamples.push(allSamples[i])\n } else {\n conformingSamples.push(allSamples[i])\n }\n }\n\n // Build group map\n const groupMap = new Map<string, string[]>()\n const metadata: MetadataRow[] = []\n const enabledIndices = [...enabledFields].sort((a, b) => a - b)\n\n const suffixCount = minFieldCount - 1\n\n for (const sample of conformingSamples) {\n const parts = sample.split(delimiter)\n const splitAt = Math.max(1, parts.length - suffixCount)\n const row = [\n parts.slice(0, splitAt).join(delimiter),\n ...parts.slice(splitAt),\n ]\n\n // Build group key from enabled columns\n const keyParts: string[] = []\n for (const idx of enabledIndices) {\n if (idx < row.length && idx < columns.length) {\n keyParts.push(row[idx])\n }\n }\n const groupKey = keyParts.join(' / ')\n\n if (!groupMap.has(groupKey)) {\n groupMap.set(groupKey, [])\n }\n groupMap.get(groupKey)!.push(sample)\n\n // Build metadata row with ALL columns\n const fields: Record<string, string> = {}\n for (const col of columns) {\n if (col.index < row.length) {\n fields[col.name] = row[col.index]\n }\n }\n metadata.push({ sampleName: sample, fields, group: groupKey })\n }\n\n // Convert to SampleGroup[]\n const groups: SampleGroup[] = []\n let colorIdx = 0\n for (const [name, samples] of groupMap) {\n groups.push({\n name,\n color: DEFAULT_COLORS[colorIdx % DEFAULT_COLORS.length],\n samples,\n })\n colorIdx++\n }\n\n // QC group\n if (qcSamples.length > 0) {\n groups.push({\n name: 'QC',\n color: '#6B7280',\n samples: qcSamples,\n })\n for (const sample of qcSamples) {\n metadata.push({ sampleName: sample, fields: {}, group: 'QC' })\n }\n }\n\n return { groups, metadata, excludedSamples }\n}\n\n// --- Reactive composable ---\n\nexport function useAutoGroup() {\n const inputMode = ref<InputMode>('paste')\n const rawText = ref('')\n const csvData = ref<ParsedCsvData | null>(null)\n const delimiter = ref('_')\n const dominantFieldCount = ref(1)\n const minFieldCount = ref(1)\n const outliers = ref<OutlierInfo[]>([])\n const fields = ref<ColumnInfo[]>([])\n const fieldNames = ref<Record<number, string>>({})\n const enabledFields = ref(new Set<number>())\n\n const samples = computed(() => {\n if (inputMode.value === 'csv' && csvData.value) {\n return csvData.value.rows.map(r => r[csvData.value!.sampleColumn])\n }\n return rawText.value\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0)\n })\n\n const hasOutliers = computed(() => outliers.value.length > 0)\n\n const conformingSamples = computed(() => {\n const outlierIndices = new Set(outliers.value.map(o => o.index))\n return samples.value.filter((_, i) => !outlierIndices.has(i))\n })\n\n const outlierActions = computed(() => {\n const map = new Map<number, OutlierAction>()\n for (const o of outliers.value) {\n map.set(o.index, o.action)\n }\n return map\n })\n\n const effectiveColumns = computed(() => {\n return fields.value.map(col => ({\n ...col,\n name: fieldNames.value[col.index] ?? col.name,\n }))\n })\n\n const groups = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n const result = computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n )\n return result.groups\n })\n\n const metadata = computed(() => {\n if (effectiveColumns.value.length === 0 || enabledFields.value.size === 0) {\n return []\n }\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).metadata\n })\n\n const excludedSamples = computed(() => {\n return computeGroups(\n samples.value,\n effectiveColumns.value,\n enabledFields.value,\n outlierActions.value,\n delimiter.value,\n minFieldCount.value,\n ).excludedSamples\n })\n\n const result = computed<AutoGroupResult>(() => ({\n groups: groups.value,\n metadata: metadata.value,\n excludedSamples: excludedSamples.value,\n }))\n\n function parseInput() {\n if (inputMode.value === 'csv' && csvData.value) {\n parseCsvInput()\n } else {\n parsePasteInput()\n }\n }\n\n function parsePasteInput() {\n const lines = samples.value\n if (lines.length === 0) return\n\n const analysis = analyzeDelimiter(lines)\n delimiter.value = analysis.delimiter\n dominantFieldCount.value = analysis.dominantFieldCount\n\n // Use dominantFieldCount as outlier threshold so QC/test samples with\n // fewer fields than the majority are correctly flagged\n outliers.value = detectOutliers(lines, analysis.delimiter, analysis.dominantFieldCount)\n\n // Apply smart default actions: auto-classify QC/test samples\n for (const outlier of outliers.value) {\n outlier.action = classifyOutlierAction(outlier.sample, analysis.delimiter)\n }\n\n const conforming = lines.filter(\n (_, i) => !outliers.value.some(o => o.index === i)\n )\n\n // Recompute minFieldCount from conforming samples only\n const conformingFieldCounts = conforming.map(s => s.split(analysis.delimiter).length)\n minFieldCount.value = conformingFieldCounts.length > 0\n ? Math.min(...conformingFieldCounts)\n : analysis.dominantFieldCount\n\n fields.value = extractColumns(conforming, analysis.delimiter, minFieldCount.value)\n\n // Reset field names; auto-disable constant columns (cardinality 1)\n fieldNames.value = {}\n enabledFields.value = new Set(\n fields.value.filter(f => f.cardinality > 1).map(f => f.index)\n )\n }\n\n function parseCsvInput() {\n if (!csvData.value) return\n\n const csv = csvData.value\n const nonSampleCols = csv.columns.filter(c => c !== csv.sampleColumn)\n\n fields.value = nonSampleCols.map((col, i) => {\n const values = csv.rows.map(r => r[col])\n const unique = [...new Set(values)]\n return {\n index: i,\n name: col,\n uniqueValues: unique,\n cardinality: unique.length,\n }\n })\n\n // For CSV, no outliers\n outliers.value = []\n delimiter.value = ','\n dominantFieldCount.value = csv.columns.length\n\n fieldNames.value = {}\n for (const f of fields.value) {\n fieldNames.value[f.index] = f.name\n }\n enabledFields.value = new Set(fields.value.map(f => f.index))\n }\n\n function setOutlierAction(index: number, action: OutlierAction) {\n const outlier = outliers.value.find(o => o.index === index)\n if (outlier) {\n outlier.action = action\n // Trigger reactivity\n outliers.value = [...outliers.value]\n }\n }\n\n function setAllOutlierActions(action: OutlierAction) {\n for (const outlier of outliers.value) {\n outlier.action = action\n }\n outliers.value = [...outliers.value]\n }\n\n function toggleField(index: number) {\n const newSet = new Set(enabledFields.value)\n if (newSet.has(index)) {\n newSet.delete(index)\n } else {\n newSet.add(index)\n }\n enabledFields.value = newSet\n }\n\n function renameField(index: number, name: string) {\n fieldNames.value = { ...fieldNames.value, [index]: name }\n }\n\n function reset() {\n rawText.value = ''\n csvData.value = null\n delimiter.value = '_'\n dominantFieldCount.value = 1\n minFieldCount.value = 1\n outliers.value = []\n fields.value = []\n fieldNames.value = {}\n enabledFields.value = new Set()\n }\n\n return {\n // State\n inputMode,\n rawText,\n csvData,\n delimiter,\n dominantFieldCount,\n minFieldCount,\n outliers,\n fields,\n fieldNames,\n enabledFields,\n // Computed\n samples,\n hasOutliers,\n conformingSamples,\n groups,\n metadata,\n excludedSamples,\n result,\n effectiveColumns,\n // Actions\n parseInput,\n setOutlierAction,\n setAllOutlierActions,\n toggleField,\n renameField,\n reset,\n }\n}\n"],"names":["result"],"mappings":"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"}
|