@morscherlab/mint-sdk 1.0.0-rc.4 → 1.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/components/AppTopBar.navigation.test.d.ts +1 -0
- package/dist/__tests__/components/DoseCalculatorVolumeField.test.d.ts +1 -0
- package/dist/__tests__/components/PlateMapEditorToolbarInternal.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.controls.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.navigation.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.shell.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStep.presentation.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepEditor.state.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepParameterField.test.d.ts +1 -0
- package/dist/__tests__/components/ReagentList.presentation.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.colors.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.drag.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.groups.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.selection.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelectorSampleRow.test.d.ts +1 -0
- package/dist/__tests__/components/ScheduleCalendar.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsModal.schema.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.colors.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.conditions.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.geometry.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.interaction.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.legend.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.rendering.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.sampleDrop.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/classify.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/columns.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/compose.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/cooccurrence.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/fingerprint.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/integration.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/template.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/tokenize.test.d.ts +1 -0
- package/dist/__tests__/composables/useAutoGroupInputSources.test.d.ts +1 -0
- package/dist/__tests__/composables/useScheduleCalendarLayout.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsComponents.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsExports.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsParsing.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsTheme.test.d.ts +1 -0
- package/dist/components/AppSidebar.vue.d.ts +9 -6
- package/dist/components/AppTopBar.navigation.d.ts +11 -0
- package/dist/components/BaseButton.vue.d.ts +1 -1
- package/dist/components/BaseCheckbox.vue.d.ts +7 -2
- package/dist/components/BaseInput.vue.d.ts +2 -2
- package/dist/components/BasePill.vue.d.ts +2 -2
- package/dist/components/BaseRadioGroup.vue.d.ts +2 -2
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseSlider.vue.d.ts +2 -2
- package/dist/components/BaseTextarea.vue.d.ts +2 -2
- package/dist/components/BaseToggle.vue.d.ts +7 -2
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
- package/dist/components/CollapsibleCard.vue.d.ts +9 -0
- package/dist/components/ColorSlider.vue.d.ts +2 -2
- package/dist/components/ConcentrationInput.vue.d.ts +2 -2
- package/dist/components/ControlWorkspaceView.vue.d.ts +4 -4
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +2 -2
- package/dist/components/DoseCalculatorVolumeField.vue.d.ts +15 -0
- package/dist/components/DoseDesignWorkspaceView.vue.d.ts +2 -2
- package/dist/components/DropdownButton.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +2 -2
- package/dist/components/FormulaInput.vue.d.ts +2 -2
- package/dist/components/IconButton.vue.d.ts +1 -1
- package/dist/components/LoadingSpinner.vue.d.ts +1 -1
- package/dist/components/MoleculeInput.vue.d.ts +2 -2
- package/dist/components/MultiSelect.vue.d.ts +1 -1
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PlateMapEditor.vue.d.ts +6 -6
- package/dist/components/PluginWorkspaceView.controls.d.ts +28 -0
- package/dist/components/PluginWorkspaceView.navigation.d.ts +29 -0
- package/dist/components/PluginWorkspaceView.props.d.ts +151 -0
- package/dist/components/PluginWorkspaceView.shell.d.ts +19 -0
- package/dist/components/PluginWorkspaceView.vue.d.ts +51 -196
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStep.presentation.d.ts +4 -0
- package/dist/components/ProtocolStepEditor.state.d.ts +18 -0
- package/dist/components/ProtocolStepParameterField.vue.d.ts +12 -0
- package/dist/components/ReagentList.presentation.d.ts +16 -0
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SampleSelector.colors.d.ts +13 -0
- package/dist/components/SampleSelector.drag.d.ts +24 -0
- package/dist/components/SampleSelector.groups.d.ts +15 -0
- package/dist/components/SampleSelector.selection.d.ts +26 -0
- package/dist/components/SampleSelector.vue.d.ts +4 -1
- package/dist/components/SampleSelectorSampleRow.vue.d.ts +21 -0
- package/dist/components/SegmentedControl.vue.d.ts +1 -1
- package/dist/components/SequenceInput.vue.d.ts +2 -2
- package/dist/components/SequenceProgressBar.vue.d.ts +1 -1
- package/dist/components/SettingsModal.schema.d.ts +9 -0
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +2 -2
- package/dist/components/TimePicker.vue.d.ts +2 -2
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +2 -2
- package/dist/components/WellPlate.colors.d.ts +9 -0
- package/dist/components/WellPlate.conditions.d.ts +26 -0
- package/dist/components/WellPlate.geometry.d.ts +23 -0
- package/dist/components/WellPlate.interaction.d.ts +71 -0
- package/dist/components/WellPlate.legend.d.ts +2 -0
- package/dist/components/WellPlate.rendering.d.ts +24 -0
- package/dist/components/WellPlate.sampleDrop.d.ts +8 -0
- package/dist/components/WellPlate.vue.d.ts +1 -1
- package/dist/components/index.js +2 -2
- package/dist/components/internal/ActionItemInternal.vue.d.ts +1 -1
- package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +28 -0
- package/dist/{components-DafPc4rM.js → components-Blx4MG--.js} +4073 -4222
- package/dist/components-Blx4MG--.js.map +1 -0
- package/dist/composables/autoGroup/classKey.d.ts +4 -0
- package/dist/composables/autoGroup/classify.d.ts +28 -0
- package/dist/composables/autoGroup/colors.d.ts +2 -0
- package/dist/composables/autoGroup/columns.d.ts +10 -0
- package/dist/composables/autoGroup/compose.d.ts +8 -0
- package/dist/composables/autoGroup/cooccurrence.d.ts +2 -0
- package/dist/composables/autoGroup/csv-shim.d.ts +2 -0
- package/dist/composables/autoGroup/fingerprint.d.ts +3 -0
- package/dist/composables/autoGroup/index.d.ts +16 -0
- package/dist/composables/autoGroup/replicatePreGroup.d.ts +38 -0
- package/dist/composables/autoGroup/template.d.ts +15 -0
- package/dist/composables/autoGroup/tokenize.d.ts +8 -0
- package/dist/composables/autoGroupConstants.d.ts +1 -0
- package/dist/composables/autoGroupGrouping.d.ts +3 -0
- package/dist/composables/controlComponentBindings.d.ts +1 -1
- package/dist/composables/controlSchemaAdapters.d.ts +1 -1
- package/dist/composables/controlSchemaDoseDesign.d.ts +11 -0
- package/dist/composables/controlSchemaLayout.d.ts +1 -1
- package/dist/composables/controlSchemaModel.d.ts +5 -0
- package/dist/composables/controlSchemaNormalize.d.ts +1 -1
- package/dist/composables/controlSchemaTypes.d.ts +311 -0
- package/dist/composables/controlWorkspaceOptions.d.ts +1 -1
- package/dist/composables/formBuilderSchema.d.ts +18 -0
- package/dist/composables/index.js +3 -3
- package/dist/composables/pluginEndpointBuilder.d.ts +13 -0
- package/dist/composables/protocolTemplateCatalog.d.ts +26 -0
- package/dist/composables/useAutoGroup.d.ts +61 -74
- package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
- package/dist/composables/useBioTemplateControls.d.ts +1 -1
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +1 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +1 -1
- package/dist/composables/useControlSchema.d.ts +4 -316
- package/dist/composables/useControlWorkspace.d.ts +1 -1
- package/dist/composables/useForm.d.ts +2 -33
- package/dist/composables/useFormBuilder.d.ts +2 -9
- package/dist/composables/useFormValidation.d.ts +34 -0
- package/dist/composables/usePluginClient.d.ts +1 -4
- package/dist/composables/useProtocolTemplates.d.ts +2 -24
- package/dist/composables/useScheduleCalendarLayout.d.ts +49 -0
- package/dist/{composables-BMkPQhVK.js → composables-CHDjDIQT.js} +33 -31
- package/dist/composables-CHDjDIQT.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/install.js +2 -2
- package/dist/styles.css +1432 -660
- package/dist/templates/controlSchemaTypes.d.ts +1 -1
- package/dist/templates/index.js +1 -1
- package/dist/templates/templateAdapterTypes.d.ts +48 -0
- package/dist/templates/templateCreateOptions.d.ts +165 -0
- package/dist/templates/templateQpcrTypes.d.ts +42 -0
- package/dist/templates/types.d.ts +5 -250
- package/dist/{templates-bUAWMn5L.js → templates-DSbHJC4v.js} +1536 -297
- package/dist/templates-DSbHJC4v.js.map +1 -0
- package/dist/types/auto-group.d.ts +79 -9
- package/dist/types/componentLabTypes.d.ts +161 -0
- package/dist/types/componentWorkflowTypes.d.ts +150 -0
- package/dist/types/components.d.ts +14 -311
- package/dist/types/form-builder.d.ts +3 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/{useProtocolTemplates-QZtHFFH2.js → useProtocolTemplates-BbPOYPzO.js} +1220 -454
- package/dist/useProtocolTemplates-BbPOYPzO.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppSidebar.test.ts +67 -0
- package/src/__tests__/components/AppTopBar.navigation.test.ts +70 -0
- package/src/__tests__/components/CollapsibleCard.test.ts +47 -0
- package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
- package/src/__tests__/components/FormBuilder.test.ts +57 -0
- package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
- package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
- package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
- package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +43 -0
- package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
- package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
- package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
- package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
- package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +100 -0
- package/src/__tests__/components/SampleSelector.groups.test.ts +81 -0
- package/src/__tests__/components/SampleSelector.selection.test.ts +70 -0
- package/src/__tests__/components/SampleSelector.test.ts +32 -0
- package/src/__tests__/components/SampleSelectorSampleRow.test.ts +37 -0
- package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
- package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
- package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
- package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
- package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
- package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
- package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
- package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
- package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
- package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
- package/src/__tests__/composables/autoGroup/compose.test.ts +227 -0
- package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
- package/src/__tests__/composables/autoGroup/fingerprint.test.ts +50 -0
- package/src/__tests__/composables/autoGroup/integration.test.ts +79 -0
- package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
- package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
- package/src/__tests__/composables/useAutoGroup.test.ts +129 -625
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +107 -0
- package/src/__tests__/composables/useControlSchema.test.ts +23 -0
- package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
- package/src/__tests__/docs/extractDocsComponents.test.ts +142 -0
- package/src/__tests__/docs/extractDocsExports.test.ts +77 -0
- package/src/__tests__/docs/extractDocsParsing.test.ts +69 -0
- package/src/__tests__/docs/extractDocsTemplates.test.ts +54 -0
- package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +1 -1
- package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
- package/src/components/AppSidebar.story.vue +79 -6
- package/src/components/AppSidebar.vue +74 -6
- package/src/components/AppTopBar.navigation.ts +62 -0
- package/src/components/AppTopBar.vue +17 -44
- package/src/components/AutoGroupModal.story.vue +50 -0
- package/src/components/AutoGroupModal.vue +441 -158
- package/src/components/BaseCheckbox.story.vue +27 -0
- package/src/components/BaseCheckbox.vue +63 -1
- package/src/components/BaseToggle.story.vue +27 -0
- package/src/components/BaseToggle.vue +66 -1
- package/src/components/CollapsibleCard.vue +123 -45
- package/src/components/ControlWorkspaceView.vue +2 -6
- package/src/components/DoseCalculator.vue +13 -73
- package/src/components/DoseCalculatorVolumeField.vue +61 -0
- package/src/components/ExperimentTimeline.vue +6 -31
- package/src/components/FormBuilder.story.vue +13 -0
- package/src/components/FormBuilder.vue +2 -7
- package/src/components/PlateMapEditor.vue +32 -106
- package/src/components/PluginWorkspaceView.controls.ts +182 -0
- package/src/components/PluginWorkspaceView.navigation.ts +106 -0
- package/src/components/PluginWorkspaceView.props.ts +174 -0
- package/src/components/PluginWorkspaceView.shell.ts +67 -0
- package/src/components/PluginWorkspaceView.vue +88 -404
- package/src/components/ProtocolStep.presentation.ts +31 -0
- package/src/components/ProtocolStepEditor.state.ts +104 -0
- package/src/components/ProtocolStepEditor.vue +48 -179
- package/src/components/ProtocolStepParameterField.vue +134 -0
- package/src/components/ReagentList.presentation.ts +105 -0
- package/src/components/ReagentList.vue +16 -79
- package/src/components/SampleSelector.colors.ts +43 -0
- package/src/components/SampleSelector.drag.ts +164 -0
- package/src/components/SampleSelector.groups.ts +109 -0
- package/src/components/SampleSelector.selection.ts +103 -0
- package/src/components/SampleSelector.vue +82 -349
- package/src/components/SampleSelectorSampleRow.vue +64 -0
- package/src/components/ScheduleCalendar.vue +44 -199
- package/src/components/SettingsModal.schema.ts +71 -0
- package/src/components/SettingsModal.vue +16 -46
- package/src/components/WellPlate.colors.ts +56 -0
- package/src/components/WellPlate.conditions.ts +100 -0
- package/src/components/WellPlate.geometry.ts +91 -0
- package/src/components/WellPlate.interaction.ts +272 -0
- package/src/components/WellPlate.legend.ts +8 -0
- package/src/components/WellPlate.rendering.ts +105 -0
- package/src/components/WellPlate.sampleDrop.ts +73 -0
- package/src/components/WellPlate.vue +102 -550
- package/src/components/internal/FormFieldRendererInternal.vue +23 -5
- package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
- package/src/composables/autoGroup/classKey.ts +5 -0
- package/src/composables/autoGroup/classify.ts +205 -0
- package/src/composables/autoGroup/colors.ts +6 -0
- package/src/composables/autoGroup/columns.ts +226 -0
- package/src/composables/autoGroup/compose.ts +156 -0
- package/src/composables/autoGroup/cooccurrence.ts +46 -0
- package/src/composables/autoGroup/csv-shim.ts +44 -0
- package/src/composables/autoGroup/fingerprint.ts +49 -0
- package/src/composables/autoGroup/index.ts +20 -0
- package/src/composables/autoGroup/replicatePreGroup.ts +90 -0
- package/src/composables/autoGroup/template.ts +126 -0
- package/src/composables/autoGroup/tokenize.ts +41 -0
- package/src/composables/autoGroup/vocab.json +67 -0
- package/src/composables/autoGroupConstants.ts +4 -0
- package/src/composables/autoGroupGrouping.ts +148 -0
- package/src/composables/controlComponentBindings.ts +1 -1
- package/src/composables/controlSchemaAdapters.ts +4 -1
- package/src/composables/controlSchemaDoseDesign.ts +215 -0
- package/src/composables/controlSchemaFormFields.ts +4 -1
- package/src/composables/controlSchemaLayout.ts +1 -1
- package/src/composables/controlSchemaModel.ts +163 -0
- package/src/composables/controlSchemaNormalize.ts +1 -1
- package/src/composables/controlSchemaTypes.ts +372 -0
- package/src/composables/controlWorkspaceOptions.ts +1 -1
- package/src/composables/formBuilderSchema.ts +153 -0
- package/src/composables/pluginEndpointBuilder.ts +203 -0
- package/src/composables/protocolTemplateCatalog.ts +325 -0
- package/src/composables/useAutoGroup.ts +395 -549
- package/src/composables/useAutoGroupInputSources.ts +147 -0
- package/src/composables/useBioTemplateControls.ts +1 -1
- package/src/composables/useBioTemplatePresetWorkspace.ts +1 -1
- package/src/composables/useBioTemplateWorkspace.ts +1 -1
- package/src/composables/useControlSchema.ts +21 -692
- package/src/composables/useControlWorkspace.ts +7 -13
- package/src/composables/useForm.ts +5 -187
- package/src/composables/useFormBuilder.ts +11 -153
- package/src/composables/useFormValidation.ts +154 -0
- package/src/composables/usePluginClient.ts +10 -193
- package/src/composables/useProtocolTemplates.ts +10 -328
- package/src/composables/useScheduleCalendarLayout.ts +287 -0
- package/src/styles/components/app-sidebar.css +134 -6
- package/src/styles/components/auto-group-modal.css +248 -310
- package/src/styles/components/checkbox.css +87 -0
- package/src/styles/components/collapsible-card.css +154 -14
- package/src/styles/components/toggle.css +80 -0
- package/src/templates/controlSchemaTypes.ts +1 -1
- package/src/templates/templateAdapterTypes.ts +58 -0
- package/src/templates/templateCreateOptions.ts +208 -0
- package/src/templates/templateQpcrTypes.ts +48 -0
- package/src/templates/types.ts +79 -275
- package/src/types/auto-group.ts +107 -9
- package/src/types/componentLabTypes.ts +235 -0
- package/src/types/componentWorkflowTypes.ts +190 -0
- package/src/types/components.ts +95 -424
- package/src/types/form-builder.ts +3 -0
- package/src/types/index.ts +2 -0
- package/dist/components-DafPc4rM.js.map +0 -1
- package/dist/composables-BMkPQhVK.js.map +0 -1
- package/dist/templates-bUAWMn5L.js.map +0 -1
- package/dist/useProtocolTemplates-QZtHFFH2.js.map +0 -1
|
@@ -5,10 +5,13 @@ import type {
|
|
|
5
5
|
ScheduleView,
|
|
6
6
|
ScheduleEvent,
|
|
7
7
|
ScheduleBlockedSlot,
|
|
8
|
-
ScheduleEventStatus,
|
|
9
8
|
} from '../types/components'
|
|
10
|
-
import { formatTime } from '../composables/useTimeUtils'
|
|
11
9
|
import { useScheduleDrag } from '../composables/useScheduleDrag'
|
|
10
|
+
import {
|
|
11
|
+
SCHEDULE_SLOT_HEIGHT,
|
|
12
|
+
parseScheduleModelDate,
|
|
13
|
+
useScheduleCalendarLayout,
|
|
14
|
+
} from '../composables/useScheduleCalendarLayout'
|
|
12
15
|
|
|
13
16
|
interface Props {
|
|
14
17
|
modelValue?: Date | string
|
|
@@ -52,21 +55,26 @@ const emit = defineEmits<{
|
|
|
52
55
|
'navigate': [context: { direction: string; date: Date }]
|
|
53
56
|
}>()
|
|
54
57
|
|
|
55
|
-
const SLOT_HEIGHT =
|
|
58
|
+
const SLOT_HEIGHT = SCHEDULE_SLOT_HEIGHT
|
|
56
59
|
const currentView = ref<ScheduleView>(props.view)
|
|
57
|
-
const currentDate = ref<Date>(
|
|
60
|
+
const currentDate = ref<Date>(parseScheduleModelDate(props.modelValue))
|
|
58
61
|
const nowTimer = ref<ReturnType<typeof setInterval>>()
|
|
59
62
|
const now = ref(new Date())
|
|
60
63
|
const bodyRef = ref<HTMLDivElement>()
|
|
61
64
|
|
|
62
65
|
watch(() => props.view, (v) => { currentView.value = v })
|
|
63
|
-
watch(() => props.modelValue, (v) => { if (v) currentDate.value =
|
|
66
|
+
watch(() => props.modelValue, (v) => { if (v) currentDate.value = parseScheduleModelDate(v) })
|
|
64
67
|
|
|
65
68
|
const slotHeightRef = computed(() => SLOT_HEIGHT)
|
|
66
69
|
const slotDurationRef = computed(() => props.slotDuration)
|
|
67
70
|
const dayStartRef = computed(() => props.dayStartHour)
|
|
68
71
|
const dayEndRef = computed(() => props.dayEndHour)
|
|
69
72
|
const readonlyRef = computed(() => props.readonly)
|
|
73
|
+
const eventsRef = computed(() => props.events)
|
|
74
|
+
const blockedSlotsRef = computed(() => props.blockedSlots)
|
|
75
|
+
const weekStartsOnRef = computed(() => props.weekStartsOn)
|
|
76
|
+
const localeRef = computed(() => props.locale)
|
|
77
|
+
const showNowIndicatorRef = computed(() => props.showNowIndicator)
|
|
70
78
|
|
|
71
79
|
const { isDragging, ghost, startCreate, startMove, startResize } = useScheduleDrag({
|
|
72
80
|
slotDuration: slotDurationRef,
|
|
@@ -85,124 +93,39 @@ const { isDragging, ghost, startCreate, startMove, startResize } = useScheduleDr
|
|
|
85
93
|
},
|
|
86
94
|
})
|
|
87
95
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const headerTitle = computed(() => {
|
|
121
|
-
if (currentView.value === 'day') {
|
|
122
|
-
return currentDate.value.toLocaleDateString(props.locale, {
|
|
123
|
-
weekday: 'long',
|
|
124
|
-
year: 'numeric',
|
|
125
|
-
month: 'long',
|
|
126
|
-
day: 'numeric',
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
if (currentView.value === 'week') {
|
|
130
|
-
const days = weekDays.value
|
|
131
|
-
const first = days[0]
|
|
132
|
-
const last = days[6]
|
|
133
|
-
const sameMonth = first.getMonth() === last.getMonth()
|
|
134
|
-
if (sameMonth) {
|
|
135
|
-
return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.getDate()}, ${last.getFullYear()}`
|
|
136
|
-
}
|
|
137
|
-
return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })}, ${last.getFullYear()}`
|
|
138
|
-
}
|
|
139
|
-
return currentDate.value.toLocaleDateString(props.locale, { year: 'numeric', month: 'long' })
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
// Month view computations
|
|
143
|
-
const monthDays = computed(() => {
|
|
144
|
-
const year = currentDate.value.getFullYear()
|
|
145
|
-
const month = currentDate.value.getMonth()
|
|
146
|
-
const firstDay = new Date(year, month, 1)
|
|
147
|
-
const lastDay = new Date(year, month + 1, 0)
|
|
148
|
-
|
|
149
|
-
const days: { date: Date; isCurrentMonth: boolean }[] = []
|
|
150
|
-
|
|
151
|
-
// Adjust for weekStartsOn
|
|
152
|
-
let startPadding = firstDay.getDay() - props.weekStartsOn
|
|
153
|
-
if (startPadding < 0) startPadding += 7
|
|
154
|
-
|
|
155
|
-
for (let i = startPadding - 1; i >= 0; i--) {
|
|
156
|
-
const date = new Date(year, month, -i)
|
|
157
|
-
days.push({ date, isCurrentMonth: false })
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
for (let i = 1; i <= lastDay.getDate(); i++) {
|
|
161
|
-
days.push({ date: new Date(year, month, i), isCurrentMonth: true })
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const remaining = 42 - days.length
|
|
165
|
-
for (let i = 1; i <= remaining; i++) {
|
|
166
|
-
days.push({ date: new Date(year, month + 1, i), isCurrentMonth: false })
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return days
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const monthWeekdayLabels = computed(() => {
|
|
173
|
-
const labels: string[] = []
|
|
174
|
-
const base = new Date(2024, 0, props.weekStartsOn === 1 ? 1 : 7) // A known Monday or Sunday
|
|
175
|
-
for (let i = 0; i < 7; i++) {
|
|
176
|
-
const d = new Date(base)
|
|
177
|
-
d.setDate(d.getDate() + i)
|
|
178
|
-
labels.push(d.toLocaleDateString(props.locale, { weekday: 'short' }))
|
|
179
|
-
}
|
|
180
|
-
return labels
|
|
96
|
+
const {
|
|
97
|
+
totalSlots,
|
|
98
|
+
timeLabels,
|
|
99
|
+
visibleDays,
|
|
100
|
+
headerTitle,
|
|
101
|
+
monthDays,
|
|
102
|
+
monthWeekdayLabels,
|
|
103
|
+
isToday,
|
|
104
|
+
isSameDay,
|
|
105
|
+
getEventStyle,
|
|
106
|
+
getEventsForDay,
|
|
107
|
+
getEventsForDate,
|
|
108
|
+
getBlockedForDay,
|
|
109
|
+
getBlockedStyle,
|
|
110
|
+
getNowIndicatorStyle,
|
|
111
|
+
getStatusClass,
|
|
112
|
+
getMonthStatusClass,
|
|
113
|
+
formatEventTime,
|
|
114
|
+
} = useScheduleCalendarLayout({
|
|
115
|
+
currentView,
|
|
116
|
+
currentDate,
|
|
117
|
+
now,
|
|
118
|
+
events: eventsRef,
|
|
119
|
+
blockedSlots: blockedSlotsRef,
|
|
120
|
+
dayStartHour: dayStartRef,
|
|
121
|
+
dayEndHour: dayEndRef,
|
|
122
|
+
slotDuration: slotDurationRef,
|
|
123
|
+
weekStartsOn: weekStartsOnRef,
|
|
124
|
+
locale: localeRef,
|
|
125
|
+
showNowIndicator: showNowIndicatorRef,
|
|
126
|
+
slotHeight: slotHeightRef,
|
|
181
127
|
})
|
|
182
128
|
|
|
183
|
-
function parseModelDate(value?: Date | string): Date {
|
|
184
|
-
if (!value) return new Date()
|
|
185
|
-
if (value instanceof Date) return value
|
|
186
|
-
const d = new Date(value)
|
|
187
|
-
return isNaN(d.getTime()) ? new Date() : d
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function getWeekStart(date: Date): Date {
|
|
191
|
-
const d = new Date(date)
|
|
192
|
-
const day = d.getDay()
|
|
193
|
-
const diff = (day - props.weekStartsOn + 7) % 7
|
|
194
|
-
d.setDate(d.getDate() - diff)
|
|
195
|
-
return d
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function isToday(date: Date): boolean {
|
|
199
|
-
return date.toDateString() === now.value.toDateString()
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function isSameDay(a: Date, b: Date): boolean {
|
|
203
|
-
return a.toDateString() === b.toDateString()
|
|
204
|
-
}
|
|
205
|
-
|
|
206
129
|
function navigate(direction: 'prev' | 'next' | 'today') {
|
|
207
130
|
const d = new Date(currentDate.value)
|
|
208
131
|
if (direction === 'today') {
|
|
@@ -223,78 +146,6 @@ function setView(view: ScheduleView) {
|
|
|
223
146
|
emit('update:view', view)
|
|
224
147
|
}
|
|
225
148
|
|
|
226
|
-
function getEventStyle(event: ScheduleEvent, dayDate: Date) {
|
|
227
|
-
const eventStart = new Date(event.start)
|
|
228
|
-
const eventEnd = new Date(event.end)
|
|
229
|
-
if (!isSameDay(eventStart, dayDate) && !isSameDay(eventEnd, dayDate)) return null
|
|
230
|
-
|
|
231
|
-
const dayStartMin = props.dayStartHour * 60
|
|
232
|
-
const startMin = Math.max(eventStart.getHours() * 60 + eventStart.getMinutes(), dayStartMin) - dayStartMin
|
|
233
|
-
const endMin = Math.min(eventEnd.getHours() * 60 + eventEnd.getMinutes(), props.dayEndHour * 60) - dayStartMin
|
|
234
|
-
|
|
235
|
-
const pixelsPerMin = SLOT_HEIGHT / props.slotDuration
|
|
236
|
-
return {
|
|
237
|
-
top: `${startMin * pixelsPerMin}px`,
|
|
238
|
-
height: `${Math.max((endMin - startMin) * pixelsPerMin, SLOT_HEIGHT / 2)}px`,
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function getEventsForDay(day: Date): ScheduleEvent[] {
|
|
243
|
-
return props.events.filter((e) => {
|
|
244
|
-
const start = new Date(e.start)
|
|
245
|
-
return isSameDay(start, day)
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function getEventsForDate(date: Date): ScheduleEvent[] {
|
|
250
|
-
return props.events.filter((e) => {
|
|
251
|
-
const start = new Date(e.start)
|
|
252
|
-
return isSameDay(start, date)
|
|
253
|
-
})
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function getBlockedForDay(day: Date): ScheduleBlockedSlot[] {
|
|
257
|
-
return props.blockedSlots.filter((b) => {
|
|
258
|
-
const start = new Date(b.start)
|
|
259
|
-
return isSameDay(start, day)
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function getBlockedStyle(slot: ScheduleBlockedSlot) {
|
|
264
|
-
const start = new Date(slot.start)
|
|
265
|
-
const end = new Date(slot.end)
|
|
266
|
-
const dayStartMin = props.dayStartHour * 60
|
|
267
|
-
const startMin = Math.max(start.getHours() * 60 + start.getMinutes(), dayStartMin) - dayStartMin
|
|
268
|
-
const endMin = Math.min(end.getHours() * 60 + end.getMinutes(), props.dayEndHour * 60) - dayStartMin
|
|
269
|
-
|
|
270
|
-
const pixelsPerMin = SLOT_HEIGHT / props.slotDuration
|
|
271
|
-
return {
|
|
272
|
-
top: `${startMin * pixelsPerMin}px`,
|
|
273
|
-
height: `${(endMin - startMin) * pixelsPerMin}px`,
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function getNowIndicatorStyle(): Record<string, string> | null {
|
|
278
|
-
if (!props.showNowIndicator) return null
|
|
279
|
-
const nowMin = now.value.getHours() * 60 + now.value.getMinutes()
|
|
280
|
-
const dayStartMin = props.dayStartHour * 60
|
|
281
|
-
const dayEndMin = props.dayEndHour * 60
|
|
282
|
-
if (nowMin < dayStartMin || nowMin > dayEndMin) return null
|
|
283
|
-
|
|
284
|
-
const pixelsPerMin = SLOT_HEIGHT / props.slotDuration
|
|
285
|
-
return { top: `${(nowMin - dayStartMin) * pixelsPerMin}px` }
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function getStatusClass(status?: ScheduleEventStatus): string {
|
|
289
|
-
if (!status) return 'mint-schedule__event--confirmed'
|
|
290
|
-
return `mint-schedule__event--${status}`
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function getMonthStatusClass(status?: ScheduleEventStatus): string {
|
|
294
|
-
if (!status) return 'mint-schedule__month-event--confirmed'
|
|
295
|
-
return `mint-schedule__month-event--${status}`
|
|
296
|
-
}
|
|
297
|
-
|
|
298
149
|
function onEventClick(event: ScheduleEvent, e: MouseEvent) {
|
|
299
150
|
e.stopPropagation()
|
|
300
151
|
emit('event-click', event)
|
|
@@ -335,12 +186,6 @@ function onMonthDayClick(date: Date) {
|
|
|
335
186
|
setView('day')
|
|
336
187
|
}
|
|
337
188
|
|
|
338
|
-
function formatEventTime(event: ScheduleEvent): string {
|
|
339
|
-
const start = new Date(event.start)
|
|
340
|
-
const end = new Date(event.end)
|
|
341
|
-
return `${formatTime(start.getHours(), start.getMinutes())} - ${formatTime(end.getHours(), end.getMinutes())}`
|
|
342
|
-
}
|
|
343
|
-
|
|
344
189
|
onMounted(() => {
|
|
345
190
|
nowTimer.value = setInterval(() => { now.value = new Date() }, 60000)
|
|
346
191
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FormSchema,
|
|
3
|
+
FormSectionSchema,
|
|
4
|
+
SettingsGroup,
|
|
5
|
+
SettingsModalSchema,
|
|
6
|
+
SettingsTab,
|
|
7
|
+
SettingsTabInput,
|
|
8
|
+
} from '../types'
|
|
9
|
+
import type { AccessControlled } from '../permissions'
|
|
10
|
+
import { normalizeItemInput } from '../utils/items'
|
|
11
|
+
|
|
12
|
+
export const APPEARANCE_TAB_ID = 'appearance'
|
|
13
|
+
|
|
14
|
+
export const APPEARANCE_TAB: SettingsTab = {
|
|
15
|
+
id: APPEARANCE_TAB_ID,
|
|
16
|
+
label: 'Appearance',
|
|
17
|
+
description: 'Theme, color palette, and table density',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Map settings groups onto the form-builder's flat-section shape.
|
|
21
|
+
// `title: ''` because the rail/pane header already shows the group name.
|
|
22
|
+
export function buildFlatSettingsSchema(schema: SettingsModalSchema): FormSchema {
|
|
23
|
+
return {
|
|
24
|
+
sections: schema.groups.map<FormSectionSchema>((group) => ({
|
|
25
|
+
id: group.id,
|
|
26
|
+
title: '',
|
|
27
|
+
fields: group.fields,
|
|
28
|
+
columns: group.columns,
|
|
29
|
+
condition: group.condition,
|
|
30
|
+
})),
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function filterSettingsSchemaByAccess(
|
|
35
|
+
schema: SettingsModalSchema,
|
|
36
|
+
canShow: (item: AccessControlled) => boolean
|
|
37
|
+
): SettingsModalSchema {
|
|
38
|
+
const groups = schema.groups.flatMap((group) => {
|
|
39
|
+
if (!canShow(group)) return []
|
|
40
|
+
|
|
41
|
+
const fields = group.fields.filter(canShow)
|
|
42
|
+
if (fields.length === 0) return []
|
|
43
|
+
|
|
44
|
+
return [{ ...group, fields }]
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return { groups }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function settingsGroupToTab(group: SettingsGroup): SettingsTab {
|
|
51
|
+
return {
|
|
52
|
+
id: group.id,
|
|
53
|
+
label: group.label,
|
|
54
|
+
icon: group.icon,
|
|
55
|
+
description: group.description,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function normalizeVisibleSettingsTabs(
|
|
60
|
+
tabs: SettingsTabInput[],
|
|
61
|
+
canShow: (item: AccessControlled) => boolean
|
|
62
|
+
): SettingsTab[] {
|
|
63
|
+
return tabs.map(normalizeItemInput).filter(canShow)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function buildSettingsTabs(
|
|
67
|
+
baseTabs: SettingsTab[],
|
|
68
|
+
showAppearance: boolean
|
|
69
|
+
): SettingsTab[] {
|
|
70
|
+
return showAppearance ? [...baseTabs, APPEARANCE_TAB] : baseTabs
|
|
71
|
+
}
|
|
@@ -24,8 +24,8 @@ import FormFieldRendererInternal from './internal/FormFieldRendererInternal.vue'
|
|
|
24
24
|
import { useFormBuilder } from '../composables/useFormBuilder'
|
|
25
25
|
import {
|
|
26
26
|
controlsToSettingsSchema,
|
|
27
|
-
defineControlModel,
|
|
28
27
|
mergeControlWorkspaceOptions,
|
|
28
|
+
resolveControlModel,
|
|
29
29
|
type ControlModel,
|
|
30
30
|
type ControlModelBinding,
|
|
31
31
|
type ControlSchema,
|
|
@@ -50,26 +50,17 @@ import type {
|
|
|
50
50
|
SettingsModalLayout,
|
|
51
51
|
SettingsModalSchema,
|
|
52
52
|
SettingsUserType,
|
|
53
|
-
FormSchema,
|
|
54
|
-
FormSectionSchema,
|
|
55
53
|
FormEnhancements,
|
|
56
54
|
} from '../types'
|
|
57
55
|
import type { AccessControlled, PermissionUser } from '../permissions'
|
|
58
|
-
import {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
title: '',
|
|
67
|
-
fields: g.fields,
|
|
68
|
-
columns: g.columns,
|
|
69
|
-
condition: g.condition,
|
|
70
|
-
})),
|
|
71
|
-
}
|
|
72
|
-
}
|
|
56
|
+
import {
|
|
57
|
+
APPEARANCE_TAB_ID,
|
|
58
|
+
buildFlatSettingsSchema,
|
|
59
|
+
buildSettingsTabs,
|
|
60
|
+
filterSettingsSchemaByAccess,
|
|
61
|
+
normalizeVisibleSettingsTabs,
|
|
62
|
+
settingsGroupToTab,
|
|
63
|
+
} from './SettingsModal.schema'
|
|
73
64
|
|
|
74
65
|
interface Props {
|
|
75
66
|
modelValue: boolean
|
|
@@ -114,14 +105,6 @@ const settings = useSettingsStore()
|
|
|
114
105
|
const auth = useAuthStore()
|
|
115
106
|
const { user: platformUser } = usePlatformContext()
|
|
116
107
|
|
|
117
|
-
const APPEARANCE_TAB_ID = 'appearance'
|
|
118
|
-
|
|
119
|
-
const APPEARANCE_TAB: SettingsTab = {
|
|
120
|
-
id: APPEARANCE_TAB_ID,
|
|
121
|
-
label: 'Appearance',
|
|
122
|
-
description: 'Theme, color palette, and table density',
|
|
123
|
-
}
|
|
124
|
-
|
|
125
108
|
const isVertical = computed(() => props.layout === 'vertical')
|
|
126
109
|
|
|
127
110
|
// Per-instance prefix for ARIA tab/tabpanel id wiring. Stable across renders.
|
|
@@ -130,8 +113,7 @@ const tabId = (id: string) => `${uid}-tab-${id}`
|
|
|
130
113
|
const panelId = (id: string) => `${uid}-panel-${id}`
|
|
131
114
|
|
|
132
115
|
const resolvedModel = computed<ControlModelBinding | undefined>(() => {
|
|
133
|
-
|
|
134
|
-
return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)
|
|
116
|
+
return resolveControlModel(props.model)
|
|
135
117
|
})
|
|
136
118
|
const resolvedControls = computed<ControlSchema | undefined>(() =>
|
|
137
119
|
props.controls ?? resolvedModel.value?.controls,
|
|
@@ -157,15 +139,7 @@ const isAccessUserAuthenticated = computed(() =>
|
|
|
157
139
|
)
|
|
158
140
|
const settingsSchema = computed<SettingsModalSchema | undefined>(() => {
|
|
159
141
|
if (!sourceSettingsSchema.value) return undefined
|
|
160
|
-
|
|
161
|
-
if (!canShowForCurrentUser(group)) return []
|
|
162
|
-
|
|
163
|
-
const fields = group.fields.filter(canShowForCurrentUser)
|
|
164
|
-
if (fields.length === 0) return []
|
|
165
|
-
|
|
166
|
-
return [{ ...group, fields }]
|
|
167
|
-
})
|
|
168
|
-
return { groups }
|
|
142
|
+
return filterSettingsSchemaByAccess(sourceSettingsSchema.value, canShowForCurrentUser)
|
|
169
143
|
})
|
|
170
144
|
const isSchemaDriven = computed(() => !!settingsSchema.value)
|
|
171
145
|
const resolvedValues = computed<Record<string, unknown>>(() => ({
|
|
@@ -176,7 +150,7 @@ const resolvedValues = computed<Record<string, unknown>>(() => ({
|
|
|
176
150
|
// Schema-driven mode keeps one form-builder instance and syncs schema changes
|
|
177
151
|
// into it, so callers can swap groups/fields without remounting the modal.
|
|
178
152
|
const builder = useFormBuilder(
|
|
179
|
-
settingsSchema.value ?
|
|
153
|
+
settingsSchema.value ? buildFlatSettingsSchema(settingsSchema.value) : { sections: [] },
|
|
180
154
|
resolvedValues.value,
|
|
181
155
|
props.enhancements,
|
|
182
156
|
)
|
|
@@ -189,7 +163,7 @@ watch(
|
|
|
189
163
|
return
|
|
190
164
|
}
|
|
191
165
|
|
|
192
|
-
const flatSchema =
|
|
166
|
+
const flatSchema = buildFlatSettingsSchema(schema)
|
|
193
167
|
const fieldNames = formSchemaFieldNames(flatSchema)
|
|
194
168
|
const sourceValues = props.values === undefined
|
|
195
169
|
? {
|
|
@@ -232,14 +206,14 @@ const visibleSchemaGroups = computed(() =>
|
|
|
232
206
|
)
|
|
233
207
|
|
|
234
208
|
const manualTabs = computed<SettingsTab[]>(() =>
|
|
235
|
-
props.tabs
|
|
209
|
+
normalizeVisibleSettingsTabs(props.tabs, canShowForCurrentUser)
|
|
236
210
|
)
|
|
237
211
|
|
|
238
212
|
const allTabs = computed<SettingsTab[]>(() => {
|
|
239
213
|
const base: SettingsTab[] = settingsSchema.value
|
|
240
|
-
? visibleSchemaGroups.value.map(
|
|
214
|
+
? visibleSchemaGroups.value.map(settingsGroupToTab)
|
|
241
215
|
: manualTabs.value
|
|
242
|
-
return props.showAppearance
|
|
216
|
+
return buildSettingsTabs(base, props.showAppearance)
|
|
243
217
|
})
|
|
244
218
|
|
|
245
219
|
const activeTab = ref(allTabs.value[0]?.id || APPEARANCE_TAB_ID)
|
|
@@ -285,10 +259,6 @@ function builderFieldNames(): string[] {
|
|
|
285
259
|
return builder.fields.map(field => field.name)
|
|
286
260
|
}
|
|
287
261
|
|
|
288
|
-
function isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {
|
|
289
|
-
return 'controls' in model && 'controlOptions' in model
|
|
290
|
-
}
|
|
291
|
-
|
|
292
262
|
function canShowForCurrentUser(item: AccessControlled): boolean {
|
|
293
263
|
return canAccessByPolicy(
|
|
294
264
|
currentAccessUser.value,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { HeatmapConfig } from '../types'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_SAMPLE_TYPE_COLORS: Record<string, { bg: string; border: string }> = {
|
|
4
|
+
sample: { bg: 'rgba(16, 185, 129, 0.15)', border: 'rgba(16, 185, 129, 0.4)' },
|
|
5
|
+
control: { bg: 'rgba(59, 130, 246, 0.15)', border: 'rgba(59, 130, 246, 0.4)' },
|
|
6
|
+
blank: { bg: 'rgba(249, 115, 22, 0.15)', border: 'rgba(249, 115, 22, 0.4)' },
|
|
7
|
+
qc: { bg: 'rgba(139, 92, 246, 0.15)', border: 'rgba(139, 92, 246, 0.4)' },
|
|
8
|
+
iqc: { bg: 'rgba(236, 72, 153, 0.15)', border: 'rgba(236, 72, 153, 0.4)' },
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const HEATMAP_COLORS: Record<string, string[]> = {
|
|
12
|
+
viridis: ['#440154', '#482878', '#3e4989', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725'],
|
|
13
|
+
plasma: ['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921'],
|
|
14
|
+
turbo: ['#30123b', '#4145ab', '#4675ed', '#39a2fc', '#1bcfd4', '#24e79e', '#71f05f', '#c1f034', '#f1c83c', '#f99538', '#e45a31', '#ba2512', '#7a0403'],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function conditionGradientStyle(
|
|
18
|
+
color: string,
|
|
19
|
+
concentration: number,
|
|
20
|
+
concentrations: number[],
|
|
21
|
+
): Record<string, string> {
|
|
22
|
+
const min = Math.min(...concentrations)
|
|
23
|
+
const max = Math.max(...concentrations)
|
|
24
|
+
const t = max <= min ? 1 : (concentration - min) / (max - min)
|
|
25
|
+
const opacity = 0.25 + t * 0.50
|
|
26
|
+
const r = parseInt(color.slice(1, 3), 16)
|
|
27
|
+
const g = parseInt(color.slice(3, 5), 16)
|
|
28
|
+
const b = parseInt(color.slice(5, 7), 16)
|
|
29
|
+
const er = 255 * (1 - opacity) + r * opacity
|
|
30
|
+
const eg = 255 * (1 - opacity) + g * opacity
|
|
31
|
+
const eb = 255 * (1 - opacity) + b * opacity
|
|
32
|
+
const luminance = (0.299 * er + 0.587 * eg + 0.114 * eb) / 255
|
|
33
|
+
return {
|
|
34
|
+
backgroundColor: `rgba(${r}, ${g}, ${b}, ${opacity})`,
|
|
35
|
+
color: luminance > 0.55 ? '#1e293b' : '#ffffff',
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function formatConcentration(value: number): string {
|
|
40
|
+
if (value >= 1000) return `${value / 1000}k`
|
|
41
|
+
if (value < 0.01) return value.toExponential(0)
|
|
42
|
+
return String(value)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getHeatmapColor(config: HeatmapConfig | undefined, value: number | undefined): string | null {
|
|
46
|
+
if (!config?.enabled || value === undefined) return null
|
|
47
|
+
|
|
48
|
+
const min = config.min ?? 0
|
|
49
|
+
const max = config.max ?? 1
|
|
50
|
+
const normalized = Math.max(0, Math.min(1, (value - min) / (max - min)))
|
|
51
|
+
const colors = config.colorScale === 'custom' && config.customColors?.length
|
|
52
|
+
? config.customColors
|
|
53
|
+
: HEATMAP_COLORS[config.colorScale || 'viridis']
|
|
54
|
+
const index = Math.min(Math.floor(normalized * (colors.length - 1)), colors.length - 1)
|
|
55
|
+
return colors[index]
|
|
56
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ColumnCondition, RowCondition } from '../types'
|
|
2
|
+
|
|
3
|
+
export interface ConditionEntry<TCondition> {
|
|
4
|
+
condition: TCondition
|
|
5
|
+
indexInGroup: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type ColumnConditionSpan =
|
|
9
|
+
| { condition: ColumnCondition; colspan: number }
|
|
10
|
+
| { gap: true; colspan: number }
|
|
11
|
+
|
|
12
|
+
export type RowConditionSpan =
|
|
13
|
+
| { condition: RowCondition; rowspan: number; startRow: number }
|
|
14
|
+
| { gap: true; rowspan: number; startRow: number }
|
|
15
|
+
|
|
16
|
+
export function createColumnConditionMap(
|
|
17
|
+
conditions: ColumnCondition[],
|
|
18
|
+
): Map<number, ConditionEntry<ColumnCondition>> {
|
|
19
|
+
const map = new Map<number, ConditionEntry<ColumnCondition>>()
|
|
20
|
+
for (const condition of conditions) {
|
|
21
|
+
condition.cols.forEach((col, indexInGroup) => {
|
|
22
|
+
map.set(col, { condition, indexInGroup })
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
return map
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createRowConditionMap(
|
|
29
|
+
conditions: RowCondition[],
|
|
30
|
+
): Map<string, ConditionEntry<RowCondition>> {
|
|
31
|
+
const map = new Map<string, ConditionEntry<RowCondition>>()
|
|
32
|
+
for (const condition of conditions) {
|
|
33
|
+
condition.rows.forEach((row, indexInGroup) => {
|
|
34
|
+
map.set(row, { condition, indexInGroup })
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
return map
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createColumnConditionSpans(
|
|
41
|
+
cols: number[],
|
|
42
|
+
conditionMap: ReadonlyMap<number, ConditionEntry<ColumnCondition>>,
|
|
43
|
+
): ColumnConditionSpan[] {
|
|
44
|
+
const spans: ColumnConditionSpan[] = []
|
|
45
|
+
let index = 0
|
|
46
|
+
|
|
47
|
+
while (index < cols.length) {
|
|
48
|
+
const entry = conditionMap.get(cols[index])
|
|
49
|
+
if (entry?.indexInGroup === 0) {
|
|
50
|
+
spans.push({ condition: entry.condition, colspan: entry.condition.cols.length })
|
|
51
|
+
index += entry.condition.cols.length
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let gapCount = 0
|
|
56
|
+
while (index + gapCount < cols.length && !conditionMap.has(cols[index + gapCount])) {
|
|
57
|
+
gapCount++
|
|
58
|
+
}
|
|
59
|
+
spans.push({ gap: true, colspan: gapCount || 1 })
|
|
60
|
+
index += gapCount || 1
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return spans
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createRowConditionSpans(
|
|
67
|
+
rows: string[],
|
|
68
|
+
conditionMap: ReadonlyMap<string, ConditionEntry<RowCondition>>,
|
|
69
|
+
): RowConditionSpan[] {
|
|
70
|
+
const spans: RowConditionSpan[] = []
|
|
71
|
+
let index = 0
|
|
72
|
+
|
|
73
|
+
while (index < rows.length) {
|
|
74
|
+
const entry = conditionMap.get(rows[index])
|
|
75
|
+
if (entry?.indexInGroup === 0) {
|
|
76
|
+
spans.push({ condition: entry.condition, rowspan: entry.condition.rows.length, startRow: index })
|
|
77
|
+
index += entry.condition.rows.length
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let gapCount = 0
|
|
82
|
+
while (index + gapCount < rows.length && !conditionMap.has(rows[index + gapCount])) {
|
|
83
|
+
gapCount++
|
|
84
|
+
}
|
|
85
|
+
spans.push({ gap: true, rowspan: gapCount || 1, startRow: index })
|
|
86
|
+
index += gapCount || 1
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return spans
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function createRowConditionSpanByRow(
|
|
93
|
+
spans: RowConditionSpan[],
|
|
94
|
+
): Map<number, RowConditionSpan> {
|
|
95
|
+
const map = new Map<number, RowConditionSpan>()
|
|
96
|
+
for (const span of spans) {
|
|
97
|
+
map.set(span.startRow, span)
|
|
98
|
+
}
|
|
99
|
+
return map
|
|
100
|
+
}
|