@morscherlab/mint-sdk 1.0.0-rc.4 → 1.0.0-rc.5

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 (304) 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 +3 -3
  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 +1 -1
  44. package/dist/components/BaseInput.vue.d.ts +2 -2
  45. package/dist/components/BasePill.vue.d.ts +1 -1
  46. package/dist/components/BaseRadioGroup.vue.d.ts +1 -1
  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 +1 -1
  51. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
  52. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
  53. package/dist/components/ColorSlider.vue.d.ts +2 -2
  54. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  55. package/dist/components/ControlWorkspaceView.vue.d.ts +3 -3
  56. package/dist/components/DatePicker.vue.d.ts +1 -1
  57. package/dist/components/DateTimePicker.vue.d.ts +2 -2
  58. package/dist/components/DoseCalculatorVolumeField.vue.d.ts +15 -0
  59. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +1 -1
  60. package/dist/components/DropdownButton.vue.d.ts +1 -1
  61. package/dist/components/FileUploader.vue.d.ts +2 -2
  62. package/dist/components/FormulaInput.vue.d.ts +2 -2
  63. package/dist/components/IconButton.vue.d.ts +1 -1
  64. package/dist/components/LoadingSpinner.vue.d.ts +1 -1
  65. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  66. package/dist/components/MultiSelect.vue.d.ts +1 -1
  67. package/dist/components/NumberInput.vue.d.ts +1 -1
  68. package/dist/components/PlateMapEditor.vue.d.ts +6 -6
  69. package/dist/components/PluginWorkspaceView.controls.d.ts +28 -0
  70. package/dist/components/PluginWorkspaceView.navigation.d.ts +29 -0
  71. package/dist/components/PluginWorkspaceView.props.d.ts +151 -0
  72. package/dist/components/PluginWorkspaceView.shell.d.ts +19 -0
  73. package/dist/components/PluginWorkspaceView.vue.d.ts +46 -195
  74. package/dist/components/ProgressBar.vue.d.ts +1 -1
  75. package/dist/components/ProtocolStep.presentation.d.ts +4 -0
  76. package/dist/components/ProtocolStepEditor.state.d.ts +18 -0
  77. package/dist/components/ProtocolStepParameterField.vue.d.ts +12 -0
  78. package/dist/components/ReagentList.presentation.d.ts +16 -0
  79. package/dist/components/ResourceCard.vue.d.ts +1 -1
  80. package/dist/components/SampleSelector.colors.d.ts +13 -0
  81. package/dist/components/SampleSelector.drag.d.ts +24 -0
  82. package/dist/components/SampleSelector.groups.d.ts +15 -0
  83. package/dist/components/SampleSelector.selection.d.ts +26 -0
  84. package/dist/components/SampleSelector.vue.d.ts +4 -1
  85. package/dist/components/SampleSelectorSampleRow.vue.d.ts +21 -0
  86. package/dist/components/SegmentedControl.vue.d.ts +1 -1
  87. package/dist/components/SequenceInput.vue.d.ts +2 -2
  88. package/dist/components/SequenceProgressBar.vue.d.ts +1 -1
  89. package/dist/components/SettingsModal.schema.d.ts +9 -0
  90. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  91. package/dist/components/TagsInput.vue.d.ts +2 -2
  92. package/dist/components/TimePicker.vue.d.ts +2 -2
  93. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  94. package/dist/components/UnitInput.vue.d.ts +2 -2
  95. package/dist/components/WellPlate.colors.d.ts +9 -0
  96. package/dist/components/WellPlate.conditions.d.ts +26 -0
  97. package/dist/components/WellPlate.geometry.d.ts +23 -0
  98. package/dist/components/WellPlate.interaction.d.ts +71 -0
  99. package/dist/components/WellPlate.legend.d.ts +2 -0
  100. package/dist/components/WellPlate.rendering.d.ts +24 -0
  101. package/dist/components/WellPlate.sampleDrop.d.ts +8 -0
  102. package/dist/components/WellPlate.vue.d.ts +1 -1
  103. package/dist/components/index.js +2 -2
  104. package/dist/components/internal/ActionItemInternal.vue.d.ts +1 -1
  105. package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +28 -0
  106. package/dist/{components-DafPc4rM.js → components-DtHA2bgp.js} +3754 -2991
  107. package/dist/components-DtHA2bgp.js.map +1 -0
  108. package/dist/composables/autoGroup/classKey.d.ts +4 -0
  109. package/dist/composables/autoGroup/classify.d.ts +28 -0
  110. package/dist/composables/autoGroup/colors.d.ts +2 -0
  111. package/dist/composables/autoGroup/columns.d.ts +10 -0
  112. package/dist/composables/autoGroup/compose.d.ts +8 -0
  113. package/dist/composables/autoGroup/cooccurrence.d.ts +2 -0
  114. package/dist/composables/autoGroup/csv-shim.d.ts +2 -0
  115. package/dist/composables/autoGroup/fingerprint.d.ts +3 -0
  116. package/dist/composables/autoGroup/index.d.ts +16 -0
  117. package/dist/composables/autoGroup/replicatePreGroup.d.ts +38 -0
  118. package/dist/composables/autoGroup/template.d.ts +15 -0
  119. package/dist/composables/autoGroup/tokenize.d.ts +8 -0
  120. package/dist/composables/autoGroupConstants.d.ts +1 -0
  121. package/dist/composables/autoGroupGrouping.d.ts +3 -0
  122. package/dist/composables/controlComponentBindings.d.ts +1 -1
  123. package/dist/composables/controlSchemaAdapters.d.ts +1 -1
  124. package/dist/composables/controlSchemaDoseDesign.d.ts +11 -0
  125. package/dist/composables/controlSchemaLayout.d.ts +1 -1
  126. package/dist/composables/controlSchemaModel.d.ts +5 -0
  127. package/dist/composables/controlSchemaNormalize.d.ts +1 -1
  128. package/dist/composables/controlSchemaTypes.d.ts +305 -0
  129. package/dist/composables/controlWorkspaceOptions.d.ts +1 -1
  130. package/dist/composables/formBuilderSchema.d.ts +18 -0
  131. package/dist/composables/index.js +3 -3
  132. package/dist/composables/pluginEndpointBuilder.d.ts +13 -0
  133. package/dist/composables/protocolTemplateCatalog.d.ts +26 -0
  134. package/dist/composables/useAutoGroup.d.ts +61 -74
  135. package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
  136. package/dist/composables/useBioTemplateControls.d.ts +1 -1
  137. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +1 -1
  138. package/dist/composables/useBioTemplateWorkspace.d.ts +1 -1
  139. package/dist/composables/useControlSchema.d.ts +4 -316
  140. package/dist/composables/useControlWorkspace.d.ts +1 -1
  141. package/dist/composables/useForm.d.ts +2 -33
  142. package/dist/composables/useFormBuilder.d.ts +2 -9
  143. package/dist/composables/useFormValidation.d.ts +34 -0
  144. package/dist/composables/usePluginClient.d.ts +1 -4
  145. package/dist/composables/useProtocolTemplates.d.ts +2 -24
  146. package/dist/composables/useScheduleCalendarLayout.d.ts +49 -0
  147. package/dist/{composables-BMkPQhVK.js → composables-Dlg8jenH.js} +33 -31
  148. package/dist/composables-Dlg8jenH.js.map +1 -0
  149. package/dist/index.js +4 -4
  150. package/dist/install.js +2 -2
  151. package/dist/styles.css +547 -516
  152. package/dist/templates/controlSchemaTypes.d.ts +1 -1
  153. package/dist/templates/index.js +1 -1
  154. package/dist/templates/templateAdapterTypes.d.ts +48 -0
  155. package/dist/templates/templateCreateOptions.d.ts +165 -0
  156. package/dist/templates/templateQpcrTypes.d.ts +42 -0
  157. package/dist/templates/types.d.ts +5 -250
  158. package/dist/{templates-bUAWMn5L.js → templates-DtdUvJ4c.js} +144 -136
  159. package/dist/templates-DtdUvJ4c.js.map +1 -0
  160. package/dist/types/auto-group.d.ts +79 -9
  161. package/dist/types/componentLabTypes.d.ts +161 -0
  162. package/dist/types/componentWorkflowTypes.d.ts +150 -0
  163. package/dist/types/components.d.ts +2 -311
  164. package/dist/{useProtocolTemplates-QZtHFFH2.js → useProtocolTemplates-Bm5vyH4_.js} +1220 -454
  165. package/dist/useProtocolTemplates-Bm5vyH4_.js.map +1 -0
  166. package/package.json +1 -1
  167. package/src/__tests__/components/AppTopBar.navigation.test.ts +70 -0
  168. package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
  169. package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
  170. package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
  171. package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
  172. package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +41 -0
  173. package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
  174. package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
  175. package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
  176. package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
  177. package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
  178. package/src/__tests__/components/SampleSelector.drag.test.ts +100 -0
  179. package/src/__tests__/components/SampleSelector.groups.test.ts +81 -0
  180. package/src/__tests__/components/SampleSelector.selection.test.ts +70 -0
  181. package/src/__tests__/components/SampleSelector.test.ts +32 -0
  182. package/src/__tests__/components/SampleSelectorSampleRow.test.ts +37 -0
  183. package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
  184. package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
  185. package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
  186. package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
  187. package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
  188. package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
  189. package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
  190. package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
  191. package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
  192. package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
  193. package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
  194. package/src/__tests__/composables/autoGroup/compose.test.ts +227 -0
  195. package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
  196. package/src/__tests__/composables/autoGroup/fingerprint.test.ts +50 -0
  197. package/src/__tests__/composables/autoGroup/integration.test.ts +79 -0
  198. package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
  199. package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
  200. package/src/__tests__/composables/useAutoGroup.test.ts +129 -625
  201. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +107 -0
  202. package/src/__tests__/composables/useControlSchema.test.ts +23 -0
  203. package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
  204. package/src/__tests__/docs/extractDocsComponents.test.ts +142 -0
  205. package/src/__tests__/docs/extractDocsExports.test.ts +77 -0
  206. package/src/__tests__/docs/extractDocsParsing.test.ts +69 -0
  207. package/src/__tests__/docs/extractDocsTemplates.test.ts +54 -0
  208. package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
  209. package/src/__tests__/docs/frontendDocsCatalog.test.ts +1 -1
  210. package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
  211. package/src/components/AppSidebar.vue +2 -6
  212. package/src/components/AppTopBar.navigation.ts +62 -0
  213. package/src/components/AppTopBar.vue +17 -44
  214. package/src/components/AutoGroupModal.story.vue +50 -0
  215. package/src/components/AutoGroupModal.vue +441 -158
  216. package/src/components/ControlWorkspaceView.vue +2 -6
  217. package/src/components/DoseCalculator.vue +13 -73
  218. package/src/components/DoseCalculatorVolumeField.vue +61 -0
  219. package/src/components/ExperimentTimeline.vue +6 -31
  220. package/src/components/FormBuilder.vue +2 -7
  221. package/src/components/PlateMapEditor.vue +32 -106
  222. package/src/components/PluginWorkspaceView.controls.ts +182 -0
  223. package/src/components/PluginWorkspaceView.navigation.ts +106 -0
  224. package/src/components/PluginWorkspaceView.props.ts +174 -0
  225. package/src/components/PluginWorkspaceView.shell.ts +66 -0
  226. package/src/components/PluginWorkspaceView.vue +85 -404
  227. package/src/components/ProtocolStep.presentation.ts +31 -0
  228. package/src/components/ProtocolStepEditor.state.ts +104 -0
  229. package/src/components/ProtocolStepEditor.vue +48 -179
  230. package/src/components/ProtocolStepParameterField.vue +134 -0
  231. package/src/components/ReagentList.presentation.ts +105 -0
  232. package/src/components/ReagentList.vue +16 -79
  233. package/src/components/SampleSelector.colors.ts +43 -0
  234. package/src/components/SampleSelector.drag.ts +164 -0
  235. package/src/components/SampleSelector.groups.ts +109 -0
  236. package/src/components/SampleSelector.selection.ts +103 -0
  237. package/src/components/SampleSelector.vue +82 -349
  238. package/src/components/SampleSelectorSampleRow.vue +64 -0
  239. package/src/components/ScheduleCalendar.vue +44 -199
  240. package/src/components/SettingsModal.schema.ts +71 -0
  241. package/src/components/SettingsModal.vue +16 -46
  242. package/src/components/WellPlate.colors.ts +56 -0
  243. package/src/components/WellPlate.conditions.ts +100 -0
  244. package/src/components/WellPlate.geometry.ts +91 -0
  245. package/src/components/WellPlate.interaction.ts +272 -0
  246. package/src/components/WellPlate.legend.ts +8 -0
  247. package/src/components/WellPlate.rendering.ts +105 -0
  248. package/src/components/WellPlate.sampleDrop.ts +73 -0
  249. package/src/components/WellPlate.vue +102 -550
  250. package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
  251. package/src/composables/autoGroup/classKey.ts +5 -0
  252. package/src/composables/autoGroup/classify.ts +205 -0
  253. package/src/composables/autoGroup/colors.ts +6 -0
  254. package/src/composables/autoGroup/columns.ts +226 -0
  255. package/src/composables/autoGroup/compose.ts +156 -0
  256. package/src/composables/autoGroup/cooccurrence.ts +46 -0
  257. package/src/composables/autoGroup/csv-shim.ts +44 -0
  258. package/src/composables/autoGroup/fingerprint.ts +49 -0
  259. package/src/composables/autoGroup/index.ts +20 -0
  260. package/src/composables/autoGroup/replicatePreGroup.ts +90 -0
  261. package/src/composables/autoGroup/template.ts +126 -0
  262. package/src/composables/autoGroup/tokenize.ts +41 -0
  263. package/src/composables/autoGroup/vocab.json +67 -0
  264. package/src/composables/autoGroupConstants.ts +4 -0
  265. package/src/composables/autoGroupGrouping.ts +148 -0
  266. package/src/composables/controlComponentBindings.ts +1 -1
  267. package/src/composables/controlSchemaAdapters.ts +1 -1
  268. package/src/composables/controlSchemaDoseDesign.ts +215 -0
  269. package/src/composables/controlSchemaFormFields.ts +1 -1
  270. package/src/composables/controlSchemaLayout.ts +1 -1
  271. package/src/composables/controlSchemaModel.ts +163 -0
  272. package/src/composables/controlSchemaNormalize.ts +1 -1
  273. package/src/composables/controlSchemaTypes.ts +364 -0
  274. package/src/composables/controlWorkspaceOptions.ts +1 -1
  275. package/src/composables/formBuilderSchema.ts +153 -0
  276. package/src/composables/pluginEndpointBuilder.ts +203 -0
  277. package/src/composables/protocolTemplateCatalog.ts +325 -0
  278. package/src/composables/useAutoGroup.ts +395 -549
  279. package/src/composables/useAutoGroupInputSources.ts +147 -0
  280. package/src/composables/useBioTemplateControls.ts +1 -1
  281. package/src/composables/useBioTemplatePresetWorkspace.ts +1 -1
  282. package/src/composables/useBioTemplateWorkspace.ts +1 -1
  283. package/src/composables/useControlSchema.ts +21 -692
  284. package/src/composables/useControlWorkspace.ts +7 -13
  285. package/src/composables/useForm.ts +5 -187
  286. package/src/composables/useFormBuilder.ts +11 -153
  287. package/src/composables/useFormValidation.ts +154 -0
  288. package/src/composables/usePluginClient.ts +10 -193
  289. package/src/composables/useProtocolTemplates.ts +10 -328
  290. package/src/composables/useScheduleCalendarLayout.ts +287 -0
  291. package/src/styles/components/auto-group-modal.css +248 -310
  292. package/src/templates/controlSchemaTypes.ts +1 -1
  293. package/src/templates/templateAdapterTypes.ts +58 -0
  294. package/src/templates/templateCreateOptions.ts +208 -0
  295. package/src/templates/templateQpcrTypes.ts +48 -0
  296. package/src/templates/types.ts +79 -275
  297. package/src/types/auto-group.ts +107 -9
  298. package/src/types/componentLabTypes.ts +235 -0
  299. package/src/types/componentWorkflowTypes.ts +190 -0
  300. package/src/types/components.ts +74 -424
  301. package/dist/components-DafPc4rM.js.map +0 -1
  302. package/dist/composables-BMkPQhVK.js.map +0 -1
  303. package/dist/templates-bUAWMn5L.js.map +0 -1
  304. package/dist/useProtocolTemplates-QZtHFFH2.js.map +0 -1
@@ -0,0 +1,91 @@
1
+ import type { Well, WellPlateFormat, WellPlateSize } from '../types'
2
+
3
+ export interface WellPlateConfig {
4
+ rows: number
5
+ cols: number
6
+ }
7
+
8
+ export interface WellPlateCoordinate {
9
+ row: number
10
+ col: number
11
+ }
12
+
13
+ export interface WellPlateSizeConfig {
14
+ cellWidth: string
15
+ cellHeight: string
16
+ headerWidth: string
17
+ headerHeight: string
18
+ fontSize: string
19
+ gap: string
20
+ }
21
+
22
+ export const PLATE_CONFIGS: Record<WellPlateFormat, WellPlateConfig> = {
23
+ 6: { rows: 2, cols: 3 },
24
+ 12: { rows: 3, cols: 4 },
25
+ 24: { rows: 4, cols: 6 },
26
+ 48: { rows: 6, cols: 8 },
27
+ 54: { rows: 6, cols: 9 },
28
+ 96: { rows: 8, cols: 12 },
29
+ 384: { rows: 16, cols: 24 },
30
+ }
31
+
32
+ export const WELL_PLATE_SIZE_CONFIGS: Record<WellPlateSize, WellPlateSizeConfig> = {
33
+ sm: { cellWidth: '40px', cellHeight: '40px', headerWidth: '32px', headerHeight: '32px', fontSize: '0.625rem', gap: '2px' },
34
+ md: { cellWidth: '56px', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '3px' },
35
+ lg: { cellWidth: '80px', cellHeight: '40px', headerWidth: '40px', headerHeight: '32px', fontSize: '0.875rem', gap: '4px' },
36
+ xl: { cellWidth: '96px', cellHeight: '48px', headerWidth: '48px', headerHeight: '40px', fontSize: '1rem', gap: '4px' },
37
+ fill: { cellWidth: '100%', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '2px' },
38
+ }
39
+
40
+ export function createPlateRowLabels(config: WellPlateConfig): string[] {
41
+ return Array.from({ length: config.rows }, (_, index) => String.fromCharCode(65 + index))
42
+ }
43
+
44
+ export function createPlateColumnLabels(config: WellPlateConfig): number[] {
45
+ return Array.from({ length: config.cols }, (_, index) => index + 1)
46
+ }
47
+
48
+ export function createWellGrid(
49
+ config: WellPlateConfig,
50
+ rowLabels: string[],
51
+ wells: Record<string, Partial<Well>>,
52
+ ): Well[][] {
53
+ const grid: Well[][] = []
54
+ for (let row = 0; row < config.rows; row++) {
55
+ const rowWells: Well[] = []
56
+ for (let col = 0; col < config.cols; col++) {
57
+ const id = `${rowLabels[row]}${col + 1}`
58
+ const wellData = wells[id] || {}
59
+ rowWells.push({
60
+ id,
61
+ row,
62
+ col,
63
+ state: wellData.state || 'empty',
64
+ sampleType: wellData.sampleType,
65
+ value: wellData.value,
66
+ metadata: wellData.metadata,
67
+ })
68
+ }
69
+ grid.push(rowWells)
70
+ }
71
+ return grid
72
+ }
73
+
74
+ export function getRectangleWellIds(
75
+ start: WellPlateCoordinate,
76
+ end: WellPlateCoordinate,
77
+ rowLabels: string[],
78
+ ): Set<string> {
79
+ const minRow = Math.min(start.row, end.row)
80
+ const maxRow = Math.max(start.row, end.row)
81
+ const minCol = Math.min(start.col, end.col)
82
+ const maxCol = Math.max(start.col, end.col)
83
+
84
+ const wells = new Set<string>()
85
+ for (let row = minRow; row <= maxRow; row++) {
86
+ for (let col = minCol; col <= maxCol; col++) {
87
+ wells.add(`${rowLabels[row]}${col + 1}`)
88
+ }
89
+ }
90
+ return wells
91
+ }
@@ -0,0 +1,272 @@
1
+ import { computed, ref } from 'vue'
2
+ import type {
3
+ Well,
4
+ WellEditData,
5
+ WellPlateSelectionMode,
6
+ WellSampleDropData,
7
+ WellSampleDropParser,
8
+ } from '../types'
9
+ import {
10
+ getRectangleWellIds,
11
+ type WellPlateCoordinate,
12
+ } from './WellPlate.geometry'
13
+ import { parseDroppedSampleTransfer } from './WellPlate.sampleDrop'
14
+
15
+ export interface WellPlateInteractionOptions {
16
+ modelValue: () => string[]
17
+ selectionMode: () => WellPlateSelectionMode
18
+ disabled: () => boolean
19
+ readonly: () => boolean
20
+ editable: () => boolean
21
+ allowSampleDrop: () => boolean
22
+ sampleDropParser: () => WellSampleDropParser | undefined
23
+ rowLabels: () => string[]
24
+ wellGrid: () => Well[][]
25
+ emitSelected: (wellIds: string[]) => void
26
+ emitWellClick: (wellId: string, event: MouseEvent) => void
27
+ emitWellHover: (wellId: string | null, event?: MouseEvent) => void
28
+ emitContextMenu: (wellId: string, event: MouseEvent) => void
29
+ emitWellMove: (sourceWellId: string, targetWellId: string) => void
30
+ emitWellEdit: (wellId: string, data: WellEditData) => void
31
+ emitWellClear: (wellId: string) => void
32
+ emitSampleDrop: (wellId: string, data: WellSampleDropData, event: DragEvent) => void
33
+ }
34
+
35
+ export function nextClickedWellSelection(
36
+ selectedWellIds: string[],
37
+ wellId: string,
38
+ selectionMode: WellPlateSelectionMode,
39
+ event: Pick<MouseEvent, 'shiftKey' | 'metaKey' | 'ctrlKey'>,
40
+ ): string[] {
41
+ const selectedWellSet = new Set(selectedWellIds)
42
+ const isCurrentlySelected = selectedWellSet.has(wellId)
43
+ const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey
44
+
45
+ if (selectionMode === 'single') {
46
+ return isCurrentlySelected ? [] : [wellId]
47
+ }
48
+
49
+ if (isMultiSelect) {
50
+ return isCurrentlySelected
51
+ ? selectedWellIds.filter(id => id !== wellId)
52
+ : [...selectedWellIds, wellId]
53
+ }
54
+
55
+ return isCurrentlySelected && selectedWellIds.length === 1 ? [] : [wellId]
56
+ }
57
+
58
+ export function useWellPlateInteraction(options: WellPlateInteractionOptions) {
59
+ const dragSourceWell = ref<string | null>(null)
60
+ const dragTargetWell = ref<string | null>(null)
61
+ const isDragging = ref(false)
62
+ const dragStart = ref<WellPlateCoordinate | null>(null)
63
+ const dragEnd = ref<WellPlateCoordinate | null>(null)
64
+ const hoveredWell = ref<string | null>(null)
65
+ const editingWellId = ref<string | null>(null)
66
+ const editPopupPosition = ref({ x: 0, y: 0 })
67
+
68
+ const selectedWellSet = computed(() => new Set(options.modelValue()))
69
+
70
+ const dragSelectedWells = computed(() => {
71
+ if (!isDragging.value || !dragStart.value || !dragEnd.value) return new Set<string>()
72
+ return getRectangleWellIds(dragStart.value, dragEnd.value, options.rowLabels())
73
+ })
74
+
75
+ function emitSelected(wellIds: string[]) {
76
+ options.emitSelected(wellIds)
77
+ }
78
+
79
+ function isSelected(wellId: string): boolean {
80
+ return selectedWellSet.value.has(wellId) || dragSelectedWells.value.has(wellId)
81
+ }
82
+
83
+ function openEditPopup(wellId: string, event: MouseEvent) {
84
+ const target = event.currentTarget as HTMLElement
85
+ const rect = target.getBoundingClientRect()
86
+ editPopupPosition.value = {
87
+ x: rect.right + 8,
88
+ y: rect.top,
89
+ }
90
+ editingWellId.value = wellId
91
+ }
92
+
93
+ function handleWellClick(well: Well, event: MouseEvent) {
94
+ if (options.disabled() || options.readonly()) return
95
+
96
+ options.emitWellClick(well.id, event)
97
+
98
+ if (options.editable()) {
99
+ openEditPopup(well.id, event)
100
+ return
101
+ }
102
+
103
+ const selectionMode = options.selectionMode()
104
+ if (selectionMode === 'none') return
105
+
106
+ emitSelected(nextClickedWellSelection(options.modelValue(), well.id, selectionMode, event))
107
+ }
108
+
109
+ function handleEditSave(data: WellEditData) {
110
+ options.emitWellEdit(data.wellId, data)
111
+ editingWellId.value = null
112
+ }
113
+
114
+ function handleEditClear() {
115
+ if (editingWellId.value) options.emitWellClear(editingWellId.value)
116
+ editingWellId.value = null
117
+ }
118
+
119
+ function handleEditClose() {
120
+ editingWellId.value = null
121
+ }
122
+
123
+ function handleWellMouseDown(well: Well, event: MouseEvent) {
124
+ if (options.disabled() || options.readonly() || options.selectionMode() !== 'rectangle') return
125
+ if (options.editable()) return
126
+ if (event.button !== 0) return
127
+
128
+ isDragging.value = true
129
+ dragStart.value = { row: well.row, col: well.col }
130
+ dragEnd.value = { row: well.row, col: well.col }
131
+ }
132
+
133
+ function handleWellMouseEnter(well: Well, event: MouseEvent) {
134
+ hoveredWell.value = well.id
135
+ options.emitWellHover(well.id, event)
136
+
137
+ if (isDragging.value && options.selectionMode() === 'rectangle') {
138
+ dragEnd.value = { row: well.row, col: well.col }
139
+ }
140
+ }
141
+
142
+ function handleWellMouseLeave() {
143
+ hoveredWell.value = null
144
+ options.emitWellHover(null)
145
+ }
146
+
147
+ function handleMouseUp() {
148
+ if (!isDragging.value || options.selectionMode() !== 'rectangle') return
149
+
150
+ const newSelection = Array.from(dragSelectedWells.value)
151
+ isDragging.value = false
152
+ dragStart.value = null
153
+ dragEnd.value = null
154
+
155
+ if (newSelection.length > 0) emitSelected(newSelection)
156
+ }
157
+
158
+ function handleContextMenu(well: Well, event: MouseEvent) {
159
+ event.preventDefault()
160
+ options.emitContextMenu(well.id, event)
161
+ }
162
+
163
+ function handleDragStart(well: Well, event: DragEvent) {
164
+ if (options.disabled() || options.readonly() || options.selectionMode() !== 'drag') return
165
+ if (!well.sampleType) return
166
+
167
+ dragSourceWell.value = well.id
168
+ if (event.dataTransfer) {
169
+ event.dataTransfer.effectAllowed = 'move'
170
+ event.dataTransfer.setData('text/plain', well.id)
171
+ }
172
+ }
173
+
174
+ function handleDragOver(well: Well, event: DragEvent) {
175
+ const isMovingWell = options.selectionMode() === 'drag' && !!dragSourceWell.value
176
+ if (!isMovingWell && !options.allowSampleDrop()) return
177
+
178
+ event.preventDefault()
179
+ if (event.dataTransfer) {
180
+ event.dataTransfer.dropEffect = isMovingWell ? 'move' : 'copy'
181
+ }
182
+ dragTargetWell.value = well.id
183
+ }
184
+
185
+ function handleDragLeave() {
186
+ dragTargetWell.value = null
187
+ }
188
+
189
+ function handleDrop(well: Well, event: DragEvent) {
190
+ const isMovingWell = options.selectionMode() === 'drag' && !!dragSourceWell.value
191
+ if (!isMovingWell && !options.allowSampleDrop()) return
192
+
193
+ event.preventDefault()
194
+
195
+ if (isMovingWell && dragSourceWell.value) {
196
+ const sourceId = dragSourceWell.value
197
+ const targetId = well.id
198
+
199
+ if (sourceId !== targetId) options.emitWellMove(sourceId, targetId)
200
+
201
+ dragSourceWell.value = null
202
+ dragTargetWell.value = null
203
+ return
204
+ }
205
+
206
+ const droppedSample = parseDroppedSample(event, well.id)
207
+ if (droppedSample) options.emitSampleDrop(well.id, droppedSample, event)
208
+
209
+ dragSourceWell.value = null
210
+ dragTargetWell.value = null
211
+ }
212
+
213
+ function handleDragEnd() {
214
+ dragSourceWell.value = null
215
+ dragTargetWell.value = null
216
+ }
217
+
218
+ function parseDroppedSample(event: DragEvent, wellId: string): WellSampleDropData | null {
219
+ const customParser = options.sampleDropParser()
220
+ if (customParser) return customParser(event, { wellId, event }) ?? null
221
+
222
+ const transfer = event.dataTransfer
223
+ if (!transfer) return null
224
+ return parseDroppedSampleTransfer(transfer)
225
+ }
226
+
227
+ function handleKeyDown(event: KeyboardEvent) {
228
+ if (options.disabled() || options.readonly()) return
229
+
230
+ if (event.key === 'Escape') {
231
+ if (editingWellId.value) {
232
+ editingWellId.value = null
233
+ return
234
+ }
235
+ emitSelected([])
236
+ }
237
+
238
+ if ((event.key === 'a' || event.key === 'A') && (event.metaKey || event.ctrlKey)) {
239
+ event.preventDefault()
240
+ emitSelected(options.wellGrid().flat().map(w => w.id))
241
+ }
242
+ }
243
+
244
+ return {
245
+ dragSourceWell,
246
+ dragTargetWell,
247
+ isDragging,
248
+ dragStart,
249
+ dragEnd,
250
+ hoveredWell,
251
+ editingWellId,
252
+ editPopupPosition,
253
+ selectedWellSet,
254
+ dragSelectedWells,
255
+ isSelected,
256
+ handleWellClick,
257
+ handleEditSave,
258
+ handleEditClear,
259
+ handleEditClose,
260
+ handleWellMouseDown,
261
+ handleWellMouseEnter,
262
+ handleWellMouseLeave,
263
+ handleMouseUp,
264
+ handleContextMenu,
265
+ handleDragStart,
266
+ handleDragOver,
267
+ handleDragLeave,
268
+ handleDrop,
269
+ handleDragEnd,
270
+ handleKeyDown,
271
+ }
272
+ }
@@ -0,0 +1,8 @@
1
+ import type { WellLegendItem } from '../types'
2
+
3
+ export const DEFAULT_WELL_LEGEND_ITEMS: WellLegendItem[] = [
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
+ ]
@@ -0,0 +1,105 @@
1
+ import type { HeatmapConfig, Well, WellPlateSelectionMode, WellShape } from '../types'
2
+ import {
3
+ DEFAULT_SAMPLE_TYPE_COLORS,
4
+ getHeatmapColor,
5
+ } from './WellPlate.colors'
6
+
7
+ export interface WellClassState {
8
+ wellShape: WellShape
9
+ selectionMode: WellPlateSelectionMode
10
+ selected: boolean
11
+ dragOver: boolean
12
+ hovered: boolean
13
+ disabled: boolean
14
+ readonly: boolean
15
+ dragSource: boolean
16
+ dragTarget: boolean
17
+ }
18
+
19
+ export interface WellStyleState {
20
+ heatmap: HeatmapConfig
21
+ sampleColors: Record<string, string>
22
+ }
23
+
24
+ export function wellClasses(well: Well, state: WellClassState): string[] {
25
+ const classes = [
26
+ 'mint-well-plate__well',
27
+ state.wellShape === 'circle' ? 'mint-well-plate__well--circle' : 'mint-well-plate__well--rounded',
28
+ ]
29
+
30
+ if (state.selectionMode === 'drag' && well.sampleType) {
31
+ classes.push('mint-well-plate__well--draggable')
32
+ }
33
+
34
+ if (state.dragSource) classes.push('mint-well-plate__well--drag-source')
35
+ else if (state.dragTarget) classes.push('mint-well-plate__well--drag-target')
36
+ else if (state.selected) classes.push('mint-well-plate__well--selected')
37
+ else if (state.dragOver) classes.push('mint-well-plate__well--drag-over')
38
+ else if (state.hovered && !state.readonly) classes.push('mint-well-plate__well--hovered')
39
+
40
+ if (state.disabled) classes.push('mint-well-plate__well--disabled')
41
+ if (well.state === 'filled') classes.push('mint-well-plate__well--filled')
42
+
43
+ return classes
44
+ }
45
+
46
+ export function wellStyle(well: Well, state: WellStyleState): Record<string, string> {
47
+ const heatmapColor = getHeatmapColor(state.heatmap, well.value)
48
+ if (heatmapColor) {
49
+ return { backgroundColor: heatmapColor, border: '1px solid transparent' }
50
+ }
51
+
52
+ if (well.sampleType && state.sampleColors[well.sampleType]) {
53
+ const color = state.sampleColors[well.sampleType]
54
+ return {
55
+ backgroundColor: `${color}26`,
56
+ border: `1px solid ${color}66`,
57
+ }
58
+ }
59
+
60
+ if (well.sampleType && DEFAULT_SAMPLE_TYPE_COLORS[well.sampleType]) {
61
+ const colors = DEFAULT_SAMPLE_TYPE_COLORS[well.sampleType]
62
+ return {
63
+ backgroundColor: colors.bg,
64
+ border: `1px solid ${colors.border}`,
65
+ }
66
+ }
67
+
68
+ const borderStyle = well.state === 'filled' ? 'solid' : 'dashed'
69
+ return {
70
+ backgroundColor: 'var(--bg-tertiary)',
71
+ border: `1px ${borderStyle} var(--border-color)`,
72
+ }
73
+ }
74
+
75
+ export function sampleTypeIndicator(well: Well, showSampleTypeIndicator: boolean): string | null {
76
+ if (!showSampleTypeIndicator || !well.sampleType) return null
77
+ const typeMap: Record<string, string> = {
78
+ sample: 'S',
79
+ control: 'C',
80
+ blank: 'B',
81
+ qc: 'Q',
82
+ iqc: 'I',
83
+ }
84
+ return typeMap[well.sampleType] || well.sampleType.charAt(0).toUpperCase()
85
+ }
86
+
87
+ export function wellLabel(well: Well, showWellLabels: boolean): string | undefined {
88
+ if (!showWellLabels) return undefined
89
+ return well.metadata?.label as string | undefined
90
+ }
91
+
92
+ export function wellBadge(
93
+ well: Well,
94
+ showBadges: boolean,
95
+ ): { text: string; color: string } | null {
96
+ if (!showBadges || !well.metadata) return null
97
+ const count = well.metadata.injectionCount as number | undefined
98
+ if (count && count > 1) {
99
+ return { text: String(count), color: '#6366f1' }
100
+ }
101
+ if (well.metadata.customMethod) {
102
+ return { text: '+', color: '#ec4899' }
103
+ }
104
+ return null
105
+ }
@@ -0,0 +1,73 @@
1
+ import type { WellSampleDropData } from '../types'
2
+
3
+ interface DroppedSampleTransfer {
4
+ getData(format: string): string
5
+ }
6
+
7
+ export function parseDroppedSampleTransfer(transfer: DroppedSampleTransfer): WellSampleDropData | null {
8
+ const json = transfer.getData('application/json') || transfer.getData('text/json')
9
+ if (json) {
10
+ try {
11
+ return normalizeDroppedSample(JSON.parse(json))
12
+ } catch {
13
+ return null
14
+ }
15
+ }
16
+
17
+ return createTextDroppedSample(transfer.getData('text/plain'))
18
+ }
19
+
20
+ export function createTextDroppedSample(text: string | null | undefined): WellSampleDropData | null {
21
+ const sampleName = text?.trim()
22
+ if (!sampleName) return null
23
+
24
+ return {
25
+ sampleName,
26
+ label: sampleName,
27
+ raw: sampleName,
28
+ }
29
+ }
30
+
31
+ export function normalizeDroppedSample(raw: unknown): WellSampleDropData | null {
32
+ if (!raw || typeof raw !== 'object') return null
33
+
34
+ const record = raw as Record<string, unknown>
35
+ const sampleName =
36
+ getString(record.sampleName) ??
37
+ getString(record.sample_name) ??
38
+ getString(record.name) ??
39
+ getString(record.label)
40
+ const label = getString(record.label) ?? sampleName
41
+ const sampleType =
42
+ getString(record.sampleType) ??
43
+ getString(record.sample_type) ??
44
+ (record.type === 'sample' ? 'sample' : undefined)
45
+
46
+ if (!sampleName && !label && !getString(record.id)) return null
47
+
48
+ return {
49
+ id: getString(record.id),
50
+ sampleName,
51
+ label,
52
+ sampleType,
53
+ injectionVolume: getNumber(record.injectionVolume ?? record.injection_volume),
54
+ injectionCount: getNumber(record.injectionCount ?? record.injection_count),
55
+ customMethod: getString(record.customMethod ?? record.custom_method) ?? null,
56
+ metadata: getRecord(record.metadata),
57
+ raw,
58
+ }
59
+ }
60
+
61
+ function getString(value: unknown): string | undefined {
62
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined
63
+ }
64
+
65
+ function getNumber(value: unknown): number | undefined {
66
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined
67
+ }
68
+
69
+ function getRecord(value: unknown): Record<string, unknown> | undefined {
70
+ return value && typeof value === 'object' && !Array.isArray(value)
71
+ ? value as Record<string, unknown>
72
+ : undefined
73
+ }