@morscherlab/mint-sdk 1.0.0-alpha.9 → 1.0.0-beta.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.
- package/dist/__tests__/components/PluginIcon.test.d.ts +1 -0
- package/dist/components/AppTopBar.vue.d.ts +2 -0
- package/dist/components/BaseButton.vue.d.ts +1 -1
- package/dist/components/BaseCheckbox.vue.d.ts +1 -1
- package/dist/components/BaseInput.vue.d.ts +1 -1
- package/dist/components/BasePill.vue.d.ts +1 -1
- package/dist/components/BaseRadioGroup.vue.d.ts +1 -1
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseSlider.vue.d.ts +1 -1
- package/dist/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/components/BaseToggle.vue.d.ts +1 -1
- package/dist/components/ColorSlider.vue.d.ts +1 -1
- package/dist/components/ConcentrationInput.vue.d.ts +1 -1
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +1 -1
- package/dist/components/Divider.vue.d.ts +1 -1
- package/dist/components/DropdownButton.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +1 -1
- package/dist/components/FormulaInput.vue.d.ts +1 -1
- package/dist/components/IconButton.vue.d.ts +1 -1
- package/dist/components/LoadingSpinner.vue.d.ts +1 -1
- package/dist/components/MultiSelect.vue.d.ts +1 -1
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PluginIcon.vue.d.ts +11 -0
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ReagentEditor.vue.d.ts +1 -1
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SampleSelector.vue.d.ts +1 -1
- package/dist/components/ScientificNumber.vue.d.ts +1 -1
- package/dist/components/SegmentedControl.vue.d.ts +1 -1
- package/dist/components/SettingsModal.vue.d.ts +22 -2
- package/dist/components/TagsInput.vue.d.ts +1 -1
- package/dist/components/TimePicker.vue.d.ts +1 -1
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +1 -1
- package/dist/components/WellPlate.vue.d.ts +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +3 -3
- package/dist/{components-CzbQQPCb.js → components-_XqPEhP9.js} +572 -362
- package/dist/components-_XqPEhP9.js.map +1 -0
- package/dist/composables/index.js +2 -2
- package/dist/composables/usePlatformContext.d.ts +3 -0
- package/dist/{composables-BXklV5ii.js → composables-tiZqLu1M.js} +2 -2
- package/dist/{composables-BXklV5ii.js.map → composables-tiZqLu1M.js.map} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/install.js +2 -2
- package/dist/stores/auth.d.ts +1 -1
- package/dist/styles.css +896 -553
- package/dist/types/components.d.ts +39 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/platform.d.ts +1 -0
- package/dist/{useScheduleDrag-CxBeqYcu.js → useScheduleDrag-CA9sGNJG.js} +4000 -4000
- package/dist/useScheduleDrag-CA9sGNJG.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppTopBar.test.ts +31 -13
- package/src/__tests__/components/PluginIcon.test.ts +119 -0
- package/src/components/AppTopBar.vue +32 -27
- package/src/components/PluginIcon.story.vue +71 -0
- package/src/components/PluginIcon.vue +88 -0
- package/src/components/SettingsModal.story.vue +337 -45
- package/src/components/SettingsModal.vue +251 -64
- package/src/components/index.ts +1 -0
- package/src/index.ts +4 -0
- package/src/styles/components/app-pill-nav.css +1 -2
- package/src/styles/components/app-top-bar.css +1 -2
- package/src/styles/components/button.css +3 -7
- package/src/styles/components/dropdown-button.css +4 -4
- package/src/styles/components/input.css +4 -5
- package/src/styles/components/number-input.css +3 -3
- package/src/styles/components/plugin-icon.css +38 -0
- package/src/styles/components/segmented-control.css +4 -7
- package/src/styles/components/settings-modal.css +184 -0
- package/src/styles/components/tabs.css +1 -2
- package/src/styles/components/textarea.css +4 -5
- package/src/styles/components/unit-input.css +3 -3
- package/src/types/components.ts +42 -0
- package/src/types/index.ts +3 -0
- package/src/types/platform.ts +1 -0
- package/dist/components-CzbQQPCb.js.map +0 -1
- package/dist/useScheduleDrag-CxBeqYcu.js.map +0 -1
|
@@ -1,16 +1,67 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Tabbed settings modal with three usage modes:
|
|
4
|
+
*
|
|
5
|
+
* 1. Schema-driven (recommended) — pass `schema` + `v-model:values`. Each
|
|
6
|
+
* group becomes a tab; fields auto-render via the SDK's FormFieldRenderer
|
|
7
|
+
* registry (text, select, number, toggle, molecule, concentration, …).
|
|
8
|
+
* Conditional visibility, validation, and dynamic options come for free.
|
|
9
|
+
*
|
|
10
|
+
* 2. Manual tabs + slots — pass `tabs` and a `<template #tab-{id}>` slot
|
|
11
|
+
* per tab. Use this when you need bespoke widgets the form-builder
|
|
12
|
+
* registry doesn't cover (or for legacy plugins).
|
|
13
|
+
*
|
|
14
|
+
* 3. Appearance only — `showAppearance` (default true) renders the built-in
|
|
15
|
+
* theme / palette / table-density tab. Works alongside both modes above.
|
|
16
|
+
*
|
|
17
|
+
* Layout: `horizontal` (underline tabs, default) or `vertical` (sidebar rail
|
|
18
|
+
* with optional icons + descriptions, recommended for 5+ groups).
|
|
19
|
+
*/
|
|
20
|
+
import { ref, computed, watch } from 'vue'
|
|
4
21
|
import BaseModal from './BaseModal.vue'
|
|
22
|
+
import FormFieldRenderer from './FormFieldRenderer.vue'
|
|
23
|
+
import { useFormBuilder } from '../composables/useFormBuilder'
|
|
5
24
|
import { useSettingsStore, colorPalettes } from '../stores/settings'
|
|
6
|
-
import type {
|
|
25
|
+
import type {
|
|
26
|
+
ThemeMode,
|
|
27
|
+
ColorPalette,
|
|
28
|
+
TableDensity,
|
|
29
|
+
SettingsTab,
|
|
30
|
+
SettingsModalLayout,
|
|
31
|
+
SettingsModalSchema,
|
|
32
|
+
FormSchema,
|
|
33
|
+
FormSectionSchema,
|
|
34
|
+
FormEnhancements,
|
|
35
|
+
} from '../types'
|
|
36
|
+
|
|
37
|
+
// Map our settings groups onto the form-builder's flat-section shape.
|
|
38
|
+
// `title: ''` because the rail/pane header already shows the group name.
|
|
39
|
+
function buildFlatSchema(schema: SettingsModalSchema): FormSchema {
|
|
40
|
+
return {
|
|
41
|
+
sections: schema.groups.map<FormSectionSchema>((g) => ({
|
|
42
|
+
id: g.id,
|
|
43
|
+
title: '',
|
|
44
|
+
fields: g.fields,
|
|
45
|
+
columns: g.columns,
|
|
46
|
+
condition: g.condition,
|
|
47
|
+
})),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
7
50
|
|
|
8
51
|
interface Props {
|
|
9
52
|
modelValue: boolean
|
|
10
53
|
title?: string
|
|
54
|
+
/** Manual tab descriptors. Ignored when `schema` is set (groups become tabs). */
|
|
11
55
|
tabs?: SettingsTab[]
|
|
12
56
|
showAppearance?: boolean
|
|
13
57
|
size?: 'md' | 'lg' | 'xl'
|
|
58
|
+
layout?: SettingsModalLayout
|
|
59
|
+
/** Declarative schema — fields auto-render via SDK form components. */
|
|
60
|
+
schema?: SettingsModalSchema
|
|
61
|
+
/** Two-way bound values when `schema` is set. */
|
|
62
|
+
values?: Record<string, unknown>
|
|
63
|
+
/** Optional dynamic enhancements (validators, dynamic options, callbacks). */
|
|
64
|
+
enhancements?: FormEnhancements<Record<string, unknown>>
|
|
14
65
|
}
|
|
15
66
|
|
|
16
67
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -18,22 +69,85 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
18
69
|
tabs: () => [],
|
|
19
70
|
showAppearance: true,
|
|
20
71
|
size: 'lg',
|
|
72
|
+
layout: 'horizontal',
|
|
21
73
|
})
|
|
22
74
|
|
|
23
75
|
const emit = defineEmits<{
|
|
24
76
|
'update:modelValue': [value: boolean]
|
|
77
|
+
'update:values': [data: Record<string, unknown>]
|
|
25
78
|
close: []
|
|
26
79
|
}>()
|
|
27
80
|
|
|
28
81
|
const settings = useSettingsStore()
|
|
29
82
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
83
|
+
const APPEARANCE_TAB_ID = 'appearance'
|
|
84
|
+
|
|
85
|
+
const APPEARANCE_TAB: SettingsTab = {
|
|
86
|
+
id: APPEARANCE_TAB_ID,
|
|
87
|
+
label: 'Appearance',
|
|
88
|
+
description: 'Theme, color palette, and table density',
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const isVertical = computed(() => props.layout === 'vertical')
|
|
92
|
+
|
|
93
|
+
// Per-instance prefix for ARIA tab/tabpanel id wiring. Stable across renders.
|
|
94
|
+
const uid = `mint-settings-${Math.random().toString(36).slice(2, 9)}`
|
|
95
|
+
const tabId = (id: string) => `${uid}-tab-${id}`
|
|
96
|
+
const panelId = (id: string) => `${uid}-panel-${id}`
|
|
97
|
+
|
|
98
|
+
// `props.schema` is read once at setup. Pass a stable schema object (declare it
|
|
99
|
+
// at module scope, not inside a render function) — the form-builder is not
|
|
100
|
+
// re-initialized when the prop changes. To swap schemas, key the modal:
|
|
101
|
+
// `<SettingsModal :key="schemaId" :schema="schemaA" />`.
|
|
102
|
+
const builder = props.schema
|
|
103
|
+
? useFormBuilder(buildFlatSchema(props.schema), props.values, props.enhancements)
|
|
104
|
+
: null
|
|
105
|
+
|
|
106
|
+
if (builder) {
|
|
107
|
+
watch(
|
|
108
|
+
() => ({ ...builder.form.data }),
|
|
109
|
+
(data) => emit('update:values', data as Record<string, unknown>),
|
|
110
|
+
{ deep: true },
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Schema groups whose `condition` evaluates false against the current data are
|
|
115
|
+
// dropped from the rail entirely — same semantics as section visibility in
|
|
116
|
+
// FormBuilder. Manual `tabs` have no condition mechanism, so they pass through.
|
|
117
|
+
const visibleSchemaGroups = computed(() =>
|
|
118
|
+
builder && props.schema
|
|
119
|
+
? props.schema.groups.filter((g) => builder.isSectionVisible(g.id))
|
|
120
|
+
: [],
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
const allTabs = computed<SettingsTab[]>(() => {
|
|
124
|
+
const base: SettingsTab[] = props.schema
|
|
125
|
+
? visibleSchemaGroups.value.map((g) => ({ id: g.id, label: g.label, icon: g.icon, description: g.description }))
|
|
33
126
|
: props.tabs
|
|
127
|
+
return props.showAppearance ? [...base, APPEARANCE_TAB] : base
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const activeTab = ref(allTabs.value[0]?.id || APPEARANCE_TAB_ID)
|
|
131
|
+
|
|
132
|
+
const activeTabMeta = computed<SettingsTab | undefined>(() =>
|
|
133
|
+
allTabs.value.find((t) => t.id === activeTab.value),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
// If the active tab vanishes (group hidden by condition), drop back to the first available.
|
|
137
|
+
watch(allTabs, (tabs) => {
|
|
138
|
+
if (!tabs.some((t) => t.id === activeTab.value)) {
|
|
139
|
+
activeTab.value = tabs[0]?.id ?? APPEARANCE_TAB_ID
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const activeGroup = computed(() =>
|
|
144
|
+
visibleSchemaGroups.value.find((g) => g.id === activeTab.value),
|
|
34
145
|
)
|
|
35
146
|
|
|
36
|
-
const
|
|
147
|
+
const activeGroupVisibleFields = computed(() => {
|
|
148
|
+
if (!activeGroup.value || !builder) return []
|
|
149
|
+
return activeGroup.value.fields.filter((f) => builder.isFieldVisible(f.name))
|
|
150
|
+
})
|
|
37
151
|
|
|
38
152
|
const themeOptions: { value: ThemeMode; label: string }[] = [
|
|
39
153
|
{ value: 'light', label: 'Light' },
|
|
@@ -61,9 +175,11 @@ function handleClose() {
|
|
|
61
175
|
@update:model-value="emit('update:modelValue', $event)"
|
|
62
176
|
@close="handleClose"
|
|
63
177
|
>
|
|
64
|
-
<div class="mint-settings-modal">
|
|
65
|
-
|
|
66
|
-
|
|
178
|
+
<div :class="['mint-settings-modal', `mint-settings-modal--${layout}`]">
|
|
179
|
+
<div
|
|
180
|
+
v-if="!isVertical && allTabs.length > 1"
|
|
181
|
+
class="mint-settings-modal__tabs"
|
|
182
|
+
>
|
|
67
183
|
<button
|
|
68
184
|
v-for="tab in allTabs"
|
|
69
185
|
:key="tab.id"
|
|
@@ -75,68 +191,139 @@ function handleClose() {
|
|
|
75
191
|
</button>
|
|
76
192
|
</div>
|
|
77
193
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
194
|
+
<div
|
|
195
|
+
v-else-if="isVertical"
|
|
196
|
+
class="mint-settings-modal__rail"
|
|
197
|
+
role="tablist"
|
|
198
|
+
aria-orientation="vertical"
|
|
199
|
+
aria-label="Settings sections"
|
|
200
|
+
>
|
|
201
|
+
<button
|
|
202
|
+
v-for="tab in allTabs"
|
|
203
|
+
:id="tabId(tab.id)"
|
|
204
|
+
:key="tab.id"
|
|
205
|
+
type="button"
|
|
206
|
+
role="tab"
|
|
207
|
+
:class="['mint-settings-modal__rail-item', { 'mint-settings-modal__rail-item--active': activeTab === tab.id }]"
|
|
208
|
+
:aria-selected="activeTab === tab.id"
|
|
209
|
+
:aria-controls="panelId(tab.id)"
|
|
210
|
+
:tabindex="activeTab === tab.id ? 0 : -1"
|
|
211
|
+
@click="activeTab = tab.id"
|
|
212
|
+
>
|
|
213
|
+
<span class="mint-settings-modal__rail-item-icon" aria-hidden="true">
|
|
214
|
+
<span v-if="tab.icon" v-html="tab.icon" />
|
|
215
|
+
</span>
|
|
216
|
+
<span class="mint-settings-modal__rail-item-text">
|
|
217
|
+
<span class="mint-settings-modal__rail-item-label">{{ tab.label }}</span>
|
|
218
|
+
<span v-if="tab.description" class="mint-settings-modal__rail-item-description">
|
|
219
|
+
{{ tab.description }}
|
|
220
|
+
</span>
|
|
221
|
+
</span>
|
|
222
|
+
</button>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<!-- Vertical mode wraps content in a tabpanel <section> with a header bar; horizontal mode stays a plain <div>. -->
|
|
226
|
+
<component
|
|
227
|
+
:is="isVertical ? 'section' : 'div'"
|
|
228
|
+
:class="isVertical ? 'mint-settings-modal__pane' : 'mint-settings-modal__content'"
|
|
229
|
+
:id="isVertical && activeTabMeta ? panelId(activeTabMeta.id) : undefined"
|
|
230
|
+
:role="isVertical ? 'tabpanel' : undefined"
|
|
231
|
+
:aria-labelledby="isVertical && activeTabMeta ? tabId(activeTabMeta.id) : undefined"
|
|
232
|
+
:tabindex="isVertical ? 0 : undefined"
|
|
233
|
+
>
|
|
234
|
+
<header
|
|
235
|
+
v-if="isVertical && activeTabMeta?.label"
|
|
236
|
+
class="mint-settings-modal__pane-header"
|
|
237
|
+
>
|
|
238
|
+
<h4 class="mint-settings-modal__pane-title">{{ activeTabMeta.label }}</h4>
|
|
239
|
+
<p v-if="activeTabMeta.description" class="mint-settings-modal__pane-subtitle">
|
|
240
|
+
{{ activeTabMeta.description }}
|
|
241
|
+
</p>
|
|
242
|
+
</header>
|
|
243
|
+
|
|
244
|
+
<div :class="isVertical ? 'mint-settings-modal__pane-body' : null">
|
|
245
|
+
<div
|
|
246
|
+
v-if="schema && builder && activeGroup"
|
|
247
|
+
class="mint-settings-modal__group-grid"
|
|
248
|
+
:style="{ '--mint-settings-cols': activeGroup.columns ?? 1 }"
|
|
249
|
+
>
|
|
250
|
+
<template v-for="field in activeGroupVisibleFields" :key="field.name">
|
|
251
|
+
<div :style="field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined">
|
|
252
|
+
<slot
|
|
253
|
+
:name="`field:${field.name}`"
|
|
254
|
+
:field="field"
|
|
255
|
+
:form="builder.form"
|
|
256
|
+
:field-props="builder.form.getFieldProps(field.name)"
|
|
257
|
+
>
|
|
258
|
+
<FormFieldRenderer
|
|
259
|
+
:field="field"
|
|
260
|
+
:resolved-props="builder.getResolvedFieldProps(field)"
|
|
261
|
+
:form="builder.form"
|
|
262
|
+
/>
|
|
263
|
+
</slot>
|
|
264
|
+
</div>
|
|
265
|
+
</template>
|
|
83
266
|
</div>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
267
|
+
|
|
268
|
+
<template v-else-if="!builder">
|
|
269
|
+
<template v-for="tab in tabs" :key="tab.id">
|
|
270
|
+
<div v-show="activeTab === tab.id">
|
|
271
|
+
<slot :name="`tab-${tab.id}`" />
|
|
272
|
+
</div>
|
|
273
|
+
</template>
|
|
274
|
+
</template>
|
|
275
|
+
|
|
276
|
+
<div v-if="showAppearance" v-show="activeTab === APPEARANCE_TAB_ID">
|
|
277
|
+
<div class="mint-settings-modal__section">
|
|
278
|
+
<div class="mint-settings-modal__section-label">Theme</div>
|
|
279
|
+
<div class="mint-settings-modal__option-group">
|
|
280
|
+
<button
|
|
281
|
+
v-for="opt in themeOptions"
|
|
282
|
+
:key="opt.value"
|
|
283
|
+
type="button"
|
|
284
|
+
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.theme === opt.value }]"
|
|
285
|
+
@click="settings.theme = opt.value"
|
|
286
|
+
>
|
|
287
|
+
{{ opt.label }}
|
|
288
|
+
</button>
|
|
289
|
+
</div>
|
|
101
290
|
</div>
|
|
102
|
-
</div>
|
|
103
291
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
</
|
|
292
|
+
<div class="mint-settings-modal__section">
|
|
293
|
+
<div class="mint-settings-modal__section-label">Color Palette</div>
|
|
294
|
+
<div class="mint-settings-modal__option-group">
|
|
295
|
+
<button
|
|
296
|
+
v-for="(palette, key) in colorPalettes"
|
|
297
|
+
:key="key"
|
|
298
|
+
type="button"
|
|
299
|
+
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.colorPalette === key }]"
|
|
300
|
+
@click="settings.colorPalette = key as ColorPalette"
|
|
301
|
+
>
|
|
302
|
+
{{ palette.name }}
|
|
303
|
+
</button>
|
|
304
|
+
</div>
|
|
117
305
|
</div>
|
|
118
|
-
</div>
|
|
119
306
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
</
|
|
307
|
+
<div class="mint-settings-modal__section">
|
|
308
|
+
<div class="mint-settings-modal__section-label">Table Density</div>
|
|
309
|
+
<div class="mint-settings-modal__option-group">
|
|
310
|
+
<button
|
|
311
|
+
v-for="opt in densityOptions"
|
|
312
|
+
:key="opt.value"
|
|
313
|
+
type="button"
|
|
314
|
+
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.tableDensity === opt.value }]"
|
|
315
|
+
@click="settings.tableDensity = opt.value"
|
|
316
|
+
>
|
|
317
|
+
{{ opt.label }}
|
|
318
|
+
</button>
|
|
319
|
+
</div>
|
|
320
|
+
<p class="mint-settings-modal__note">Adjusts row height in data tables.</p>
|
|
133
321
|
</div>
|
|
134
|
-
<p class="mint-settings-modal__note">Adjusts row height in data tables.</p>
|
|
135
|
-
</div>
|
|
136
322
|
|
|
137
|
-
|
|
323
|
+
<slot name="appearance" />
|
|
324
|
+
</div>
|
|
138
325
|
</div>
|
|
139
|
-
</
|
|
326
|
+
</component>
|
|
140
327
|
</div>
|
|
141
328
|
</BaseModal>
|
|
142
329
|
</template>
|
package/src/components/index.ts
CHANGED
|
@@ -45,6 +45,7 @@ export { default as AppPluginSwitcher } from './AppPluginSwitcher.vue'
|
|
|
45
45
|
export { default as AppSidebar } from './AppSidebar.vue'
|
|
46
46
|
export { default as AppLayout } from './AppLayout.vue'
|
|
47
47
|
export { default as AppContainer } from './AppContainer.vue'
|
|
48
|
+
export { default as PluginIcon } from './PluginIcon.vue'
|
|
48
49
|
|
|
49
50
|
// Utility components
|
|
50
51
|
export { default as Skeleton } from './Skeleton.vue'
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ export {
|
|
|
38
38
|
AppSidebar,
|
|
39
39
|
AppLayout,
|
|
40
40
|
AppContainer,
|
|
41
|
+
PluginIcon,
|
|
41
42
|
Skeleton,
|
|
42
43
|
// Biological experiment components
|
|
43
44
|
WellPlate,
|
|
@@ -301,6 +302,9 @@ export type {
|
|
|
301
302
|
ConfirmVariant,
|
|
302
303
|
// SettingsModal types
|
|
303
304
|
SettingsTab,
|
|
305
|
+
SettingsModalLayout,
|
|
306
|
+
SettingsGroup,
|
|
307
|
+
SettingsModalSchema,
|
|
304
308
|
// ScientificNumber types
|
|
305
309
|
NumberNotation,
|
|
306
310
|
// TimePicker types
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
display: inline-flex;
|
|
14
14
|
align-items: center;
|
|
15
15
|
border: 0;
|
|
16
|
-
|
|
17
|
-
padding: 0.3125rem 0.875rem 0.4375rem;
|
|
16
|
+
padding: 0.375rem 0.875rem;
|
|
18
17
|
border-radius: 9999px;
|
|
19
18
|
font-size: 0.8125rem;
|
|
20
19
|
font-weight: 500;
|
|
@@ -278,8 +278,7 @@
|
|
|
278
278
|
display: inline-flex;
|
|
279
279
|
align-items: center;
|
|
280
280
|
gap: 0.25rem;
|
|
281
|
-
|
|
282
|
-
padding: 0.3125rem 0.875rem 0.4375rem;
|
|
281
|
+
padding: 0.375rem 0.875rem;
|
|
283
282
|
border: 0;
|
|
284
283
|
background: transparent;
|
|
285
284
|
border-radius: 9999px;
|
|
@@ -133,24 +133,20 @@
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/* Sizes */
|
|
136
|
-
/* Optical centering: Fira Sans' cap-letter midpoint sits above the em-box center.
|
|
137
|
-
Old rule (pre-0.16) compensated with a 2px upward shift; user testing showed
|
|
138
|
-
that read as "too high" across sizes. New rule: 1px upward shift universally.
|
|
139
|
-
See CLAUDE.md § Optical Centering. */
|
|
140
136
|
.mint-button--sm {
|
|
141
|
-
padding: 0.
|
|
137
|
+
padding: 0.375rem 0.75rem;
|
|
142
138
|
font-size: 0.875rem;
|
|
143
139
|
min-height: var(--form-height-sm);
|
|
144
140
|
}
|
|
145
141
|
|
|
146
142
|
.mint-button--md {
|
|
147
|
-
padding: 0.
|
|
143
|
+
padding: 0.5rem 1rem;
|
|
148
144
|
font-size: 0.875rem;
|
|
149
145
|
min-height: var(--form-height-md);
|
|
150
146
|
}
|
|
151
147
|
|
|
152
148
|
.mint-button--lg {
|
|
153
|
-
padding: 0.
|
|
149
|
+
padding: 0.75rem 1.5rem;
|
|
154
150
|
font-size: 1rem;
|
|
155
151
|
min-height: var(--form-height-lg);
|
|
156
152
|
}
|
|
@@ -125,21 +125,21 @@
|
|
|
125
125
|
background-color: var(--bg-tertiary);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
/* Trigger sizes
|
|
128
|
+
/* Trigger sizes */
|
|
129
129
|
.mint-dropdown-button__trigger--sm {
|
|
130
|
-
padding: 0.
|
|
130
|
+
padding: 0.375rem 0.75rem;
|
|
131
131
|
font-size: 0.875rem;
|
|
132
132
|
min-height: var(--form-height-sm);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
.mint-dropdown-button__trigger--md {
|
|
136
|
-
padding: 0.
|
|
136
|
+
padding: 0.5rem 1rem;
|
|
137
137
|
font-size: 0.875rem;
|
|
138
138
|
min-height: var(--form-height-md);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
.mint-dropdown-button__trigger--lg {
|
|
142
|
-
padding: 0.
|
|
142
|
+
padding: 0.75rem 1.5rem;
|
|
143
143
|
font-size: 1rem;
|
|
144
144
|
min-height: var(--form-height-lg);
|
|
145
145
|
}
|
|
@@ -45,22 +45,21 @@
|
|
|
45
45
|
cursor: not-allowed;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/* Size variants
|
|
49
|
-
See CLAUDE.md § Optical Centering. */
|
|
48
|
+
/* Size variants */
|
|
50
49
|
.mint-input--sm {
|
|
51
|
-
padding: 0.
|
|
50
|
+
padding: 0.375rem 0.625rem;
|
|
52
51
|
font-size: 0.8125rem;
|
|
53
52
|
min-height: var(--form-height-sm);
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
.mint-input--md {
|
|
57
|
-
padding: 0.
|
|
56
|
+
padding: 0.5rem 0.75rem;
|
|
58
57
|
font-size: 0.875rem;
|
|
59
58
|
min-height: var(--form-height-md);
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
.mint-input--lg {
|
|
63
|
-
padding: 0.
|
|
62
|
+
padding: 0.75rem 1rem;
|
|
64
63
|
font-size: 1rem;
|
|
65
64
|
min-height: var(--form-height-lg);
|
|
66
65
|
}
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
|
|
73
73
|
/* Horizontal padding matches BaseInput canonical scale (10/12/16) so FormField's
|
|
74
74
|
12px label indent aligns with the numeric text across all input types. */
|
|
75
|
-
.mint-number-input__input--sm { padding: 0.
|
|
76
|
-
.mint-number-input__input--md { padding: 0.
|
|
77
|
-
.mint-number-input__input--lg { padding: 0.
|
|
75
|
+
.mint-number-input__input--sm { padding: 0.375rem 0.625rem; font-size: 0.875rem; }
|
|
76
|
+
.mint-number-input__input--md { padding: 0.5rem 0.75rem; font-size: 0.875rem; }
|
|
77
|
+
.mint-number-input__input--lg { padding: 0.75rem 1rem; font-size: 1rem; }
|
|
78
78
|
|
|
79
79
|
/* ---------- Unit suffix ---------- */
|
|
80
80
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* PluginIcon — BEM. Renders a plugin's icon inside a sized chip with two
|
|
2
|
+
visual variants. Tintable via `--mint-plugin-icon-tone` custom property. */
|
|
3
|
+
|
|
4
|
+
.mint-plugin-icon {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
flex-shrink: 0;
|
|
9
|
+
border-radius: var(--radius-md, 0.5rem);
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Sizes */
|
|
14
|
+
.mint-plugin-icon--sm { width: 1.5rem; height: 1.5rem; }
|
|
15
|
+
.mint-plugin-icon--md { width: 2rem; height: 2rem; }
|
|
16
|
+
.mint-plugin-icon--lg { width: 3rem; height: 3rem; border-radius: 0.75rem; }
|
|
17
|
+
|
|
18
|
+
/* Variants — `--mint-plugin-icon-tone` falls back to --color-primary */
|
|
19
|
+
.mint-plugin-icon--solid {
|
|
20
|
+
background-color: var(--mint-plugin-icon-tone, var(--color-primary));
|
|
21
|
+
color: white;
|
|
22
|
+
}
|
|
23
|
+
.mint-plugin-icon--tinted {
|
|
24
|
+
background-color: color-mix(in srgb, var(--mint-plugin-icon-tone, var(--color-primary)) 12%, transparent);
|
|
25
|
+
color: var(--mint-plugin-icon-tone, var(--color-primary));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Inner svg/img — sized to ~62% of chip */
|
|
29
|
+
.mint-plugin-icon--sm .mint-plugin-icon__svg,
|
|
30
|
+
.mint-plugin-icon--sm .mint-plugin-icon__img { width: 0.9375rem; height: 0.9375rem; }
|
|
31
|
+
.mint-plugin-icon--md .mint-plugin-icon__svg,
|
|
32
|
+
.mint-plugin-icon--md .mint-plugin-icon__img { width: 1.125rem; height: 1.125rem; }
|
|
33
|
+
.mint-plugin-icon--lg .mint-plugin-icon__svg,
|
|
34
|
+
.mint-plugin-icon--lg .mint-plugin-icon__img { width: 1.5rem; height: 1.5rem; }
|
|
35
|
+
|
|
36
|
+
.mint-plugin-icon__img {
|
|
37
|
+
object-fit: contain;
|
|
38
|
+
}
|
|
@@ -114,24 +114,21 @@
|
|
|
114
114
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
/* Size variants - simple
|
|
118
|
-
See CLAUDE.md § Optical Centering. */
|
|
117
|
+
/* Size variants - simple */
|
|
119
118
|
.mint-segmented-control__option--simple.mint-segmented-control__option--sm {
|
|
120
|
-
padding: 0.
|
|
119
|
+
padding: 0.375rem 0.75rem;
|
|
121
120
|
font-size: 0.75rem;
|
|
122
121
|
min-height: var(--form-height-sm);
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
.mint-segmented-control__option--simple.mint-segmented-control__option--md {
|
|
126
|
-
padding: 0.
|
|
125
|
+
padding: 0.5rem 1rem;
|
|
127
126
|
font-size: 0.875rem;
|
|
128
127
|
min-height: var(--form-height-md);
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
.mint-segmented-control__option--simple.mint-segmented-control__option--lg {
|
|
132
|
-
|
|
133
|
-
em-box at 1rem font + 1.25 line-height, so the 1px shift lands here. */
|
|
134
|
-
padding: 0.5625rem 1.25rem 0.6875rem;
|
|
131
|
+
padding: 0.625rem 1.25rem;
|
|
135
132
|
font-size: 1rem;
|
|
136
133
|
min-height: var(--form-height-lg);
|
|
137
134
|
}
|