@morscherlab/mint-sdk 1.0.0-rc.2 → 1.0.0-rc.4
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/components/AppSidebar.vue.d.ts +3 -3
- package/dist/components/ControlWorkspaceView.vue.d.ts +3 -3
- package/dist/components/DoseDesignWorkspaceView.vue.d.ts +1 -1
- package/dist/components/PluginWorkspaceView.vue.d.ts +3 -3
- package/dist/components/index.js +2 -2
- package/dist/{components-BhK-dW99.js → components-DafPc4rM.js} +3 -3
- package/dist/{components-BhK-dW99.js.map → components-DafPc4rM.js.map} +1 -1
- package/dist/composables/controlComponentBindings.d.ts +7 -0
- package/dist/composables/controlSchemaAdapters.d.ts +20 -0
- package/dist/composables/controlSchemaFormFields.d.ts +3 -0
- package/dist/composables/controlSchemaLayout.d.ts +7 -0
- package/dist/composables/controlSchemaNormalize.d.ts +15 -0
- package/dist/composables/controlSchemaUtils.d.ts +9 -0
- package/dist/composables/controlWorkspaceOptions.d.ts +2 -0
- package/dist/composables/index.js +3 -3
- package/dist/composables/useControlSchema.d.ts +4 -30
- package/dist/composables/useControlWorkspace.d.ts +5 -0
- package/dist/{composables-Bg7CFuNz.js → composables-BMkPQhVK.js} +2 -2
- package/dist/{composables-Bg7CFuNz.js.map → composables-BMkPQhVK.js.map} +1 -1
- package/dist/index.js +4 -4
- package/dist/install.js +2 -2
- package/dist/templates/adapters.d.ts +14 -47
- package/dist/templates/assayLookups.d.ts +3 -0
- package/dist/templates/assayMatrixAdapters.d.ts +6 -0
- package/dist/templates/assayMatrixBuilder.d.ts +2 -0
- package/dist/templates/assayNormalizers.d.ts +4 -0
- package/dist/templates/builderDefaults.d.ts +1 -0
- package/dist/templates/builderIdUtils.d.ts +4 -0
- package/dist/templates/builderPresetControls.d.ts +10 -0
- package/dist/templates/builderReadUtils.d.ts +8 -0
- package/dist/templates/builders.d.ts +26 -67
- package/dist/templates/calibrationCurveAdapters.d.ts +4 -0
- package/dist/templates/calibrationCurveBuilder.d.ts +2 -0
- package/dist/templates/calibrationNormalizers.d.ts +5 -0
- package/dist/templates/componentBindingCatalog.d.ts +25 -0
- package/dist/templates/componentBindingHelpers.d.ts +17 -0
- package/dist/templates/componentDoseResponseProps.d.ts +3 -0
- package/dist/templates/componentGenericProps.d.ts +8 -0
- package/dist/templates/componentPlateHelpers.d.ts +5 -0
- package/dist/templates/componentPlateMapProps.d.ts +3 -0
- package/dist/templates/componentPropsFactory.d.ts +3 -0
- package/dist/templates/componentQpcrPlateProps.d.ts +3 -0
- package/dist/templates/componentTargetResolvers.d.ts +5 -0
- package/dist/templates/componentTemplateProps.d.ts +3 -0
- package/dist/templates/controlSchemaClone.d.ts +4 -0
- package/dist/templates/controlSchemaConstants.d.ts +10 -0
- package/dist/templates/controlSchemaMerge.d.ts +3 -0
- package/dist/templates/controlSchemaTargets.d.ts +17 -0
- package/dist/templates/controlSchemaTypes.d.ts +4 -0
- package/dist/templates/controlSchemas.d.ts +4 -4
- package/dist/templates/defaultBioTemplateBuilder.d.ts +2 -0
- package/dist/templates/doseResponseAdapters.d.ts +4 -0
- package/dist/templates/doseResponseBuilder.d.ts +2 -0
- package/dist/templates/elisaAssayCollectionBuilder.d.ts +2 -0
- package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +2 -0
- package/dist/templates/flowCytometryPanelBuilder.d.ts +2 -0
- package/dist/templates/flowNormalizers.d.ts +8 -0
- package/dist/templates/flowPanelAdapters.d.ts +4 -0
- package/dist/templates/index.js +1 -1
- package/dist/templates/instrumentRunAdapterHelpers.d.ts +8 -0
- package/dist/templates/instrumentRunAdapters.d.ts +8 -0
- package/dist/templates/instrumentRunBuilder.d.ts +2 -0
- package/dist/templates/lcmsBatchCollectionBuilder.d.ts +2 -0
- package/dist/templates/plateGeometry.d.ts +4 -0
- package/dist/templates/plateMapAdapters.d.ts +3 -0
- package/dist/templates/plateMapBuilder.d.ts +2 -0
- package/dist/templates/presetControlSchemas.d.ts +534 -0
- package/dist/templates/protocolAdapters.d.ts +5 -0
- package/dist/templates/protocolNormalizers.d.ts +6 -0
- package/dist/templates/protocolStepsBuilder.d.ts +2 -0
- package/dist/templates/qpcrAdapters.d.ts +5 -0
- package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +2 -0
- package/dist/templates/qpcrPlateBuilder.d.ts +2 -0
- package/dist/templates/reagentAdapters.d.ts +5 -0
- package/dist/templates/reagentListBuilder.d.ts +2 -0
- package/dist/templates/runNormalizers.d.ts +10 -0
- package/dist/templates/sampleNormalizers.d.ts +4 -0
- package/dist/templates/samplePrepAdapters.d.ts +4 -0
- package/dist/templates/samplePrepBuilder.d.ts +2 -0
- package/dist/templates/sampleSheetAdapters.d.ts +5 -0
- package/dist/templates/sampleSheetBuilder.d.ts +2 -0
- package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +2 -0
- package/dist/templates/targetedMetabolomicsHelpers.d.ts +5 -0
- package/dist/templates/templateControlSchemas.d.ts +400 -0
- package/dist/templates/templateEnvelopes.d.ts +9 -0
- package/dist/templates/templatePackCollectionBuilder.d.ts +2 -0
- package/dist/templates/templatePresetCollectionBuilder.d.ts +18 -0
- package/dist/templates/templateValidators.d.ts +13 -0
- package/dist/templates/timeCourseAdapters.d.ts +5 -0
- package/dist/templates/timeCourseBuilder.d.ts +2 -0
- package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +2 -0
- package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +2 -0
- package/dist/{templates-BorLR_7p.js → templates-bUAWMn5L.js} +3574 -3428
- package/dist/templates-bUAWMn5L.js.map +1 -0
- package/dist/{useProtocolTemplates-n6AJqSqv.js → useProtocolTemplates-QZtHFFH2.js} +2 -2
- package/dist/{useProtocolTemplates-n6AJqSqv.js.map → useProtocolTemplates-QZtHFFH2.js.map} +1 -1
- package/package.json +1 -1
- package/src/composables/controlComponentBindings.ts +80 -0
- package/src/composables/controlSchemaAdapters.ts +196 -0
- package/src/composables/controlSchemaFormFields.ts +61 -0
- package/src/composables/controlSchemaLayout.ts +59 -0
- package/src/composables/controlSchemaNormalize.ts +101 -0
- package/src/composables/controlSchemaUtils.ts +36 -0
- package/src/composables/controlWorkspaceOptions.ts +21 -0
- package/src/composables/useControlSchema.ts +51 -628
- package/src/composables/useControlWorkspace.ts +207 -0
- package/src/templates/adapters.ts +89 -930
- package/src/templates/assayLookups.ts +33 -0
- package/src/templates/assayMatrixAdapters.ts +78 -0
- package/src/templates/assayMatrixBuilder.ts +59 -0
- package/src/templates/assayNormalizers.ts +34 -0
- package/src/templates/builderDefaults.ts +11 -0
- package/src/templates/builderIdUtils.ts +20 -0
- package/src/templates/builderPresetControls.ts +165 -0
- package/src/templates/builderReadUtils.ts +57 -0
- package/src/templates/builders.ts +122 -2350
- package/src/templates/calibrationCurveAdapters.ts +59 -0
- package/src/templates/calibrationCurveBuilder.ts +99 -0
- package/src/templates/calibrationNormalizers.ts +60 -0
- package/src/templates/componentBindingCatalog.ts +90 -0
- package/src/templates/componentBindingHelpers.ts +93 -0
- package/src/templates/componentBindings.ts +12 -461
- package/src/templates/componentDoseResponseProps.ts +42 -0
- package/src/templates/componentGenericProps.ts +77 -0
- package/src/templates/componentPlateHelpers.ts +29 -0
- package/src/templates/componentPlateMapProps.ts +32 -0
- package/src/templates/componentPropsFactory.ts +21 -0
- package/src/templates/componentQpcrPlateProps.ts +28 -0
- package/src/templates/componentTargetResolvers.ts +69 -0
- package/src/templates/componentTemplateProps.ts +78 -0
- package/src/templates/controlSchemaClone.ts +32 -0
- package/src/templates/controlSchemaConstants.ts +11 -0
- package/src/templates/controlSchemaMerge.ts +40 -0
- package/src/templates/controlSchemaTargets.ts +87 -0
- package/src/templates/controlSchemaTypes.ts +20 -0
- package/src/templates/controlSchemas.ts +22 -704
- package/src/templates/defaultBioTemplateBuilder.ts +124 -0
- package/src/templates/doseResponseAdapters.ts +45 -0
- package/src/templates/doseResponseBuilder.ts +44 -0
- package/src/templates/elisaAssayCollectionBuilder.ts +62 -0
- package/src/templates/flowCytometryAssayCollectionBuilder.ts +41 -0
- package/src/templates/flowCytometryPanelBuilder.ts +53 -0
- package/src/templates/flowNormalizers.ts +58 -0
- package/src/templates/flowPanelAdapters.ts +58 -0
- package/src/templates/instrumentRunAdapterHelpers.ts +94 -0
- package/src/templates/instrumentRunAdapters.ts +163 -0
- package/src/templates/instrumentRunBuilder.ts +97 -0
- package/src/templates/lcmsBatchCollectionBuilder.ts +38 -0
- package/src/templates/plateGeometry.ts +62 -0
- package/src/templates/plateMapAdapters.ts +36 -0
- package/src/templates/plateMapBuilder.ts +43 -0
- package/src/templates/presetControlSchemas.ts +258 -0
- package/src/templates/protocolAdapters.ts +69 -0
- package/src/templates/protocolNormalizers.ts +37 -0
- package/src/templates/protocolStepsBuilder.ts +36 -0
- package/src/templates/qpcrAdapters.ts +104 -0
- package/src/templates/qpcrExpressionCollectionBuilder.ts +33 -0
- package/src/templates/qpcrPlateBuilder.ts +96 -0
- package/src/templates/reagentAdapters.ts +77 -0
- package/src/templates/reagentListBuilder.ts +30 -0
- package/src/templates/runNormalizers.ts +63 -0
- package/src/templates/sampleNormalizers.ts +58 -0
- package/src/templates/samplePrepAdapters.ts +63 -0
- package/src/templates/samplePrepBuilder.ts +51 -0
- package/src/templates/sampleSheetAdapters.ts +75 -0
- package/src/templates/sampleSheetBuilder.ts +23 -0
- package/src/templates/targetedMetabolomicsCollectionBuilder.ts +79 -0
- package/src/templates/targetedMetabolomicsHelpers.ts +102 -0
- package/src/templates/templateControlSchemas.ts +320 -0
- package/src/templates/templateEnvelopes.ts +137 -0
- package/src/templates/templatePackCollectionBuilder.ts +23 -0
- package/src/templates/templatePresetCollectionBuilder.ts +139 -0
- package/src/templates/templateValidators.ts +414 -0
- package/src/templates/timeCourseAdapters.ts +73 -0
- package/src/templates/timeCourseBuilder.ts +64 -0
- package/src/templates/wellPlateScreenCollectionBuilder.ts +36 -0
- package/src/templates/westernBlotAssayCollectionBuilder.ts +68 -0
- package/dist/templates-BorLR_7p.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morscherlab/mint-sdk",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.4",
|
|
4
4
|
"description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ControlComponentBinding,
|
|
3
|
+
ControlComponentBindingConfig,
|
|
4
|
+
ControlComponentBindingRecordConfig,
|
|
5
|
+
ControlComponentBindingsById,
|
|
6
|
+
ControlComponentBindingsConfig,
|
|
7
|
+
ControlComponentPropsMap,
|
|
8
|
+
ControlComponentPropSource,
|
|
9
|
+
} from './useControlSchema'
|
|
10
|
+
|
|
11
|
+
/** Map control workspace values into component props for direct `v-bind` usage. */
|
|
12
|
+
export function controlValuesToComponentProps<TValues extends Record<string, unknown>>(
|
|
13
|
+
values: TValues,
|
|
14
|
+
mapping?: ControlComponentPropsMap<TValues>,
|
|
15
|
+
): Record<string, unknown> {
|
|
16
|
+
if (mapping === undefined) return { ...values }
|
|
17
|
+
|
|
18
|
+
if (Array.isArray(mapping)) {
|
|
19
|
+
const props: Record<string, unknown> = {}
|
|
20
|
+
for (const key of mapping) {
|
|
21
|
+
props[key] = values[key]
|
|
22
|
+
}
|
|
23
|
+
return props
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const props: Record<string, unknown> = {}
|
|
27
|
+
for (const [prop, source] of Object.entries(mapping) as Array<[string, ControlComponentPropSource<TValues>]>) {
|
|
28
|
+
props[prop] = typeof source === 'function' ? source(values) : values[source]
|
|
29
|
+
}
|
|
30
|
+
return props
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Map control workspace values into named SDK component bindings for direct slot rendering. */
|
|
34
|
+
export function controlValuesToComponentBindings<TValues extends Record<string, unknown>>(
|
|
35
|
+
values: TValues,
|
|
36
|
+
bindings?: ControlComponentBindingsConfig<TValues>,
|
|
37
|
+
): ControlComponentBinding[] {
|
|
38
|
+
if (bindings === undefined) return []
|
|
39
|
+
|
|
40
|
+
return normalizeControlComponentBindingConfigs(bindings).map(binding => ({
|
|
41
|
+
id: binding.id,
|
|
42
|
+
component: binding.component,
|
|
43
|
+
props: controlValuesToComponentProps(values, binding.props),
|
|
44
|
+
}))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Map control workspace values into SDK component bindings keyed by binding id. */
|
|
48
|
+
export function controlValuesToComponentBindingsById<TValues extends Record<string, unknown>>(
|
|
49
|
+
values: TValues,
|
|
50
|
+
bindings?: ControlComponentBindingsConfig<TValues>,
|
|
51
|
+
): ControlComponentBindingsById {
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
controlValuesToComponentBindings(values, bindings).map(binding => [binding.id, binding]),
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function normalizeControlComponentBindingConfigs<TValues extends Record<string, unknown>>(
|
|
58
|
+
bindings: ControlComponentBindingsConfig<TValues>,
|
|
59
|
+
): Array<Required<Pick<ControlComponentBindingConfig<TValues>, 'id' | 'component'>> & Pick<ControlComponentBindingConfig<TValues>, 'props'>> {
|
|
60
|
+
if (Array.isArray(bindings)) {
|
|
61
|
+
const usedIds = new Map<string, number>()
|
|
62
|
+
return bindings.map(binding => ({
|
|
63
|
+
id: uniqueComponentBindingId(binding.id ?? binding.component, usedIds),
|
|
64
|
+
component: binding.component,
|
|
65
|
+
props: binding.props,
|
|
66
|
+
}))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return Object.entries(bindings).map(([id, binding]: [string, ControlComponentBindingRecordConfig<TValues>]) => ({
|
|
70
|
+
id,
|
|
71
|
+
component: binding.component,
|
|
72
|
+
props: binding.props,
|
|
73
|
+
}))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function uniqueComponentBindingId(id: string, usedIds: Map<string, number>): string {
|
|
77
|
+
const count = usedIds.get(id) ?? 0
|
|
78
|
+
usedIds.set(id, count + 1)
|
|
79
|
+
return count === 0 ? id : `${id}-${count + 1}`
|
|
80
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PillNavItem,
|
|
3
|
+
SidebarToolSection,
|
|
4
|
+
SettingsModalSchema,
|
|
5
|
+
TopBarSettingsConfig,
|
|
6
|
+
} from '../types'
|
|
7
|
+
import type {
|
|
8
|
+
FormSectionSchema,
|
|
9
|
+
} from '../types/form-builder'
|
|
10
|
+
import {
|
|
11
|
+
controlToFormField,
|
|
12
|
+
} from './controlSchemaFormFields'
|
|
13
|
+
import {
|
|
14
|
+
controlViewItem,
|
|
15
|
+
firstSidebarConfig,
|
|
16
|
+
isSidebarEnabled,
|
|
17
|
+
sectionConfig,
|
|
18
|
+
} from './controlSchemaLayout'
|
|
19
|
+
import {
|
|
20
|
+
normalizeControls,
|
|
21
|
+
} from './controlSchemaNormalize'
|
|
22
|
+
import {
|
|
23
|
+
humanize,
|
|
24
|
+
orderedUnique,
|
|
25
|
+
} from './controlSchemaUtils'
|
|
26
|
+
import type {
|
|
27
|
+
ControlFormSchema,
|
|
28
|
+
ControlSchema,
|
|
29
|
+
ControlSchemaOptions,
|
|
30
|
+
} from './useControlSchema'
|
|
31
|
+
|
|
32
|
+
/** Convert a compact control schema into a FormBuilder schema. */
|
|
33
|
+
export function controlsToFormSchema(
|
|
34
|
+
controls: ControlSchema,
|
|
35
|
+
options: ControlSchemaOptions = {},
|
|
36
|
+
): ControlFormSchema {
|
|
37
|
+
const normalized = normalizeControls(controls, options)
|
|
38
|
+
const sectionIds = orderedUnique(normalized.map(control => control.sectionId))
|
|
39
|
+
const sections = sectionIds.map((sectionId): FormSectionSchema => {
|
|
40
|
+
const sectionControls = normalized.filter(control => control.sectionId === sectionId)
|
|
41
|
+
const config = sectionConfig(sectionId, sectionControls, options)
|
|
42
|
+
return {
|
|
43
|
+
id: sectionId,
|
|
44
|
+
title: config.title ?? config.label ?? humanize(sectionId),
|
|
45
|
+
description: config.description,
|
|
46
|
+
columns: config.columns ?? options.columns ?? 1,
|
|
47
|
+
defaultOpen: config.defaultOpen,
|
|
48
|
+
condition: config.condition,
|
|
49
|
+
fields: sectionControls.map(controlToFormField),
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
sections,
|
|
55
|
+
submitLabel: options.submitLabel,
|
|
56
|
+
cancelLabel: options.cancelLabel,
|
|
57
|
+
showCancel: options.showCancel,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Convert controls into AppSidebar panels grouped by view and section. */
|
|
62
|
+
export function controlsToSidebarPanels(
|
|
63
|
+
controls: ControlSchema,
|
|
64
|
+
options: ControlSchemaOptions = {},
|
|
65
|
+
): Record<string, SidebarToolSection[]> {
|
|
66
|
+
const normalized = normalizeControls(controls, options)
|
|
67
|
+
.filter(control => isSidebarEnabled(control.definition.sidebar))
|
|
68
|
+
const viewIds = orderedUnique(normalized.map(control => control.viewId))
|
|
69
|
+
const panels: Record<string, SidebarToolSection[]> = {}
|
|
70
|
+
|
|
71
|
+
for (const viewId of viewIds) {
|
|
72
|
+
const viewControls = normalized.filter(control => control.viewId === viewId)
|
|
73
|
+
const sectionIds = orderedUnique(viewControls.map(control => control.sectionId))
|
|
74
|
+
panels[viewId] = sectionIds.map((sectionId): SidebarToolSection => {
|
|
75
|
+
const sectionControls = viewControls.filter(control => control.sectionId === sectionId)
|
|
76
|
+
const config = sectionConfig(sectionId, sectionControls, options)
|
|
77
|
+
const sidebarConfig = firstSidebarConfig(sectionControls)
|
|
78
|
+
return {
|
|
79
|
+
id: sectionId,
|
|
80
|
+
label: sidebarConfig?.label ?? config.label ?? config.title ?? humanize(sectionId),
|
|
81
|
+
subtitle: sidebarConfig?.subtitle ?? config.subtitle,
|
|
82
|
+
icon: sidebarConfig?.icon ?? config.icon,
|
|
83
|
+
iconColor: sidebarConfig?.iconColor ?? config.iconColor,
|
|
84
|
+
iconBg: sidebarConfig?.iconBg ?? config.iconBg,
|
|
85
|
+
defaultOpen: sidebarConfig?.defaultOpen ?? config.defaultOpen,
|
|
86
|
+
showToggle: sidebarConfig?.showToggle ?? config.showToggle,
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return panels
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Convert controls into a SettingsModal schema grouped by the same sections. */
|
|
95
|
+
export function controlsToSettingsSchema(
|
|
96
|
+
controls: ControlSchema,
|
|
97
|
+
options: ControlSchemaOptions = {},
|
|
98
|
+
): SettingsModalSchema {
|
|
99
|
+
const normalized = normalizeControls(controls, options)
|
|
100
|
+
const sectionIds = orderedUnique(normalized.map(control => control.sectionId))
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
groups: sectionIds.map((sectionId) => {
|
|
104
|
+
const sectionControls = normalized.filter(control => control.sectionId === sectionId)
|
|
105
|
+
const config = sectionConfig(sectionId, sectionControls, options)
|
|
106
|
+
return {
|
|
107
|
+
id: sectionId,
|
|
108
|
+
label: config.label ?? config.title ?? humanize(sectionId),
|
|
109
|
+
description: config.description ?? config.subtitle,
|
|
110
|
+
icon: typeof config.icon === 'string' ? config.icon : undefined,
|
|
111
|
+
fields: sectionControls.map(controlToFormField),
|
|
112
|
+
columns: config.columns ?? options.columns ?? 1,
|
|
113
|
+
condition: config.condition,
|
|
114
|
+
access: config.access,
|
|
115
|
+
visibleFor: config.visibleFor,
|
|
116
|
+
requiresAdmin: config.requiresAdmin,
|
|
117
|
+
permissions: config.permissions,
|
|
118
|
+
anyPermissions: config.anyPermissions,
|
|
119
|
+
}
|
|
120
|
+
}),
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Convert controls into an AppTopBar settingsConfig object that passes compact controls through to SettingsModal. */
|
|
125
|
+
export function controlsToTopBarSettingsConfig(
|
|
126
|
+
controls: ControlSchema,
|
|
127
|
+
options: ControlSchemaOptions = {},
|
|
128
|
+
config: Omit<TopBarSettingsConfig, 'schema' | 'controls' | 'controlOptions'> = {},
|
|
129
|
+
): TopBarSettingsConfig {
|
|
130
|
+
return {
|
|
131
|
+
...config,
|
|
132
|
+
controls,
|
|
133
|
+
controlOptions: options,
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Return generated control view IDs that have at least one sidebar panel. */
|
|
138
|
+
export function controlsToViewIds(
|
|
139
|
+
controls: ControlSchema,
|
|
140
|
+
options: ControlSchemaOptions = {},
|
|
141
|
+
): string[] {
|
|
142
|
+
return controlsToViewItems(controls, options).map(item => item.id)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Return AppTopBar pillNav-compatible view items for switching generated control sidebars. */
|
|
146
|
+
export function controlsToViewItems(
|
|
147
|
+
controls: ControlSchema,
|
|
148
|
+
options: ControlSchemaOptions = {},
|
|
149
|
+
): PillNavItem[] {
|
|
150
|
+
return Object.entries(controlsToSidebarPanels(controls, options))
|
|
151
|
+
.filter(([, sections]) => sections.length > 0)
|
|
152
|
+
.map(([id]) => controlViewItem(id, options))
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Return the first generated sidebar view ID, or an empty string when controls render no sidebar panels. */
|
|
156
|
+
export function getDefaultControlView(
|
|
157
|
+
controls: ControlSchema,
|
|
158
|
+
options: ControlSchemaOptions = {},
|
|
159
|
+
): string {
|
|
160
|
+
return controlsToViewIds(controls, options)[0] ?? ''
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Return a headerless single-section FormBuilder schema for rendering inside an AppSidebar section slot. */
|
|
164
|
+
export function controlsToSectionFormSchema(
|
|
165
|
+
controls: ControlSchema,
|
|
166
|
+
sectionId: string,
|
|
167
|
+
options: ControlSchemaOptions = {},
|
|
168
|
+
): ControlFormSchema {
|
|
169
|
+
const schema = controlsToFormSchema(controls, options)
|
|
170
|
+
return {
|
|
171
|
+
sections: schema.sections
|
|
172
|
+
.filter(section => section.id === sectionId)
|
|
173
|
+
.map(section => ({ ...section, title: '', description: undefined })),
|
|
174
|
+
submitLabel: schema.submitLabel,
|
|
175
|
+
cancelLabel: schema.cancelLabel,
|
|
176
|
+
showCancel: schema.showCancel,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Return headerless FormBuilder schemas keyed by section ID for AppSidebar auto-rendering. */
|
|
181
|
+
export function controlsToSectionFormSchemas(
|
|
182
|
+
controls: ControlSchema,
|
|
183
|
+
options: ControlSchemaOptions = {},
|
|
184
|
+
): Record<string, ControlFormSchema> {
|
|
185
|
+
const schema = controlsToFormSchema(controls, options)
|
|
186
|
+
const schemas: Record<string, ControlFormSchema> = {}
|
|
187
|
+
for (const section of schema.sections) {
|
|
188
|
+
schemas[section.id] = {
|
|
189
|
+
sections: [{ ...section, title: '', description: undefined }],
|
|
190
|
+
submitLabel: schema.submitLabel,
|
|
191
|
+
cancelLabel: schema.cancelLabel,
|
|
192
|
+
showCancel: schema.showCancel,
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return schemas
|
|
196
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FieldValidation,
|
|
3
|
+
FormFieldSchema,
|
|
4
|
+
} from '../types/form-builder'
|
|
5
|
+
import {
|
|
6
|
+
defaultValueForControl,
|
|
7
|
+
normalizeOption,
|
|
8
|
+
type NormalizedControl,
|
|
9
|
+
} from './controlSchemaNormalize'
|
|
10
|
+
import {
|
|
11
|
+
humanize,
|
|
12
|
+
numericValue,
|
|
13
|
+
} from './controlSchemaUtils'
|
|
14
|
+
import type {
|
|
15
|
+
ControlDefinition,
|
|
16
|
+
} from './useControlSchema'
|
|
17
|
+
|
|
18
|
+
export function controlToFormField(control: NormalizedControl): FormFieldSchema {
|
|
19
|
+
const { name, definition, type } = control
|
|
20
|
+
const props = fieldProps(definition)
|
|
21
|
+
return {
|
|
22
|
+
name,
|
|
23
|
+
label: definition.label ?? humanize(name),
|
|
24
|
+
type,
|
|
25
|
+
defaultValue: defaultValueForControl(definition, type),
|
|
26
|
+
placeholder: definition.placeholder,
|
|
27
|
+
hint: definition.hint,
|
|
28
|
+
size: definition.size,
|
|
29
|
+
disabled: definition.disabled,
|
|
30
|
+
readonly: definition.readonly,
|
|
31
|
+
validation: validationForControl(definition),
|
|
32
|
+
condition: definition.condition,
|
|
33
|
+
access: definition.access,
|
|
34
|
+
visibleFor: definition.visibleFor,
|
|
35
|
+
requiresAdmin: definition.requiresAdmin,
|
|
36
|
+
permissions: definition.permissions,
|
|
37
|
+
anyPermissions: definition.anyPermissions,
|
|
38
|
+
colSpan: definition.colSpan,
|
|
39
|
+
props,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function fieldProps(definition: ControlDefinition): Record<string, unknown> {
|
|
44
|
+
const props: Record<string, unknown> = {}
|
|
45
|
+
if (definition.min !== undefined) props.min = numericValue(definition.min)
|
|
46
|
+
if (definition.max !== undefined) props.max = numericValue(definition.max)
|
|
47
|
+
if (definition.options) props.options = definition.options.map(normalizeOption)
|
|
48
|
+
return { ...props, ...(definition.props ?? {}) }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function validationForControl(definition: ControlDefinition): FieldValidation | undefined {
|
|
52
|
+
const validation: FieldValidation = { ...(definition.validation ?? {}) }
|
|
53
|
+
if (definition.required !== undefined) validation.required = definition.required
|
|
54
|
+
if (definition.min !== undefined) validation.min = definition.min
|
|
55
|
+
if (definition.max !== undefined) validation.max = definition.max
|
|
56
|
+
if (definition.minLength !== undefined) validation.minLength = definition.minLength
|
|
57
|
+
if (definition.maxLength !== undefined) validation.maxLength = definition.maxLength
|
|
58
|
+
if (definition.email !== undefined) validation.email = definition.email
|
|
59
|
+
if (definition.pattern !== undefined) validation.pattern = definition.pattern
|
|
60
|
+
return Object.keys(validation).length > 0 ? validation : undefined
|
|
61
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PillNavItem,
|
|
3
|
+
} from '../types'
|
|
4
|
+
import type {
|
|
5
|
+
NormalizedControl,
|
|
6
|
+
} from './controlSchemaNormalize'
|
|
7
|
+
import {
|
|
8
|
+
humanize,
|
|
9
|
+
} from './controlSchemaUtils'
|
|
10
|
+
import type {
|
|
11
|
+
ControlDefinition,
|
|
12
|
+
ControlSchemaOptions,
|
|
13
|
+
ControlSectionConfig,
|
|
14
|
+
ControlSidebarConfig,
|
|
15
|
+
} from './useControlSchema'
|
|
16
|
+
|
|
17
|
+
export function sectionConfig(
|
|
18
|
+
sectionId: string,
|
|
19
|
+
controls: NormalizedControl[],
|
|
20
|
+
options: ControlSchemaOptions,
|
|
21
|
+
): ControlSectionConfig {
|
|
22
|
+
const configured = options.sections?.[sectionId]
|
|
23
|
+
const first = controls[0]?.definition
|
|
24
|
+
return {
|
|
25
|
+
...configured,
|
|
26
|
+
id: sectionId,
|
|
27
|
+
label: configured?.label ?? first?.sectionLabel,
|
|
28
|
+
title: configured?.title ?? first?.sectionLabel,
|
|
29
|
+
description: configured?.description ?? first?.sectionDescription,
|
|
30
|
+
subtitle: configured?.subtitle ?? first?.sectionSubtitle,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function firstSidebarConfig(controls: NormalizedControl[]): ControlSidebarConfig | undefined {
|
|
35
|
+
for (const control of controls) {
|
|
36
|
+
const sidebar = control.definition.sidebar
|
|
37
|
+
if (sidebar && typeof sidebar === 'object' && sidebar.enabled !== false) return sidebar
|
|
38
|
+
}
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isSidebarEnabled(sidebar: ControlDefinition['sidebar']): boolean {
|
|
43
|
+
if (sidebar === false) return false
|
|
44
|
+
if (sidebar && typeof sidebar === 'object') return sidebar.enabled !== false
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function controlViewItem(viewId: string, options: ControlSchemaOptions): PillNavItem {
|
|
49
|
+
const config = options.views?.[viewId]
|
|
50
|
+
return {
|
|
51
|
+
id: viewId,
|
|
52
|
+
label: config?.label ?? humanize(viewId),
|
|
53
|
+
...(config?.icon !== undefined ? { icon: config.icon } : {}),
|
|
54
|
+
...(config?.to !== undefined ? { to: config.to } : {}),
|
|
55
|
+
...(config?.href !== undefined ? { href: config.href } : {}),
|
|
56
|
+
...(config?.disabled !== undefined ? { disabled: config.disabled } : {}),
|
|
57
|
+
...(config?.children !== undefined ? { children: config.children } : {}),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SelectOption,
|
|
3
|
+
} from '../types'
|
|
4
|
+
import type {
|
|
5
|
+
FormFieldType,
|
|
6
|
+
} from '../types/form-builder'
|
|
7
|
+
import {
|
|
8
|
+
getTypeDefault,
|
|
9
|
+
} from './formBuilderRegistry'
|
|
10
|
+
import {
|
|
11
|
+
humanize,
|
|
12
|
+
} from './controlSchemaUtils'
|
|
13
|
+
import type {
|
|
14
|
+
ControlDefinition,
|
|
15
|
+
ControlInput,
|
|
16
|
+
ControlOption,
|
|
17
|
+
ControlOptionValue,
|
|
18
|
+
ControlSchema,
|
|
19
|
+
ControlSchemaOptions,
|
|
20
|
+
} from './useControlSchema'
|
|
21
|
+
|
|
22
|
+
export interface NormalizedControl {
|
|
23
|
+
name: string
|
|
24
|
+
definition: ControlDefinition
|
|
25
|
+
type: FormFieldType
|
|
26
|
+
sectionId: string
|
|
27
|
+
viewId: string
|
|
28
|
+
order: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function normalizeControls(
|
|
32
|
+
controls: ControlSchema,
|
|
33
|
+
options: ControlSchemaOptions = {},
|
|
34
|
+
): NormalizedControl[] {
|
|
35
|
+
return Object.entries(controls)
|
|
36
|
+
.map(([name, input], index): NormalizedControl => {
|
|
37
|
+
const definition = normalizeControlDefinition(input)
|
|
38
|
+
const type = definition.type ?? inferControlType(definition)
|
|
39
|
+
return {
|
|
40
|
+
name,
|
|
41
|
+
definition,
|
|
42
|
+
type,
|
|
43
|
+
sectionId: definition.section ?? options.section ?? 'controls',
|
|
44
|
+
viewId: definition.view ?? options.view ?? 'default',
|
|
45
|
+
order: definition.order ?? index,
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.sort((a, b) => a.order - b.order)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function normalizeControlDefinition(input: ControlInput): ControlDefinition {
|
|
52
|
+
if (isControlOptionArray(input)) {
|
|
53
|
+
if (input.length === 0) return { type: 'tags', default: [] }
|
|
54
|
+
return {
|
|
55
|
+
default: optionValue(input[0]),
|
|
56
|
+
options: input,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean') {
|
|
61
|
+
return { default: input }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return input
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function defaultValueForControl(definition: ControlDefinition, type: FormFieldType): unknown {
|
|
68
|
+
if (definition.default !== undefined) return definition.default
|
|
69
|
+
if (definition.defaultValue !== undefined) return definition.defaultValue
|
|
70
|
+
return getTypeDefault(type)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function normalizeOption(option: ControlOption): SelectOption<unknown> {
|
|
74
|
+
if (typeof option === 'object' && option !== null && 'label' in option) {
|
|
75
|
+
return option
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
value: option,
|
|
79
|
+
label: humanize(String(option)),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isControlOptionArray(input: ControlInput): input is readonly ControlOption[] {
|
|
84
|
+
return Array.isArray(input)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function inferControlType(definition: ControlDefinition): FormFieldType {
|
|
88
|
+
const value = definition.default ?? definition.defaultValue
|
|
89
|
+
if (definition.options?.length) return Array.isArray(value) ? 'multiselect' : 'select'
|
|
90
|
+
if (typeof value === 'boolean') return 'toggle'
|
|
91
|
+
if (typeof value === 'number') return 'number'
|
|
92
|
+
if (Array.isArray(value)) return 'tags'
|
|
93
|
+
return 'text'
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function optionValue(option: ControlOption): ControlOptionValue | unknown {
|
|
97
|
+
if (typeof option === 'object' && option !== null && 'value' in option) {
|
|
98
|
+
return option.value
|
|
99
|
+
}
|
|
100
|
+
return option
|
|
101
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function recordValue(value: unknown): Record<string, unknown> {
|
|
2
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
3
|
+
? value as Record<string, unknown>
|
|
4
|
+
: {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function numericValue(value: number | { value: number; message: string }): number {
|
|
8
|
+
return typeof value === 'number' ? value : value.value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function orderedUnique(values: string[]): string[] {
|
|
12
|
+
return [...new Set(values)]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function replaceRecord(target: Record<string, unknown>, values: Record<string, unknown>) {
|
|
16
|
+
for (const key of Object.keys(target)) {
|
|
17
|
+
delete target[key]
|
|
18
|
+
}
|
|
19
|
+
Object.assign(target, values)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function omitUndefined<T extends object>(record: T): T {
|
|
23
|
+
const next: Record<string, unknown> = {}
|
|
24
|
+
for (const [key, value] of Object.entries(record as Record<string, unknown>)) {
|
|
25
|
+
if (value !== undefined) next[key] = value
|
|
26
|
+
}
|
|
27
|
+
return next as T
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function humanize(value: string): string {
|
|
31
|
+
return value
|
|
32
|
+
.replace(/[_-]+/g, ' ')
|
|
33
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
34
|
+
.trim()
|
|
35
|
+
.replace(/\w\S*/g, word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ControlWorkspaceOptions,
|
|
3
|
+
} from './useControlSchema'
|
|
4
|
+
|
|
5
|
+
export function mergeControlWorkspaceOptions(
|
|
6
|
+
base?: ControlWorkspaceOptions,
|
|
7
|
+
override?: ControlWorkspaceOptions,
|
|
8
|
+
): ControlWorkspaceOptions {
|
|
9
|
+
return {
|
|
10
|
+
...(base ?? {}),
|
|
11
|
+
...(override ?? {}),
|
|
12
|
+
initialValues: {
|
|
13
|
+
...(base?.initialValues ?? {}),
|
|
14
|
+
...(override?.initialValues ?? {}),
|
|
15
|
+
},
|
|
16
|
+
topBarSettings: {
|
|
17
|
+
...(base?.topBarSettings ?? {}),
|
|
18
|
+
...(override?.topBarSettings ?? {}),
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
}
|