@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.
Files changed (324) hide show
  1. package/dist/__tests__/components/AppTopBar.navigation.test.d.ts +1 -0
  2. package/dist/__tests__/components/DoseCalculatorVolumeField.test.d.ts +1 -0
  3. package/dist/__tests__/components/PlateMapEditorToolbarInternal.test.d.ts +1 -0
  4. package/dist/__tests__/components/PluginWorkspaceView.controls.test.d.ts +1 -0
  5. package/dist/__tests__/components/PluginWorkspaceView.navigation.test.d.ts +1 -0
  6. package/dist/__tests__/components/PluginWorkspaceView.shell.test.d.ts +1 -0
  7. package/dist/__tests__/components/ProtocolStep.presentation.test.d.ts +1 -0
  8. package/dist/__tests__/components/ProtocolStepEditor.state.test.d.ts +1 -0
  9. package/dist/__tests__/components/ProtocolStepParameterField.test.d.ts +1 -0
  10. package/dist/__tests__/components/ReagentList.presentation.test.d.ts +1 -0
  11. package/dist/__tests__/components/SampleSelector.colors.test.d.ts +1 -0
  12. package/dist/__tests__/components/SampleSelector.drag.test.d.ts +1 -0
  13. package/dist/__tests__/components/SampleSelector.groups.test.d.ts +1 -0
  14. package/dist/__tests__/components/SampleSelector.selection.test.d.ts +1 -0
  15. package/dist/__tests__/components/SampleSelectorSampleRow.test.d.ts +1 -0
  16. package/dist/__tests__/components/ScheduleCalendar.test.d.ts +1 -0
  17. package/dist/__tests__/components/SettingsModal.schema.test.d.ts +1 -0
  18. package/dist/__tests__/components/WellPlate.colors.test.d.ts +1 -0
  19. package/dist/__tests__/components/WellPlate.conditions.test.d.ts +1 -0
  20. package/dist/__tests__/components/WellPlate.geometry.test.d.ts +1 -0
  21. package/dist/__tests__/components/WellPlate.interaction.test.d.ts +1 -0
  22. package/dist/__tests__/components/WellPlate.legend.test.d.ts +1 -0
  23. package/dist/__tests__/components/WellPlate.rendering.test.d.ts +1 -0
  24. package/dist/__tests__/components/WellPlate.sampleDrop.test.d.ts +1 -0
  25. package/dist/__tests__/composables/autoGroup/classify.test.d.ts +1 -0
  26. package/dist/__tests__/composables/autoGroup/columns.test.d.ts +1 -0
  27. package/dist/__tests__/composables/autoGroup/compose.test.d.ts +1 -0
  28. package/dist/__tests__/composables/autoGroup/cooccurrence.test.d.ts +1 -0
  29. package/dist/__tests__/composables/autoGroup/fingerprint.test.d.ts +1 -0
  30. package/dist/__tests__/composables/autoGroup/integration.test.d.ts +1 -0
  31. package/dist/__tests__/composables/autoGroup/template.test.d.ts +1 -0
  32. package/dist/__tests__/composables/autoGroup/tokenize.test.d.ts +1 -0
  33. package/dist/__tests__/composables/useAutoGroupInputSources.test.d.ts +1 -0
  34. package/dist/__tests__/composables/useScheduleCalendarLayout.test.d.ts +1 -0
  35. package/dist/__tests__/docs/extractDocsComponents.test.d.ts +1 -0
  36. package/dist/__tests__/docs/extractDocsExports.test.d.ts +1 -0
  37. package/dist/__tests__/docs/extractDocsParsing.test.d.ts +1 -0
  38. package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +1 -0
  39. package/dist/__tests__/docs/extractDocsTheme.test.d.ts +1 -0
  40. package/dist/components/AppSidebar.vue.d.ts +9 -6
  41. package/dist/components/AppTopBar.navigation.d.ts +11 -0
  42. package/dist/components/BaseButton.vue.d.ts +1 -1
  43. package/dist/components/BaseCheckbox.vue.d.ts +7 -2
  44. package/dist/components/BaseInput.vue.d.ts +2 -2
  45. package/dist/components/BasePill.vue.d.ts +2 -2
  46. package/dist/components/BaseRadioGroup.vue.d.ts +2 -2
  47. package/dist/components/BaseSelect.vue.d.ts +1 -1
  48. package/dist/components/BaseSlider.vue.d.ts +2 -2
  49. package/dist/components/BaseTextarea.vue.d.ts +2 -2
  50. package/dist/components/BaseToggle.vue.d.ts +7 -2
  51. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
  52. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
  53. package/dist/components/CollapsibleCard.vue.d.ts +9 -0
  54. package/dist/components/ColorSlider.vue.d.ts +2 -2
  55. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  56. package/dist/components/ControlWorkspaceView.vue.d.ts +4 -4
  57. package/dist/components/DatePicker.vue.d.ts +1 -1
  58. package/dist/components/DateTimePicker.vue.d.ts +2 -2
  59. package/dist/components/DoseCalculatorVolumeField.vue.d.ts +15 -0
  60. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +2 -2
  61. package/dist/components/DropdownButton.vue.d.ts +1 -1
  62. package/dist/components/FileUploader.vue.d.ts +2 -2
  63. package/dist/components/FormulaInput.vue.d.ts +2 -2
  64. package/dist/components/IconButton.vue.d.ts +1 -1
  65. package/dist/components/LoadingSpinner.vue.d.ts +1 -1
  66. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  67. package/dist/components/MultiSelect.vue.d.ts +1 -1
  68. package/dist/components/NumberInput.vue.d.ts +1 -1
  69. package/dist/components/PlateMapEditor.vue.d.ts +6 -6
  70. package/dist/components/PluginWorkspaceView.controls.d.ts +28 -0
  71. package/dist/components/PluginWorkspaceView.navigation.d.ts +29 -0
  72. package/dist/components/PluginWorkspaceView.props.d.ts +151 -0
  73. package/dist/components/PluginWorkspaceView.shell.d.ts +19 -0
  74. package/dist/components/PluginWorkspaceView.vue.d.ts +51 -196
  75. package/dist/components/ProgressBar.vue.d.ts +1 -1
  76. package/dist/components/ProtocolStep.presentation.d.ts +4 -0
  77. package/dist/components/ProtocolStepEditor.state.d.ts +18 -0
  78. package/dist/components/ProtocolStepParameterField.vue.d.ts +12 -0
  79. package/dist/components/ReagentList.presentation.d.ts +16 -0
  80. package/dist/components/ResourceCard.vue.d.ts +1 -1
  81. package/dist/components/SampleSelector.colors.d.ts +13 -0
  82. package/dist/components/SampleSelector.drag.d.ts +24 -0
  83. package/dist/components/SampleSelector.groups.d.ts +15 -0
  84. package/dist/components/SampleSelector.selection.d.ts +26 -0
  85. package/dist/components/SampleSelector.vue.d.ts +4 -1
  86. package/dist/components/SampleSelectorSampleRow.vue.d.ts +21 -0
  87. package/dist/components/SegmentedControl.vue.d.ts +1 -1
  88. package/dist/components/SequenceInput.vue.d.ts +2 -2
  89. package/dist/components/SequenceProgressBar.vue.d.ts +1 -1
  90. package/dist/components/SettingsModal.schema.d.ts +9 -0
  91. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  92. package/dist/components/TagsInput.vue.d.ts +2 -2
  93. package/dist/components/TimePicker.vue.d.ts +2 -2
  94. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  95. package/dist/components/UnitInput.vue.d.ts +2 -2
  96. package/dist/components/WellPlate.colors.d.ts +9 -0
  97. package/dist/components/WellPlate.conditions.d.ts +26 -0
  98. package/dist/components/WellPlate.geometry.d.ts +23 -0
  99. package/dist/components/WellPlate.interaction.d.ts +71 -0
  100. package/dist/components/WellPlate.legend.d.ts +2 -0
  101. package/dist/components/WellPlate.rendering.d.ts +24 -0
  102. package/dist/components/WellPlate.sampleDrop.d.ts +8 -0
  103. package/dist/components/WellPlate.vue.d.ts +1 -1
  104. package/dist/components/index.js +2 -2
  105. package/dist/components/internal/ActionItemInternal.vue.d.ts +1 -1
  106. package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +28 -0
  107. package/dist/{components-DafPc4rM.js → components-Blx4MG--.js} +4073 -4222
  108. package/dist/components-Blx4MG--.js.map +1 -0
  109. package/dist/composables/autoGroup/classKey.d.ts +4 -0
  110. package/dist/composables/autoGroup/classify.d.ts +28 -0
  111. package/dist/composables/autoGroup/colors.d.ts +2 -0
  112. package/dist/composables/autoGroup/columns.d.ts +10 -0
  113. package/dist/composables/autoGroup/compose.d.ts +8 -0
  114. package/dist/composables/autoGroup/cooccurrence.d.ts +2 -0
  115. package/dist/composables/autoGroup/csv-shim.d.ts +2 -0
  116. package/dist/composables/autoGroup/fingerprint.d.ts +3 -0
  117. package/dist/composables/autoGroup/index.d.ts +16 -0
  118. package/dist/composables/autoGroup/replicatePreGroup.d.ts +38 -0
  119. package/dist/composables/autoGroup/template.d.ts +15 -0
  120. package/dist/composables/autoGroup/tokenize.d.ts +8 -0
  121. package/dist/composables/autoGroupConstants.d.ts +1 -0
  122. package/dist/composables/autoGroupGrouping.d.ts +3 -0
  123. package/dist/composables/controlComponentBindings.d.ts +1 -1
  124. package/dist/composables/controlSchemaAdapters.d.ts +1 -1
  125. package/dist/composables/controlSchemaDoseDesign.d.ts +11 -0
  126. package/dist/composables/controlSchemaLayout.d.ts +1 -1
  127. package/dist/composables/controlSchemaModel.d.ts +5 -0
  128. package/dist/composables/controlSchemaNormalize.d.ts +1 -1
  129. package/dist/composables/controlSchemaTypes.d.ts +311 -0
  130. package/dist/composables/controlWorkspaceOptions.d.ts +1 -1
  131. package/dist/composables/formBuilderSchema.d.ts +18 -0
  132. package/dist/composables/index.js +3 -3
  133. package/dist/composables/pluginEndpointBuilder.d.ts +13 -0
  134. package/dist/composables/protocolTemplateCatalog.d.ts +26 -0
  135. package/dist/composables/useAutoGroup.d.ts +61 -74
  136. package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
  137. package/dist/composables/useBioTemplateControls.d.ts +1 -1
  138. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +1 -1
  139. package/dist/composables/useBioTemplateWorkspace.d.ts +1 -1
  140. package/dist/composables/useControlSchema.d.ts +4 -316
  141. package/dist/composables/useControlWorkspace.d.ts +1 -1
  142. package/dist/composables/useForm.d.ts +2 -33
  143. package/dist/composables/useFormBuilder.d.ts +2 -9
  144. package/dist/composables/useFormValidation.d.ts +34 -0
  145. package/dist/composables/usePluginClient.d.ts +1 -4
  146. package/dist/composables/useProtocolTemplates.d.ts +2 -24
  147. package/dist/composables/useScheduleCalendarLayout.d.ts +49 -0
  148. package/dist/{composables-BMkPQhVK.js → composables-CHDjDIQT.js} +33 -31
  149. package/dist/composables-CHDjDIQT.js.map +1 -0
  150. package/dist/index.js +4 -4
  151. package/dist/install.js +2 -2
  152. package/dist/styles.css +1432 -660
  153. package/dist/templates/controlSchemaTypes.d.ts +1 -1
  154. package/dist/templates/index.js +1 -1
  155. package/dist/templates/templateAdapterTypes.d.ts +48 -0
  156. package/dist/templates/templateCreateOptions.d.ts +165 -0
  157. package/dist/templates/templateQpcrTypes.d.ts +42 -0
  158. package/dist/templates/types.d.ts +5 -250
  159. package/dist/{templates-bUAWMn5L.js → templates-DSbHJC4v.js} +1536 -297
  160. package/dist/templates-DSbHJC4v.js.map +1 -0
  161. package/dist/types/auto-group.d.ts +79 -9
  162. package/dist/types/componentLabTypes.d.ts +161 -0
  163. package/dist/types/componentWorkflowTypes.d.ts +150 -0
  164. package/dist/types/components.d.ts +14 -311
  165. package/dist/types/form-builder.d.ts +3 -0
  166. package/dist/types/index.d.ts +1 -1
  167. package/dist/{useProtocolTemplates-QZtHFFH2.js → useProtocolTemplates-BbPOYPzO.js} +1220 -454
  168. package/dist/useProtocolTemplates-BbPOYPzO.js.map +1 -0
  169. package/package.json +1 -1
  170. package/src/__tests__/components/AppSidebar.test.ts +67 -0
  171. package/src/__tests__/components/AppTopBar.navigation.test.ts +70 -0
  172. package/src/__tests__/components/CollapsibleCard.test.ts +47 -0
  173. package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
  174. package/src/__tests__/components/FormBuilder.test.ts +57 -0
  175. package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
  176. package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
  177. package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
  178. package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +43 -0
  179. package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
  180. package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
  181. package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
  182. package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
  183. package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
  184. package/src/__tests__/components/SampleSelector.drag.test.ts +100 -0
  185. package/src/__tests__/components/SampleSelector.groups.test.ts +81 -0
  186. package/src/__tests__/components/SampleSelector.selection.test.ts +70 -0
  187. package/src/__tests__/components/SampleSelector.test.ts +32 -0
  188. package/src/__tests__/components/SampleSelectorSampleRow.test.ts +37 -0
  189. package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
  190. package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
  191. package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
  192. package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
  193. package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
  194. package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
  195. package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
  196. package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
  197. package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
  198. package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
  199. package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
  200. package/src/__tests__/composables/autoGroup/compose.test.ts +227 -0
  201. package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
  202. package/src/__tests__/composables/autoGroup/fingerprint.test.ts +50 -0
  203. package/src/__tests__/composables/autoGroup/integration.test.ts +79 -0
  204. package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
  205. package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
  206. package/src/__tests__/composables/useAutoGroup.test.ts +129 -625
  207. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +107 -0
  208. package/src/__tests__/composables/useControlSchema.test.ts +23 -0
  209. package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
  210. package/src/__tests__/docs/extractDocsComponents.test.ts +142 -0
  211. package/src/__tests__/docs/extractDocsExports.test.ts +77 -0
  212. package/src/__tests__/docs/extractDocsParsing.test.ts +69 -0
  213. package/src/__tests__/docs/extractDocsTemplates.test.ts +54 -0
  214. package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
  215. package/src/__tests__/docs/frontendDocsCatalog.test.ts +1 -1
  216. package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
  217. package/src/components/AppSidebar.story.vue +79 -6
  218. package/src/components/AppSidebar.vue +74 -6
  219. package/src/components/AppTopBar.navigation.ts +62 -0
  220. package/src/components/AppTopBar.vue +17 -44
  221. package/src/components/AutoGroupModal.story.vue +50 -0
  222. package/src/components/AutoGroupModal.vue +441 -158
  223. package/src/components/BaseCheckbox.story.vue +27 -0
  224. package/src/components/BaseCheckbox.vue +63 -1
  225. package/src/components/BaseToggle.story.vue +27 -0
  226. package/src/components/BaseToggle.vue +66 -1
  227. package/src/components/CollapsibleCard.vue +123 -45
  228. package/src/components/ControlWorkspaceView.vue +2 -6
  229. package/src/components/DoseCalculator.vue +13 -73
  230. package/src/components/DoseCalculatorVolumeField.vue +61 -0
  231. package/src/components/ExperimentTimeline.vue +6 -31
  232. package/src/components/FormBuilder.story.vue +13 -0
  233. package/src/components/FormBuilder.vue +2 -7
  234. package/src/components/PlateMapEditor.vue +32 -106
  235. package/src/components/PluginWorkspaceView.controls.ts +182 -0
  236. package/src/components/PluginWorkspaceView.navigation.ts +106 -0
  237. package/src/components/PluginWorkspaceView.props.ts +174 -0
  238. package/src/components/PluginWorkspaceView.shell.ts +67 -0
  239. package/src/components/PluginWorkspaceView.vue +88 -404
  240. package/src/components/ProtocolStep.presentation.ts +31 -0
  241. package/src/components/ProtocolStepEditor.state.ts +104 -0
  242. package/src/components/ProtocolStepEditor.vue +48 -179
  243. package/src/components/ProtocolStepParameterField.vue +134 -0
  244. package/src/components/ReagentList.presentation.ts +105 -0
  245. package/src/components/ReagentList.vue +16 -79
  246. package/src/components/SampleSelector.colors.ts +43 -0
  247. package/src/components/SampleSelector.drag.ts +164 -0
  248. package/src/components/SampleSelector.groups.ts +109 -0
  249. package/src/components/SampleSelector.selection.ts +103 -0
  250. package/src/components/SampleSelector.vue +82 -349
  251. package/src/components/SampleSelectorSampleRow.vue +64 -0
  252. package/src/components/ScheduleCalendar.vue +44 -199
  253. package/src/components/SettingsModal.schema.ts +71 -0
  254. package/src/components/SettingsModal.vue +16 -46
  255. package/src/components/WellPlate.colors.ts +56 -0
  256. package/src/components/WellPlate.conditions.ts +100 -0
  257. package/src/components/WellPlate.geometry.ts +91 -0
  258. package/src/components/WellPlate.interaction.ts +272 -0
  259. package/src/components/WellPlate.legend.ts +8 -0
  260. package/src/components/WellPlate.rendering.ts +105 -0
  261. package/src/components/WellPlate.sampleDrop.ts +73 -0
  262. package/src/components/WellPlate.vue +102 -550
  263. package/src/components/internal/FormFieldRendererInternal.vue +23 -5
  264. package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
  265. package/src/composables/autoGroup/classKey.ts +5 -0
  266. package/src/composables/autoGroup/classify.ts +205 -0
  267. package/src/composables/autoGroup/colors.ts +6 -0
  268. package/src/composables/autoGroup/columns.ts +226 -0
  269. package/src/composables/autoGroup/compose.ts +156 -0
  270. package/src/composables/autoGroup/cooccurrence.ts +46 -0
  271. package/src/composables/autoGroup/csv-shim.ts +44 -0
  272. package/src/composables/autoGroup/fingerprint.ts +49 -0
  273. package/src/composables/autoGroup/index.ts +20 -0
  274. package/src/composables/autoGroup/replicatePreGroup.ts +90 -0
  275. package/src/composables/autoGroup/template.ts +126 -0
  276. package/src/composables/autoGroup/tokenize.ts +41 -0
  277. package/src/composables/autoGroup/vocab.json +67 -0
  278. package/src/composables/autoGroupConstants.ts +4 -0
  279. package/src/composables/autoGroupGrouping.ts +148 -0
  280. package/src/composables/controlComponentBindings.ts +1 -1
  281. package/src/composables/controlSchemaAdapters.ts +4 -1
  282. package/src/composables/controlSchemaDoseDesign.ts +215 -0
  283. package/src/composables/controlSchemaFormFields.ts +4 -1
  284. package/src/composables/controlSchemaLayout.ts +1 -1
  285. package/src/composables/controlSchemaModel.ts +163 -0
  286. package/src/composables/controlSchemaNormalize.ts +1 -1
  287. package/src/composables/controlSchemaTypes.ts +372 -0
  288. package/src/composables/controlWorkspaceOptions.ts +1 -1
  289. package/src/composables/formBuilderSchema.ts +153 -0
  290. package/src/composables/pluginEndpointBuilder.ts +203 -0
  291. package/src/composables/protocolTemplateCatalog.ts +325 -0
  292. package/src/composables/useAutoGroup.ts +395 -549
  293. package/src/composables/useAutoGroupInputSources.ts +147 -0
  294. package/src/composables/useBioTemplateControls.ts +1 -1
  295. package/src/composables/useBioTemplatePresetWorkspace.ts +1 -1
  296. package/src/composables/useBioTemplateWorkspace.ts +1 -1
  297. package/src/composables/useControlSchema.ts +21 -692
  298. package/src/composables/useControlWorkspace.ts +7 -13
  299. package/src/composables/useForm.ts +5 -187
  300. package/src/composables/useFormBuilder.ts +11 -153
  301. package/src/composables/useFormValidation.ts +154 -0
  302. package/src/composables/usePluginClient.ts +10 -193
  303. package/src/composables/useProtocolTemplates.ts +10 -328
  304. package/src/composables/useScheduleCalendarLayout.ts +287 -0
  305. package/src/styles/components/app-sidebar.css +134 -6
  306. package/src/styles/components/auto-group-modal.css +248 -310
  307. package/src/styles/components/checkbox.css +87 -0
  308. package/src/styles/components/collapsible-card.css +154 -14
  309. package/src/styles/components/toggle.css +80 -0
  310. package/src/templates/controlSchemaTypes.ts +1 -1
  311. package/src/templates/templateAdapterTypes.ts +58 -0
  312. package/src/templates/templateCreateOptions.ts +208 -0
  313. package/src/templates/templateQpcrTypes.ts +48 -0
  314. package/src/templates/types.ts +79 -275
  315. package/src/types/auto-group.ts +107 -9
  316. package/src/types/componentLabTypes.ts +235 -0
  317. package/src/types/componentWorkflowTypes.ts +190 -0
  318. package/src/types/components.ts +95 -424
  319. package/src/types/form-builder.ts +3 -0
  320. package/src/types/index.ts +2 -0
  321. package/dist/components-DafPc4rM.js.map +0 -1
  322. package/dist/composables-BMkPQhVK.js.map +0 -1
  323. package/dist/templates-bUAWMn5L.js.map +0 -1
  324. package/dist/useProtocolTemplates-QZtHFFH2.js.map +0 -1
@@ -4,6 +4,10 @@ import BaseCheckbox from './BaseCheckbox.vue'
4
4
 
5
5
  const sizes = ['sm', 'md', 'lg'] as const
6
6
  const checked = ref(true)
7
+ const icons = {
8
+ database: ['M12 3c4.418 0 8 1.343 8 3s-3.582 3-8 3-8-1.343-8-3z', 'M4 6v6c0 1.657 3.582 3 8 3s8-1.343 8-3V6', 'M4 12v6c0 1.657 3.582 3 8 3s8-1.343 8-3v-6'],
9
+ file: ['M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z', 'M14 2v6h6', 'M16 13H8', 'M16 17H8'],
10
+ }
7
11
  </script>
8
12
 
9
13
  <template>
@@ -53,6 +57,29 @@ const checked = ref(true)
53
57
  </div>
54
58
  </Variant>
55
59
 
60
+ <Variant title="Choice Rows">
61
+ <div style="padding: 2rem; max-width: 360px; display: flex; flex-direction: column; gap: 0.5rem;">
62
+ <BaseCheckbox
63
+ v-model="checked"
64
+ variant="row"
65
+ label="Include metadata"
66
+ description="Add method and sample context"
67
+ :icon="icons.database"
68
+ icon-color="#10b981"
69
+ icon-bg="#d1fae5"
70
+ />
71
+ <BaseCheckbox
72
+ :model-value="false"
73
+ variant="row"
74
+ label="Attach report"
75
+ description="Include the rendered summary PDF"
76
+ :icon="icons.file"
77
+ icon-color="#6366f1"
78
+ icon-bg="#e0e7ff"
79
+ />
80
+ </div>
81
+ </Variant>
82
+
56
83
  <Variant title="Disabled States">
57
84
  <div style="padding: 2rem; display: flex; flex-direction: column; gap: 1rem;">
58
85
  <BaseCheckbox :model-value="false" label="Disabled unchecked" disabled />
@@ -1,16 +1,24 @@
1
1
  <script setup lang="ts">
2
2
  /** Renders a styled checkbox with an optional label, size variants, and disabled state. */
3
+ import Tooltip from './Tooltip.vue'
4
+
3
5
  interface Props {
4
6
  modelValue?: boolean
5
7
  label?: string
8
+ description?: string
9
+ icon?: string | string[]
10
+ iconColor?: string
11
+ iconBg?: string
6
12
  disabled?: boolean
7
13
  size?: 'sm' | 'md' | 'lg'
14
+ variant?: 'default' | 'row'
8
15
  }
9
16
 
10
17
  const props = withDefaults(defineProps<Props>(), {
11
18
  modelValue: false,
12
19
  disabled: false,
13
20
  size: 'md',
21
+ variant: 'default',
14
22
  })
15
23
 
16
24
  const emit = defineEmits<{
@@ -21,15 +29,69 @@ function handleChange(event: Event) {
21
29
  const target = event.target as HTMLInputElement
22
30
  emit('update:modelValue', target.checked)
23
31
  }
32
+
33
+ function isSvgIcon(icon?: string | string[]): boolean {
34
+ if (!icon) return false
35
+ return Array.isArray(icon) || icon.startsWith('M') || icon.startsWith('m')
36
+ }
37
+
38
+ function iconStyle(): Record<string, string> {
39
+ return {
40
+ color: props.iconColor ?? (props.modelValue ? 'var(--color-primary)' : 'var(--text-muted)'),
41
+ }
42
+ }
24
43
  </script>
25
44
 
26
45
  <template>
27
46
  <label
28
47
  :class="[
29
48
  'mint-checkbox',
49
+ `mint-checkbox--${props.variant}`,
30
50
  props.disabled ? 'mint-checkbox--disabled' : '',
51
+ props.modelValue ? 'mint-checkbox--checked' : '',
31
52
  ]"
32
53
  >
54
+ <span
55
+ v-if="props.variant === 'row' && props.icon"
56
+ class="mint-checkbox__row-icon"
57
+ :style="iconStyle()"
58
+ aria-hidden="true"
59
+ >
60
+ <svg
61
+ v-if="isSvgIcon(props.icon)"
62
+ class="mint-checkbox__row-svg"
63
+ viewBox="0 0 24 24"
64
+ fill="none"
65
+ stroke="currentColor"
66
+ stroke-width="2"
67
+ stroke-linecap="round"
68
+ stroke-linejoin="round"
69
+ >
70
+ <template v-if="Array.isArray(props.icon)">
71
+ <path v-for="(d, i) in props.icon" :key="i" :d="d" />
72
+ </template>
73
+ <path v-else :d="props.icon" />
74
+ </svg>
75
+ <span v-else class="mint-checkbox__row-text-icon">{{ props.icon }}</span>
76
+ </span>
77
+ <span
78
+ v-if="props.variant === 'row' && props.label"
79
+ class="mint-checkbox__copy"
80
+ >
81
+ <Tooltip
82
+ v-if="props.description"
83
+ :text="props.description"
84
+ position="right"
85
+ max-width="16rem"
86
+ >
87
+ <span :class="['mint-checkbox__label', `mint-checkbox__label--${props.size}`, 'mint-checkbox__label--tooltip']">
88
+ {{ props.label }}
89
+ </span>
90
+ </Tooltip>
91
+ <span v-else :class="['mint-checkbox__label', `mint-checkbox__label--${props.size}`]">
92
+ {{ props.label }}
93
+ </span>
94
+ </span>
33
95
  <div class="mint-checkbox__input-wrapper">
34
96
  <input
35
97
  type="checkbox"
@@ -58,7 +120,7 @@ function handleChange(event: Event) {
58
120
  </svg>
59
121
  </div>
60
122
  </div>
61
- <span v-if="props.label" :class="['mint-checkbox__label', `mint-checkbox__label--${props.size}`]">
123
+ <span v-if="props.label && props.variant !== 'row'" :class="['mint-checkbox__label', `mint-checkbox__label--${props.size}`]">
62
124
  {{ props.label }}
63
125
  </span>
64
126
  </label>
@@ -5,6 +5,10 @@ import BaseToggle from './BaseToggle.vue'
5
5
  const sizes = ['sm', 'md', 'lg'] as const
6
6
  const toggleOn = ref(true)
7
7
  const toggleOff = ref(false)
8
+ const icons = {
9
+ eye: ['M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z', 'M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z'],
10
+ chart: ['M3 3v18h18', 'M7 15l3-3 3 2 4-6'],
11
+ }
8
12
  </script>
9
13
 
10
14
  <template>
@@ -54,6 +58,29 @@ const toggleOff = ref(false)
54
58
  </div>
55
59
  </Variant>
56
60
 
61
+ <Variant title="Choice Rows">
62
+ <div style="padding: 2rem; max-width: 360px; display: flex; flex-direction: column; gap: 0.5rem;">
63
+ <BaseToggle
64
+ v-model="toggleOn"
65
+ variant="row"
66
+ label="Show outliers"
67
+ description="Keep flagged peaks visible"
68
+ :icon="icons.eye"
69
+ icon-color="#0ea5e9"
70
+ icon-bg="#e0f2fe"
71
+ />
72
+ <BaseToggle
73
+ v-model="toggleOff"
74
+ variant="row"
75
+ label="Log scale"
76
+ description="Compress high-intensity spread"
77
+ :icon="icons.chart"
78
+ icon-color="#8b5cf6"
79
+ icon-bg="#ede9fe"
80
+ />
81
+ </div>
82
+ </Variant>
83
+
57
84
  <Variant title="Without Label">
58
85
  <div style="padding: 2rem; display: flex; gap: 1.5rem; align-items: center;">
59
86
  <BaseToggle v-model="toggleOn" size="sm" />
@@ -1,11 +1,18 @@
1
1
  <script setup lang="ts">
2
2
  /** Renders an on/off toggle switch with label, size, and keyboard interaction support. */
3
+ import Tooltip from './Tooltip.vue'
4
+
3
5
  interface Props {
4
6
  modelValue?: boolean
5
7
  label?: string
8
+ description?: string
9
+ icon?: string | string[]
10
+ iconColor?: string
11
+ iconBg?: string
6
12
  disabled?: boolean
7
13
  size?: 'sm' | 'md' | 'lg'
8
14
  reverse?: boolean
15
+ variant?: 'default' | 'row'
9
16
  }
10
17
 
11
18
  const props = withDefaults(defineProps<Props>(), {
@@ -13,6 +20,7 @@ const props = withDefaults(defineProps<Props>(), {
13
20
  disabled: false,
14
21
  size: 'md',
15
22
  reverse: false,
23
+ variant: 'default',
16
24
  })
17
25
 
18
26
  const emit = defineEmits<{
@@ -31,17 +39,74 @@ function handleKeydown(event: KeyboardEvent) {
31
39
  toggle()
32
40
  }
33
41
  }
42
+
43
+ function isSvgIcon(icon?: string | string[]): boolean {
44
+ if (!icon) return false
45
+ return Array.isArray(icon) || icon.startsWith('M') || icon.startsWith('m')
46
+ }
47
+
48
+ function iconStyle(): Record<string, string> {
49
+ return {
50
+ color: props.iconColor ?? (props.modelValue ? 'var(--color-primary)' : 'var(--text-muted)'),
51
+ }
52
+ }
34
53
  </script>
35
54
 
36
55
  <template>
37
56
  <div
38
57
  :class="[
39
58
  'mint-toggle',
59
+ `mint-toggle--${variant}`,
40
60
  reverse ? 'mint-toggle--reverse' : '',
41
61
  disabled ? 'mint-toggle--disabled' : '',
62
+ modelValue ? 'mint-toggle--checked' : '',
42
63
  ]"
43
64
  @click="toggle"
44
65
  >
66
+ <span
67
+ v-if="variant === 'row' && icon"
68
+ class="mint-toggle__row-icon"
69
+ :style="iconStyle()"
70
+ aria-hidden="true"
71
+ >
72
+ <svg
73
+ v-if="isSvgIcon(icon)"
74
+ class="mint-toggle__row-svg"
75
+ viewBox="0 0 24 24"
76
+ fill="none"
77
+ stroke="currentColor"
78
+ stroke-width="2"
79
+ stroke-linecap="round"
80
+ stroke-linejoin="round"
81
+ >
82
+ <template v-if="Array.isArray(icon)">
83
+ <path v-for="(d, i) in icon" :key="i" :d="d" />
84
+ </template>
85
+ <path v-else :d="icon" />
86
+ </svg>
87
+ <span v-else class="mint-toggle__row-text-icon">{{ icon }}</span>
88
+ </span>
89
+ <span
90
+ v-if="variant === 'row' && label"
91
+ class="mint-toggle__copy"
92
+ >
93
+ <Tooltip
94
+ v-if="description"
95
+ :text="description"
96
+ position="right"
97
+ max-width="16rem"
98
+ >
99
+ <span :class="['mint-toggle__label', `mint-toggle__label--${size}`, 'mint-toggle__label--tooltip']">
100
+ {{ label }}
101
+ </span>
102
+ </Tooltip>
103
+ <span
104
+ v-else
105
+ :class="['mint-toggle__label', `mint-toggle__label--${size}`]"
106
+ >
107
+ {{ label }}
108
+ </span>
109
+ </span>
45
110
  <div
46
111
  role="switch"
47
112
  :tabindex="disabled ? -1 : 0"
@@ -63,7 +128,7 @@ function handleKeydown(event: KeyboardEvent) {
63
128
  />
64
129
  </div>
65
130
  <span
66
- v-if="label"
131
+ v-if="label && variant !== 'row'"
67
132
  :class="['mint-toggle__label', `mint-toggle__label--${size}`]"
68
133
  >
69
134
  {{ label }}
@@ -1,25 +1,32 @@
1
1
  <script setup lang="ts">
2
2
  /** Card whose body collapses with a chevron-rotate animation, with an optional inline toggle switch independent of expand state. */
3
3
  import { ref, computed } from 'vue'
4
+ import type { SidebarBadgeTone, SidebarToolSectionAction } from '../types'
4
5
 
5
6
  interface Props {
6
7
  title: string
7
8
  subtitle?: string
9
+ badge?: string | number
10
+ badgeTone?: SidebarBadgeTone
8
11
  defaultOpen?: boolean
9
12
  disabled?: boolean
10
13
  dense?: boolean
11
14
  icon?: string | string[]
12
15
  iconColor?: string
13
16
  iconBg?: string
17
+ actions?: SidebarToolSectionAction[]
14
18
  showToggle?: boolean
15
19
  toggleValue?: boolean
16
20
  toggleColor?: string
17
21
  }
18
22
 
19
23
  const props = withDefaults(defineProps<Props>(), {
24
+ badge: undefined,
25
+ badgeTone: 'cta',
20
26
  defaultOpen: false,
21
27
  disabled: false,
22
28
  dense: false,
29
+ actions: () => [],
23
30
  showToggle: false,
24
31
  toggleValue: false,
25
32
  toggleColor: '',
@@ -27,6 +34,7 @@ const props = withDefaults(defineProps<Props>(), {
27
34
 
28
35
  const emit = defineEmits<{
29
36
  'update:toggleValue': [value: boolean]
37
+ action: [actionId: string]
30
38
  }>()
31
39
 
32
40
  const isOpen = ref(props.defaultOpen)
@@ -55,10 +63,7 @@ const iconColorStyle = computed(() => ({
55
63
  color: props.iconColor || 'var(--color-primary)',
56
64
  }))
57
65
 
58
- const isSvgIcon = computed(() => {
59
- if (!props.icon) return false
60
- return Array.isArray(props.icon) || props.icon.startsWith('M') || props.icon.startsWith('m')
61
- })
66
+ const isSvgIcon = computed(() => isSvgIconValue(props.icon))
62
67
 
63
68
  const toggleTrackStyle = computed(() => {
64
69
  if (!props.toggleValue || !props.toggleColor) return {}
@@ -67,47 +72,111 @@ const toggleTrackStyle = computed(() => {
67
72
  borderColor: props.toggleColor,
68
73
  }
69
74
  })
75
+
76
+ const badgeClasses = computed(() => [
77
+ 'mint-collapsible-card__badge',
78
+ `mint-collapsible-card__badge--${props.badgeTone}`,
79
+ ])
80
+
81
+ function isSvgIconValue(icon?: string | string[]): boolean {
82
+ if (!icon) return false
83
+ return Array.isArray(icon) || icon.startsWith('M') || icon.startsWith('m')
84
+ }
85
+
86
+ function actionClasses(action: SidebarToolSectionAction): string[] {
87
+ return [
88
+ 'mint-collapsible-card__action',
89
+ action.tone ? `mint-collapsible-card__action--${action.tone}` : '',
90
+ action.disabled ? 'mint-collapsible-card__action--disabled' : '',
91
+ ]
92
+ }
93
+
94
+ function actionStyle(action: SidebarToolSectionAction): Record<string, string> {
95
+ return action.iconColor ? { color: action.iconColor } : {}
96
+ }
97
+
98
+ function handleActionClick(action: SidebarToolSectionAction) {
99
+ if (props.disabled || action.disabled) return
100
+ emit('action', action.id)
101
+ }
70
102
  </script>
71
103
 
72
104
  <template>
73
105
  <div :class="['mint-collapsible-card', dense ? 'mint-collapsible-card--dense' : '']">
74
- <button
75
- type="button"
76
- :class="headerClasses"
77
- :disabled="disabled"
78
- :aria-expanded="isOpen"
79
- @click="toggle"
80
- >
81
- <div class="mint-collapsible-card__title-section">
82
- <!-- Icon badge -->
83
- <div v-if="icon" class="mint-collapsible-card__icon-badge" :style="iconBgStyle">
106
+ <div class="mint-collapsible-card__header-shell">
107
+ <button
108
+ type="button"
109
+ :class="headerClasses"
110
+ :disabled="disabled"
111
+ :aria-expanded="isOpen"
112
+ @click="toggle"
113
+ >
114
+ <div class="mint-collapsible-card__title-section">
115
+ <!-- Icon badge -->
116
+ <div v-if="icon" class="mint-collapsible-card__icon-badge" :style="iconBgStyle">
117
+ <svg
118
+ v-if="isSvgIcon"
119
+ class="mint-collapsible-card__icon"
120
+ :style="iconColorStyle"
121
+ viewBox="0 0 24 24"
122
+ fill="none"
123
+ stroke="currentColor"
124
+ stroke-width="2"
125
+ stroke-linecap="round"
126
+ stroke-linejoin="round"
127
+ >
128
+ <template v-if="Array.isArray(icon)">
129
+ <path v-for="(d, i) in icon" :key="i" :d="d" />
130
+ </template>
131
+ <path v-else :d="icon" />
132
+ </svg>
133
+ <span v-else class="mint-collapsible-card__icon-text" :style="iconColorStyle">{{ icon }}</span>
134
+ </div>
135
+
136
+ <div class="mint-collapsible-card__titles">
137
+ <h3 class="mint-collapsible-card__title">{{ title }}</h3>
138
+ <p v-if="subtitle" class="mint-collapsible-card__subtitle">{{ subtitle }}</p>
139
+ </div>
140
+ </div>
141
+ </button>
142
+
143
+ <div class="mint-collapsible-card__actions">
144
+ <span v-if="badge !== undefined" :class="badgeClasses">
145
+ {{ badge }}
146
+ </span>
147
+
148
+ <button
149
+ v-for="action in actions"
150
+ :key="action.id"
151
+ type="button"
152
+ :class="actionClasses(action)"
153
+ :style="actionStyle(action)"
154
+ :disabled="disabled || action.disabled"
155
+ :aria-label="action.label"
156
+ :title="action.label"
157
+ @click="handleActionClick(action)"
158
+ >
84
159
  <svg
85
- v-if="isSvgIcon"
86
- class="mint-collapsible-card__icon"
87
- :style="iconColorStyle"
160
+ v-if="action.icon && isSvgIconValue(action.icon)"
161
+ class="mint-collapsible-card__action-icon"
88
162
  viewBox="0 0 24 24"
89
163
  fill="none"
90
164
  stroke="currentColor"
91
165
  stroke-width="2"
92
166
  stroke-linecap="round"
93
167
  stroke-linejoin="round"
168
+ aria-hidden="true"
94
169
  >
95
- <template v-if="Array.isArray(icon)">
96
- <path v-for="(d, i) in icon" :key="i" :d="d" />
170
+ <template v-if="Array.isArray(action.icon)">
171
+ <path v-for="(d, i) in action.icon" :key="i" :d="d" />
97
172
  </template>
98
- <path v-else :d="icon" />
173
+ <path v-else :d="action.icon" />
99
174
  </svg>
100
- <span v-else class="mint-collapsible-card__icon-text" :style="iconColorStyle">{{ icon }}</span>
101
- </div>
102
-
103
- <div class="mint-collapsible-card__titles">
104
- <h3 class="mint-collapsible-card__title">{{ title }}</h3>
105
- <p v-if="subtitle" class="mint-collapsible-card__subtitle">{{ subtitle }}</p>
106
- </div>
107
- </div>
175
+ <span v-else-if="action.icon" class="mint-collapsible-card__action-text-icon">
176
+ {{ action.icon }}
177
+ </span>
178
+ </button>
108
179
 
109
- <div class="mint-collapsible-card__actions">
110
- <!-- Toggle switch -->
111
180
  <div
112
181
  v-if="showToggle"
113
182
  class="mint-collapsible-card__toggle"
@@ -134,23 +203,32 @@ const toggleTrackStyle = computed(() => {
134
203
  </div>
135
204
  </div>
136
205
 
137
- <!-- Chevron -->
138
- <svg
139
- :class="[
140
- 'mint-collapsible-card__chevron',
141
- isOpen ? 'mint-collapsible-card__chevron--open' : '',
142
- ]"
143
- fill="none"
144
- stroke="currentColor"
145
- stroke-width="2"
146
- stroke-linecap="round"
147
- stroke-linejoin="round"
148
- viewBox="0 0 24 24"
206
+ <button
207
+ type="button"
208
+ class="mint-collapsible-card__chevron-button"
209
+ :disabled="disabled"
210
+ :aria-label="isOpen ? 'Collapse section' : 'Expand section'"
211
+ :aria-expanded="isOpen"
212
+ @click="toggle"
149
213
  >
150
- <path d="m6 9 6 6 6-6" />
151
- </svg>
214
+ <svg
215
+ :class="[
216
+ 'mint-collapsible-card__chevron',
217
+ isOpen ? 'mint-collapsible-card__chevron--open' : '',
218
+ ]"
219
+ fill="none"
220
+ stroke="currentColor"
221
+ stroke-width="2"
222
+ stroke-linecap="round"
223
+ stroke-linejoin="round"
224
+ viewBox="0 0 24 24"
225
+ aria-hidden="true"
226
+ >
227
+ <path d="m6 9 6 6 6-6" />
228
+ </svg>
229
+ </button>
152
230
  </div>
153
- </button>
231
+ </div>
154
232
 
155
233
  <Transition name="collapse">
156
234
  <div v-show="isOpen" class="mint-collapsible-card__body">
@@ -13,7 +13,7 @@ import type {
13
13
  ControlWorkspaceOptions,
14
14
  UseControlWorkspaceReturn,
15
15
  } from '../composables/useControlSchema'
16
- import { defineControlModel, mergeControlWorkspaceOptions, useControlWorkspace } from '../composables/useControlSchema'
16
+ import { mergeControlWorkspaceOptions, resolveControlModel, useControlWorkspace } from '../composables/useControlSchema'
17
17
  import AppLayout from './AppLayout.vue'
18
18
  import AppSidebar from './AppSidebar.vue'
19
19
  import AppTopBar from './AppTopBar.vue'
@@ -156,8 +156,7 @@ const props = withDefaults(defineProps<Props>(), {
156
156
 
157
157
  const externalValues = computed(() => props.modelValue ?? props.values)
158
158
  const resolvedModel = computed<ControlModelBinding | undefined>(() => {
159
- if (props.model === undefined) return undefined
160
- return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)
159
+ return resolveControlModel(props.model)
161
160
  })
162
161
  const resolvedControls = computed<ControlSchema>(() => props.controls ?? resolvedModel.value?.controls ?? {})
163
162
  const baseControlOptions = computed<ControlWorkspaceOptions>(() =>
@@ -282,9 +281,6 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
282
281
  return typeof value === 'object' && value !== null && !Array.isArray(value)
283
282
  }
284
283
 
285
- function isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {
286
- return 'controlOptions' in model
287
- }
288
284
  </script>
289
285
 
290
286
  <template>
@@ -9,11 +9,11 @@ import {
9
9
  import {
10
10
  useDoseCalculator,
11
11
  type VolumeValue,
12
- type VolumeUnit,
13
12
  type DilutionResult,
14
13
  type SerialDilutionResult,
15
14
  type WellConcentration,
16
15
  } from '../composables/useDoseCalculator'
16
+ import DoseCalculatorVolumeField from './DoseCalculatorVolumeField.vue'
17
17
 
18
18
  type CalculatorMode = 'dilution' | 'serial' | 'conversion'
19
19
 
@@ -140,30 +140,6 @@ function handleApplyToWells() {
140
140
  }
141
141
  }
142
142
 
143
- // Volume unit change handler
144
- function handleVolumeChange(event: Event, target: 'final' | 'serial') {
145
- const input = event.target as HTMLInputElement
146
- const value = parseFloat(input.value)
147
- if (isNaN(value)) return
148
-
149
- if (target === 'final') {
150
- finalVolume.value = { ...finalVolume.value, value }
151
- } else {
152
- serialVolumePerWell.value = { ...serialVolumePerWell.value, value }
153
- }
154
- }
155
-
156
- function handleVolumeUnitChange(event: Event, target: 'final' | 'serial') {
157
- const select = event.target as HTMLSelectElement
158
- const unit = select.value as VolumeUnit
159
-
160
- if (target === 'final') {
161
- finalVolume.value = { ...finalVolume.value, unit }
162
- } else {
163
- serialVolumePerWell.value = { ...serialVolumePerWell.value, unit }
164
- }
165
- }
166
-
167
143
  // Watch for changes and recalculate
168
144
  watch(
169
145
  [stockConcentration, finalConcentration, finalVolume],
@@ -267,30 +243,12 @@ watch(activeMode, (mode) => {
267
243
  />
268
244
  </div>
269
245
 
270
- <div class="mint-dose-calculator__field">
271
- <label class="mint-dose-calculator__field-label">Final Volume</label>
272
- <div class="mint-dose-calculator__field-row">
273
- <input
274
- type="number"
275
- :value="finalVolume.value"
276
- :disabled="disabled"
277
- class="mint-dose-calculator__input"
278
- min="0"
279
- step="any"
280
- @input="handleVolumeChange($event, 'final')"
281
- />
282
- <select
283
- :value="finalVolume.unit"
284
- :disabled="disabled"
285
- class="mint-dose-calculator__select"
286
- @change="handleVolumeUnitChange($event, 'final')"
287
- >
288
- <option v-for="unit in volumeUnits" :key="unit" :value="unit">
289
- {{ unit }}
290
- </option>
291
- </select>
292
- </div>
293
- </div>
246
+ <DoseCalculatorVolumeField
247
+ v-model="finalVolume"
248
+ label="Final Volume"
249
+ :units="volumeUnits"
250
+ :disabled="disabled"
251
+ />
294
252
 
295
253
  <div class="mint-dose-calculator__divider" />
296
254
 
@@ -367,30 +325,12 @@ watch(activeMode, (mode) => {
367
325
  </div>
368
326
  </div>
369
327
 
370
- <div class="mint-dose-calculator__field">
371
- <label class="mint-dose-calculator__field-label">Volume per Well</label>
372
- <div class="mint-dose-calculator__field-row">
373
- <input
374
- type="number"
375
- :value="serialVolumePerWell.value"
376
- :disabled="disabled"
377
- class="mint-dose-calculator__input"
378
- min="0"
379
- step="any"
380
- @input="handleVolumeChange($event, 'serial')"
381
- />
382
- <select
383
- :value="serialVolumePerWell.unit"
384
- :disabled="disabled"
385
- class="mint-dose-calculator__select"
386
- @change="handleVolumeUnitChange($event, 'serial')"
387
- >
388
- <option v-for="unit in volumeUnits" :key="unit" :value="unit">
389
- {{ unit }}
390
- </option>
391
- </select>
392
- </div>
393
- </div>
328
+ <DoseCalculatorVolumeField
329
+ v-model="serialVolumePerWell"
330
+ label="Volume per Well"
331
+ :units="volumeUnits"
332
+ :disabled="disabled"
333
+ />
394
334
 
395
335
  <div class="mint-dose-calculator__divider" />
396
336