@bagelink/vue 1.15.331 → 1.15.337
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/Icon/Icon.vue.d.ts +3 -0
- package/dist/components/Icon/Icon.vue.d.ts.map +1 -1
- package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
- package/dist/form-flow/form-flow.d.ts +1 -0
- package/dist/form-flow/form-flow.d.ts.map +1 -1
- package/dist/form-flow/schema-fields.d.ts +1 -0
- package/dist/form-flow/schema-fields.d.ts.map +1 -1
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/index.cjs +93 -93
- package/dist/index.mjs +6720 -6702
- package/package.json +1 -1
- package/src/components/Icon/Icon.vue +23 -9
- package/src/form-flow/FormFlow.vue +13 -4
- package/src/form-flow/form-flow.ts +22 -0
- package/src/form-flow/schema-fields.ts +7 -1
- package/src/i18n/index.ts +32 -4
package/package.json
CHANGED
|
@@ -17,6 +17,9 @@ const props = withDefaults(defineProps<{
|
|
|
17
17
|
(theme name → `pair-*`, or a raw CSS color) and contrasts the glyph.
|
|
18
18
|
Without `color`, just the circular shape. */
|
|
19
19
|
round?: boolean
|
|
20
|
+
/** Soft circular chip: light tinted background + full-color glyph and border.
|
|
21
|
+
Uses `color`, and defaults to black when no color is provided. */
|
|
22
|
+
soft?: boolean
|
|
20
23
|
weight?: number | string
|
|
21
24
|
fontAwesome?: boolean
|
|
22
25
|
fill?: boolean
|
|
@@ -159,12 +162,16 @@ const isRawCssColor = computed(() => {
|
|
|
159
162
|
|| /^(rgb|hsl|hwb|lab|lch|oklch|color)\(/i.test(c)
|
|
160
163
|
})
|
|
161
164
|
|
|
165
|
+
const hasChip = computed(() => props.round || props.soft)
|
|
166
|
+
|
|
162
167
|
const roundPairClass = computed(() => {
|
|
163
|
-
if (!
|
|
164
|
-
return `pair-${props.color}`
|
|
168
|
+
if (!hasChip.value || isRawCssColor.value) { return undefined }
|
|
169
|
+
if (props.color) { return `pair-${props.color}` }
|
|
170
|
+
return props.soft ? 'pair-black' : undefined
|
|
165
171
|
})
|
|
166
172
|
|
|
167
|
-
const roundClass = computed(() => (
|
|
173
|
+
const roundClass = computed(() => (hasChip.value ? 'bgl_icon-round' : undefined))
|
|
174
|
+
const softClass = computed(() => (props.soft ? 'soft' : undefined))
|
|
168
175
|
|
|
169
176
|
const iconStyle = computed(() => {
|
|
170
177
|
const style: Record<string, string> = {
|
|
@@ -177,11 +184,17 @@ const iconStyle = computed(() => {
|
|
|
177
184
|
} else {
|
|
178
185
|
style['font-variation-settings'] = `'wght' ${props.weight || 400}`
|
|
179
186
|
}
|
|
180
|
-
if (
|
|
187
|
+
if (hasChip.value) {
|
|
181
188
|
style.width = `calc(${computedSize.value}rem * 2)`
|
|
182
189
|
style.height = `calc(${computedSize.value}rem * 2)`
|
|
183
190
|
if (isRawCssColor.value && props.color) {
|
|
184
|
-
|
|
191
|
+
if (props.soft) {
|
|
192
|
+
style.backgroundColor = `color-mix(in srgb, ${props.color} 18%, transparent)`
|
|
193
|
+
style.color = props.color
|
|
194
|
+
style.border = `1px solid ${props.color}`
|
|
195
|
+
} else {
|
|
196
|
+
style.backgroundColor = props.color
|
|
197
|
+
}
|
|
185
198
|
}
|
|
186
199
|
} else if (props.color) {
|
|
187
200
|
style.color = props.color
|
|
@@ -193,23 +206,24 @@ const iconStyle = computed(() => {
|
|
|
193
206
|
<template>
|
|
194
207
|
<Transition :name="transitionName" mode="out-in">
|
|
195
208
|
<span
|
|
196
|
-
v-if="iconRenderType === 'material'" :key="iconRender" class="bgl_icon-font notranslate"
|
|
197
|
-
:class="[animateClass, roundClass, roundPairClass, { 'color-white keep-white': round && isRawCssColor }]"
|
|
209
|
+
v-if="iconRenderType === 'material'" :key="`material-${iconRender}`" class="bgl_icon-font notranslate"
|
|
210
|
+
:class="[animateClass, roundClass, roundPairClass, softClass, { 'color-white keep-white': round && !soft && isRawCssColor }]"
|
|
198
211
|
:style="iconStyle" translate="no"
|
|
199
212
|
>
|
|
200
213
|
{{ iconRender }}
|
|
201
214
|
</span>
|
|
202
215
|
<span
|
|
203
|
-
v-else-if="iconRenderType === 'font-awesome'" :key="iconRender" class="fa bgl_icon-font notranslate" :class="[
|
|
216
|
+
v-else-if="iconRenderType === 'font-awesome'" :key="`font-awesome-${iconRender}`" class="fa bgl_icon-font notranslate" :class="[
|
|
204
217
|
`fa-${iconRender}`,
|
|
205
218
|
animateClass,
|
|
206
219
|
roundClass,
|
|
207
220
|
roundPairClass,
|
|
221
|
+
softClass,
|
|
208
222
|
{
|
|
209
223
|
'fa-brands': isFaBrand,
|
|
210
224
|
'fa-solid': fill,
|
|
211
225
|
'far': !fill && !isFaBrand,
|
|
212
|
-
'color-white keep-white': round && isRawCssColor,
|
|
226
|
+
'color-white keep-white': round && !soft && isRawCssColor,
|
|
213
227
|
},
|
|
214
228
|
]" :style="iconStyle"
|
|
215
229
|
translate="no"
|
|
@@ -230,7 +230,10 @@ function updateField(key: string, value: any, field: FieldBuilder) {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
function isFieldEmpty(value: any, field: FieldBuilder): boolean {
|
|
233
|
-
|
|
233
|
+
// `false` is a valid answer for boolean radio/toggle fields (for example a
|
|
234
|
+
// required Yes/No question). Only an unchecked checkbox uses false as its
|
|
235
|
+
// empty state.
|
|
236
|
+
return value == null || value === '' || (value === false && field._type === 'checkbox')
|
|
234
237
|
|| (Array.isArray(value) && value.length === 0)
|
|
235
238
|
|| (field._type === 'tel' && typeof value === 'string' && /^\+\d+$/.test(value))
|
|
236
239
|
}
|
|
@@ -386,6 +389,13 @@ function getFieldProps(field: FieldBuilder, key: string) {
|
|
|
386
389
|
return baseProps
|
|
387
390
|
}
|
|
388
391
|
|
|
392
|
+
function getCustomFieldProps(field: FieldBuilder, key: string) {
|
|
393
|
+
return {
|
|
394
|
+
...(field._component?.props ?? {}),
|
|
395
|
+
...getFieldProps(field, key),
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
389
399
|
function resolveComponent(componentRef: any): any {
|
|
390
400
|
if (typeof componentRef === 'string') {
|
|
391
401
|
if (props.components?.[componentRef] != null) {
|
|
@@ -445,6 +455,7 @@ async function handleSubmit(event?: Event): Promise<boolean> {
|
|
|
445
455
|
|
|
446
456
|
if (!isValid) {
|
|
447
457
|
event?.stopPropagation()
|
|
458
|
+
formElement.value?.reportValidity()
|
|
448
459
|
|
|
449
460
|
const firstErrorKey = visibleFields.value.find(({ key }) => validationErrors.value[key])?.key
|
|
450
461
|
|
|
@@ -552,9 +563,7 @@ defineExpose({
|
|
|
552
563
|
<template v-else-if="usesCustomComponent(field)">
|
|
553
564
|
<component
|
|
554
565
|
:is="resolveComponent(field._component!.component)"
|
|
555
|
-
|
|
556
|
-
v-bind="field._component!.props"
|
|
557
|
-
@update:model-value="(val: any) => updateField(key, val, field)"
|
|
566
|
+
v-bind="getCustomFieldProps(field, key)"
|
|
558
567
|
/>
|
|
559
568
|
</template>
|
|
560
569
|
|
|
@@ -759,6 +759,7 @@ interface JSONSchemaProperty {
|
|
|
759
759
|
'title'?: string
|
|
760
760
|
'description'?: string
|
|
761
761
|
'default'?: any
|
|
762
|
+
'const'?: any
|
|
762
763
|
'enum'?: any[]
|
|
763
764
|
'format'?: string
|
|
764
765
|
'minimum'?: number
|
|
@@ -1065,6 +1066,27 @@ function createFieldFromProperty(
|
|
|
1065
1066
|
field = $.text(config)
|
|
1066
1067
|
}
|
|
1067
1068
|
|
|
1069
|
+
// Rendering is orthogonal to semantics: a checkbox may use a branded
|
|
1070
|
+
// component while remaining a checkbox for validation and serialization.
|
|
1071
|
+
if (
|
|
1072
|
+
xUi.component
|
|
1073
|
+
&& fieldType !== 'component'
|
|
1074
|
+
&& fieldType !== 'display'
|
|
1075
|
+
) {
|
|
1076
|
+
field._component = {
|
|
1077
|
+
component: xUi.component,
|
|
1078
|
+
props: xUi.componentProps || {},
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
if (resolvedProp.const !== undefined) {
|
|
1083
|
+
field.validate(value =>
|
|
1084
|
+
value === resolvedProp.const
|
|
1085
|
+
? true
|
|
1086
|
+
: config.requiredMessage || 'form.required'
|
|
1087
|
+
)
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1068
1090
|
// Apply class
|
|
1069
1091
|
if (xUi.class) {
|
|
1070
1092
|
field.class(xUi.class)
|
|
@@ -101,6 +101,7 @@ export interface FormField {
|
|
|
101
101
|
export interface JSONSchemaProperty {
|
|
102
102
|
type?: string | string[]
|
|
103
103
|
items?: JSONSchemaProperty
|
|
104
|
+
const?: any
|
|
104
105
|
title?: string
|
|
105
106
|
description?: string
|
|
106
107
|
format?: string
|
|
@@ -354,7 +355,12 @@ export function fieldsToSchema(fields: FormField[]): JSONSchemaObject {
|
|
|
354
355
|
if (field.validation?.pattern !== undefined) prop.pattern = field.validation.pattern
|
|
355
356
|
|
|
356
357
|
// Required
|
|
357
|
-
if (field.required)
|
|
358
|
+
if (field.required) {
|
|
359
|
+
required.push(field.key)
|
|
360
|
+
// Required checkboxes represent explicit acceptance, not merely
|
|
361
|
+
// presence of a boolean key.
|
|
362
|
+
if (field.type === 'checkbox') prop.const = true
|
|
363
|
+
}
|
|
358
364
|
}
|
|
359
365
|
|
|
360
366
|
// Placeholder
|
package/src/i18n/index.ts
CHANGED
|
@@ -19,6 +19,34 @@ const bagelinkLocales = {
|
|
|
19
19
|
ru,
|
|
20
20
|
} as const
|
|
21
21
|
|
|
22
|
+
function mergeMessages(
|
|
23
|
+
base: Record<string, unknown>,
|
|
24
|
+
overrides: Record<string, unknown>,
|
|
25
|
+
): Record<string, unknown> {
|
|
26
|
+
const merged = { ...base }
|
|
27
|
+
|
|
28
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
29
|
+
const baseValue = merged[key]
|
|
30
|
+
if (
|
|
31
|
+
baseValue != null
|
|
32
|
+
&& value != null
|
|
33
|
+
&& typeof baseValue === 'object'
|
|
34
|
+
&& typeof value === 'object'
|
|
35
|
+
&& !Array.isArray(baseValue)
|
|
36
|
+
&& !Array.isArray(value)
|
|
37
|
+
) {
|
|
38
|
+
merged[key] = mergeMessages(
|
|
39
|
+
baseValue as Record<string, unknown>,
|
|
40
|
+
value as Record<string, unknown>,
|
|
41
|
+
)
|
|
42
|
+
} else {
|
|
43
|
+
merged[key] = value
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return merged
|
|
48
|
+
}
|
|
49
|
+
|
|
22
50
|
export interface CreateBagelI18nOptions {
|
|
23
51
|
/** Active locale (default: 'en'). Same as vue-i18n's `locale`. */
|
|
24
52
|
locale?: string
|
|
@@ -46,10 +74,10 @@ export function createI18n(options: CreateBagelI18nOptions = {}): I18n {
|
|
|
46
74
|
const mergedMessages: Record<string, Record<string, unknown>> = {}
|
|
47
75
|
|
|
48
76
|
for (const lang of supportedLocales) {
|
|
49
|
-
mergedMessages[lang] =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
77
|
+
mergedMessages[lang] = mergeMessages(
|
|
78
|
+
bagelinkLocales[lang as keyof typeof bagelinkLocales] || {},
|
|
79
|
+
messages[lang] || {},
|
|
80
|
+
)
|
|
53
81
|
}
|
|
54
82
|
|
|
55
83
|
// Also include any user-provided locales not in bagelink's built-ins
|