@morscherlab/mint-sdk 1.3.0-beta.1 → 1.3.0-beta.10
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/README.md +23 -140
- package/dist/{ResourcePickerListInternal-c3IT8PIu.js → ResourcePickerListInternal-CFAw4vTZ.js} +26 -19
- package/dist/ResourcePickerListInternal-CFAw4vTZ.js.map +1 -0
- package/dist/components/FilePicker.vue.d.ts +4 -1
- package/dist/components/SettingsModal.schema.d.ts +1 -1
- package/dist/components/SettingsModal.vue.d.ts +2 -0
- package/dist/components/WellPlate.colors.d.ts +1 -5
- package/dist/components/WellPlate.rendering.d.ts +5 -2
- package/dist/components/index.d.ts +32 -17
- package/dist/components/index.js +3 -3
- package/dist/components/internal/FilePickerListInternal.vue.d.ts +2 -0
- package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +9 -3
- package/dist/{components-CvY105kJ.js → components-GDDvof8D.js} +414 -368
- package/dist/components-GDDvof8D.js.map +1 -0
- package/dist/composables/createFilePickerAdapter.d.ts +5 -1
- package/dist/composables/filePicker/usePickerNavigation.d.ts +3 -2
- package/dist/composables/index.d.ts +0 -3
- package/dist/composables/index.js +4 -4
- package/dist/composables/useAutoGroup.d.ts +1 -1
- package/dist/composables/useBioTemplateComponents.d.ts +15 -3
- package/dist/composables/useBioTemplateControls.d.ts +5 -1
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +2 -0
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +5 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +5 -1
- package/dist/composables/useExperimentSave.d.ts +5 -1
- package/dist/composables/useFilePicker.d.ts +4 -1
- package/dist/composables/useGroupAssignment.d.ts +5 -1
- package/dist/composables/usePlatformContext.d.ts +3 -0
- package/dist/composables/useTemplateCollection.d.ts +5 -1
- package/dist/composables-D6uqp0UF.js +591 -0
- package/dist/composables-D6uqp0UF.js.map +1 -0
- package/dist/index.js +24 -24
- package/dist/index.js.map +1 -1
- package/dist/install.js +1 -1
- package/dist/lcms.d.ts +11 -11
- package/dist/styles.css +170 -200
- package/dist/templates/index.js +1 -1
- package/dist/{templates-Bj9hZ7iV.js → templates-BU1pet28.js} +31 -44
- package/dist/templates-BU1pet28.js.map +1 -0
- package/dist/types/auth.d.ts +0 -27
- package/dist/types/components.d.ts +8 -0
- package/dist/types/fileBrowserTypes.d.ts +13 -0
- package/dist/types/filePicker.d.ts +8 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/platform.d.ts +2 -0
- package/dist/utils/rack.d.ts +16 -16
- package/dist/{validation-B4Y8_eEe.js → validation-Dmp9Q5W0.js} +50 -12
- package/dist/validation-Dmp9Q5W0.js.map +1 -0
- package/package.json +1 -6
- package/src/__tests__/components/ActionMenu.test.ts +10 -0
- package/src/__tests__/components/AppTopBar.test.ts +24 -8
- package/src/__tests__/components/EmptyState.test.ts +15 -0
- package/src/__tests__/components/ExperimentSelectorModal.test.ts +12 -5
- package/src/__tests__/components/FilePicker.test.ts +65 -1
- package/src/__tests__/components/FileUploader.test.ts +6 -9
- package/src/__tests__/components/ResourcePickerListInternal.test.ts +23 -4
- package/src/__tests__/components/SettingsModal.test.ts +22 -0
- package/src/__tests__/components/WellPlate.decoration.test.ts +4 -28
- package/src/__tests__/components/WellPlate.rendering.test.ts +20 -33
- package/src/__tests__/components/WellPlate.sampleTypeIndicator.test.ts +7 -7
- package/src/__tests__/composables/createFilePickerAdapter.test.ts +92 -1
- package/src/__tests__/composables/usePickerNavigation.test.ts +61 -1
- package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +18 -6
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +2 -2
- package/src/__tests__/styles/controlRefreshRules.test.ts +21 -8
- package/src/__tests__/styles/menusShellRefresh.test.ts +20 -5
- package/src/__tests__/templates/templatesDeprecation.test.ts +58 -11
- package/src/components/ActionMenu.vue +10 -2
- package/src/components/AppTopBar.vue +6 -4
- package/src/components/EmptyState.vue +3 -1
- package/src/components/ExperimentSelectorModal.vue +4 -1
- package/src/components/FilePicker.vue +42 -0
- package/src/components/FileUploader.vue +30 -46
- package/src/components/SettingsModal.schema.ts +4 -2
- package/src/components/SettingsModal.vue +4 -2
- package/src/components/WellPlate.colors.ts +6 -10
- package/src/components/WellPlate.legend.ts +4 -4
- package/src/components/WellPlate.rendering.ts +9 -8
- package/src/components/WellPlate.vue +5 -22
- package/src/components/index.ts +32 -17
- package/src/components/internal/FilePickerListInternal.vue +11 -2
- package/src/components/internal/ResourcePickerListInternal.vue +21 -27
- package/src/composables/autoGroup/index.ts +1 -1
- package/src/composables/createFilePickerAdapter.ts +82 -22
- package/src/composables/filePicker/usePickerNavigation.ts +29 -2
- package/src/composables/index.ts +0 -7
- package/src/composables/useAutoGroup.ts +1 -1
- package/src/composables/useBioTemplateComponents.ts +15 -3
- package/src/composables/useBioTemplateControls.ts +5 -1
- package/src/composables/useBioTemplatePackWorkspace.ts +2 -0
- package/src/composables/useBioTemplatePresetWorkspace.ts +5 -1
- package/src/composables/useBioTemplateWorkspace.ts +5 -1
- package/src/composables/useExperimentSave.ts +5 -1
- package/src/composables/useFilePicker.ts +8 -1
- package/src/composables/useGroupAssignment.ts +5 -1
- package/src/composables/usePlatformFilePickerAdapter.ts +26 -4
- package/src/composables/useTemplateCollection.ts +5 -1
- package/src/index.ts +1 -1
- package/src/lcms.ts +11 -11
- package/src/styles/components/action-menu.css +10 -0
- package/src/styles/components/app-page-selector.css +3 -3
- package/src/styles/components/app-pill-nav.css +1 -1
- package/src/styles/components/app-top-bar.css +18 -38
- package/src/styles/components/empty-state.css +3 -2
- package/src/styles/components/experiment-selector-modal.css +3 -9
- package/src/styles/components/file-picker.css +23 -1
- package/src/styles/components/file-uploader.css +22 -55
- package/src/styles/components/resource-picker-list.css +70 -36
- package/src/styles/components/well-plate.css +18 -55
- package/src/types/auth.ts +0 -37
- package/src/types/components.ts +8 -0
- package/src/types/fileBrowserTypes.ts +15 -0
- package/src/types/filePicker.ts +8 -1
- package/src/types/index.ts +2 -5
- package/src/types/platform.ts +2 -0
- package/src/utils/rack.ts +16 -16
- package/dist/ResourcePickerListInternal-c3IT8PIu.js.map +0 -1
- package/dist/__tests__/composables/useAuth.test.d.ts +0 -1
- package/dist/__tests__/composables/usePasskey.test.d.ts +0 -1
- package/dist/components-CvY105kJ.js.map +0 -1
- package/dist/composables/useAuth.d.ts +0 -47
- package/dist/composables/usePasskey.d.ts +0 -10
- package/dist/composables/useWellPlateEditor.d.ts +0 -33
- package/dist/composables-DL4Vca7L.js +0 -1193
- package/dist/composables-DL4Vca7L.js.map +0 -1
- package/dist/plotly.min-D6Ge6qgX.js +0 -168582
- package/dist/plotly.min-D6Ge6qgX.js.map +0 -1
- package/dist/templates-Bj9hZ7iV.js.map +0 -1
- package/dist/validation-B4Y8_eEe.js.map +0 -1
- package/src/__tests__/composables/useAuth.test.ts +0 -290
- package/src/__tests__/composables/usePasskey.test.ts +0 -38
- package/src/composables/useAuth.ts +0 -470
- package/src/composables/usePasskey.ts +0 -216
- package/src/composables/useWellPlateEditor.ts +0 -415
|
@@ -90,6 +90,8 @@ interface Props {
|
|
|
90
90
|
/** Manual tab descriptors. Ignored when `schema` is set (groups become tabs). */
|
|
91
91
|
tabs?: SettingsTabInput[]
|
|
92
92
|
showAppearance?: boolean
|
|
93
|
+
/** List Appearance before the other tabs and open on it. */
|
|
94
|
+
appearanceFirst?: boolean
|
|
93
95
|
size?: 'md' | 'lg' | 'xl'
|
|
94
96
|
layout?: SettingsModalLayout
|
|
95
97
|
/** Declarative schema — fields auto-render via SDK form components. */
|
|
@@ -238,7 +240,7 @@ const allTabs = computed<SettingsTab[]>(() => {
|
|
|
238
240
|
const base: SettingsTab[] = settingsSchema.value
|
|
239
241
|
? visibleSchemaGroups.value.map(settingsGroupToTab)
|
|
240
242
|
: manualTabs.value
|
|
241
|
-
return buildSettingsTabs(base, props.showAppearance)
|
|
243
|
+
return buildSettingsTabs(base, props.showAppearance, props.appearanceFirst)
|
|
242
244
|
})
|
|
243
245
|
|
|
244
246
|
const activeTab = ref(allTabs.value[0]?.id || APPEARANCE_TAB_ID)
|
|
@@ -272,7 +274,7 @@ const modalTabs = computed<ModalTab[]>(() =>
|
|
|
272
274
|
description: tab.description,
|
|
273
275
|
meta: group ? (modified ? String(modified) : undefined) : tab.meta,
|
|
274
276
|
// Sections only split plugin groups from Appearance; without it there is nothing to split.
|
|
275
|
-
section: props.showAppearance ? (tab.id === APPEARANCE_TAB_ID ? 'Personal' : 'Plugin') : undefined,
|
|
277
|
+
section: props.showAppearance ? (tab.id === APPEARANCE_TAB_ID ? 'Personal' : tab.section ?? 'Plugin') : undefined,
|
|
276
278
|
}
|
|
277
279
|
}),
|
|
278
280
|
)
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import type { HeatmapConfig } from '../types'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Group color for the built-in sample types when `sampleColors` has no entry.
|
|
5
|
-
* Color encodes the group and shape encodes the type (3a): blank and QC render
|
|
6
|
-
* neutral markers and ignore it; iQC takes it like a sample.
|
|
7
|
-
*/
|
|
3
|
+
/** Color for the built-in sample types when `sampleColors` has no entry. */
|
|
8
4
|
export const DEFAULT_SAMPLE_TYPE_COLORS: Record<string, string> = {
|
|
9
|
-
sample: '
|
|
10
|
-
control: '
|
|
11
|
-
blank: '
|
|
12
|
-
qc: '
|
|
13
|
-
iqc: '
|
|
5
|
+
sample: '#10b981',
|
|
6
|
+
control: '#3b82f6',
|
|
7
|
+
blank: '#f97316',
|
|
8
|
+
qc: '#8b5cf6',
|
|
9
|
+
iqc: '#ec4899',
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
export const HEATMAP_COLORS: Record<string, string[]> = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { WellLegendItem } from '../types'
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_WELL_LEGEND_ITEMS: WellLegendItem[] = [
|
|
4
|
-
{ type: 'sample', label: 'Sample', color: '
|
|
5
|
-
{ type: 'blank', label: 'Blank', color: '
|
|
6
|
-
{ type: 'qc', label: 'QC', color: '
|
|
7
|
-
{ type: 'iqc', label: 'IQC', color: '
|
|
4
|
+
{ type: 'sample', label: 'Sample', color: '#10b981' },
|
|
5
|
+
{ type: 'blank', label: 'Blank', color: '#f97316' },
|
|
6
|
+
{ type: 'qc', label: 'QC', color: '#8b5cf6' },
|
|
7
|
+
{ type: 'iqc', label: 'IQC', color: '#ec4899' },
|
|
8
8
|
]
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { HeatmapConfig, Well, WellPlateSelectionMode, WellShape } from '../types'
|
|
2
|
-
import { sampleTypeMarker } from '../utils/sampleTypeMarker'
|
|
3
2
|
import {
|
|
4
3
|
DEFAULT_SAMPLE_TYPE_COLORS,
|
|
5
4
|
getHeatmapColor,
|
|
@@ -55,13 +54,15 @@ export function wellStyle(well: Well, state: WellStyleState): Record<string, str
|
|
|
55
54
|
?? state.sampleColors[well.sampleType] ?? DEFAULT_SAMPLE_TYPE_COLORS[well.sampleType])
|
|
56
55
|
if (!groupColor) return {} // empty / filled wells are styled by well-plate.css
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
return { backgroundColor: background, border }
|
|
57
|
+
return sampleTint(groupColor)
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
/**
|
|
63
|
-
export function
|
|
64
|
-
return
|
|
60
|
+
/** 15% fill and 40% outline of a sample color; color-mix accepts hex and `var()` tokens alike. */
|
|
61
|
+
export function sampleTint(color: string): { backgroundColor: string; border: string } {
|
|
62
|
+
return {
|
|
63
|
+
backgroundColor: `color-mix(in srgb, ${color} 15%, transparent)`,
|
|
64
|
+
border: `1px solid color-mix(in srgb, ${color} 40%, transparent)`,
|
|
65
|
+
}
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
let sampleTypeIndicatorWarned = false
|
|
@@ -89,10 +90,10 @@ export function wellBadge(
|
|
|
89
90
|
if (!showBadges || !well.metadata) return null
|
|
90
91
|
const count = well.metadata.injectionCount as number | undefined
|
|
91
92
|
if (count && count > 1) {
|
|
92
|
-
return { text: String(count), color: '
|
|
93
|
+
return { text: String(count), color: '#6366f1' }
|
|
93
94
|
}
|
|
94
95
|
if (well.metadata.customMethod) {
|
|
95
|
-
return { text: '', color: '
|
|
96
|
+
return { text: '+', color: '#ec4899' }
|
|
96
97
|
}
|
|
97
98
|
return null
|
|
98
99
|
}
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from './WellPlate.geometry'
|
|
26
26
|
import { useWellPlateInteraction } from './WellPlate.interaction'
|
|
27
27
|
import {
|
|
28
|
-
|
|
28
|
+
sampleTint,
|
|
29
29
|
warnSampleTypeIndicatorDeprecated,
|
|
30
30
|
wellBadge,
|
|
31
31
|
wellClasses,
|
|
@@ -34,7 +34,6 @@ import {
|
|
|
34
34
|
} from './WellPlate.rendering'
|
|
35
35
|
import { DEFAULT_WELL_LEGEND_ITEMS } from './WellPlate.legend'
|
|
36
36
|
import DataStateInternal from './internal/DataStateInternal.vue'
|
|
37
|
-
import { sampleTypeMarker } from '../utils/sampleTypeMarker'
|
|
38
37
|
|
|
39
38
|
interface Props {
|
|
40
39
|
modelValue?: string[]
|
|
@@ -476,27 +475,13 @@ const tableStyle = computed(() => ({
|
|
|
476
475
|
>
|
|
477
476
|
{{ well.id }}
|
|
478
477
|
</span>
|
|
479
|
-
<!-- Sample-type marker glyph (Q / i) -->
|
|
480
|
-
<span
|
|
481
|
-
v-else-if="sampleTypeGlyph(well)"
|
|
482
|
-
class="mint-well-plate__indicator"
|
|
483
|
-
>
|
|
484
|
-
{{ sampleTypeGlyph(well) }}
|
|
485
|
-
</span>
|
|
486
|
-
<!-- The glyph moves to a corner when a label or well ID takes the centre -->
|
|
487
|
-
<span
|
|
488
|
-
v-if="sampleTypeGlyph(well) && (getWellLabel(well) || props.showWellIds)"
|
|
489
|
-
class="mint-well-plate__corner-glyph"
|
|
490
|
-
>
|
|
491
|
-
{{ sampleTypeGlyph(well) }}
|
|
492
|
-
</span>
|
|
493
478
|
|
|
494
479
|
<!-- Badge (injection count or custom method) -->
|
|
495
480
|
<span
|
|
496
481
|
v-if="getWellBadge(well)"
|
|
497
|
-
|
|
482
|
+
class="mint-well-plate__badge"
|
|
498
483
|
:style="{ backgroundColor: getWellBadge(well)?.color }"
|
|
499
|
-
:title="getWellBadge(well)?.text ? `${getWellBadge(well)?.text}x injections`
|
|
484
|
+
:title="getWellBadge(well)?.text === '+' ? 'Custom method' : `${getWellBadge(well)?.text ?? ''}x injections`"
|
|
500
485
|
>
|
|
501
486
|
{{ getWellBadge(well)?.text }}
|
|
502
487
|
</span>
|
|
@@ -531,11 +516,9 @@ const tableStyle = computed(() => ({
|
|
|
531
516
|
class="mint-well-plate__sample-legend-item"
|
|
532
517
|
>
|
|
533
518
|
<span
|
|
534
|
-
v-for="marker in [sampleTypeMarker(item.type, item.color)]"
|
|
535
|
-
:key="item.type"
|
|
536
519
|
class="mint-well-plate__sample-legend-dot"
|
|
537
|
-
:style="
|
|
538
|
-
|
|
520
|
+
:style="sampleTint(item.color)"
|
|
521
|
+
/>
|
|
539
522
|
<span class="mint-well-plate__sample-legend-text">{{ item.label }}</span>
|
|
540
523
|
</div>
|
|
541
524
|
</div>
|
package/src/components/index.ts
CHANGED
|
@@ -48,7 +48,8 @@ export { default as GeneratedResultRenderer } from './GeneratedResultRenderer.vu
|
|
|
48
48
|
export { default as GeneratedPlotlyResult } from './GeneratedPlotlyResult.vue'
|
|
49
49
|
export { default as ComponentBindingRenderer } from './ComponentBindingRenderer.vue'
|
|
50
50
|
export { default as ControlWorkspaceView } from './ControlWorkspaceView.vue'
|
|
51
|
-
|
|
51
|
+
/** Complete ControlWorkspaceView page shell for WellPlate + DoseCalculator dose design. @deprecated Scheduled for removal in MINT 1.4; use `ControlWorkspaceView` instead, with `defineDoseDesignControlModel()`. */
|
|
52
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as DoseDesignWorkspaceView } from './DoseDesignWorkspaceView.vue'
|
|
52
53
|
export { default as AppContainer } from './AppContainer.vue'
|
|
53
54
|
export { default as PluginIcon } from './PluginIcon.vue'
|
|
54
55
|
export { default as LayoutResizeHandle } from './LayoutResizeHandle.vue'
|
|
@@ -69,30 +70,43 @@ export { default as AdductText } from './AdductText.vue'
|
|
|
69
70
|
export { default as SettingsModal } from './SettingsModal.vue'
|
|
70
71
|
|
|
71
72
|
// Biological experiment components
|
|
72
|
-
|
|
73
|
-
export { default as
|
|
74
|
-
|
|
75
|
-
export { default as
|
|
73
|
+
/** Render biology template envelopes with matching SDK components such as WellPlate, DoseCalculator, and DataFrame. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
74
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as BioTemplateRenderer } from './BioTemplateRenderer.vue'
|
|
75
|
+
/** Current-experiment template workspace with load/save/reset controls and WellPlate/DataFrame/DoseCalculator previews. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
76
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as BioTemplateExperimentWorkspaceView } from './BioTemplateExperimentWorkspaceView.vue'
|
|
77
|
+
/** Complete current-experiment workspace for a curated biology template pack with save/load controls and component-prop slots. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
78
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as BioTemplatePackWorkspaceView } from './BioTemplatePackWorkspaceView.vue'
|
|
79
|
+
/** Complete editable biology template preset workspace for WellPlate/DoseCalculator, LC-MS, qPCR, generated controls, preview components, and current-experiment save wiring. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
80
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as BioTemplatePresetWorkspaceView } from './BioTemplatePresetWorkspaceView.vue'
|
|
76
81
|
export { default as WellPlate } from './WellPlate.vue'
|
|
77
82
|
export { default as PlateEditor } from './PlateEditor.vue'
|
|
78
|
-
|
|
79
|
-
export { default as
|
|
83
|
+
/** Clickable color-swatch legend for plate sample types, supporting selection, count display, add/remove, and custom color assignment. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
84
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as SampleLegend } from './SampleLegend.vue'
|
|
85
|
+
/** Horizontal or vertical timeline of ordered protocol steps with status indicators, drag-to-reorder, and step-type iconography. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
86
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as ExperimentTimeline } from './ExperimentTimeline.vue'
|
|
80
87
|
|
|
81
88
|
// Sample management components
|
|
89
|
+
/** Interactive sample list with drag-and-drop group assignment, color labeling, and smart-group support. */
|
|
82
90
|
export { default as SampleSelector } from './SampleSelector.vue'
|
|
83
|
-
/** Smart Group modal wired to the `useAutoGroup` engine. @deprecated Scheduled for removal in MINT 1.4;
|
|
84
|
-
export { default as AutoGroupModal } from './AutoGroupModal.vue'
|
|
85
|
-
|
|
86
|
-
export { default as
|
|
87
|
-
|
|
88
|
-
export { default as
|
|
91
|
+
/** Smart Group modal wired to the `useAutoGroup` engine. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
92
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as AutoGroupModal } from './AutoGroupModal.vue'
|
|
93
|
+
/** Drag-and-drop two-zone assigner for splitting sample groups into control and treatment arms. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
94
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as GroupAssigner } from './GroupAssigner.vue'
|
|
95
|
+
/** Smart Group modal with the auto Field Recipe and the manual cohort builder. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
96
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as SmartGroupModal } from './SmartGroupModal.vue'
|
|
97
|
+
/** Smart Group auto mode: groups samples by a field recipe. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
98
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as SmartGroupFieldRecipe } from './SmartGroupFieldRecipe.vue'
|
|
99
|
+
/** Smart Group manual mode: builds cohorts from selected samples. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
100
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as SmartGroupManual } from './SmartGroupManual.vue'
|
|
89
101
|
|
|
90
102
|
// Lab/Experiment components
|
|
91
103
|
export { default as MoleculeInput } from './MoleculeInput.vue'
|
|
92
104
|
export { default as ConcentrationInput } from './ConcentrationInput.vue'
|
|
93
105
|
export { default as DoseCalculator } from './DoseCalculator.vue'
|
|
94
|
-
|
|
95
|
-
export { default as
|
|
106
|
+
/** Editor for a single reagent definition with axis orientation, dilution-series generator, and position-count validation for 96/384-well plates. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
107
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as ReagentEditor, type ReagentDefinition } from './ReagentEditor.vue'
|
|
108
|
+
/** Sortable, searchable table of experiment reagents showing lot number, expiry, storage conditions, and stock level with low-stock warnings. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
109
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as ReagentList } from './ReagentList.vue'
|
|
96
110
|
export { default as SampleHierarchyTree } from './SampleHierarchyTree.vue'
|
|
97
111
|
export { default as ProtocolStepEditor } from './ProtocolStepEditor.vue'
|
|
98
112
|
|
|
@@ -100,7 +114,7 @@ export { default as ProtocolStepEditor } from './ProtocolStepEditor.vue'
|
|
|
100
114
|
export { default as PlotlyChart } from './PlotlyChart.vue'
|
|
101
115
|
export { default as ScientificNumber } from './ScientificNumber.vue'
|
|
102
116
|
export { default as ChemicalFormula } from './ChemicalFormula.vue'
|
|
103
|
-
/** Displays LC/MS or GC/MS acquisition sequence progress with ETA and remaining-time labels.
|
|
117
|
+
/** Displays LC/MS or GC/MS acquisition sequence progress with ETA and remaining-time labels. */
|
|
104
118
|
export { default as SequenceProgressBar } from './SequenceProgressBar.vue'
|
|
105
119
|
/** Status badge for instrument states: a tone dot (or dash / hollow shape for offline, never-reported and inactive) plus a label. */
|
|
106
120
|
export { default as InstrumentStateBadge } from './InstrumentStateBadge.vue'
|
|
@@ -138,6 +152,7 @@ export { default as ArtifactSelectorModal } from './ArtifactSelectorModal.vue'
|
|
|
138
152
|
export { default as GeneratedPathInput } from './GeneratedPathInput.vue'
|
|
139
153
|
export { default as ArtifactSaveDialog } from './ArtifactSaveDialog.vue'
|
|
140
154
|
export { default as ExperimentPopover } from './ExperimentPopover.vue'
|
|
141
|
-
|
|
155
|
+
/** Control panel for running a curve-fit job with progress bar, results table, and error display. @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
|
|
156
|
+
export { /** @deprecated Removed in MINT 1.4. */ default as FitPanel } from './FitPanel.vue'
|
|
142
157
|
|
|
143
158
|
export { default as FilePicker } from './FilePicker.vue'
|
|
@@ -22,6 +22,8 @@ const props = defineProps<{
|
|
|
22
22
|
loading: boolean
|
|
23
23
|
error: string
|
|
24
24
|
query: string
|
|
25
|
+
/** Search results stopped at a limit; the rows are the first matches only. */
|
|
26
|
+
truncated?: boolean
|
|
25
27
|
mode: PickerSelectionMode
|
|
26
28
|
selected: (node: PickerNode) => boolean
|
|
27
29
|
selectable: (node: PickerNode) => boolean
|
|
@@ -171,6 +173,9 @@ function parts(name: string) {
|
|
|
171
173
|
<div class="mint-picker__list-head" aria-hidden="true">
|
|
172
174
|
<span /><span>Name</span><span>Size</span><span>Modified</span>
|
|
173
175
|
</div>
|
|
176
|
+
<p v-if="truncated && !loading && !error && rows.length" class="mint-picker__truncated" role="status">
|
|
177
|
+
Showing the first {{ rows.length }} matches. Search a smaller folder or use a more specific search to see the rest.
|
|
178
|
+
</p>
|
|
174
179
|
<div v-if="loading" class="mint-picker__skeleton">
|
|
175
180
|
<Skeleton v-for="n in 6" :key="n" width="100%" height="1.25rem" />
|
|
176
181
|
</div>
|
|
@@ -180,10 +185,14 @@ function parts(name: string) {
|
|
|
180
185
|
</div>
|
|
181
186
|
<div v-else-if="!rows.length" class="mint-picker__empty" role="status">
|
|
182
187
|
<strong>{{
|
|
183
|
-
|
|
188
|
+
truncated
|
|
189
|
+
? 'Search stopped before finding a match'
|
|
190
|
+
: query ? 'No matching folders or files' : emptyTitle || 'No readable files'
|
|
184
191
|
}}</strong>
|
|
185
192
|
<span>{{
|
|
186
|
-
|
|
193
|
+
truncated
|
|
194
|
+
? 'Search a smaller folder or use a more specific search.'
|
|
195
|
+
: query ? 'Try a different search or include subfolders.' : 'Choose another location.'
|
|
187
196
|
}}</span>
|
|
188
197
|
</div>
|
|
189
198
|
<template v-for="row in rows" v-else :key="row.node.path">
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
<script setup lang="ts" generic="T">
|
|
2
2
|
/**
|
|
3
3
|
* Searchable, optionally grouped single-select list shared by the experiment and
|
|
4
|
-
* artifact selector modals
|
|
4
|
+
* artifact selector modals: two-line rows (title, then code + meta) with status and date
|
|
5
|
+
* in a right-hand column, a filled selected row, skeleton,
|
|
5
6
|
* error and empty states, and the combobox keyboard model (↑ ↓ Enter).
|
|
6
7
|
*/
|
|
7
8
|
import { computed, nextTick, reactive, ref, useId, watch } from 'vue'
|
|
8
9
|
import BaseInput from '../BaseInput.vue'
|
|
9
10
|
import Skeleton from '../Skeleton.vue'
|
|
10
11
|
import EmptyState from '../EmptyState.vue'
|
|
12
|
+
import type { PillVariant } from '../../types'
|
|
11
13
|
|
|
12
14
|
export interface ResourcePickerRow {
|
|
13
|
-
/** Identifier shown
|
|
15
|
+
/** Identifier shown as a gray pill at the start of the second line, e.g. an experiment code. */
|
|
14
16
|
code?: string
|
|
15
17
|
title: string
|
|
16
|
-
/** Second-line facts, joined with " · ". */
|
|
18
|
+
/** Second-line facts after the code, joined with " · ". */
|
|
17
19
|
meta?: string[]
|
|
18
|
-
/**
|
|
20
|
+
/** Right column, above the date, tinted by its variant. */
|
|
21
|
+
status?: { label: string; variant: PillVariant }
|
|
22
|
+
/** Right column, in mono. */
|
|
19
23
|
date?: string
|
|
20
24
|
}
|
|
21
25
|
|
|
@@ -206,36 +210,26 @@ watch(
|
|
|
206
210
|
@mouseenter="activeIndex = indexOf(item)"
|
|
207
211
|
>
|
|
208
212
|
<span class="mint-resource-picker__text">
|
|
209
|
-
<span class="mint-resource-picker__title"
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
<span class="mint-resource-picker__title">{{ describe(item).title }}</span>
|
|
214
|
+
<span v-if="describe(item).code || describe(item).meta?.length" class="mint-resource-picker__meta"
|
|
215
|
+
><span v-if="describe(item).code" class="mint-resource-picker__code mint-exp-code mint-exp-code--sm">{{
|
|
216
|
+
describe(item).code
|
|
217
|
+
}}</span
|
|
218
|
+
><span v-if="describe(item).meta?.length">{{ describe(item).meta!.join(' · ') }}</span></span
|
|
213
219
|
>
|
|
220
|
+
</span>
|
|
221
|
+
<span v-if="describe(item).status || describe(item).date" class="mint-resource-picker__aside">
|
|
214
222
|
<span
|
|
215
|
-
v-if="describe(item).
|
|
216
|
-
class="mint-resource-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
>{{ describe(item).meta?.length ? ' · ' : ''
|
|
220
|
-
}}<span class="mint-resource-picker__date">{{ describe(item).date }}</span></template
|
|
221
|
-
></span
|
|
223
|
+
v-if="describe(item).status"
|
|
224
|
+
class="mint-resource-picker__status"
|
|
225
|
+
:class="`mint-resource-picker__status--${describe(item).status!.variant}`"
|
|
226
|
+
>{{ describe(item).status!.label }}</span
|
|
222
227
|
>
|
|
228
|
+
<span v-if="describe(item).date" class="mint-resource-picker__date">{{ describe(item).date }}</span>
|
|
223
229
|
</span>
|
|
224
230
|
<span v-if="$slots.actions" class="mint-resource-picker__actions" @click.stop>
|
|
225
231
|
<slot name="actions" :item="item" />
|
|
226
232
|
</span>
|
|
227
|
-
<span class="mint-resource-picker__check-slot" aria-hidden="true">
|
|
228
|
-
<svg
|
|
229
|
-
v-if="itemKey(item) === selectedKey"
|
|
230
|
-
class="mint-resource-picker__check"
|
|
231
|
-
viewBox="0 0 16 16"
|
|
232
|
-
fill="none"
|
|
233
|
-
stroke="currentColor"
|
|
234
|
-
stroke-width="2"
|
|
235
|
-
>
|
|
236
|
-
<path d="M3.5 8.5l3 3 6-7" />
|
|
237
|
-
</svg>
|
|
238
|
-
</span>
|
|
239
233
|
</div>
|
|
240
234
|
</template>
|
|
241
235
|
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Internal engine for `useAutoGroup` / `AutoGroupModal`. Deprecated: scheduled
|
|
2
|
-
// for removal in MINT 1.4 together with `useAutoGroup`;
|
|
2
|
+
// for removal in MINT 1.4 together with `useAutoGroup`; no SDK replacement.
|
|
3
3
|
|
|
4
4
|
export { splitMulti, pickPrimaryDelimiter } from './tokenize'
|
|
5
5
|
export type { Delimiter } from './tokenize'
|
|
@@ -1,10 +1,26 @@
|
|
|
1
|
-
import type { PickerAdapter, PickerNode, PickerRequest } from '../types/filePicker'
|
|
2
|
-
import type {
|
|
1
|
+
import type { PickerAdapter, PickerNode, PickerRequest, PickerSearchResult } from '../types/filePicker'
|
|
2
|
+
import type {
|
|
3
|
+
FileBrowserLocation,
|
|
4
|
+
FileDirectoryListing,
|
|
5
|
+
FileDirectoryTree,
|
|
6
|
+
FileEntry,
|
|
7
|
+
FileSearchResult,
|
|
8
|
+
ServerMount,
|
|
9
|
+
} from '../types/fileBrowserTypes'
|
|
3
10
|
import { pickerFormat } from './filePicker/validation'
|
|
4
11
|
|
|
5
12
|
export interface FilePickerTransport {
|
|
6
13
|
listMounts(request?: PickerRequest): Promise<ServerMount[]>
|
|
7
14
|
browse(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryListing>
|
|
15
|
+
/** `GET …/filesystem/tree`: one request fills the cache for the subfolders too. */
|
|
16
|
+
tree?(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryTree>
|
|
17
|
+
/** `GET …/filesystem/search`: replaces the folder-by-folder search walk. */
|
|
18
|
+
search?(
|
|
19
|
+
location: FileBrowserLocation,
|
|
20
|
+
query: string,
|
|
21
|
+
recursive: boolean,
|
|
22
|
+
request?: PickerRequest,
|
|
23
|
+
): Promise<FileSearchResult>
|
|
8
24
|
}
|
|
9
25
|
export interface FilePickerAdapterOptions {
|
|
10
26
|
/** Show this directory's contents directly and prevent navigation outside it. */
|
|
@@ -82,31 +98,75 @@ export function createFilePickerAdapter(
|
|
|
82
98
|
unavailableReason: mount.available === false ? 'This mount is offline.' : undefined,
|
|
83
99
|
}
|
|
84
100
|
})
|
|
101
|
+
} else if (transport.tree) {
|
|
102
|
+
const tree = await transport.tree(location, request)
|
|
103
|
+
check(request, token)
|
|
104
|
+
for (const listing of tree.listings ?? []) {
|
|
105
|
+
const at = { mount_id: listing.mount_id, path: listing.path }
|
|
106
|
+
validateLocation(at)
|
|
107
|
+
if (at.mount_id !== location.mount_id) throw new Error('Invalid file picker directory entry.')
|
|
108
|
+
// A capped subfolder is read on its own when opened, and reports the cap then.
|
|
109
|
+
if (!listing.truncated) listings.set(encodePlatformPickerPath(at.mount_id, at.path), toNodes(at, listing))
|
|
110
|
+
}
|
|
111
|
+
const cached = listings.get(key)
|
|
112
|
+
if (!cached)
|
|
113
|
+
throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')
|
|
114
|
+
return cached
|
|
85
115
|
} else {
|
|
86
116
|
const listing = await transport.browse(location, request)
|
|
87
117
|
check(request, token)
|
|
88
118
|
if (listing.truncated)
|
|
89
119
|
throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')
|
|
90
|
-
|
|
91
|
-
nodes = (listing.entries ?? []).map((entry): PickerNode => {
|
|
92
|
-
if (!isRelativePath(entry.path) || !entry.name ||
|
|
93
|
-
entry.path !== `${location.path ? `${location.path}/` : ''}${entry.name}` ||
|
|
94
|
-
entry.name.includes('/')) throw new Error('Invalid file picker directory entry.')
|
|
95
|
-
const common = {
|
|
96
|
-
path: encodePlatformPickerPath(location.mount_id, entry.path),
|
|
97
|
-
parentPath: atRoot ? null : key,
|
|
98
|
-
parentName: `${mountLabels.get(location.mount_id) ?? location.mount_id}${location.path ? ` / ${location.path}` : ''}`,
|
|
99
|
-
name: entry.name, bytes: entry.size_bytes, modifiedAt: entry.modified_at,
|
|
100
|
-
}
|
|
101
|
-
return entry.kind === 'directory'
|
|
102
|
-
// Counts may include only matching formats, not nested directories.
|
|
103
|
-
? { ...common, kind: 'folder' }
|
|
104
|
-
: { ...common, kind: 'file', format: pickerFormat(entry.name) }
|
|
105
|
-
})
|
|
120
|
+
nodes = toNodes(location, listing)
|
|
106
121
|
}
|
|
107
122
|
listings.set(key, nodes)
|
|
108
123
|
return nodes
|
|
109
124
|
}
|
|
125
|
+
function toNodes(location: FileBrowserLocation, listing: FileDirectoryListing): PickerNode[] {
|
|
126
|
+
return (listing.entries ?? []).map((entry) => {
|
|
127
|
+
if (entry.path !== `${location.path ? `${location.path}/` : ''}${entry.name}`)
|
|
128
|
+
throw new Error('Invalid file picker directory entry.')
|
|
129
|
+
return toNode(location.mount_id, entry)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
/** One entry, parented on the folder its mount-relative path names. */
|
|
133
|
+
function toNode(mountId: string, entry: FileEntry): PickerNode {
|
|
134
|
+
if (!isRelativePath(entry.path) || !entry.name || entry.name.includes('/') ||
|
|
135
|
+
(entry.path !== entry.name && !entry.path.endsWith(`/${entry.name}`)))
|
|
136
|
+
throw new Error('Invalid file picker directory entry.')
|
|
137
|
+
const parent = entry.path.slice(0, Math.max(0, entry.path.length - entry.name.length - 1))
|
|
138
|
+
const atRoot = root && mountId === root.mount_id && parent === root.path
|
|
139
|
+
const common = {
|
|
140
|
+
path: encodePlatformPickerPath(mountId, entry.path),
|
|
141
|
+
parentPath: atRoot ? null : encodePlatformPickerPath(mountId, parent),
|
|
142
|
+
parentName: `${mountLabels.get(mountId) ?? mountId}${parent ? ` / ${parent}` : ''}`,
|
|
143
|
+
name: entry.name, bytes: entry.size_bytes, modifiedAt: entry.modified_at,
|
|
144
|
+
}
|
|
145
|
+
return entry.kind === 'directory'
|
|
146
|
+
// Counts may include only matching formats, not nested directories.
|
|
147
|
+
? { ...common, kind: 'folder' }
|
|
148
|
+
: { ...common, kind: 'file', format: pickerFormat(entry.name) }
|
|
149
|
+
}
|
|
150
|
+
async function serverSearch(
|
|
151
|
+
query: string,
|
|
152
|
+
scope: { path: string; recursive: boolean },
|
|
153
|
+
request: PickerRequest | undefined,
|
|
154
|
+
): Promise<PickerNode[] | PickerSearchResult> {
|
|
155
|
+
const token = begin(request)
|
|
156
|
+
const decoded = scope.path ? decodePlatformPickerPath(scope.path) : undefined
|
|
157
|
+
const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root!
|
|
158
|
+
validateLocation(location)
|
|
159
|
+
const result = await transport.search!(location, query, scope.recursive, request)
|
|
160
|
+
check(request, token)
|
|
161
|
+
const prefix = location.path ? `${location.path}/` : ''
|
|
162
|
+
const nodes = (result.entries ?? []).map((entry) => {
|
|
163
|
+
// "This folder" searches may return direct children only.
|
|
164
|
+
if (!entry.path.startsWith(prefix) || (!scope.recursive && entry.path.slice(prefix.length).includes('/')))
|
|
165
|
+
throw new Error('Invalid file picker directory entry.')
|
|
166
|
+
return toNode(location.mount_id, entry)
|
|
167
|
+
})
|
|
168
|
+
return result.truncated ? { nodes, truncated: true } : nodes
|
|
169
|
+
}
|
|
110
170
|
return {
|
|
111
171
|
mounts: !root,
|
|
112
172
|
mountOf(path) {
|
|
@@ -120,6 +180,8 @@ export function createFilePickerAdapter(
|
|
|
120
180
|
listRoot: async (request) => read('', request, begin(request)),
|
|
121
181
|
listChildren: async (path, request) => read(path, request, begin(request)),
|
|
122
182
|
async search(query, scope, request) {
|
|
183
|
+
// The mount list itself has no server to search, so it keeps the walk.
|
|
184
|
+
if (transport.search && (scope.path || root)) return serverSearch(query, scope, request)
|
|
123
185
|
const token = begin(request)
|
|
124
186
|
const matches: PickerNode[] = []
|
|
125
187
|
const queue = [scope.path]
|
|
@@ -131,8 +193,7 @@ export function createFilePickerAdapter(
|
|
|
131
193
|
const path = queue.shift()!
|
|
132
194
|
if (visited.has(path)) continue
|
|
133
195
|
visited.add(path)
|
|
134
|
-
if (visited.size > 500)
|
|
135
|
-
throw new Error('Search reached its folder limit. Search a smaller location or turn off Subfolders.')
|
|
196
|
+
if (visited.size > 500) return { nodes: matches, truncated: true }
|
|
136
197
|
const nodes = await read(path, currentRequest, token)
|
|
137
198
|
check(request, token)
|
|
138
199
|
if (currentRequest?.refresh) {
|
|
@@ -144,8 +205,7 @@ export function createFilePickerAdapter(
|
|
|
144
205
|
if (scope.recursive && node.kind === 'folder' && !node.unavailableReason)
|
|
145
206
|
queue.push(node.path)
|
|
146
207
|
}
|
|
147
|
-
if (matches.length > 10000)
|
|
148
|
-
throw new Error('Too many search results. Use a more specific search.')
|
|
208
|
+
if (matches.length > 10000) return { nodes: matches.slice(0, 10000), truncated: true }
|
|
149
209
|
}
|
|
150
210
|
return matches
|
|
151
211
|
},
|
|
@@ -3,13 +3,33 @@ import type {
|
|
|
3
3
|
PickerAdapter,
|
|
4
4
|
PickerNode,
|
|
5
5
|
PickerRequest,
|
|
6
|
+
PickerSort,
|
|
6
7
|
PickerTreeRow,
|
|
7
8
|
} from '../../types/filePicker'
|
|
8
9
|
|
|
10
|
+
const byName = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }).compare
|
|
11
|
+
/** Folders first; undated rows go last in either time order. */
|
|
12
|
+
function ordered(items: PickerNode[], sort: PickerSort | undefined): PickerNode[] {
|
|
13
|
+
if (!sort) return items
|
|
14
|
+
// Parse each timestamp once rather than on every comparison.
|
|
15
|
+
const times = new Map(items.map((node) => [node, node.modifiedAt ? Date.parse(node.modifiedAt) : NaN]))
|
|
16
|
+
const time = (node: PickerNode) => times.get(node)!
|
|
17
|
+
return [...items].sort((a, b) => {
|
|
18
|
+
if (a.kind !== b.kind) return a.kind === 'folder' ? -1 : 1
|
|
19
|
+
if (sort === 'name') return byName(a.name, b.name)
|
|
20
|
+
if (sort === 'name-desc') return byName(b.name, a.name)
|
|
21
|
+
const [ta, tb] = [time(a), time(b)]
|
|
22
|
+
if (Number.isNaN(ta) || Number.isNaN(tb))
|
|
23
|
+
return Number.isNaN(ta) === Number.isNaN(tb) ? byName(a.name, b.name) : Number.isNaN(ta) ? 1 : -1
|
|
24
|
+
return (sort === 'newest' ? tb - ta : ta - tb) || byName(a.name, b.name)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
export function usePickerNavigation(
|
|
10
29
|
adapter: () => PickerAdapter,
|
|
11
30
|
request: () => PickerRequest,
|
|
12
31
|
systemFilter: (node: PickerNode) => boolean = () => true,
|
|
32
|
+
sort: () => PickerSort | undefined = () => undefined,
|
|
13
33
|
) {
|
|
14
34
|
const path = shallowRef('')
|
|
15
35
|
const trail = ref<{ path: string; label: string }[]>([])
|
|
@@ -21,6 +41,8 @@ export function usePickerNavigation(
|
|
|
21
41
|
const expansionErrors = ref(new Map<string, string>())
|
|
22
42
|
const loading = shallowRef(false)
|
|
23
43
|
const error = shallowRef('')
|
|
44
|
+
/** True when the current search results stopped at an adapter limit. */
|
|
45
|
+
const truncated = shallowRef(false)
|
|
24
46
|
let epoch = 0
|
|
25
47
|
let navigation = 0
|
|
26
48
|
let controller: AbortController | undefined
|
|
@@ -154,14 +176,17 @@ export function usePickerNavigation(
|
|
|
154
176
|
const options = { ...request(), ...(refresh && { refresh }), signal: controller.signal }
|
|
155
177
|
loading.value = true
|
|
156
178
|
error.value = ''
|
|
179
|
+
truncated.value = false
|
|
157
180
|
entries.value = []
|
|
158
181
|
try {
|
|
159
|
-
const
|
|
182
|
+
const found = query.trim()
|
|
160
183
|
? await adapter().search(query.trim(), { path: path.value, recursive }, options)
|
|
161
184
|
: path.value
|
|
162
185
|
? await loadChildren(path.value, [], refresh)
|
|
163
186
|
: await adapter().listRoot(options)
|
|
164
187
|
if (token !== navigation) return
|
|
188
|
+
const items = Array.isArray(found) ? found : found.nodes
|
|
189
|
+
truncated.value = !Array.isArray(found) && found.truncated
|
|
165
190
|
register(items, query.trim() ? undefined : path.value)
|
|
166
191
|
entries.value = items
|
|
167
192
|
if (!query.trim()) prefetch(items)
|
|
@@ -215,8 +240,9 @@ export function usePickerNavigation(
|
|
|
215
240
|
}
|
|
216
241
|
const rows = computed(() => {
|
|
217
242
|
const result: PickerTreeRow[] = []
|
|
243
|
+
const order = sort()
|
|
218
244
|
function visit(items: PickerNode[], depth: number, ancestors: Set<string>) {
|
|
219
|
-
for (const item of items) {
|
|
245
|
+
for (const item of ordered(items, order)) {
|
|
220
246
|
if (ancestors.has(item.path) || depth > 30) continue
|
|
221
247
|
const node = nodes.value.get(item.path) ?? item
|
|
222
248
|
if (!isAllowed(node)) continue
|
|
@@ -246,6 +272,7 @@ export function usePickerNavigation(
|
|
|
246
272
|
expansionErrors,
|
|
247
273
|
loading,
|
|
248
274
|
error,
|
|
275
|
+
truncated,
|
|
249
276
|
rows,
|
|
250
277
|
isAllowed,
|
|
251
278
|
clear,
|
package/src/composables/index.ts
CHANGED
|
@@ -4,8 +4,6 @@ export {
|
|
|
4
4
|
type ApiClientOptions,
|
|
5
5
|
type UseApiReturn,
|
|
6
6
|
} from './useApi'
|
|
7
|
-
export { useAuth } from './useAuth'
|
|
8
|
-
export { usePasskey } from './usePasskey'
|
|
9
7
|
export { useTheme } from './useTheme'
|
|
10
8
|
export { useToast } from './useToast'
|
|
11
9
|
export { usePlatformContext } from './usePlatformContext'
|
|
@@ -32,11 +30,6 @@ export {
|
|
|
32
30
|
type FieldState,
|
|
33
31
|
type UseFormReturn,
|
|
34
32
|
} from './useForm'
|
|
35
|
-
export {
|
|
36
|
-
useWellPlateEditor,
|
|
37
|
-
type UseWellPlateEditorOptions,
|
|
38
|
-
type UseWellPlateEditorReturn,
|
|
39
|
-
} from './useWellPlateEditor'
|
|
40
33
|
export {
|
|
41
34
|
useConcentrationUnits,
|
|
42
35
|
type ConcentrationValue,
|
|
@@ -62,7 +62,7 @@ export { parseCSV } from './autoGroup/csv-shim'
|
|
|
62
62
|
/**
|
|
63
63
|
* Sample-grouping engine behind `AutoGroupModal`.
|
|
64
64
|
*
|
|
65
|
-
* @deprecated Scheduled for removal in MINT 1.4;
|
|
65
|
+
* @deprecated Scheduled for removal in MINT 1.4; no SDK replacement.
|
|
66
66
|
*/
|
|
67
67
|
export function useAutoGroup() {
|
|
68
68
|
const inputMode = ref<InputMode>('paste')
|