@morscherlab/mint-sdk 1.2.1 → 1.2.2

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 (84) hide show
  1. package/dist/{SettingsModal-DOcCf0Vo.js → SettingsModal-DELMkUFR.js} +3 -3
  2. package/dist/{SettingsModal-DOcCf0Vo.js.map → SettingsModal-DELMkUFR.js.map} +1 -1
  3. package/dist/__tests__/components/ChartContainer.test.d.ts +1 -0
  4. package/dist/__tests__/components/ChemicalFormula.test.d.ts +1 -0
  5. package/dist/__tests__/components/LayoutResizeHandle.test.d.ts +1 -0
  6. package/dist/__tests__/components/SearchableSelect.test.d.ts +1 -0
  7. package/dist/__tests__/components/WellPlate.decoration.test.d.ts +1 -0
  8. package/dist/__tests__/composables/useManualLayoutResize.test.d.ts +1 -0
  9. package/dist/components/AdductText.vue.d.ts +17 -0
  10. package/dist/components/ChartContainer.vue.d.ts +19 -5
  11. package/dist/components/ChemicalFormula.vue.d.ts +28 -2
  12. package/dist/components/DataFrame.vue.d.ts +1 -1
  13. package/dist/components/LayoutResizeHandle.vue.d.ts +23 -0
  14. package/dist/components/PlotlyChart.vue.d.ts +34 -6
  15. package/dist/components/SearchableSelect.vue.d.ts +57 -0
  16. package/dist/components/WellPlate.vue.d.ts +5 -1
  17. package/dist/components/index.d.ts +3 -1
  18. package/dist/components/index.js +4 -4
  19. package/dist/{components-Cp8DcNrU.js → components-CxQVQCpP.js} +773 -317
  20. package/dist/components-CxQVQCpP.js.map +1 -0
  21. package/dist/composables/index.d.ts +1 -0
  22. package/dist/composables/index.js +6 -6
  23. package/dist/composables/useManualLayoutResize.d.ts +56 -0
  24. package/dist/{composables-D_4qqOlp.js → composables-DAqOU8Aq.js} +96 -7
  25. package/dist/composables-DAqOU8Aq.js.map +1 -0
  26. package/dist/index.js +8 -8
  27. package/dist/install.js +2 -2
  28. package/dist/styles.css +294 -16
  29. package/dist/templates/index.js +2 -2
  30. package/dist/{templates-Dd9bBSs6.js → templates-NuaNHohp.js} +2 -2
  31. package/dist/{templates-Dd9bBSs6.js.map → templates-NuaNHohp.js.map} +1 -1
  32. package/dist/types/componentLabTypes.d.ts +3 -0
  33. package/dist/types/components.d.ts +5 -1
  34. package/dist/types/index.d.ts +1 -1
  35. package/dist/{useControlSchema-BB2GRPhW.js → useControlSchema-vLiRPUmj.js} +37 -7
  36. package/dist/useControlSchema-vLiRPUmj.js.map +1 -0
  37. package/dist/{useFormBuilder-BnGSdSRG.js → useFormBuilder-D37ajgS8.js} +2 -2
  38. package/dist/{useFormBuilder-BnGSdSRG.js.map → useFormBuilder-D37ajgS8.js.map} +1 -1
  39. package/dist/{validation-CTxuzLIX.js → validation-ChMyixAf.js} +3 -3
  40. package/dist/{validation-CTxuzLIX.js.map → validation-ChMyixAf.js.map} +1 -1
  41. package/package.json +1 -1
  42. package/src/__tests__/components/ChartContainer.test.ts +24 -0
  43. package/src/__tests__/components/ChemicalFormula.test.ts +27 -0
  44. package/src/__tests__/components/LayoutResizeHandle.test.ts +19 -0
  45. package/src/__tests__/components/NumberInput.test.ts +45 -1
  46. package/src/__tests__/components/PlotlyChart.test.ts +48 -0
  47. package/src/__tests__/components/RackEditor.test.ts +9 -0
  48. package/src/__tests__/components/SearchableSelect.test.ts +61 -0
  49. package/src/__tests__/components/WellPlate.decoration.test.ts +28 -0
  50. package/src/__tests__/composables/useManualLayoutResize.test.ts +21 -0
  51. package/src/components/AdductText.story.vue +15 -0
  52. package/src/components/AdductText.vue +40 -0
  53. package/src/components/ChartContainer.story.vue +21 -0
  54. package/src/components/ChartContainer.vue +27 -6
  55. package/src/components/ChemicalFormula.story.vue +7 -0
  56. package/src/components/ChemicalFormula.vue +40 -11
  57. package/src/components/LayoutResizeHandle.story.vue +26 -0
  58. package/src/components/LayoutResizeHandle.vue +62 -0
  59. package/src/components/NumberInput.story.vue +1 -1
  60. package/src/components/NumberInput.vue +37 -9
  61. package/src/components/PlotlyChart.vue +77 -11
  62. package/src/components/RackEditor.vue +38 -32
  63. package/src/components/SearchableSelect.story.vue +24 -0
  64. package/src/components/SearchableSelect.vue +223 -0
  65. package/src/components/WellPlate.story.vue +16 -0
  66. package/src/components/WellPlate.vue +17 -8
  67. package/src/components/index.ts +3 -1
  68. package/src/composables/index.ts +10 -0
  69. package/src/composables/useManualLayoutResize.ts +165 -0
  70. package/src/styles/components/adduct-text.css +45 -0
  71. package/src/styles/components/chart-container.css +92 -0
  72. package/src/styles/components/chemical-formula.css +34 -4
  73. package/src/styles/components/layout-resize-handle.css +60 -0
  74. package/src/styles/components/number-input.css +2 -0
  75. package/src/styles/components/rack-editor.css +31 -9
  76. package/src/styles/components/searchable-select.css +63 -0
  77. package/src/styles/components/well-plate.css +9 -0
  78. package/src/styles/index.css +3 -0
  79. package/src/types/componentLabTypes.ts +6 -0
  80. package/src/types/components.ts +7 -0
  81. package/src/types/index.ts +3 -0
  82. package/dist/components-Cp8DcNrU.js.map +0 -1
  83. package/dist/composables-D_4qqOlp.js.map +0 -1
  84. package/dist/useControlSchema-BB2GRPhW.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  /** Interactive 96/384-well plate grid with drag-to-select, heatmap overlays, per-well editing, and sample-color mapping. */
3
3
  import { ref, computed } from 'vue'
4
- import type { WellPlateFormat, WellPlateSelectionMode, WellPlateSize, Well, HeatmapConfig, WellShape, WellEditField, WellEditData, WellLegendItem, ColumnCondition, RowCondition, WellSampleDropData, WellSampleDropParser } from '../types'
4
+ import type { WellPlateFormat, WellPlateSelectionMode, WellPlateSize, Well, HeatmapConfig, WellShape, WellEditField, WellEditData, WellLegendItem, ColumnCondition, RowCondition, WellSampleDropData, WellSampleDropParser, WellClassResolver, WellClassValue, WellStyleResolver } from '../types'
5
5
  import { useEventListener } from '../composables/useEventListener'
6
6
  import WellEditPopupInternal from './internal/WellEditPopupInternal.vue'
7
7
  import {
@@ -73,6 +73,10 @@ interface Props {
73
73
  * carry data.
74
74
  */
75
75
  emptyMessage?: string
76
+ /** Add semantic classes to individual wells without overloading sample data. */
77
+ wellClass?: WellClassResolver
78
+ /** Add or override individual well styles after heatmap/sample styling. */
79
+ wellStyle?: WellStyleResolver
76
80
  }
77
81
 
78
82
  const props = withDefaults(defineProps<Props>(), {
@@ -213,8 +217,8 @@ const {
213
217
  emitSampleDrop: (wellId, data, event) => emit('sample-drop', wellId, data, event),
214
218
  })
215
219
 
216
- function getWellClasses(well: Well): string[] {
217
- return wellClasses(well, {
220
+ function getWellClasses(well: Well): WellClassValue[] {
221
+ const base = wellClasses(well, {
218
222
  wellShape: props.wellShape,
219
223
  selectionMode: props.selectionMode,
220
224
  selected: isSelected(well.id),
@@ -225,13 +229,18 @@ function getWellClasses(well: Well): string[] {
225
229
  dragSource: dragSourceWell.value === well.id,
226
230
  dragTarget: dragTargetWell.value === well.id,
227
231
  })
232
+ const custom = props.wellClass?.(well)
233
+ return custom ? [base, custom] : [base]
228
234
  }
229
235
 
230
- function getWellStyle(well: Well): Record<string, string> {
231
- return wellStyle(well, {
232
- heatmap: props.heatmap,
233
- sampleColors: props.sampleColors,
234
- })
236
+ function getWellStyle(well: Well): Record<string, string | number | undefined> {
237
+ return {
238
+ ...wellStyle(well, {
239
+ heatmap: props.heatmap,
240
+ sampleColors: props.sampleColors,
241
+ }),
242
+ ...props.wellStyle?.(well),
243
+ }
235
244
  }
236
245
 
237
246
  function getSampleTypeIndicator(well: Well): string | null {
@@ -3,6 +3,7 @@ export { default as BaseButton } from './BaseButton.vue'
3
3
  export { default as BaseInput } from './BaseInput.vue'
4
4
  export { default as BaseTextarea } from './BaseTextarea.vue'
5
5
  export { default as BaseSelect } from './BaseSelect.vue'
6
+ export { default as SearchableSelect } from './SearchableSelect.vue'
6
7
  export { default as BaseCheckbox } from './BaseCheckbox.vue'
7
8
  export { default as BaseToggle } from './BaseToggle.vue'
8
9
  export { default as BaseRadioGroup } from './BaseRadioGroup.vue'
@@ -55,6 +56,7 @@ export { default as ControlWorkspaceView } from './ControlWorkspaceView.vue'
55
56
  export { default as DoseDesignWorkspaceView } from './DoseDesignWorkspaceView.vue'
56
57
  export { default as AppContainer } from './AppContainer.vue'
57
58
  export { default as PluginIcon } from './PluginIcon.vue'
59
+ export { default as LayoutResizeHandle } from './LayoutResizeHandle.vue'
58
60
 
59
61
  // Utility components
60
62
  export { default as Skeleton } from './Skeleton.vue'
@@ -68,6 +70,7 @@ export { default as Breadcrumb } from './Breadcrumb.vue'
68
70
  export { default as Tooltip } from './Tooltip.vue'
69
71
  export { default as ConfirmDialog } from './ConfirmDialog.vue'
70
72
  export { default as ChartContainer } from './ChartContainer.vue'
73
+ export { default as AdductText } from './AdductText.vue'
71
74
  export { default as SettingsModal } from './SettingsModal.vue'
72
75
 
73
76
  // Biological experiment components
@@ -101,7 +104,6 @@ export { default as ProtocolStepEditor } from './ProtocolStepEditor.vue'
101
104
  // Scientific display components
102
105
  export { default as PlotlyChart } from './PlotlyChart.vue'
103
106
  export { default as ScientificNumber } from './ScientificNumber.vue'
104
- /** Renders a chemical formula string (H₂O, C₆H₁₂O₆, Ca(OH)₂) with typographically correct subscripts and superscripts for charges. @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; no platform replacement is planned. */
105
107
  export { default as ChemicalFormula } from './ChemicalFormula.vue'
106
108
  export { default as SequenceProgressBar } from './SequenceProgressBar.vue'
107
109
  /** Filterable instrument alert/event list keyed by each alert's required stable event_key. @deprecated Unused by the MINT platform and scheduled for removal in MINT 1.3; instrument UI moves to the mld-ms plugins. */
@@ -139,6 +139,16 @@ export {
139
139
  type UseDebouncedWatchOptions,
140
140
  type UseDebouncedWatchReturn,
141
141
  } from './useDebouncedWatch'
142
+ export {
143
+ resizedLeadingPanelWidth,
144
+ resizedTrailingPanelWidth,
145
+ resizedVerticalSplitPercent,
146
+ useManualLayoutResize,
147
+ type ManualLayoutResizeContext,
148
+ type ManualLayoutResizeTarget,
149
+ type ResizePanelWidthConstraints,
150
+ type ResizePercentConstraints,
151
+ } from './useManualLayoutResize'
142
152
  export { useFormBuilder, evaluateCondition } from './useFormBuilder'
143
153
  export {
144
154
  createGeneratedAnalysisTransport,
@@ -0,0 +1,165 @@
1
+ import { computed, onBeforeUnmount, shallowRef, toValue, watch, type MaybeRefOrGetter } from 'vue'
2
+
3
+ export interface ResizePanelWidthConstraints {
4
+ minWidth: number
5
+ maxWidth: number
6
+ mainMinWidth?: number
7
+ layoutGap?: number
8
+ }
9
+
10
+ export interface ResizePercentConstraints {
11
+ minPercent: number
12
+ maxPercent: number
13
+ }
14
+
15
+ function clamp(value: number, min: number, max: number): number {
16
+ return Math.min(max, Math.max(min, value))
17
+ }
18
+
19
+ function availablePanelWidth(
20
+ containerWidth: number,
21
+ constraints: ResizePanelWidthConstraints,
22
+ ): number {
23
+ return Math.min(
24
+ constraints.maxWidth,
25
+ containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0),
26
+ )
27
+ }
28
+
29
+ export function resizedLeadingPanelWidth(
30
+ startWidth: number,
31
+ deltaX: number,
32
+ containerWidth: number,
33
+ constraints: ResizePanelWidthConstraints,
34
+ ): number {
35
+ return Math.round(clamp(
36
+ startWidth + deltaX,
37
+ constraints.minWidth,
38
+ Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints)),
39
+ ))
40
+ }
41
+
42
+ export function resizedTrailingPanelWidth(
43
+ startWidth: number,
44
+ deltaX: number,
45
+ containerWidth: number,
46
+ constraints: ResizePanelWidthConstraints,
47
+ ): number {
48
+ return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints)
49
+ }
50
+
51
+ export function resizedVerticalSplitPercent(
52
+ startPercent: number,
53
+ deltaY: number,
54
+ containerHeight: number,
55
+ constraints: ResizePercentConstraints,
56
+ ): number {
57
+ if (containerHeight <= 0) return startPercent
58
+ return Math.round(clamp(
59
+ startPercent + (deltaY / containerHeight) * 100,
60
+ constraints.minPercent,
61
+ constraints.maxPercent,
62
+ ))
63
+ }
64
+
65
+ export interface ManualLayoutResizeContext<TKind extends string> {
66
+ kind: TKind
67
+ startValue: number
68
+ deltaX: number
69
+ deltaY: number
70
+ containerRect: DOMRectReadOnly
71
+ secondaryRect: DOMRectReadOnly | null
72
+ }
73
+
74
+ export interface ManualLayoutResizeTarget<TKind extends string> {
75
+ cursor?: string
76
+ getContainerElement: () => HTMLElement | null
77
+ getSecondaryElement?: () => HTMLElement | null
78
+ getStartValue: () => number
79
+ onResize: (context: ManualLayoutResizeContext<TKind>) => void
80
+ }
81
+
82
+ /** Coordinates pointer-driven resizing for one or more layout splitters. */
83
+ export function useManualLayoutResize<TKind extends string>(options: {
84
+ enabled: MaybeRefOrGetter<boolean>
85
+ resolveTarget: (kind: TKind) => ManualLayoutResizeTarget<TKind> | null
86
+ }) {
87
+ const activeResize = shallowRef<{
88
+ kind: TKind
89
+ startClientX: number
90
+ startClientY: number
91
+ startValue: number
92
+ containerRect: DOMRectReadOnly
93
+ secondaryRect: DOMRectReadOnly | null
94
+ cursor: string
95
+ } | null>(null)
96
+ const isResizing = computed(() => activeResize.value !== null)
97
+ let previousBodyCursor = ''
98
+ let previousBodyUserSelect = ''
99
+
100
+ function stopResize(): void {
101
+ if (!activeResize.value) return
102
+ activeResize.value = null
103
+ window.removeEventListener('pointermove', handlePointerMove)
104
+ window.removeEventListener('pointerup', stopResize)
105
+ window.removeEventListener('pointercancel', stopResize)
106
+ if (typeof document !== 'undefined') {
107
+ document.body.style.cursor = previousBodyCursor
108
+ document.body.style.userSelect = previousBodyUserSelect
109
+ }
110
+ }
111
+
112
+ function handlePointerMove(event: PointerEvent): void {
113
+ const state = activeResize.value
114
+ if (!state) return
115
+ options.resolveTarget(state.kind)?.onResize({
116
+ kind: state.kind,
117
+ startValue: state.startValue,
118
+ deltaX: event.clientX - state.startClientX,
119
+ deltaY: event.clientY - state.startClientY,
120
+ containerRect: state.containerRect,
121
+ secondaryRect: state.secondaryRect,
122
+ })
123
+ }
124
+
125
+ function startResize(kind: TKind, event: PointerEvent): void {
126
+ if (!toValue(options.enabled) || event.button !== 0) return
127
+ const target = options.resolveTarget(kind)
128
+ const containerRect = target?.getContainerElement()?.getBoundingClientRect()
129
+ if (!target || !containerRect) return
130
+
131
+ event.preventDefault()
132
+ if (event.currentTarget instanceof HTMLElement) {
133
+ event.currentTarget.setPointerCapture(event.pointerId)
134
+ }
135
+ const secondary = target.getSecondaryElement?.() ?? null
136
+ activeResize.value = {
137
+ kind,
138
+ startClientX: event.clientX,
139
+ startClientY: event.clientY,
140
+ startValue: target.getStartValue(),
141
+ containerRect,
142
+ secondaryRect: secondary?.getBoundingClientRect() ?? null,
143
+ cursor: target.cursor ?? 'col-resize',
144
+ }
145
+
146
+ if (typeof document !== 'undefined') {
147
+ previousBodyCursor = document.body.style.cursor
148
+ previousBodyUserSelect = document.body.style.userSelect
149
+ document.body.style.cursor = activeResize.value.cursor
150
+ document.body.style.userSelect = 'none'
151
+ }
152
+ window.addEventListener('pointermove', handlePointerMove)
153
+ window.addEventListener('pointerup', stopResize)
154
+ window.addEventListener('pointercancel', stopResize)
155
+ }
156
+
157
+ function isResizingKind(kind: TKind): boolean {
158
+ return activeResize.value?.kind === kind
159
+ }
160
+
161
+ watch(() => toValue(options.enabled), enabled => { if (!enabled) stopResize() })
162
+ onBeforeUnmount(stopResize)
163
+
164
+ return { activeResize, isResizing, isResizingKind, startResize, stopResize }
165
+ }
@@ -0,0 +1,45 @@
1
+ .mint-adduct-text {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ flex-shrink: 0;
5
+ max-width: 100%;
6
+ overflow: hidden;
7
+ color: var(--text-muted);
8
+ font-variant-numeric: tabular-nums;
9
+ font-weight: 600;
10
+ letter-spacing: 0;
11
+ text-overflow: ellipsis;
12
+ white-space: nowrap;
13
+ }
14
+
15
+ .mint-adduct-text--mono { font-family: var(--font-mono); font-feature-settings: 'liga' 0, 'calt' 0; }
16
+ .mint-adduct-text--sans { font-family: var(--font-sans); font-feature-settings: 'liga' 1, 'calt' 1; font-weight: 650; }
17
+
18
+ .mint-adduct-text--xs {
19
+ font-size: 0.625rem;
20
+ line-height: 1rem;
21
+ }
22
+
23
+ .mint-adduct-text--inherit {
24
+ font-size: inherit;
25
+ line-height: inherit;
26
+ }
27
+
28
+ .mint-adduct-text--badge {
29
+ padding: 0.125rem 0.25rem;
30
+ border-radius: var(--radius-sm);
31
+ background: var(--bg-tertiary);
32
+ }
33
+
34
+ .mint-adduct-text--on-primary {
35
+ color: rgb(255 255 255 / 0.82);
36
+ }
37
+
38
+ .mint-adduct-text--badge.mint-adduct-text--on-primary {
39
+ background: rgb(255 255 255 / 0.2);
40
+ }
41
+
42
+ .mint-adduct-text--muted,
43
+ .mint-adduct-text--empty {
44
+ color: var(--text-muted);
45
+ }
@@ -7,6 +7,16 @@
7
7
  overflow: hidden;
8
8
  }
9
9
 
10
+ .mint-chart--frame {
11
+ display: flex;
12
+ flex-direction: column;
13
+ height: 100%;
14
+ min-height: 0;
15
+ border: 0;
16
+ border-radius: 0;
17
+ background: var(--bg-card);
18
+ }
19
+
10
20
  .mint-chart__header {
11
21
  display: flex;
12
22
  align-items: flex-start;
@@ -51,6 +61,44 @@
51
61
  min-height: 12rem;
52
62
  }
53
63
 
64
+ .mint-chart--frame .mint-chart__header {
65
+ align-items: center;
66
+ gap: 0.5rem;
67
+ padding: 0.625rem 0.875rem;
68
+ flex-shrink: 0;
69
+ }
70
+
71
+ .mint-chart--frame .mint-chart__header-text {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 0.5rem;
75
+ }
76
+
77
+ .mint-chart--frame .mint-chart__title {
78
+ overflow: hidden;
79
+ font-size: 0.8125rem;
80
+ letter-spacing: -0.005em;
81
+ text-overflow: ellipsis;
82
+ white-space: nowrap;
83
+ }
84
+
85
+ .mint-chart--frame .mint-chart__description {
86
+ margin-top: 0 !important;
87
+ overflow: hidden;
88
+ font-size: 0.75rem;
89
+ text-overflow: ellipsis;
90
+ white-space: nowrap;
91
+ }
92
+
93
+ .mint-chart--frame .mint-chart__toolbar {
94
+ gap: 0.375rem;
95
+ }
96
+
97
+ .mint-chart--frame .mint-chart__body {
98
+ flex: 1;
99
+ min-height: 0;
100
+ }
101
+
54
102
  .mint-chart__loading-overlay {
55
103
  position: absolute;
56
104
  inset: 0;
@@ -61,9 +109,53 @@
61
109
  z-index: 1;
62
110
  }
63
111
 
112
+ .mint-chart--frame .mint-chart__loading-overlay {
113
+ z-index: 10;
114
+ padding: 1.5rem;
115
+ }
116
+
117
+ .mint-chart--frame .mint-chart__body > .mint-empty-state {
118
+ position: absolute;
119
+ inset: 0;
120
+ z-index: 10;
121
+ max-width: none;
122
+ padding: 1.5rem;
123
+ background: var(--bg-card);
124
+ }
125
+
126
+ .mint-chart--frame .mint-empty-state__icon-wrapper {
127
+ width: 2.5rem;
128
+ height: 2.5rem;
129
+ margin-bottom: 0.5rem;
130
+ background: transparent;
131
+ }
132
+
133
+ .mint-chart--frame .mint-empty-state__title {
134
+ font-size: 0.8125rem;
135
+ }
136
+
64
137
  .mint-chart__legend {
65
138
  padding: 0.75rem 1.5rem;
66
139
  border-top: 1px solid var(--border-color);
67
140
  font-size: 0.8125rem;
68
141
  color: var(--text-secondary);
69
142
  }
143
+
144
+ .mint-chart__footer {
145
+ padding: 0.625rem 0.875rem;
146
+ border-top: 1px solid var(--border-color);
147
+ color: var(--text-secondary);
148
+ font-size: 0.75rem;
149
+ }
150
+
151
+ .mint-chart--frame .mint-chart__footer {
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 0.75rem;
155
+ padding: 0.4375rem 0.875rem;
156
+ background: var(--bg-primary);
157
+ color: var(--text-muted);
158
+ font-family: var(--font-mono);
159
+ font-size: 0.65625rem;
160
+ flex-shrink: 0;
161
+ }
@@ -1,18 +1,48 @@
1
1
  /* ChemicalFormula Component Styles */
2
2
 
3
3
  .mint-chem-formula {
4
- display: inline;
5
- font-family: Arial, Helvetica, sans-serif;
4
+ display: inline-flex;
5
+ align-items: baseline;
6
+ gap: 0.25rem;
7
+ max-width: 100%;
8
+ font-family: var(--font-mono);
9
+ font-weight: 600;
6
10
  color: var(--text-primary);
7
- letter-spacing: 0.01em;
11
+ letter-spacing: 0;
12
+ white-space: nowrap;
8
13
  }
9
14
 
10
15
  .mint-chem-formula--block {
11
- display: block;
16
+ display: flex;
12
17
  font-size: 1.25em;
13
18
  padding: 0.25rem 0;
14
19
  }
15
20
 
21
+ .mint-chem-formula--xs { font-size: 0.625rem; line-height: 1rem; }
22
+ .mint-chem-formula--sm { font-size: 0.75rem; line-height: 1rem; }
23
+ .mint-chem-formula--primary { color: var(--text-primary); }
24
+ .mint-chem-formula--secondary { color: var(--text-secondary); }
25
+ .mint-chem-formula--muted,
26
+ .mint-chem-formula--empty { color: var(--text-muted); }
27
+
28
+ .mint-chem-formula--pill {
29
+ padding: 0.125rem 0.375rem;
30
+ border-radius: 0.375rem;
31
+ background: var(--bg-tertiary);
32
+ }
33
+
34
+ .mint-chem-formula--pill.mint-chem-formula--sm {
35
+ padding: 0.125rem 0.625rem;
36
+ border: 1px solid var(--border-color);
37
+ border-radius: 999px;
38
+ }
39
+
40
+ .mint-chem-formula__formula {
41
+ min-width: 0;
42
+ overflow: hidden;
43
+ text-overflow: ellipsis;
44
+ }
45
+
16
46
  .mint-chem-formula__element {
17
47
  /* default styling inherited */
18
48
  }
@@ -0,0 +1,60 @@
1
+ .mint-layout-resize-handle {
2
+ display: flex;
3
+ flex-shrink: 0;
4
+ align-items: center;
5
+ justify-content: center;
6
+ background: transparent;
7
+ cursor: col-resize;
8
+ }
9
+
10
+ .mint-layout-resize-handle--vertical { width: 1rem; min-height: 2.75rem; }
11
+ .mint-layout-resize-handle--horizontal { min-width: 2.75rem; height: 1rem; cursor: row-resize; }
12
+ .mint-layout-resize-handle--edge-left,
13
+ .mint-layout-resize-handle--edge-right { position: absolute; top: 0; bottom: 0; z-index: 10; height: auto; }
14
+ .mint-layout-resize-handle--edge-left { left: 0; transform: translateX(-100%); }
15
+ .mint-layout-resize-handle--edge-right { right: 0; transform: translateX(100%); }
16
+
17
+ .mint-layout-resize-handle__bar {
18
+ display: block;
19
+ width: 0.25rem;
20
+ height: 2.75rem;
21
+ border-radius: 999px;
22
+ background: var(--border-color);
23
+ opacity: 0.72;
24
+ transition: background-color 150ms ease, opacity 150ms ease, transform 150ms ease;
25
+ }
26
+
27
+ .mint-layout-resize-handle--horizontal .mint-layout-resize-handle__bar {
28
+ width: 2.75rem;
29
+ height: 0.25rem;
30
+ }
31
+
32
+ .mint-layout-resize-handle:hover .mint-layout-resize-handle__bar,
33
+ .mint-layout-resize-handle:focus-visible .mint-layout-resize-handle__bar {
34
+ background: var(--color-primary);
35
+ opacity: 1;
36
+ }
37
+
38
+ .mint-layout-resize-handle--vertical:hover .mint-layout-resize-handle__bar,
39
+ .mint-layout-resize-handle--vertical:focus-visible .mint-layout-resize-handle__bar { transform: scaleX(1.35); }
40
+ .mint-layout-resize-handle--horizontal:hover .mint-layout-resize-handle__bar,
41
+ .mint-layout-resize-handle--horizontal:focus-visible .mint-layout-resize-handle__bar { transform: scaleY(1.35); }
42
+
43
+ .mint-layout-resize-handle:focus-visible {
44
+ outline: 2px solid var(--color-primary);
45
+ outline-offset: 2px;
46
+ }
47
+
48
+ .mint-layout-resize-handle--edge-left:focus-visible,
49
+ .mint-layout-resize-handle--edge-right:focus-visible { outline-offset: -2px; }
50
+
51
+ .mint-layout-resize-handle[aria-disabled='true'] { cursor: not-allowed; opacity: 0.5; }
52
+
53
+ @media (prefers-reduced-motion: reduce) {
54
+ .mint-layout-resize-handle__bar { transition: none; }
55
+ }
56
+
57
+ @media (pointer: coarse) {
58
+ .mint-layout-resize-handle--vertical { width: 2.75rem; }
59
+ .mint-layout-resize-handle--horizontal { height: 2.75rem; }
60
+ }
@@ -105,6 +105,8 @@
105
105
  -webkit-appearance: textfield;
106
106
  -moz-appearance: textfield;
107
107
  appearance: textfield;
108
+ cursor: ew-resize;
109
+ touch-action: pan-y;
108
110
  }
109
111
 
110
112
  .mint-number-input__input::-webkit-outer-spin-button,
@@ -20,6 +20,12 @@
20
20
  gap: 2px;
21
21
  }
22
22
 
23
+ .mint-rack-editor__tab-item {
24
+ position: relative;
25
+ display: flex;
26
+ align-items: flex-end;
27
+ }
28
+
23
29
  .mint-rack-editor__tab {
24
30
  position: relative;
25
31
  display: flex;
@@ -32,7 +38,7 @@
32
38
  border-top-right-radius: 0.5rem;
33
39
  border: 1px solid transparent;
34
40
  cursor: grab;
35
- transition: all 0.2s;
41
+ transition: background-color 0.2s, border-color 0.2s, color 0.2s, opacity 0.2s;
36
42
  background: transparent;
37
43
  color: var(--text-muted);
38
44
  white-space: nowrap;
@@ -47,6 +53,11 @@
47
53
  color: var(--text-secondary);
48
54
  }
49
55
 
56
+ .mint-rack-editor__tab:focus-visible {
57
+ outline: 2px solid var(--color-primary);
58
+ outline-offset: -2px;
59
+ }
60
+
50
61
  .mint-rack-editor__tab--active {
51
62
  background-color: var(--bg-secondary);
52
63
  color: var(--text-primary);
@@ -61,7 +72,7 @@
61
72
  }
62
73
 
63
74
  .mint-rack-editor__tab--drag-over {
64
- box-shadow: 0 0 0 2px #6366f1, 0 0 0 3px white;
75
+ box-shadow: var(--focus-ring);
65
76
  }
66
77
 
67
78
  /* Slot color dot */
@@ -82,8 +93,8 @@
82
93
  }
83
94
 
84
95
  .mint-rack-editor__count--active {
85
- background-color: rgba(16, 185, 129, 0.15);
86
- color: #10b981;
96
+ background-color: color-mix(in srgb, var(--mint-success) 15%, transparent);
97
+ color: var(--mint-success);
87
98
  }
88
99
 
89
100
  .mint-rack-editor__count--inactive {
@@ -104,7 +115,7 @@
104
115
  justify-content: center;
105
116
  font-size: 10px;
106
117
  font-weight: 700;
107
- background-color: rgba(239, 68, 68, 0.9);
118
+ background-color: color-mix(in srgb, var(--mint-error) 90%, transparent);
108
119
  color: white;
109
120
  border: none;
110
121
  cursor: pointer;
@@ -112,7 +123,8 @@
112
123
  transition: opacity 0.15s, transform 0.15s;
113
124
  }
114
125
 
115
- .mint-rack-editor__tab:hover .mint-rack-editor__tab-remove {
126
+ .mint-rack-editor__tab-item:hover .mint-rack-editor__tab-remove,
127
+ .mint-rack-editor__tab-item:focus-within .mint-rack-editor__tab-remove {
116
128
  opacity: 1;
117
129
  }
118
130
 
@@ -120,6 +132,16 @@
120
132
  transform: scale(1.1);
121
133
  }
122
134
 
135
+ .mint-rack-editor__tab-remove:focus-visible {
136
+ opacity: 1;
137
+ outline: 2px solid var(--color-primary);
138
+ outline-offset: 2px;
139
+ }
140
+
141
+ @media (hover: none) {
142
+ .mint-rack-editor__tab-remove { opacity: 1; }
143
+ }
144
+
123
145
  /* Add rack button */
124
146
  .mint-rack-editor__add-btn {
125
147
  display: flex;
@@ -131,15 +153,15 @@
131
153
  font-weight: 500;
132
154
  border-radius: var(--radius-md);
133
155
  cursor: pointer;
134
- transition: all 0.2s;
156
+ transition: background-color 0.2s, color 0.2s;
135
157
  border: none;
136
158
  background: transparent;
137
159
  color: var(--text-muted);
138
160
  }
139
161
 
140
162
  .mint-rack-editor__add-btn:hover {
141
- background-color: rgba(16, 185, 129, 0.1);
142
- color: #10b981;
163
+ background-color: color-mix(in srgb, var(--mint-success) 10%, transparent);
164
+ color: var(--mint-success);
143
165
  }
144
166
 
145
167
  /* Toolbar */