@bagelink/vue 1.14.15 → 1.15.0
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/Alert.vue.d.ts.map +1 -1
- package/dist/components/Badge.vue.d.ts.map +1 -1
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/Image.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/MapEmbed/Index.vue.d.ts.map +1 -1
- package/dist/components/Pagination.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts.map +1 -1
- package/dist/components/Toast.vue.d.ts.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/layout/Panel.vue.d.ts.map +1 -1
- package/dist/components/layout/Resizable.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
- package/dist/components/layout/appLayoutContext.d.ts +24 -0
- package/dist/components/layout/appLayoutContext.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useDevice.d.ts.map +1 -1
- package/dist/composables/useEscapeKey.d.ts +12 -0
- package/dist/composables/useEscapeKey.d.ts.map +1 -0
- package/dist/composables/useSchemaField.d.ts.map +1 -1
- package/dist/composables/useTheme.d.ts.map +1 -1
- package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
- package/dist/form-flow/form-flow.d.ts.map +1 -1
- package/dist/index.cjs +203 -207
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +25819 -28870
- package/dist/style.css +1 -1
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/types/BtnOptions.d.ts.map +1 -1
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +3 -6
- package/src/components/Alert.vue +34 -14
- package/src/components/Badge.vue +145 -22
- package/src/components/Btn.vue +43 -31
- package/src/components/Dropdown.vue +5 -12
- package/src/components/FilterQuery.vue +1 -1
- package/src/components/Image.vue +3 -2
- package/src/components/JSONSchema.vue +2 -2
- package/src/components/JsonBuilder.vue +1 -1
- package/src/components/ListItem.vue +1 -3
- package/src/components/MapEmbed/Index.vue +10 -9
- package/src/components/NavBar.vue +2 -2
- package/src/components/Spreadsheet/Index.vue +1 -1
- package/src/components/Swiper.vue +3 -1
- package/src/components/Toast.vue +23 -8
- package/src/components/calendar/Index.vue +4 -4
- package/src/components/calendar/views/MonthView.vue +3 -3
- package/src/components/form/index.ts +0 -4
- package/src/components/form/inputs/EmailInput.vue +1 -1
- package/src/components/form/inputs/NumberInput.vue +1 -1
- package/src/components/form/inputs/OTP.vue +2 -2
- package/src/components/form/inputs/SelectInput.vue +3 -3
- package/src/components/form/inputs/TelInput.vue +2 -2
- package/src/components/form/inputs/TextInput.vue +1 -1
- package/src/components/form/inputs/Upload/upload.css +2 -2
- package/src/components/index.ts +2 -6
- package/src/components/layout/AppContent.vue +5 -19
- package/src/components/layout/AppLayout.vue +47 -18
- package/src/components/layout/AppSidebar.vue +16 -33
- package/src/components/layout/Resizable.vue +5 -2
- package/src/components/layout/TabsNav.vue +5 -5
- package/src/components/layout/appLayoutContext.ts +44 -0
- package/src/components/layout/index.ts +2 -0
- package/src/components/lightbox/Lightbox.vue +3 -9
- package/src/composables/index.ts +1 -0
- package/src/composables/useDevice.ts +2 -1
- package/src/composables/useEscapeKey.ts +56 -0
- package/src/composables/useSchemaField.ts +2 -17
- package/src/composables/useTheme.ts +23 -19
- package/src/form-flow/FormFlow.vue +2 -0
- package/src/form-flow/form-flow.ts +7 -0
- package/src/index.ts +0 -2
- package/src/styles/inputs.css +1 -1
- package/src/types/BagelForm.ts +46 -151
- package/src/types/BtnOptions.ts +5 -3
- package/src/utils/constants.ts +7 -0
- package/src/utils/index.ts +19 -3
- package/src/utils/sizeParsing.ts +5 -5
- package/vite.config.ts +5 -1
- package/src/components/Carousel.vue +0 -724
- package/src/components/ImportData.vue +0 -1749
- package/src/components/Pill.vue +0 -150
- package/src/components/Slider.vue +0 -1446
- package/src/components/Title.vue +0 -23
- package/src/components/ToolBar.vue +0 -9
- package/src/components/form/BagelForm.vue +0 -219
- package/src/components/form/BglFieldSet.vue +0 -14
- package/src/components/form/BglMultiStepForm.vue +0 -469
- package/src/components/form/FieldArray.vue +0 -422
- package/src/components/form/useBagelFormState.ts +0 -76
- package/src/composables/useFormField.ts +0 -38
- package/src/dialog/DialogOLD.vue +0 -358
- package/src/utils/BagelFormUtils.ts +0 -684
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
<script lang="ts" setup generic="T extends {[key: string]: any}, P extends Path<T>">
|
|
2
|
-
import type {
|
|
3
|
-
AttributeFn,
|
|
4
|
-
AttributeValue,
|
|
5
|
-
Attributes,
|
|
6
|
-
BagelFieldOptions,
|
|
7
|
-
BaseBagelField,
|
|
8
|
-
BglFormSchemaT,
|
|
9
|
-
Field,
|
|
10
|
-
Path
|
|
11
|
-
} from '@bagelink/vue'
|
|
12
|
-
import type { MaybeRefOrGetter } from 'vue'
|
|
13
|
-
import { BagelForm, Btn, Loading, Icon, Card, useI18n } from '@bagelink/vue'
|
|
14
|
-
import { ref, computed, watch, toValue, onMounted } from 'vue'
|
|
15
|
-
|
|
16
|
-
export interface FieldArrayProps<T, P extends Path<T>> {
|
|
17
|
-
el?: any
|
|
18
|
-
id?: string
|
|
19
|
-
label?: string
|
|
20
|
-
placeholder?: string
|
|
21
|
-
children?: Field<T>[]
|
|
22
|
-
class?: AttributeValue | AttributeFn<T, P>
|
|
23
|
-
attrs?: Attributes<T, P>
|
|
24
|
-
required?: boolean
|
|
25
|
-
disabled?: boolean
|
|
26
|
-
helptext?: string
|
|
27
|
-
options?: BagelFieldOptions<T, P>
|
|
28
|
-
defaultValue?: T[]
|
|
29
|
-
add?: boolean
|
|
30
|
-
delete?: boolean
|
|
31
|
-
collapsed?: boolean
|
|
32
|
-
transform?: (value: T) => T
|
|
33
|
-
schema?: MaybeRefOrGetter<BglFormSchemaT<T>>
|
|
34
|
-
modelValue?: T[]
|
|
35
|
-
type?: 'object' | 'number' | 'text' | 'array'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface PrimitiveArrFieldT extends BaseBagelField<{ value: string }, 'value'> {
|
|
39
|
-
type: FieldArrayProps<{ value: string }, 'value'>['type']
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const props = withDefaults(
|
|
43
|
-
defineProps<FieldArrayProps<T, P>>(),
|
|
44
|
-
{
|
|
45
|
-
delete: true,
|
|
46
|
-
add: true,
|
|
47
|
-
el: 'div',
|
|
48
|
-
type: 'object',
|
|
49
|
-
collapsed: false,
|
|
50
|
-
}
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
const emit = defineEmits(['update:modelValue'])
|
|
54
|
-
|
|
55
|
-
const { $t } = useI18n()
|
|
56
|
-
|
|
57
|
-
const BagelFormFA = BagelForm<T, P>
|
|
58
|
-
|
|
59
|
-
// State
|
|
60
|
-
const minimizedItems = ref<boolean[]>([])
|
|
61
|
-
const internalData = ref<any[]>(props.modelValue || [])
|
|
62
|
-
const schemaState = ref<'loading' | 'loaded' | 'error'>('loaded')
|
|
63
|
-
let lastEmittedValue = '' // Event handlers state
|
|
64
|
-
|
|
65
|
-
// Generate schema for primitive types
|
|
66
|
-
const primitiveSchema = computed<PrimitiveArrFieldT[]>(() => {
|
|
67
|
-
if (props.type === 'text') {
|
|
68
|
-
return [{ id: 'value', type: 'text', label: '', $el: 'text' }]
|
|
69
|
-
} if (props.type === 'number') {
|
|
70
|
-
return [{ id: 'value', type: 'number', label: '', $el: 'number' }]
|
|
71
|
-
}
|
|
72
|
-
return []
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
const resolvedSchemaData = computed(() => {
|
|
76
|
-
// Handle 'array' type as equivalent to 'object' type
|
|
77
|
-
const isObjectType = props.type === 'object' || props.type === 'array'
|
|
78
|
-
|
|
79
|
-
if (!isObjectType) { return primitiveSchema.value as BglFormSchemaT<T> }
|
|
80
|
-
|
|
81
|
-
// Use children prop first, then schema prop, then attrs.schema
|
|
82
|
-
const attrsSchema = props.attrs?.schema !== undefined ? toValue(props.attrs.schema) : null
|
|
83
|
-
const schemaToUse = props.children || toValue(props.schema) || attrsSchema
|
|
84
|
-
|
|
85
|
-
// // If no schema but we have defaultValue, try to infer schema from first item
|
|
86
|
-
// const noSchema = null === schemaToUse || schemaToUse === undefined
|
|
87
|
-
// || (Array.isArray(schemaToUse) && 0 === schemaToUse.length)
|
|
88
|
-
// const hasDefaultValue = props.defaultValue !== undefined
|
|
89
|
-
// && null !== props.defaultValue
|
|
90
|
-
// && 0 < props.defaultValue.length
|
|
91
|
-
|
|
92
|
-
// Don't infer schema from defaultValue - this causes unwanted restoration
|
|
93
|
-
// Let the component work with empty arrays
|
|
94
|
-
return schemaToUse as BglFormSchemaT<T>
|
|
95
|
-
})// Watch for external changes to modelValue - simple and straightforward
|
|
96
|
-
watch(() => props.modelValue, (newValue) => {
|
|
97
|
-
// Simply sync with external changes, no automatic defaultValue restoration
|
|
98
|
-
if (newValue && Array.isArray(newValue)) {
|
|
99
|
-
internalData.value = [...newValue]
|
|
100
|
-
} else {
|
|
101
|
-
internalData.value = []
|
|
102
|
-
}
|
|
103
|
-
}, { deep: true, immediate: false })
|
|
104
|
-
|
|
105
|
-
// Watch for changes to internalData length to sync minimizedItems
|
|
106
|
-
watch(() => internalData.value.length, (newLength) => {
|
|
107
|
-
// Resize minimizedItems array to match internalData length
|
|
108
|
-
if (minimizedItems.value.length !== newLength) {
|
|
109
|
-
// If array grew, add new items with collapsed state
|
|
110
|
-
if (minimizedItems.value.length < newLength) {
|
|
111
|
-
const itemsToAdd = newLength - minimizedItems.value.length
|
|
112
|
-
for (let i = 0; i < itemsToAdd; i++) {
|
|
113
|
-
minimizedItems.value.push(props.collapsed || false)
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
// If array shrank, remove excess items
|
|
117
|
-
minimizedItems.value = minimizedItems.value.slice(0, newLength)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
// Initialize with existing data or defaultValue on mount - only once!
|
|
123
|
-
onMounted(() => {
|
|
124
|
-
// If no modelValue provided, use defaultValue as initial data (one time only)
|
|
125
|
-
if (!props.modelValue || props.modelValue.length === 0) {
|
|
126
|
-
if (props.defaultValue && props.defaultValue.length > 0) {
|
|
127
|
-
internalData.value = [...props.defaultValue]
|
|
128
|
-
emitValue() // This will set the initial value and then it becomes "regular" data
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Initialize minimized state based on collapsed prop
|
|
133
|
-
if (props.collapsed) {
|
|
134
|
-
minimizedItems.value = Array.from({ length: internalData.value.length }, () => true)
|
|
135
|
-
} else {
|
|
136
|
-
minimizedItems.value = Array.from({ length: internalData.value.length }, () => false)
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
// Function to load default values (explicit user action)
|
|
141
|
-
function loadDefaults() {
|
|
142
|
-
if (props.defaultValue && props.defaultValue.length > 0) {
|
|
143
|
-
internalData.value = [...props.defaultValue]
|
|
144
|
-
emitValue()
|
|
145
|
-
} else {
|
|
146
|
-
// If no defaultValue in props, add a single empty item
|
|
147
|
-
addItem()
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Resolve schema
|
|
152
|
-
// async function resolveSchema() {
|
|
153
|
-
// // For primitive types, use the predefined schemaks
|
|
154
|
-
// if (props.type !== 'object') {
|
|
155
|
-
// resolvedSchemaData.value = primitiveSchema.value
|
|
156
|
-
// schemaState.value = 'loaded'
|
|
157
|
-
// return
|
|
158
|
-
// }
|
|
159
|
-
|
|
160
|
-
// // For object type without schema
|
|
161
|
-
// if (!props.schema) {
|
|
162
|
-
// resolvedSchemaData.value = []
|
|
163
|
-
// schemaState.value = 'loaded'
|
|
164
|
-
// return
|
|
165
|
-
// }
|
|
166
|
-
|
|
167
|
-
// // For object type with schema
|
|
168
|
-
// try {
|
|
169
|
-
// // schemaState.value = 'loading'
|
|
170
|
-
// const isPromise = (obj: any) => obj && typeof obj.then === 'function'
|
|
171
|
-
|
|
172
|
-
// let result: any
|
|
173
|
-
// if (typeof props.schema === 'function') {
|
|
174
|
-
// result = props.schema()
|
|
175
|
-
// result = isPromise(result) ? await result : result
|
|
176
|
-
// } else {
|
|
177
|
-
// result = isPromise(props.schema) ? await props.schema : props.schema
|
|
178
|
-
// }
|
|
179
|
-
|
|
180
|
-
// resolvedSchemaData.value = result
|
|
181
|
-
// schemaState.value = 'loaded'
|
|
182
|
-
// } catch (error) {
|
|
183
|
-
// console.error('Schema error:', error)
|
|
184
|
-
// schemaState.value = 'error'
|
|
185
|
-
// resolvedSchemaData.value = []
|
|
186
|
-
// }
|
|
187
|
-
// }
|
|
188
|
-
|
|
189
|
-
// // Initialize schema on mount
|
|
190
|
-
// onMounted(resolveSchema)
|
|
191
|
-
|
|
192
|
-
// Event handlers
|
|
193
|
-
|
|
194
|
-
function emitValue() {
|
|
195
|
-
// Only emit if the value has actually changed to prevent recursive updates
|
|
196
|
-
const currentValue = JSON.stringify(props.modelValue || [])
|
|
197
|
-
const newValue = JSON.stringify(internalData.value || [])
|
|
198
|
-
|
|
199
|
-
// Double-check against last emitted value to prevent loops
|
|
200
|
-
if (currentValue !== newValue && lastEmittedValue !== newValue) {
|
|
201
|
-
lastEmittedValue = newValue
|
|
202
|
-
emit('update:modelValue', internalData.value)
|
|
203
|
-
// Reset after a short delay to allow for new changes
|
|
204
|
-
setTimeout(() => {
|
|
205
|
-
lastEmittedValue = ''
|
|
206
|
-
}, 50)
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function deleteItem(i: number) {
|
|
211
|
-
internalData.value.splice(i, 1)
|
|
212
|
-
minimizedItems.value.splice(i, 1)
|
|
213
|
-
emitValue()
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function addItem() {
|
|
217
|
-
// Add appropriate default value based on type
|
|
218
|
-
const defaultValues: { [key: string]: any } = {
|
|
219
|
-
text: '',
|
|
220
|
-
number: 0,
|
|
221
|
-
object: {},
|
|
222
|
-
array: {}
|
|
223
|
-
}
|
|
224
|
-
const defaultValue = defaultValues[props.type]
|
|
225
|
-
internalData.value.push(defaultValue !== undefined ? defaultValue : {})
|
|
226
|
-
|
|
227
|
-
// Add minimized state for new item - always open (false) when newly added
|
|
228
|
-
minimizedItems.value.push(false)
|
|
229
|
-
|
|
230
|
-
emitValue()
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function toggleMinimized(index: number) {
|
|
234
|
-
minimizedItems.value[index] = !minimizedItems.value[index]
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function updateItem(index: number, value: any) {
|
|
238
|
-
// Handle primitive types by extracting the value property
|
|
239
|
-
if (props.type === 'text' || props.type === 'number') {
|
|
240
|
-
internalData.value[index] = props.type === 'number' ? Number(value.value) : value.value
|
|
241
|
-
} else {
|
|
242
|
-
internalData.value[index] = value
|
|
243
|
-
}
|
|
244
|
-
emitValue()
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Computed properties for rendering
|
|
248
|
-
const isPrimitiveType = computed(() => props.type === 'text' || props.type === 'number')
|
|
249
|
-
const canRenderItems = computed(() => {
|
|
250
|
-
const schemaData = resolvedSchemaData.value
|
|
251
|
-
const hasValidSchema = (Array.isArray(schemaData) && schemaData.length > 0)
|
|
252
|
-
|| (props.children && props.children.length > 0)
|
|
253
|
-
const hasDefaultValue = props.defaultValue && props.defaultValue.length > 0
|
|
254
|
-
|
|
255
|
-
// For primitive types or when we have schema or defaultValue
|
|
256
|
-
const isObjectType = props.type === 'object' || props.type === 'array'
|
|
257
|
-
const shouldRender = isPrimitiveType.value
|
|
258
|
-
|| (isObjectType && (hasValidSchema === true || hasDefaultValue === true))
|
|
259
|
-
return shouldRender
|
|
260
|
-
})
|
|
261
|
-
const showMinimizeButton = computed(() => {
|
|
262
|
-
const schemaData = resolvedSchemaData.value
|
|
263
|
-
const hasLongSchema = Array.isArray(schemaData) && schemaData.length > 2
|
|
264
|
-
const hasRichText = Array.isArray(schemaData)
|
|
265
|
-
&& schemaData.some((schema: any) => schema.$el === 'richtext')
|
|
266
|
-
return hasLongSchema || hasRichText
|
|
267
|
-
})
|
|
268
|
-
</script>
|
|
269
|
-
|
|
270
|
-
<template>
|
|
271
|
-
<div :class="props.class">
|
|
272
|
-
<!-- Label -->
|
|
273
|
-
<p v-if="label" class="label mb-05">
|
|
274
|
-
{{ label }}
|
|
275
|
-
</p>
|
|
276
|
-
<div class="ps-025 mb-05">
|
|
277
|
-
<!-- Loading/Error States -->
|
|
278
|
-
<div v-if="schemaState !== 'loaded'" class="flex-center h-300px">
|
|
279
|
-
<Loading v-if="schemaState === 'loading'" />
|
|
280
|
-
<Icon v-else-if="schemaState === 'error'" icon="error" color="red" />
|
|
281
|
-
</div>
|
|
282
|
-
|
|
283
|
-
<!-- No Schema Available -->
|
|
284
|
-
<Card v-else-if="!canRenderItems" class="py-1 border mb-05">
|
|
285
|
-
<p class="opacity-7">
|
|
286
|
-
No schema available
|
|
287
|
-
</p>
|
|
288
|
-
</Card>
|
|
289
|
-
|
|
290
|
-
<!-- Items Rendering -->
|
|
291
|
-
<template v-else>
|
|
292
|
-
<!-- Empty Array Message -->
|
|
293
|
-
<Card v-if="internalData.length === 0" class="py-1 border mb-05">
|
|
294
|
-
<p class="opacity-7 txt-center txt14">
|
|
295
|
-
No {{ label?.toLowerCase() || 'items' }} added yet
|
|
296
|
-
</p>
|
|
297
|
-
<!-- Load defaults button if defaultValue exists -->
|
|
298
|
-
<Btn
|
|
299
|
-
v-if="props.defaultValue && props.defaultValue.length > 0" thin color="primary"
|
|
300
|
-
class="txt12 mb-05" @click="loadDefaults"
|
|
301
|
-
>
|
|
302
|
-
{{ $t('fieldArray.loadDefault', { label: label || 'Items' }) }}
|
|
303
|
-
</Btn>
|
|
304
|
-
</Card>
|
|
305
|
-
|
|
306
|
-
<!-- Array Items -->
|
|
307
|
-
<div
|
|
308
|
-
v-for="(item, i) in internalData" :key="i" outline thin
|
|
309
|
-
class="mb-05 itemBox transition radius-05 overflow-hidden txt12 border"
|
|
310
|
-
style="--bgl-input-font-size: 12px" :class="{ minimized: minimizedItems[i] }"
|
|
311
|
-
>
|
|
312
|
-
<div>
|
|
313
|
-
<!-- Minimized View -->
|
|
314
|
-
<div class="flex pointer">
|
|
315
|
-
<p class="minimizedText txt12 p-05 opacity-7 flex-grow-2" @click="toggleMinimized(i)">
|
|
316
|
-
{{ label }} {{ i + 1 }}
|
|
317
|
-
</p>
|
|
318
|
-
<Btn
|
|
319
|
-
v-if="showMinimizeButton" class="rotate-180 txt10 opacity-7 p-025" flat thin
|
|
320
|
-
icon="keyboard_arrow_down" @click="toggleMinimized(i)"
|
|
321
|
-
/>
|
|
322
|
-
</div>
|
|
323
|
-
|
|
324
|
-
<!-- Form View -->
|
|
325
|
-
<BagelFormFA
|
|
326
|
-
v-if="!minimizedItems[i]" :model-value="isPrimitiveType ? { value: item } : item"
|
|
327
|
-
:schema="resolvedSchemaData" class="bginputbg p-05 grid gap-05"
|
|
328
|
-
@update:model-value="val => updateItem(i, val)"
|
|
329
|
-
/>
|
|
330
|
-
<Btn
|
|
331
|
-
v-if="props.delete" icon="delete" class="txt10 opacity-7" thin flat
|
|
332
|
-
@click="deleteItem(i)"
|
|
333
|
-
/>
|
|
334
|
-
</div>
|
|
335
|
-
</div>
|
|
336
|
-
|
|
337
|
-
<!-- Add Button -->
|
|
338
|
-
<Btn v-if="add" thin icon="add" color="gray" class="txt12 arrayAddButton" @click="addItem">
|
|
339
|
-
<p>{{ label }}</p>
|
|
340
|
-
</Btn>
|
|
341
|
-
</template>
|
|
342
|
-
</div>
|
|
343
|
-
</div>
|
|
344
|
-
</template>
|
|
345
|
-
|
|
346
|
-
<style>
|
|
347
|
-
.minimized {
|
|
348
|
-
height: 2.3rem;
|
|
349
|
-
overflow: hidden;
|
|
350
|
-
border-color: transparent !important;
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
.minimizedText {
|
|
355
|
-
/* display: none; */
|
|
356
|
-
padding-top: 1rem !important;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
.minimized .minimizedText {
|
|
360
|
-
display: block;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
.minimized .rotate-180 {
|
|
364
|
-
transform: rotate(0deg);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.itemBox {
|
|
368
|
-
background: var(--btn-bg);
|
|
369
|
-
--bgl-label-font-size: 0.6rem;
|
|
370
|
-
--bgl-input-height: 30px;
|
|
371
|
-
--bgl-input-font-size: 7px;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
.bginputbg {
|
|
375
|
-
background: var(--bgl-input-bg);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.pt-065 {
|
|
379
|
-
padding-top: 0.65rem;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
.itemBox .bagel-input input,
|
|
383
|
-
.itemBox .bagel-input textarea,
|
|
384
|
-
.itemBox .bagel-input select,
|
|
385
|
-
.itemBox .custom-select .input {
|
|
386
|
-
background: var(--bgl-white) !important;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.itemBox .code-editor-wrap textarea {
|
|
390
|
-
background: transparent !important;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.itemBox .bagel-input {
|
|
394
|
-
margin-bottom: 0.15rem !important;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
.itemBox .richtext-editor-content {
|
|
398
|
-
--richtext-font-size: 12px !important;
|
|
399
|
-
font-size: 12px !important;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
.itemBox iframe.editableContent {
|
|
403
|
-
--richtext-font-size: 12px !important;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
.itemBox .bagel-input.richtext-input {
|
|
407
|
-
--richtext-font-size: 12px !important;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/* More specific selectors to override richtext font size */
|
|
411
|
-
.itemBox .richtext-input iframe {
|
|
412
|
-
font-size: 12px !important;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
.itemBox .richtext-input .editableContent body {
|
|
416
|
-
font-size: 12px !important;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.itemBox .richtext-input {
|
|
420
|
-
font-size: 12px !important;
|
|
421
|
-
}
|
|
422
|
-
</style>
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import type { Ref } from 'vue'
|
|
2
|
-
import { inject, provide, ref } from 'vue'
|
|
3
|
-
import { getNestedValue } from '../../utils'
|
|
4
|
-
|
|
5
|
-
export const FORM_STATE_KEY = Symbol('bagelFormState')
|
|
6
|
-
|
|
7
|
-
export interface BagelFormState<T> {
|
|
8
|
-
data: Ref<T>
|
|
9
|
-
getFieldData: (path?: string) => any
|
|
10
|
-
updateField: (path: string, value: any) => void
|
|
11
|
-
isDirty: Ref<boolean>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Helper function to safely clone objects without circular references
|
|
15
|
-
function safeClone(obj: any): any {
|
|
16
|
-
if (obj === null || typeof obj !== 'object') { return obj }
|
|
17
|
-
|
|
18
|
-
const seen = new WeakSet()
|
|
19
|
-
return JSON.parse(JSON.stringify(obj, (key, value) => {
|
|
20
|
-
if (typeof value === 'object' && value !== null) {
|
|
21
|
-
if (seen.has(value)) {
|
|
22
|
-
return undefined // Remove circular reference
|
|
23
|
-
}
|
|
24
|
-
seen.add(value)
|
|
25
|
-
}
|
|
26
|
-
return value
|
|
27
|
-
}))
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function provideBagelFormState<T>(initialData: T) {
|
|
31
|
-
const data = ref(initialData) as Ref<T>
|
|
32
|
-
const isDirty = ref(false)
|
|
33
|
-
|
|
34
|
-
const getFieldData = (path?: string) => getNestedValue(data.value, path, '')
|
|
35
|
-
|
|
36
|
-
const updateField = (path: string, value: any) => {
|
|
37
|
-
const keys = path.split(/[.[]/)
|
|
38
|
-
|
|
39
|
-
// Initialize the root if it's not an object
|
|
40
|
-
if (typeof data.value !== 'object' || data.value === null) {
|
|
41
|
-
data.value = {} as T
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let current = data.value as any
|
|
45
|
-
|
|
46
|
-
// Build the path, ensuring each level is an object
|
|
47
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
48
|
-
const key = keys[i]
|
|
49
|
-
if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
|
|
50
|
-
current[key] = {}
|
|
51
|
-
}
|
|
52
|
-
current = current[key]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Safely clone the value to remove circular references
|
|
56
|
-
const safeValue = safeClone(value)
|
|
57
|
-
current[keys[keys.length - 1]] = safeValue
|
|
58
|
-
isDirty.value = true
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const state: BagelFormState<T> = {
|
|
62
|
-
data,
|
|
63
|
-
getFieldData,
|
|
64
|
-
updateField,
|
|
65
|
-
isDirty
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
provide(FORM_STATE_KEY, state)
|
|
69
|
-
return state
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function useBagelFormState<T>(injectionKey: typeof FORM_STATE_KEY = FORM_STATE_KEY): BagelFormState<T> {
|
|
73
|
-
const state = inject<BagelFormState<T>>(injectionKey)
|
|
74
|
-
if (!state) { throw new Error('BagelFormState must be provided') }
|
|
75
|
-
return state
|
|
76
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type { Field } from '@bagelink/vue'
|
|
2
|
-
import type { BagelFormState } from '../components/form/useBagelFormState'
|
|
3
|
-
import { inject, computed } from 'vue'
|
|
4
|
-
import { FORM_STATE_KEY } from '../components/form/useBagelFormState'
|
|
5
|
-
|
|
6
|
-
export function useFormField<T>(props: {
|
|
7
|
-
field: Field<T>
|
|
8
|
-
fieldID?: string
|
|
9
|
-
modelValue?: any
|
|
10
|
-
}) {
|
|
11
|
-
const formState = inject<BagelFormState<T>>(FORM_STATE_KEY)
|
|
12
|
-
|
|
13
|
-
const fieldData = computed({
|
|
14
|
-
get: () => {
|
|
15
|
-
if (!props.fieldID || !formState) { return props.modelValue ?? props.field.defaultValue ?? '' }
|
|
16
|
-
const value = formState.getFieldData(props.fieldID)
|
|
17
|
-
// For nested form containers, default to empty object
|
|
18
|
-
if (props.field.$el === 'form' && !value) { return {} }
|
|
19
|
-
// Don't auto-restore defaultValue - let user control their content
|
|
20
|
-
return value ?? ''
|
|
21
|
-
},
|
|
22
|
-
set: (val: any) => {
|
|
23
|
-
if (!props.fieldID || !formState) { return }
|
|
24
|
-
const currentValue = formState.getFieldData(props.fieldID)
|
|
25
|
-
if (JSON.stringify(val) === JSON.stringify(currentValue)) { return }
|
|
26
|
-
|
|
27
|
-
if (props.field.onUpdate) {
|
|
28
|
-
props.field.onUpdate(val, currentValue)
|
|
29
|
-
}
|
|
30
|
-
formState.updateField(props.fieldID, val)
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
fieldData,
|
|
36
|
-
formState
|
|
37
|
-
}
|
|
38
|
-
}
|