@bagelink/vue 1.2.79 → 1.2.83
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/bin/experimentalGenTypedRoutes.ts +13 -2
- package/dist/components/Btn.vue.d.ts +1 -1
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/DataPreview.vue.d.ts +16 -5
- package/dist/components/DataPreview.vue.d.ts.map +1 -1
- package/dist/components/Icon/Icon.vue.d.ts +4 -1
- package/dist/components/Icon/Icon.vue.d.ts.map +1 -1
- package/dist/components/ModalForm.vue.d.ts +2 -1
- package/dist/components/ModalForm.vue.d.ts.map +1 -1
- package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/dataTable/useSorting.d.ts +1 -1
- package/dist/components/dataTable/useSorting.d.ts.map +1 -1
- package/dist/components/dataTable/useTableData.d.ts +7 -6
- package/dist/components/dataTable/useTableData.d.ts.map +1 -1
- package/dist/components/dataTable/useTableVirtualization.d.ts.map +1 -1
- package/dist/components/form/BagelForm.vue.d.ts +5 -10
- package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
- package/dist/components/form/BglMultiStepForm.vue.d.ts +10 -6
- package/dist/components/form/BglMultiStepForm.vue.d.ts.map +1 -1
- package/dist/components/form/FieldArray.vue.d.ts +3 -2
- package/dist/components/form/FieldArray.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts +14 -6
- package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/composables/index.d.ts +5 -5
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useSchemaField.d.ts +5 -12
- package/dist/composables/useSchemaField.d.ts.map +1 -1
- package/dist/index.cjs +298 -357
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +299 -358
- package/dist/plugins/modalTypes.d.ts +3 -2
- package/dist/plugins/modalTypes.d.ts.map +1 -1
- package/dist/style.css +165 -135
- package/dist/types/BagelForm.d.ts +14 -11
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/types/TableSchema.d.ts +9 -9
- package/dist/types/TableSchema.d.ts.map +1 -1
- package/dist/utils/BagelFormUtils.d.ts +4 -3
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/useSearch.d.ts +44 -0
- package/dist/utils/useSearch.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/DataPreview.vue +16 -5
- package/src/components/Icon/Icon.vue +12 -3
- package/src/components/ModalForm.vue +6 -9
- package/src/components/dataTable/DataTable.vue +11 -14
- package/src/components/dataTable/useSorting.ts +1 -1
- package/src/components/dataTable/useTableData.ts +19 -42
- package/src/components/dataTable/useTableVirtualization.ts +4 -8
- package/src/components/form/BagelForm.vue +33 -97
- package/src/components/form/BglMultiStepForm.vue +52 -36
- package/src/components/form/FieldArray.vue +54 -45
- package/src/components/form/inputs/CodeEditor/Index.vue +160 -98
- package/src/components/form/inputs/RichText/index.vue +13 -12
- package/src/composables/index.ts +12 -13
- package/src/composables/useSchemaField.ts +37 -35
- package/src/index.ts +1 -1
- package/src/plugins/modalTypes.ts +3 -2
- package/src/types/BagelForm.ts +22 -14
- package/src/types/TableSchema.ts +9 -9
- package/src/utils/BagelFormUtils.ts +4 -3
- package/src/utils/index.ts +38 -13
- package/src/utils/{search.ts → useSearch.ts} +1 -2
|
@@ -7,10 +7,9 @@ declare global {
|
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
import { appendStyle, appendScript } from '@bagelink/vue'
|
|
10
|
-
import {
|
|
10
|
+
import { onMounted, ref, computed, watch } from 'vue'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const { language, readonly = false, modelValue = '', autodetect, ignoreIllegals = true, label, height = '300px' } = defineProps<{
|
|
12
|
+
interface CodeEditorProps {
|
|
14
13
|
language?: Language
|
|
15
14
|
readonly?: boolean
|
|
16
15
|
modelValue?: string
|
|
@@ -18,39 +17,50 @@ const { language, readonly = false, modelValue = '', autodetect, ignoreIllegals
|
|
|
18
17
|
ignoreIllegals?: boolean
|
|
19
18
|
label?: string
|
|
20
19
|
height?: string
|
|
21
|
-
}
|
|
20
|
+
}
|
|
21
|
+
// Props with default values
|
|
22
|
+
const props = withDefaults(defineProps<CodeEditorProps>(), {
|
|
23
|
+
language: 'html',
|
|
24
|
+
readonly: false,
|
|
25
|
+
modelValue: '',
|
|
26
|
+
autodetect: true,
|
|
27
|
+
ignoreIllegals: true,
|
|
28
|
+
label: '',
|
|
29
|
+
height: '240px'
|
|
30
|
+
})
|
|
22
31
|
|
|
23
32
|
const emit = defineEmits(['update:modelValue'])
|
|
33
|
+
// State
|
|
34
|
+
const code = ref(props.modelValue || '')
|
|
35
|
+
const editorRef = ref<HTMLDivElement>()
|
|
36
|
+
const loaded = ref(false)
|
|
37
|
+
const hljs = ref<HilightJS | null>(null)
|
|
38
|
+
// Computed
|
|
39
|
+
const maxHeight = computed(() => {
|
|
40
|
+
const h = props.height ?? '240px'
|
|
41
|
+
return h.match(/^\d+$/) ? `${h}px` : h
|
|
42
|
+
})
|
|
24
43
|
|
|
25
|
-
|
|
44
|
+
const formattedCode = computed(() => {
|
|
45
|
+
if (!hljs.value) return escapeHtml(code.value)
|
|
26
46
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let code = $ref('')
|
|
30
|
-
const textarea = $ref<HTMLTextAreaElement>()
|
|
31
|
-
let loaded = $ref(false)
|
|
47
|
+
try {
|
|
48
|
+
const lang = props.language || ''
|
|
32
49
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
})
|
|
50
|
+
if (lang && !props.autodetect && !hljs.value.getLanguage(lang)) {
|
|
51
|
+
console.warn(`The language "${lang}" is not available.`)
|
|
52
|
+
return escapeHtml(code.value)
|
|
53
|
+
}
|
|
38
54
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
55
|
+
const result = props.autodetect
|
|
56
|
+
? hljs.value.highlightAuto(code.value)
|
|
57
|
+
: hljs.value.highlight(code.value, { language: lang, ignoreIllegals: props.ignoreIllegals })
|
|
43
58
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.
|
|
47
|
-
return escapeHtml(code)
|
|
59
|
+
return result.value || escapeHtml(code.value)
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('Highlighting error:', error)
|
|
62
|
+
return escapeHtml(code.value)
|
|
48
63
|
}
|
|
49
|
-
const lang = language || ''
|
|
50
|
-
const result = autodetect
|
|
51
|
-
? hljs?.highlightAuto(code)
|
|
52
|
-
: hljs?.highlight(code, { language: lang, ignoreIllegals })
|
|
53
|
-
return result?.value || ''
|
|
54
64
|
})
|
|
55
65
|
|
|
56
66
|
// Methods
|
|
@@ -67,113 +77,165 @@ function escapeHtml(unsafe: string) {
|
|
|
67
77
|
})
|
|
68
78
|
}
|
|
69
79
|
|
|
80
|
+
function handleInput(e: Event) {
|
|
81
|
+
const target = e.target as HTMLTextAreaElement
|
|
82
|
+
code.value = target.value
|
|
83
|
+
emit('update:modelValue', code.value)
|
|
84
|
+
}
|
|
85
|
+
|
|
70
86
|
function handleTab(event: KeyboardEvent) {
|
|
87
|
+
if (event.key !== 'Tab') return
|
|
88
|
+
|
|
89
|
+
event.preventDefault()
|
|
71
90
|
const target = event.target as HTMLTextAreaElement
|
|
72
91
|
const start = target.selectionStart
|
|
73
|
-
const
|
|
74
|
-
code = code.slice(0, start) + tab + code.slice(start)
|
|
75
|
-
nextTick(() => {
|
|
76
|
-
target.selectionStart = target.selectionEnd = start + tab.length
|
|
77
|
-
})
|
|
78
|
-
}
|
|
92
|
+
const end = target.selectionEnd
|
|
79
93
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
// Add tab or indent selected text
|
|
95
|
+
const newValue = `${code.value.substring(0, start)} ${code.value.substring(end)}`
|
|
96
|
+
code.value = newValue
|
|
97
|
+
emit('update:modelValue', code.value)
|
|
98
|
+
|
|
99
|
+
// Move cursor position after the inserted tab
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
target.selectionStart = target.selectionEnd = start + 2
|
|
102
|
+
}, 0)
|
|
84
103
|
}
|
|
85
104
|
|
|
86
105
|
// Lifecycle
|
|
87
106
|
onMounted(async () => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
try {
|
|
108
|
+
// Load highlight.js
|
|
109
|
+
await appendScript('https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/highlight.min.js', { id: 'hljs-cdn' })
|
|
110
|
+
await appendStyle('https://cdn.jsdelivr.net/npm/highlight.js/styles/atom-one-dark.min.css')
|
|
111
|
+
|
|
112
|
+
if (window.hljs) {
|
|
113
|
+
hljs.value = window.hljs
|
|
114
|
+
loaded.value = true
|
|
115
|
+
} else {
|
|
116
|
+
console.error('Failed to load highlight.js')
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('Error loading highlight.js:', error)
|
|
98
120
|
}
|
|
99
121
|
})
|
|
100
122
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (newVal !== code) {
|
|
104
|
-
code = newVal
|
|
123
|
+
// Watch for external modelValue changes
|
|
124
|
+
watch(() => props.modelValue, (newVal) => {
|
|
125
|
+
if (newVal !== undefined && newVal !== code.value) {
|
|
126
|
+
code.value = newVal
|
|
105
127
|
}
|
|
106
128
|
}, { immediate: true })
|
|
107
129
|
</script>
|
|
108
130
|
|
|
109
131
|
<template>
|
|
110
|
-
<div class="
|
|
111
|
-
<label v-if="label" class="label
|
|
112
|
-
<div
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
<div class="code-editor-container ltr" :style="{ maxHeight }">
|
|
133
|
+
<label v-if="label" class="label">{{ label }}</label>
|
|
134
|
+
<div
|
|
135
|
+
v-if="loaded"
|
|
136
|
+
ref="editorRef"
|
|
137
|
+
class="code-editor-grandpa"
|
|
138
|
+
>
|
|
139
|
+
<div class="editor-content-papa relative">
|
|
140
|
+
<pre class="code-display" wrap><code v-html="formattedCode" /></pre>
|
|
117
141
|
<textarea
|
|
118
142
|
v-if="!readonly"
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
class="code-editor absolute inset-0 bg-transparent overflow-hidden h-100 p-0 m-0 codeText border-none txt-start"
|
|
143
|
+
:value="code"
|
|
144
|
+
class="code-input"
|
|
122
145
|
spellcheck="false"
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@
|
|
127
|
-
@
|
|
146
|
+
autocomplete="off"
|
|
147
|
+
autocorrect="off"
|
|
148
|
+
autocapitalize="off"
|
|
149
|
+
@input="handleInput"
|
|
150
|
+
@keydown="handleTab"
|
|
128
151
|
/>
|
|
129
152
|
</div>
|
|
130
153
|
</div>
|
|
131
154
|
</div>
|
|
132
155
|
</template>
|
|
133
156
|
|
|
134
|
-
<style>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
position: absolute;
|
|
157
|
+
<style scoped>
|
|
158
|
+
.code-editor-container {
|
|
159
|
+
margin-bottom: 0.5rem;
|
|
160
|
+
height: 100%;
|
|
139
161
|
}
|
|
140
|
-
</style>
|
|
141
162
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
word-wrap: break-word;
|
|
147
|
-
caret-color: var(--bgl-white);
|
|
148
|
-
color: var(--bgl-white);
|
|
163
|
+
.label {
|
|
164
|
+
display: block;
|
|
165
|
+
text-align: left;
|
|
166
|
+
margin-bottom: 0.25rem;
|
|
149
167
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
168
|
+
|
|
169
|
+
.code-editor-grandpa {
|
|
170
|
+
background: #22252A;
|
|
171
|
+
border-radius: 0.25rem;
|
|
172
|
+
width: 100%;
|
|
173
|
+
height: 100%;
|
|
174
|
+
overflow: auto;
|
|
175
|
+
padding: 1ch;
|
|
176
|
+
padding-inline-start: 2ch;
|
|
153
177
|
}
|
|
154
|
-
.code-editor-wrap:focus-within, .code-editor-wrap:focus-visible, .code-editor-wrap:focus {
|
|
155
|
-
box-shadow: inset 0 0 10px #00000021;
|
|
156
|
-
outline: solid 1px var(--border-color);
|
|
157
|
-
/* outline: -webkit-focus-ring-color auto 1px; */
|
|
158
178
|
|
|
179
|
+
.code-editor-grandpa:focus-within {
|
|
180
|
+
outline: solid 1px var(--border-color, #4f575f);
|
|
181
|
+
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.13);
|
|
159
182
|
}
|
|
160
183
|
|
|
161
|
-
.
|
|
162
|
-
|
|
163
|
-
|
|
184
|
+
.editor-content-papa {
|
|
185
|
+
position: relative;
|
|
186
|
+
width: 100%;
|
|
187
|
+
padding-bottom: calc(100% - 5lh);
|
|
188
|
+
}
|
|
189
|
+
.code-display,
|
|
190
|
+
.code-input {
|
|
191
|
+
inset: 0;
|
|
192
|
+
margin: 0;
|
|
193
|
+
padding: 0;
|
|
194
|
+
width: 100%;
|
|
195
|
+
height: 100%;
|
|
196
|
+
overflow: auto;
|
|
197
|
+
font-family: monospace;
|
|
198
|
+
font-size: 1em;
|
|
199
|
+
line-height: 1.5;
|
|
200
|
+
tab-size: 2;
|
|
201
|
+
word-break: keep-all;
|
|
202
|
+
text-align: left;
|
|
164
203
|
}
|
|
165
204
|
|
|
166
|
-
.code-
|
|
167
|
-
|
|
168
|
-
|
|
205
|
+
.code-display {
|
|
206
|
+
position: relative;
|
|
207
|
+
color: #fff;
|
|
208
|
+
pointer-events: none;
|
|
209
|
+
z-index: 1;
|
|
169
210
|
}
|
|
170
211
|
|
|
171
|
-
.code-
|
|
172
|
-
|
|
173
|
-
|
|
212
|
+
.code-display code {
|
|
213
|
+
display: block;
|
|
214
|
+
background: transparent !important;
|
|
215
|
+
padding: 0 !important;
|
|
174
216
|
}
|
|
175
217
|
|
|
176
|
-
.code-
|
|
177
|
-
|
|
218
|
+
.code-input {
|
|
219
|
+
position: absolute;
|
|
220
|
+
background: transparent;
|
|
221
|
+
color: transparent;
|
|
222
|
+
caret-color: #fff;
|
|
223
|
+
border: none;
|
|
224
|
+
resize: none;
|
|
225
|
+
outline: none;
|
|
226
|
+
z-index: 2;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.code-input::selection {
|
|
230
|
+
background-color: rgba(36, 102, 188, 0.3);
|
|
231
|
+
color: transparent;
|
|
232
|
+
}
|
|
233
|
+
</style>
|
|
234
|
+
|
|
235
|
+
<style>
|
|
236
|
+
/* Global styles */
|
|
237
|
+
pre code.hljs {
|
|
238
|
+
padding: 0 !important;
|
|
239
|
+
background: transparent !important;
|
|
178
240
|
}
|
|
179
241
|
</style>
|
|
@@ -15,6 +15,9 @@ const editor = useEditor()
|
|
|
15
15
|
const isInitializing = ref(false)
|
|
16
16
|
const hasInitialized = ref(false)
|
|
17
17
|
|
|
18
|
+
// Initialize content from modelValue
|
|
19
|
+
editor.state.content = props.modelValue
|
|
20
|
+
|
|
18
21
|
// Initialize debugger if debug mode is enabled
|
|
19
22
|
if (props.debug) {
|
|
20
23
|
editor.initDebugger()
|
|
@@ -83,6 +86,9 @@ async function initEditor() {
|
|
|
83
86
|
// Set default direction based on content
|
|
84
87
|
doc.body.dir = hasRTL ? 'rtl' : 'ltr'
|
|
85
88
|
|
|
89
|
+
// Ensure editor.state.content is set to the current HTML content
|
|
90
|
+
editor.state.content = doc.body.innerHTML
|
|
91
|
+
|
|
86
92
|
editor.init(doc)
|
|
87
93
|
useEditorKeyboard(doc, commands)
|
|
88
94
|
|
|
@@ -92,6 +98,8 @@ async function initEditor() {
|
|
|
92
98
|
p.dir = doc.body.dir
|
|
93
99
|
p.innerHTML = '<br>'
|
|
94
100
|
doc.body.appendChild(p)
|
|
101
|
+
// Update state.content after changes
|
|
102
|
+
editor.state.content = doc.body.innerHTML
|
|
95
103
|
} else {
|
|
96
104
|
// Convert any direct text nodes to paragraphs
|
|
97
105
|
const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT)
|
|
@@ -112,6 +120,8 @@ async function initEditor() {
|
|
|
112
120
|
doc.body.removeChild(textNode)
|
|
113
121
|
}
|
|
114
122
|
})
|
|
123
|
+
// Update state.content after cleanup
|
|
124
|
+
editor.state.content = doc.body.innerHTML
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
doc.body.focus()
|
|
@@ -137,7 +147,7 @@ watch(() => props.modelValue, (newValue, oldValue) => {
|
|
|
137
147
|
})
|
|
138
148
|
|
|
139
149
|
watch(() => editor.state.content, (newValue) => {
|
|
140
|
-
if (editor.state.doc?.body
|
|
150
|
+
if (editor.state.doc?.body) {
|
|
141
151
|
editor.state.doc.body.dir = hasRTL ? 'rtl' : 'ltr'
|
|
142
152
|
}
|
|
143
153
|
emit('update:modelValue', newValue)
|
|
@@ -168,7 +178,7 @@ watch(() => editor.state.content, (newValue) => {
|
|
|
168
178
|
v-if="editor.state.isSplitView"
|
|
169
179
|
v-model="editor.state.content"
|
|
170
180
|
language="html"
|
|
171
|
-
|
|
181
|
+
:height="editor.state.isFullscreen ? 'calc(100vh - 4rem)' : '250px'"
|
|
172
182
|
@update:modelValue="editor.updateState.content('html')"
|
|
173
183
|
/>
|
|
174
184
|
</div>
|
|
@@ -196,12 +206,6 @@ watch(() => editor.state.content, (newValue) => {
|
|
|
196
206
|
.content-area li{
|
|
197
207
|
line-height: 1.65;
|
|
198
208
|
}
|
|
199
|
-
|
|
200
|
-
.code-editor {
|
|
201
|
-
flex: 1;
|
|
202
|
-
min-height: 240px !important;
|
|
203
|
-
height: 100%;
|
|
204
|
-
}
|
|
205
209
|
</style>
|
|
206
210
|
|
|
207
211
|
<style scoped>
|
|
@@ -213,7 +217,7 @@ watch(() => editor.state.content, (newValue) => {
|
|
|
213
217
|
|
|
214
218
|
.editor-container {
|
|
215
219
|
display: flex;
|
|
216
|
-
gap:
|
|
220
|
+
gap: 0.5rem;
|
|
217
221
|
}
|
|
218
222
|
|
|
219
223
|
.content-area,
|
|
@@ -274,9 +278,6 @@ watch(() => editor.state.content, (newValue) => {
|
|
|
274
278
|
height: 100%;
|
|
275
279
|
overflow-y: auto;
|
|
276
280
|
}
|
|
277
|
-
.fullscreen-mode .code-editor{
|
|
278
|
-
height: 100% !important;
|
|
279
|
-
}
|
|
280
281
|
|
|
281
282
|
.debug-controls {
|
|
282
283
|
display: flex;
|
package/src/composables/index.ts
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BglFormSchemaT, IfAny } from '@bagelink/vue'
|
|
2
2
|
|
|
3
|
-
import type { Ref, UnwrapRef } from 'vue'
|
|
3
|
+
import type { MaybeRefOrGetter, Ref, UnwrapRef } from 'vue'
|
|
4
4
|
import { getFallbackSchema } from '@bagelink/vue'
|
|
5
|
-
import { ref, watch } from 'vue'
|
|
5
|
+
import { ref, toValue, watch } from 'vue'
|
|
6
6
|
|
|
7
7
|
export { useDevice } from './useDevice'
|
|
8
8
|
export { usePolling } from './usePolling'
|
|
9
9
|
export { useValidateFieldValue } from './useValidateFieldValue'
|
|
10
10
|
interface UseBglSchemaParamsT<T> {
|
|
11
|
-
schema?:
|
|
12
|
-
columns?: string[]
|
|
13
|
-
data?:
|
|
11
|
+
schema?: MaybeRefOrGetter<BglFormSchemaT<T>>
|
|
12
|
+
columns?: MaybeRefOrGetter<string[]>
|
|
13
|
+
data?: T[]
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function useBglSchema<T = { [key: string]: unknown }>(
|
|
17
17
|
{ schema, columns, data }: UseBglSchemaParamsT<T> = {}
|
|
18
18
|
): BglFormSchemaT<T> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
19
|
+
const _schema = $computed(() => toValue(schema))
|
|
20
|
+
const _columns = $computed(() => toValue(columns))
|
|
21
|
+
|
|
23
22
|
if (_schema) {
|
|
24
23
|
return (
|
|
25
|
-
|
|
26
|
-
? _schema.filter(f =>
|
|
24
|
+
_columns && _columns.length > 0
|
|
25
|
+
? _schema.filter(f => _columns.includes(f.id as string))
|
|
27
26
|
: _schema
|
|
28
27
|
) as BglFormSchemaT<T>
|
|
29
28
|
}
|
|
30
|
-
return getFallbackSchema(data,
|
|
29
|
+
return getFallbackSchema(data, _columns)
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
export function localRef<T>(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { VNode } from 'vue'
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import type { Field, Attributes, Path, SchemaChild, BaseBagelField, VNodeFn } from '../types/BagelForm'
|
|
3
4
|
import {
|
|
4
5
|
TextInput,
|
|
5
6
|
NumberInput,
|
|
@@ -20,26 +21,20 @@ import {
|
|
|
20
21
|
RangeInput,
|
|
21
22
|
EmailInput
|
|
22
23
|
} from '@bagelink/vue'
|
|
23
|
-
|
|
24
24
|
import { h, isVNode } from 'vue'
|
|
25
25
|
|
|
26
26
|
const SLOT_VALUE_COMPONENTS = new Set(['div', 'span', 'p'])
|
|
27
27
|
|
|
28
28
|
const SRC_VALUE_COMPONENTS = new Set(['img', 'iframe'])
|
|
29
29
|
|
|
30
|
-
export interface UseSchemaFieldOptions<T
|
|
30
|
+
export interface UseSchemaFieldOptions<T, SFP extends Path<T>> {
|
|
31
31
|
mode?: 'form' | 'preview' | 'table'
|
|
32
32
|
getFormData?: () => T
|
|
33
|
-
onUpdateModelValue?: (field:
|
|
33
|
+
onUpdateModelValue?: (field: BaseBagelField<T, SFP>, value: any) => void
|
|
34
34
|
includeUnset?: boolean
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
interface SlotProps<T> { row?: T, field: Field<T> }
|
|
39
|
-
type SlotFunction<T> = (props: SlotProps<T>) => any
|
|
40
|
-
type SupportedSlot<T> = BglFormSchemaT<T> | VNode | SlotFunction<T>
|
|
41
|
-
|
|
42
|
-
export function useSchemaField<T extends { [key: string]: any }, _P>(optns: UseSchemaFieldOptions<T>) {
|
|
37
|
+
export function useSchemaField<T extends { [key: string]: any }, SP extends Path<T>>(optns: UseSchemaFieldOptions<T, SP>) {
|
|
43
38
|
const { mode = 'form', getFormData, onUpdateModelValue, includeUnset = false } = optns
|
|
44
39
|
|
|
45
40
|
// Helper function to render objects recursively
|
|
@@ -101,21 +96,21 @@ export function useSchemaField<T extends { [key: string]: any }, _P>(optns: UseS
|
|
|
101
96
|
return typeof field.$el === 'object' ? field.$el : componentMap[field.$el as keyof typeof componentMap] ?? field.$el ?? 'div'
|
|
102
97
|
}
|
|
103
98
|
|
|
104
|
-
function renderChild(child:
|
|
105
|
-
if (typeof child === 'string') return child
|
|
99
|
+
function renderChild(child: SchemaChild<T, SP>, slots?: BaseBagelField<T, SP>['slots']) {
|
|
100
|
+
if (typeof child === 'string') return h(child)
|
|
106
101
|
if (isVNode(child)) return child
|
|
107
102
|
return renderField(
|
|
108
|
-
child,
|
|
103
|
+
child as BaseBagelField<T, SP>,
|
|
109
104
|
slots
|
|
110
105
|
)
|
|
111
106
|
}
|
|
112
107
|
|
|
113
108
|
function renderField(
|
|
114
|
-
field:
|
|
115
|
-
slots?:
|
|
116
|
-
): VNode |
|
|
117
|
-
const Component = getComponent(field)
|
|
118
|
-
if (!Component) return
|
|
109
|
+
field: BaseBagelField<T, SP>,
|
|
110
|
+
slots?: BaseBagelField<T, SP>['slots']
|
|
111
|
+
): VNode | undefined {
|
|
112
|
+
const Component = getComponent(field as Field<T>)
|
|
113
|
+
if (!Component) return
|
|
119
114
|
|
|
120
115
|
const rowData = (getFormData?.() || {}) as T | undefined
|
|
121
116
|
|
|
@@ -129,14 +124,14 @@ export function useSchemaField<T extends { [key: string]: any }, _P>(optns: UseS
|
|
|
129
124
|
rowData
|
|
130
125
|
)
|
|
131
126
|
) {
|
|
132
|
-
return
|
|
127
|
+
return
|
|
133
128
|
}
|
|
134
129
|
} else if (typeof condition === 'string') {
|
|
135
130
|
if (!rowData?.[condition]) {
|
|
136
|
-
return
|
|
131
|
+
return
|
|
137
132
|
}
|
|
138
133
|
} else if (!condition) {
|
|
139
|
-
return
|
|
134
|
+
return
|
|
140
135
|
}
|
|
141
136
|
}
|
|
142
137
|
|
|
@@ -234,7 +229,8 @@ export function useSchemaField<T extends { [key: string]: any }, _P>(optns: UseS
|
|
|
234
229
|
props.class = classify(currentValue, rowData, fieldClass, props.class)
|
|
235
230
|
|
|
236
231
|
// Handle component slots with vIf aware child rendering
|
|
237
|
-
const componentSlots: { [
|
|
232
|
+
// const componentSlots: { [name: string]: Slot } = {}
|
|
233
|
+
const componentSlots: Parameters<typeof h>[1] = {}
|
|
238
234
|
|
|
239
235
|
// Add default slot if there are children
|
|
240
236
|
if (children?.length) {
|
|
@@ -254,29 +250,35 @@ export function useSchemaField<T extends { [key: string]: any }, _P>(optns: UseS
|
|
|
254
250
|
componentSlots[name] = () => {
|
|
255
251
|
if (Array.isArray(slot)) {
|
|
256
252
|
// Handle BglFormSchemaT array
|
|
257
|
-
return slot.map(schemaField =>
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
253
|
+
return slot.map((schemaField) => {
|
|
254
|
+
if (typeof schemaField === 'function') {
|
|
255
|
+
// Handle function slot
|
|
256
|
+
const slotFn = schemaField
|
|
257
|
+
return slotFn({ row: rowData, field })
|
|
258
|
+
}
|
|
259
|
+
if (isVNode(schemaField)) {
|
|
260
|
+
// Handle VNode
|
|
261
|
+
return schemaField
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return renderField(schemaField as BaseBagelField<T, SP>, slots)
|
|
265
|
+
})
|
|
265
266
|
}
|
|
266
|
-
return slot
|
|
267
267
|
}
|
|
268
268
|
})
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
// Handle custom slot content from parent
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
|
|
273
|
+
const slotContent = field.id ? (slots?.[field.id] as VNodeFn<T, SP> | undefined)?.({ row: rowData, field }) : undefined
|
|
274
|
+
// field.id && slots?.[field.id] && typeof slots[field.id] === 'function' && !Array.isArray(slots?.[field.id])
|
|
275
|
+
// ? (slots[field.id])({ row: rowData, field })
|
|
276
|
+
// : undefined
|
|
275
277
|
|
|
276
278
|
if (mode === 'preview') {
|
|
277
279
|
// Skip rendering if value is unset and includeUnset is false
|
|
278
280
|
if (!includeUnset && (transformedValue === undefined || transformedValue === null || transformedValue.length === 0)) {
|
|
279
|
-
return
|
|
281
|
+
return
|
|
280
282
|
}
|
|
281
283
|
|
|
282
284
|
return h('div', { class: 'preview-field' }, [
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BglFormSchemaT, BtnOptions, ThemeType } from '@bagelink/vue'
|
|
2
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
2
3
|
|
|
3
4
|
export interface ModalOptions {
|
|
4
5
|
title?: string
|
|
@@ -37,7 +38,7 @@ export interface ModalFormComponentProps<T extends { [key: string]: any }> exten
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface ModalFormOptions<T extends { [key: string]: any }> {
|
|
40
|
-
'schema':
|
|
41
|
+
'schema': MaybeRefOrGetter<BglFormSchemaT<T>>
|
|
41
42
|
'modelValue'?: T
|
|
42
43
|
'onUpdate:modelValue'?: (val: T) => void
|
|
43
44
|
'onSubmit'?: (formData: T) => any
|