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

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 (55) hide show
  1. package/dist/components/AppSidebar.vue.d.ts +6 -3
  2. package/dist/components/BaseCheckbox.vue.d.ts +6 -1
  3. package/dist/components/BasePill.vue.d.ts +1 -1
  4. package/dist/components/BaseRadioGroup.vue.d.ts +1 -1
  5. package/dist/components/BaseToggle.vue.d.ts +6 -1
  6. package/dist/components/CollapsibleCard.vue.d.ts +9 -0
  7. package/dist/components/ControlWorkspaceView.vue.d.ts +1 -1
  8. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +1 -1
  9. package/dist/components/PluginWorkspaceView.vue.d.ts +5 -1
  10. package/dist/components/index.js +2 -2
  11. package/dist/{components-DtHA2bgp.js → components-Blx4MG--.js} +565 -1477
  12. package/dist/components-Blx4MG--.js.map +1 -0
  13. package/dist/composables/controlSchemaTypes.d.ts +7 -1
  14. package/dist/composables/index.js +3 -3
  15. package/dist/{composables-Dlg8jenH.js → composables-CHDjDIQT.js} +2 -2
  16. package/dist/{composables-Dlg8jenH.js.map → composables-CHDjDIQT.js.map} +1 -1
  17. package/dist/index.js +4 -4
  18. package/dist/install.js +2 -2
  19. package/dist/styles.css +867 -126
  20. package/dist/templates/index.js +1 -1
  21. package/dist/{templates-DtdUvJ4c.js → templates-DSbHJC4v.js} +1351 -120
  22. package/dist/templates-DSbHJC4v.js.map +1 -0
  23. package/dist/types/components.d.ts +12 -0
  24. package/dist/types/form-builder.d.ts +3 -0
  25. package/dist/types/index.d.ts +1 -1
  26. package/dist/{useProtocolTemplates-Bm5vyH4_.js → useProtocolTemplates-BbPOYPzO.js} +2 -2
  27. package/dist/{useProtocolTemplates-Bm5vyH4_.js.map → useProtocolTemplates-BbPOYPzO.js.map} +1 -1
  28. package/package.json +1 -1
  29. package/src/__tests__/components/AppSidebar.test.ts +67 -0
  30. package/src/__tests__/components/CollapsibleCard.test.ts +47 -0
  31. package/src/__tests__/components/FormBuilder.test.ts +57 -0
  32. package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +2 -0
  33. package/src/components/AppSidebar.story.vue +79 -6
  34. package/src/components/AppSidebar.vue +72 -0
  35. package/src/components/BaseCheckbox.story.vue +27 -0
  36. package/src/components/BaseCheckbox.vue +63 -1
  37. package/src/components/BaseToggle.story.vue +27 -0
  38. package/src/components/BaseToggle.vue +66 -1
  39. package/src/components/CollapsibleCard.vue +123 -45
  40. package/src/components/FormBuilder.story.vue +13 -0
  41. package/src/components/PluginWorkspaceView.shell.ts +1 -0
  42. package/src/components/PluginWorkspaceView.vue +3 -0
  43. package/src/components/internal/FormFieldRendererInternal.vue +23 -5
  44. package/src/composables/controlSchemaAdapters.ts +3 -0
  45. package/src/composables/controlSchemaFormFields.ts +3 -0
  46. package/src/composables/controlSchemaTypes.ts +8 -0
  47. package/src/styles/components/app-sidebar.css +134 -6
  48. package/src/styles/components/checkbox.css +87 -0
  49. package/src/styles/components/collapsible-card.css +154 -14
  50. package/src/styles/components/toggle.css +80 -0
  51. package/src/types/components.ts +21 -0
  52. package/src/types/form-builder.ts +3 -0
  53. package/src/types/index.ts +2 -0
  54. package/dist/components-DtHA2bgp.js.map +0 -1
  55. package/dist/templates-DtdUvJ4c.js.map +0 -1
@@ -1,11 +1,18 @@
1
1
  <script setup lang="ts">
2
2
  /** Renders an on/off toggle switch with label, size, and keyboard interaction support. */
3
+ import Tooltip from './Tooltip.vue'
4
+
3
5
  interface Props {
4
6
  modelValue?: boolean
5
7
  label?: string
8
+ description?: string
9
+ icon?: string | string[]
10
+ iconColor?: string
11
+ iconBg?: string
6
12
  disabled?: boolean
7
13
  size?: 'sm' | 'md' | 'lg'
8
14
  reverse?: boolean
15
+ variant?: 'default' | 'row'
9
16
  }
10
17
 
11
18
  const props = withDefaults(defineProps<Props>(), {
@@ -13,6 +20,7 @@ const props = withDefaults(defineProps<Props>(), {
13
20
  disabled: false,
14
21
  size: 'md',
15
22
  reverse: false,
23
+ variant: 'default',
16
24
  })
17
25
 
18
26
  const emit = defineEmits<{
@@ -31,17 +39,74 @@ function handleKeydown(event: KeyboardEvent) {
31
39
  toggle()
32
40
  }
33
41
  }
42
+
43
+ function isSvgIcon(icon?: string | string[]): boolean {
44
+ if (!icon) return false
45
+ return Array.isArray(icon) || icon.startsWith('M') || icon.startsWith('m')
46
+ }
47
+
48
+ function iconStyle(): Record<string, string> {
49
+ return {
50
+ color: props.iconColor ?? (props.modelValue ? 'var(--color-primary)' : 'var(--text-muted)'),
51
+ }
52
+ }
34
53
  </script>
35
54
 
36
55
  <template>
37
56
  <div
38
57
  :class="[
39
58
  'mint-toggle',
59
+ `mint-toggle--${variant}`,
40
60
  reverse ? 'mint-toggle--reverse' : '',
41
61
  disabled ? 'mint-toggle--disabled' : '',
62
+ modelValue ? 'mint-toggle--checked' : '',
42
63
  ]"
43
64
  @click="toggle"
44
65
  >
66
+ <span
67
+ v-if="variant === 'row' && icon"
68
+ class="mint-toggle__row-icon"
69
+ :style="iconStyle()"
70
+ aria-hidden="true"
71
+ >
72
+ <svg
73
+ v-if="isSvgIcon(icon)"
74
+ class="mint-toggle__row-svg"
75
+ viewBox="0 0 24 24"
76
+ fill="none"
77
+ stroke="currentColor"
78
+ stroke-width="2"
79
+ stroke-linecap="round"
80
+ stroke-linejoin="round"
81
+ >
82
+ <template v-if="Array.isArray(icon)">
83
+ <path v-for="(d, i) in icon" :key="i" :d="d" />
84
+ </template>
85
+ <path v-else :d="icon" />
86
+ </svg>
87
+ <span v-else class="mint-toggle__row-text-icon">{{ icon }}</span>
88
+ </span>
89
+ <span
90
+ v-if="variant === 'row' && label"
91
+ class="mint-toggle__copy"
92
+ >
93
+ <Tooltip
94
+ v-if="description"
95
+ :text="description"
96
+ position="right"
97
+ max-width="16rem"
98
+ >
99
+ <span :class="['mint-toggle__label', `mint-toggle__label--${size}`, 'mint-toggle__label--tooltip']">
100
+ {{ label }}
101
+ </span>
102
+ </Tooltip>
103
+ <span
104
+ v-else
105
+ :class="['mint-toggle__label', `mint-toggle__label--${size}`]"
106
+ >
107
+ {{ label }}
108
+ </span>
109
+ </span>
45
110
  <div
46
111
  role="switch"
47
112
  :tabindex="disabled ? -1 : 0"
@@ -63,7 +128,7 @@ function handleKeydown(event: KeyboardEvent) {
63
128
  />
64
129
  </div>
65
130
  <span
66
- v-if="label"
131
+ v-if="label && variant !== 'row'"
67
132
  :class="['mint-toggle__label', `mint-toggle__label--${size}`]"
68
133
  >
69
134
  {{ label }}
@@ -1,25 +1,32 @@
1
1
  <script setup lang="ts">
2
2
  /** Card whose body collapses with a chevron-rotate animation, with an optional inline toggle switch independent of expand state. */
3
3
  import { ref, computed } from 'vue'
4
+ import type { SidebarBadgeTone, SidebarToolSectionAction } from '../types'
4
5
 
5
6
  interface Props {
6
7
  title: string
7
8
  subtitle?: string
9
+ badge?: string | number
10
+ badgeTone?: SidebarBadgeTone
8
11
  defaultOpen?: boolean
9
12
  disabled?: boolean
10
13
  dense?: boolean
11
14
  icon?: string | string[]
12
15
  iconColor?: string
13
16
  iconBg?: string
17
+ actions?: SidebarToolSectionAction[]
14
18
  showToggle?: boolean
15
19
  toggleValue?: boolean
16
20
  toggleColor?: string
17
21
  }
18
22
 
19
23
  const props = withDefaults(defineProps<Props>(), {
24
+ badge: undefined,
25
+ badgeTone: 'cta',
20
26
  defaultOpen: false,
21
27
  disabled: false,
22
28
  dense: false,
29
+ actions: () => [],
23
30
  showToggle: false,
24
31
  toggleValue: false,
25
32
  toggleColor: '',
@@ -27,6 +34,7 @@ const props = withDefaults(defineProps<Props>(), {
27
34
 
28
35
  const emit = defineEmits<{
29
36
  'update:toggleValue': [value: boolean]
37
+ action: [actionId: string]
30
38
  }>()
31
39
 
32
40
  const isOpen = ref(props.defaultOpen)
@@ -55,10 +63,7 @@ const iconColorStyle = computed(() => ({
55
63
  color: props.iconColor || 'var(--color-primary)',
56
64
  }))
57
65
 
58
- const isSvgIcon = computed(() => {
59
- if (!props.icon) return false
60
- return Array.isArray(props.icon) || props.icon.startsWith('M') || props.icon.startsWith('m')
61
- })
66
+ const isSvgIcon = computed(() => isSvgIconValue(props.icon))
62
67
 
63
68
  const toggleTrackStyle = computed(() => {
64
69
  if (!props.toggleValue || !props.toggleColor) return {}
@@ -67,47 +72,111 @@ const toggleTrackStyle = computed(() => {
67
72
  borderColor: props.toggleColor,
68
73
  }
69
74
  })
75
+
76
+ const badgeClasses = computed(() => [
77
+ 'mint-collapsible-card__badge',
78
+ `mint-collapsible-card__badge--${props.badgeTone}`,
79
+ ])
80
+
81
+ function isSvgIconValue(icon?: string | string[]): boolean {
82
+ if (!icon) return false
83
+ return Array.isArray(icon) || icon.startsWith('M') || icon.startsWith('m')
84
+ }
85
+
86
+ function actionClasses(action: SidebarToolSectionAction): string[] {
87
+ return [
88
+ 'mint-collapsible-card__action',
89
+ action.tone ? `mint-collapsible-card__action--${action.tone}` : '',
90
+ action.disabled ? 'mint-collapsible-card__action--disabled' : '',
91
+ ]
92
+ }
93
+
94
+ function actionStyle(action: SidebarToolSectionAction): Record<string, string> {
95
+ return action.iconColor ? { color: action.iconColor } : {}
96
+ }
97
+
98
+ function handleActionClick(action: SidebarToolSectionAction) {
99
+ if (props.disabled || action.disabled) return
100
+ emit('action', action.id)
101
+ }
70
102
  </script>
71
103
 
72
104
  <template>
73
105
  <div :class="['mint-collapsible-card', dense ? 'mint-collapsible-card--dense' : '']">
74
- <button
75
- type="button"
76
- :class="headerClasses"
77
- :disabled="disabled"
78
- :aria-expanded="isOpen"
79
- @click="toggle"
80
- >
81
- <div class="mint-collapsible-card__title-section">
82
- <!-- Icon badge -->
83
- <div v-if="icon" class="mint-collapsible-card__icon-badge" :style="iconBgStyle">
106
+ <div class="mint-collapsible-card__header-shell">
107
+ <button
108
+ type="button"
109
+ :class="headerClasses"
110
+ :disabled="disabled"
111
+ :aria-expanded="isOpen"
112
+ @click="toggle"
113
+ >
114
+ <div class="mint-collapsible-card__title-section">
115
+ <!-- Icon badge -->
116
+ <div v-if="icon" class="mint-collapsible-card__icon-badge" :style="iconBgStyle">
117
+ <svg
118
+ v-if="isSvgIcon"
119
+ class="mint-collapsible-card__icon"
120
+ :style="iconColorStyle"
121
+ viewBox="0 0 24 24"
122
+ fill="none"
123
+ stroke="currentColor"
124
+ stroke-width="2"
125
+ stroke-linecap="round"
126
+ stroke-linejoin="round"
127
+ >
128
+ <template v-if="Array.isArray(icon)">
129
+ <path v-for="(d, i) in icon" :key="i" :d="d" />
130
+ </template>
131
+ <path v-else :d="icon" />
132
+ </svg>
133
+ <span v-else class="mint-collapsible-card__icon-text" :style="iconColorStyle">{{ icon }}</span>
134
+ </div>
135
+
136
+ <div class="mint-collapsible-card__titles">
137
+ <h3 class="mint-collapsible-card__title">{{ title }}</h3>
138
+ <p v-if="subtitle" class="mint-collapsible-card__subtitle">{{ subtitle }}</p>
139
+ </div>
140
+ </div>
141
+ </button>
142
+
143
+ <div class="mint-collapsible-card__actions">
144
+ <span v-if="badge !== undefined" :class="badgeClasses">
145
+ {{ badge }}
146
+ </span>
147
+
148
+ <button
149
+ v-for="action in actions"
150
+ :key="action.id"
151
+ type="button"
152
+ :class="actionClasses(action)"
153
+ :style="actionStyle(action)"
154
+ :disabled="disabled || action.disabled"
155
+ :aria-label="action.label"
156
+ :title="action.label"
157
+ @click="handleActionClick(action)"
158
+ >
84
159
  <svg
85
- v-if="isSvgIcon"
86
- class="mint-collapsible-card__icon"
87
- :style="iconColorStyle"
160
+ v-if="action.icon && isSvgIconValue(action.icon)"
161
+ class="mint-collapsible-card__action-icon"
88
162
  viewBox="0 0 24 24"
89
163
  fill="none"
90
164
  stroke="currentColor"
91
165
  stroke-width="2"
92
166
  stroke-linecap="round"
93
167
  stroke-linejoin="round"
168
+ aria-hidden="true"
94
169
  >
95
- <template v-if="Array.isArray(icon)">
96
- <path v-for="(d, i) in icon" :key="i" :d="d" />
170
+ <template v-if="Array.isArray(action.icon)">
171
+ <path v-for="(d, i) in action.icon" :key="i" :d="d" />
97
172
  </template>
98
- <path v-else :d="icon" />
173
+ <path v-else :d="action.icon" />
99
174
  </svg>
100
- <span v-else class="mint-collapsible-card__icon-text" :style="iconColorStyle">{{ icon }}</span>
101
- </div>
102
-
103
- <div class="mint-collapsible-card__titles">
104
- <h3 class="mint-collapsible-card__title">{{ title }}</h3>
105
- <p v-if="subtitle" class="mint-collapsible-card__subtitle">{{ subtitle }}</p>
106
- </div>
107
- </div>
175
+ <span v-else-if="action.icon" class="mint-collapsible-card__action-text-icon">
176
+ {{ action.icon }}
177
+ </span>
178
+ </button>
108
179
 
109
- <div class="mint-collapsible-card__actions">
110
- <!-- Toggle switch -->
111
180
  <div
112
181
  v-if="showToggle"
113
182
  class="mint-collapsible-card__toggle"
@@ -134,23 +203,32 @@ const toggleTrackStyle = computed(() => {
134
203
  </div>
135
204
  </div>
136
205
 
137
- <!-- Chevron -->
138
- <svg
139
- :class="[
140
- 'mint-collapsible-card__chevron',
141
- isOpen ? 'mint-collapsible-card__chevron--open' : '',
142
- ]"
143
- fill="none"
144
- stroke="currentColor"
145
- stroke-width="2"
146
- stroke-linecap="round"
147
- stroke-linejoin="round"
148
- viewBox="0 0 24 24"
206
+ <button
207
+ type="button"
208
+ class="mint-collapsible-card__chevron-button"
209
+ :disabled="disabled"
210
+ :aria-label="isOpen ? 'Collapse section' : 'Expand section'"
211
+ :aria-expanded="isOpen"
212
+ @click="toggle"
149
213
  >
150
- <path d="m6 9 6 6 6-6" />
151
- </svg>
214
+ <svg
215
+ :class="[
216
+ 'mint-collapsible-card__chevron',
217
+ isOpen ? 'mint-collapsible-card__chevron--open' : '',
218
+ ]"
219
+ fill="none"
220
+ stroke="currentColor"
221
+ stroke-width="2"
222
+ stroke-linecap="round"
223
+ stroke-linejoin="round"
224
+ viewBox="0 0 24 24"
225
+ aria-hidden="true"
226
+ >
227
+ <path d="m6 9 6 6 6-6" />
228
+ </svg>
229
+ </button>
152
230
  </div>
153
- </button>
231
+ </div>
154
232
 
155
233
  <Transition name="collapse">
156
234
  <div v-show="isOpen" class="mint-collapsible-card__body">
@@ -5,6 +5,11 @@ import { defineControlModel, defineControls } from '../composables/useControlSch
5
5
 
6
6
  type FormSize = 'sm' | 'md' | 'lg'
7
7
 
8
+ const icons = {
9
+ scale: ['M16 16l3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1z', 'M2 16l3-8 3 8c-.87.65-1.92 1-3 1s-2.13-.35-3-1z', 'M7 21h10', 'M12 3v18', 'M3 7h2c2 0 5-1 7-4 2 3 5 4 7 4h2'],
10
+ eye: ['M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z', 'M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z'],
11
+ }
12
+
8
13
  const analysisModel = defineControlModel({
9
14
  views: {
10
15
  analysis: {
@@ -32,6 +37,10 @@ const analysisModel = defineControlModel({
32
37
  label: 'Normalize intensities',
33
38
  default: true,
34
39
  type: 'toggle',
40
+ hint: 'Scale each sample before fitting',
41
+ icon: icons.scale,
42
+ iconColor: '#6366f1',
43
+ iconBg: '#e0e7ff',
35
44
  },
36
45
  },
37
46
  },
@@ -50,6 +59,10 @@ const analysisModel = defineControlModel({
50
59
  label: 'Exclude outliers',
51
60
  default: true,
52
61
  type: 'checkbox',
62
+ hint: 'Hide flagged injections from summaries',
63
+ icon: icons.eye,
64
+ iconColor: '#0ea5e9',
65
+ iconBg: '#e0f2fe',
53
66
  },
54
67
  },
55
68
  },
@@ -14,6 +14,7 @@ const topBarForwardedSlots = new Set([
14
14
  const sidebarForwardedSlots = new Set([
15
15
  'header',
16
16
  'collapsed',
17
+ 'collapsed-footer',
17
18
  'footer',
18
19
  ])
19
20
 
@@ -115,6 +115,7 @@ const emit = defineEmits<{
115
115
  'update:values': [values: Record<string, unknown>]
116
116
  'update:sidebarCollapsed': [value: boolean]
117
117
  'update:toggle': [sectionId: string, value: boolean]
118
+ 'section-action': [sectionId: string, actionId: string]
118
119
  'form-submit': [sectionId: string, values: Record<string, unknown>]
119
120
  'form-cancel': [sectionId: string]
120
121
  }>()
@@ -135,6 +136,7 @@ defineSlots<{
135
136
  'settings-appearance'?: (props: PluginWorkspaceForwardedSlotProps) => unknown
136
137
  header?: (props: PluginWorkspaceForwardedSlotProps) => unknown
137
138
  collapsed?: (props: PluginWorkspaceForwardedSlotProps) => unknown
139
+ 'collapsed-footer'?: (props: PluginWorkspaceForwardedSlotProps) => unknown
138
140
  footer?: (props: PluginWorkspaceForwardedSlotProps) => unknown
139
141
  }>()
140
142
 
@@ -358,6 +360,7 @@ function handleFormCancel(sectionId: string) {
358
360
  @update:model-value="handleControlValuesUpdate"
359
361
  @update:collapsed="emit('update:sidebarCollapsed', $event)"
360
362
  @update:toggle="handleToggle"
363
+ @section-action="(sectionId, actionId) => emit('section-action', sectionId, actionId)"
361
364
  @form-submit="handleFormSubmit"
362
365
  @form-cancel="handleFormCancel"
363
366
  >
@@ -15,6 +15,24 @@ const props = defineProps<Props>()
15
15
 
16
16
  const entry = computed(() => getFieldRegistryEntry(props.field.type))
17
17
 
18
+ const isChoiceField = computed(() =>
19
+ props.field.type === 'checkbox' || props.field.type === 'toggle',
20
+ )
21
+
22
+ const componentProps = computed<Record<string, unknown>>(() => {
23
+ if (!isChoiceField.value) return props.resolvedProps
24
+
25
+ return {
26
+ variant: 'row',
27
+ label: props.field.label,
28
+ description: props.field.hint,
29
+ icon: props.field.icon,
30
+ iconColor: props.field.iconColor,
31
+ iconBg: props.field.iconBg,
32
+ ...props.resolvedProps,
33
+ }
34
+ })
35
+
18
36
  const errorMessage = computed(() => {
19
37
  const name = props.field.name
20
38
  return props.form.touched[name] ? props.form.errors[name] : null
@@ -27,22 +45,22 @@ function handleUpload(files: File[]) {
27
45
 
28
46
  <template>
29
47
  <FormField
30
- :label="field.label"
48
+ :label="isChoiceField ? undefined : field.label"
31
49
  :error="errorMessage ?? undefined"
32
- :hint="field.hint"
50
+ :hint="isChoiceField ? undefined : field.hint"
33
51
  :required="!!field.validation?.required"
34
- :html-for="field.name"
52
+ :html-for="isChoiceField ? undefined : field.name"
35
53
  >
36
54
  <slot :name="`field:${field.name}`" :field="field" :form="form" :field-props="form.getFieldProps(field.name)">
37
55
  <component
38
56
  :is="entry.component"
39
57
  v-if="entry.vModel"
40
- v-bind="resolvedProps"
58
+ v-bind="componentProps"
41
59
  />
42
60
  <component
43
61
  :is="entry.component"
44
62
  v-else
45
- v-bind="resolvedProps"
63
+ v-bind="componentProps"
46
64
  @upload="handleUpload"
47
65
  />
48
66
  </slot>
@@ -82,6 +82,9 @@ export function controlsToSidebarPanels(
82
82
  icon: sidebarConfig?.icon ?? config.icon,
83
83
  iconColor: sidebarConfig?.iconColor ?? config.iconColor,
84
84
  iconBg: sidebarConfig?.iconBg ?? config.iconBg,
85
+ badge: sidebarConfig?.badge ?? config.badge,
86
+ badgeTone: sidebarConfig?.badgeTone ?? config.badgeTone,
87
+ actions: sidebarConfig?.actions ?? config.actions,
85
88
  defaultOpen: sidebarConfig?.defaultOpen ?? config.defaultOpen,
86
89
  showToggle: sidebarConfig?.showToggle ?? config.showToggle,
87
90
  }
@@ -25,6 +25,9 @@ export function controlToFormField(control: NormalizedControl): FormFieldSchema
25
25
  defaultValue: defaultValueForControl(definition, type),
26
26
  placeholder: definition.placeholder,
27
27
  hint: definition.hint,
28
+ icon: definition.icon,
29
+ iconColor: definition.iconColor,
30
+ iconBg: definition.iconBg,
28
31
  size: definition.size,
29
32
  disabled: definition.disabled,
30
33
  readonly: definition.readonly,
@@ -4,7 +4,9 @@ import type {
4
4
  } from 'vue'
5
5
  import type {
6
6
  PillNavItem,
7
+ SidebarBadgeTone,
7
8
  SidebarToolSection,
9
+ SidebarToolSectionAction,
8
10
  SelectOption,
9
11
  SettingsModalSchema,
10
12
  TopBarSettingsConfig,
@@ -35,6 +37,9 @@ export interface ControlSectionConfig {
35
37
  icon?: string | string[]
36
38
  iconColor?: string
37
39
  iconBg?: string
40
+ badge?: string | number
41
+ badgeTone?: SidebarBadgeTone
42
+ actions?: SidebarToolSectionAction[]
38
43
  columns?: 1 | 2 | 3
39
44
  condition?: FieldCondition
40
45
  access?: AccessPolicy
@@ -66,6 +71,9 @@ export interface ControlDefinition {
66
71
  defaultValue?: unknown
67
72
  placeholder?: string
68
73
  hint?: string
74
+ icon?: string | string[]
75
+ iconColor?: string
76
+ iconBg?: string
69
77
  size?: 'sm' | 'md' | 'lg'
70
78
  disabled?: boolean
71
79
  readonly?: boolean