@koumoul/vjsf 3.0.0-alpha.2 → 3.0.0-alpha.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/package.json +2 -2
- package/src/compat/v2.js +1 -4
- package/src/compile/v-jsf-compiled.vue.ejs +1 -1
- package/src/components/fragments/help-message.vue +1 -1
- package/src/components/node.vue +15 -2
- package/src/components/nodes/autocomplete.vue +0 -6
- package/src/components/nodes/expansion-panels.vue +1 -1
- package/src/components/nodes/list.vue +11 -9
- package/src/components/nodes/markdown.vue +127 -19
- package/src/components/nodes/one-of-select.vue +1 -1
- package/src/components/nodes/section.vue +1 -0
- package/src/components/nodes/stepper.vue +1 -1
- package/src/components/nodes/tabs.vue +1 -1
- package/src/components/nodes/text-field.vue +6 -0
- package/src/components/nodes/textarea.vue +23 -2
- package/src/components/nodes/vertical-tabs.vue +1 -1
- package/src/components/vjsf.vue +1 -1
- package/src/composables/use-dnd.js +15 -0
- package/src/composables/use-vjsf.js +10 -5
- package/types/compat/v2.d.ts.map +1 -1
- package/types/components/nodes/markdown.vue.d.ts.map +1 -1
- package/types/components/vjsf.vue.d.ts +2 -2
- package/types/composables/use-vjsf.d.ts +1 -1
- package/types/composables/use-vjsf.d.ts.map +1 -1
- package/src/utils/clone.js +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koumoul/vjsf",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.4",
|
|
4
4
|
"description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "vitest",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"vuetify": "^3.4.2"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@json-layout/core": "0.
|
|
55
|
+
"@json-layout/core": "0.5.0",
|
|
56
56
|
"@vueuse/core": "^10.5.0",
|
|
57
57
|
"debug": "^4.3.4",
|
|
58
58
|
"easymde": "^2.18.0",
|
package/src/compat/v2.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import ajvModule from 'ajv'
|
|
2
|
-
import rfdc from 'rfdc'
|
|
3
2
|
import addFormats from 'ajv-formats'
|
|
4
|
-
import { resolveRefs } from '@json-layout/core'
|
|
3
|
+
import { resolveRefs, clone } from '@json-layout/core'
|
|
5
4
|
import { isPartialGetItemsObj } from '@json-layout/vocabulary'
|
|
6
5
|
|
|
7
6
|
// @ts-ignore
|
|
8
7
|
const Ajv = /** @type {typeof ajvModule.default} */ (ajvModule)
|
|
9
8
|
|
|
10
|
-
const clone = rfdc()
|
|
11
|
-
|
|
12
9
|
const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
|
|
13
10
|
if (!schema.layout) {
|
|
14
11
|
/** @type import('@json-layout/vocabulary').PartialCompObject */
|
package/src/components/node.vue
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { useTheme } from 'vuetify'
|
|
2
4
|
import { VCol } from 'vuetify/components'
|
|
3
5
|
import NodeSlot from './fragments/node-slot.vue'
|
|
4
6
|
import HelpMessage from './fragments/help-message.vue'
|
|
5
7
|
|
|
6
|
-
defineProps({
|
|
8
|
+
const props = defineProps({
|
|
7
9
|
modelValue: {
|
|
8
10
|
/** @type import('vue').PropType<import('./types.js').VjsfNode> */
|
|
9
11
|
type: Object,
|
|
@@ -22,12 +24,23 @@ const beforeAfterClasses = {
|
|
|
22
24
|
comfortable: 'my-2',
|
|
23
25
|
default: 'my-3'
|
|
24
26
|
}
|
|
27
|
+
|
|
28
|
+
const theme = useTheme()
|
|
29
|
+
|
|
30
|
+
const nodeClasses = computed(() => {
|
|
31
|
+
let classes = `vjsf-node vjsf-node-${props.modelValue.layout.comp} vjsf-density-${props.modelValue.options.density}`
|
|
32
|
+
if (props.modelValue.options.readOnly) classes += ' vjsf-readonly'
|
|
33
|
+
if (props.modelValue.options.summary) classes += ' vjsf-summary'
|
|
34
|
+
if (theme.current.value.dark) classes += ' vjsf-dark'
|
|
35
|
+
return classes
|
|
36
|
+
})
|
|
37
|
+
|
|
25
38
|
</script>
|
|
26
39
|
|
|
27
40
|
<template>
|
|
28
41
|
<v-col
|
|
29
42
|
:cols="modelValue.cols"
|
|
30
|
-
:class="
|
|
43
|
+
:class="nodeClasses"
|
|
31
44
|
>
|
|
32
45
|
<node-slot
|
|
33
46
|
v-if="modelValue.layout.slots?.before"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VContainer } from 'vuetify/components'
|
|
2
|
+
import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VContainer, VRow, VIcon } from 'vuetify/components'
|
|
3
3
|
import { isSection } from '@json-layout/core'
|
|
4
4
|
import Node from '../node.vue'
|
|
5
5
|
import SectionHeader from '../fragments/section-header.vue'
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { watch, computed, ref } from 'vue'
|
|
3
|
-
import { VList, VListItem, VListItemAction, VBtn, VMenu, VIcon } from 'vuetify/components'
|
|
4
|
-
import { isSection } from '@json-layout/core'
|
|
3
|
+
import { VList, VListItem, VListItemAction, VBtn, VMenu, VIcon, VSheet, VSpacer, VDivider, VRow, VListSubheader } from 'vuetify/components'
|
|
4
|
+
import { isSection, clone } from '@json-layout/core'
|
|
5
5
|
import Node from '../node.vue'
|
|
6
|
-
import clone from '../../utils/clone.js'
|
|
7
6
|
import { moveArrayItem } from '../../utils/arrays.js'
|
|
8
7
|
import useDnd from '../../composables/use-dnd.js'
|
|
9
8
|
|
|
@@ -21,7 +20,7 @@ const props = defineProps({
|
|
|
21
20
|
})
|
|
22
21
|
|
|
23
22
|
/* use composable for drag and drop */
|
|
24
|
-
const { activeDnd, sortableArray, draggable, itemBind, handleBind } = useDnd(props.modelValue.children, () => {
|
|
23
|
+
const { activeDnd, sortableArray, draggable, hovered, dragging, itemBind, handleBind } = useDnd(props.modelValue.children, () => {
|
|
25
24
|
props.statefulLayout.input(props.modelValue, sortableArray.value.map((child) => child.data))
|
|
26
25
|
})
|
|
27
26
|
watch(() => props.modelValue.children, (array) => { sortableArray.value = array })
|
|
@@ -30,7 +29,7 @@ watch(() => props.modelValue.children, (array) => { sortableArray.value = array
|
|
|
30
29
|
const editedItem = computed(() => {
|
|
31
30
|
return props.statefulLayout.activeItems[props.modelValue.fullKey]
|
|
32
31
|
})
|
|
33
|
-
const
|
|
32
|
+
const menuOpened = ref(-1)
|
|
34
33
|
const activeItem = computed(() => {
|
|
35
34
|
if (
|
|
36
35
|
props.modelValue.layout.listActions.includes('edit') &&
|
|
@@ -39,7 +38,9 @@ const activeItem = computed(() => {
|
|
|
39
38
|
) {
|
|
40
39
|
return editedItem.value
|
|
41
40
|
}
|
|
42
|
-
|
|
41
|
+
if (dragging.value !== -1) return -1
|
|
42
|
+
if (menuOpened.value !== -1) return menuOpened.value
|
|
43
|
+
return hovered.value
|
|
43
44
|
})
|
|
44
45
|
|
|
45
46
|
const buttonDensity = computed(() => {
|
|
@@ -65,8 +66,6 @@ const buttonDensity = computed(() => {
|
|
|
65
66
|
:draggable="draggable === childIndex"
|
|
66
67
|
:variant="editedItem === childIndex ? 'outlined' : 'flat'"
|
|
67
68
|
class="pa-1 vjsf-list-item"
|
|
68
|
-
@mouseenter="hoveredItem = childIndex"
|
|
69
|
-
@mouseleave="hoveredItem = -1"
|
|
70
69
|
>
|
|
71
70
|
<v-row class="ma-0">
|
|
72
71
|
<node
|
|
@@ -117,7 +116,10 @@ const buttonDensity = computed(() => {
|
|
|
117
116
|
<v-list-item-action
|
|
118
117
|
v-if="editedItem === undefined && (modelValue.layout.listActions.includes('delete') || modelValue.layout.listActions.includes('duplicate') || modelValue.layout.listActions.includes('sort'))"
|
|
119
118
|
>
|
|
120
|
-
<v-menu
|
|
119
|
+
<v-menu
|
|
120
|
+
location="bottom end"
|
|
121
|
+
@update:model-value="value => {menuOpened = value ? childIndex : -1}"
|
|
122
|
+
>
|
|
121
123
|
<template #activator="{props: activatorProps}">
|
|
122
124
|
<v-btn
|
|
123
125
|
v-bind="activatorProps"
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { defineComponent, h, computed, onMounted, ref, onUnmounted, watch } from 'vue'
|
|
3
|
+
import { useTheme } from 'vuetify'
|
|
3
4
|
import { VInput, VLabel } from 'vuetify/components'
|
|
5
|
+
import { marked } from 'marked'
|
|
4
6
|
import { getInputProps } from '../../utils/props.js'
|
|
5
7
|
import { getCompSlots } from '../../utils/slots.js'
|
|
6
|
-
|
|
7
|
-
// import EasyMDE from 'easymde'
|
|
8
|
+
import 'easymde/dist/easymde.min.css'
|
|
8
9
|
|
|
9
10
|
export default defineComponent({
|
|
10
11
|
props: {
|
|
@@ -19,30 +20,44 @@ export default defineComponent({
|
|
|
19
20
|
required: true
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
|
-
setup (props) {
|
|
23
|
+
setup (props, { expose }) {
|
|
23
24
|
/** @type {import('vue').Ref<null | HTMLElement>} */
|
|
24
25
|
const element = ref(null)
|
|
25
26
|
|
|
27
|
+
const renderedValue = computed(() => {
|
|
28
|
+
return props.modelValue.data && marked.parse(props.modelValue.data)
|
|
29
|
+
})
|
|
30
|
+
|
|
26
31
|
const fieldProps = computed(() => getInputProps(props.modelValue, props.statefulLayout))
|
|
27
32
|
const fieldSlots = computed(() => {
|
|
28
33
|
const fieldSlots = getCompSlots(props.modelValue, props.statefulLayout)
|
|
29
|
-
fieldSlots.default = () =>
|
|
30
|
-
|
|
34
|
+
fieldSlots.default = () => {
|
|
35
|
+
const children = [
|
|
31
36
|
h(VLabel, { text: fieldProps.value.label }),
|
|
32
37
|
h('textarea', { ref: element })
|
|
33
|
-
]
|
|
34
|
-
|
|
38
|
+
]
|
|
39
|
+
if (props.modelValue.options.summary) {
|
|
40
|
+
children.push(h('div', { innerHTML: renderedValue.value }))
|
|
41
|
+
}
|
|
42
|
+
return h('div', { class: 'vjsf-node-markdown-content' }, children)
|
|
43
|
+
}
|
|
35
44
|
return fieldSlots
|
|
36
45
|
})
|
|
37
46
|
|
|
47
|
+
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
48
|
+
let blurTimeout = null
|
|
49
|
+
|
|
38
50
|
/** @type {EasyMDE | null} */
|
|
39
51
|
let easymde = null
|
|
40
52
|
|
|
41
53
|
const initEasyMDE = async () => {
|
|
54
|
+
if (easymde) {
|
|
55
|
+
easymde.toTextArea()
|
|
56
|
+
easymde = null
|
|
57
|
+
}
|
|
58
|
+
if (props.modelValue.options.readOnly) return
|
|
42
59
|
if (!element.value) throw new Error('component was not mounted for markdown editor')
|
|
43
60
|
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
await import('easymde/dist/easymde.min.css')
|
|
46
61
|
const EasyMDE = (await import('easymde')).default
|
|
47
62
|
|
|
48
63
|
const messages = props.modelValue.messages
|
|
@@ -161,12 +176,9 @@ export default defineComponent({
|
|
|
161
176
|
className: 'mdi mdi-help-circle text-success',
|
|
162
177
|
title: messages.mdeGuide,
|
|
163
178
|
noDisable: true
|
|
164
|
-
}
|
|
165
|
-
],
|
|
179
|
+
}],
|
|
166
180
|
...props.modelValue.options.easyMDEOptions
|
|
167
181
|
}
|
|
168
|
-
|
|
169
|
-
if (easymde) easymde.toTextArea()
|
|
170
182
|
// @ts-ignore
|
|
171
183
|
easymde = new EasyMDE(config)
|
|
172
184
|
|
|
@@ -175,9 +187,8 @@ export default defineComponent({
|
|
|
175
187
|
changed = true
|
|
176
188
|
if (easymde) props.statefulLayout.input(props.modelValue, easymde.value())
|
|
177
189
|
})
|
|
178
|
-
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
179
|
-
let blurTimeout = null
|
|
180
190
|
easymde.codemirror.on('blur', () => {
|
|
191
|
+
console.log('onblur')
|
|
181
192
|
// timeout to prevent triggering save when clicking on a menu button
|
|
182
193
|
blurTimeout = setTimeout(() => {
|
|
183
194
|
if (changed) props.statefulLayout.blur(props.modelValue)
|
|
@@ -187,6 +198,10 @@ export default defineComponent({
|
|
|
187
198
|
easymde.codemirror.on('focus', () => {
|
|
188
199
|
if (blurTimeout) clearTimeout(blurTimeout)
|
|
189
200
|
})
|
|
201
|
+
|
|
202
|
+
if (props.modelValue.autofocus) {
|
|
203
|
+
easymde.codemirror.focus()
|
|
204
|
+
}
|
|
190
205
|
}
|
|
191
206
|
|
|
192
207
|
onMounted(initEasyMDE)
|
|
@@ -203,24 +218,103 @@ export default defineComponent({
|
|
|
203
218
|
})
|
|
204
219
|
|
|
205
220
|
// update easymde config from outside
|
|
206
|
-
watch(() => [props.modelValue.messages, props.modelValue.options.easyMDEOptions], () => {
|
|
207
|
-
|
|
221
|
+
watch(() => [props.modelValue.options.readOnly, props.modelValue.messages, props.modelValue.options.easyMDEOptions], (newValues, oldValues) => {
|
|
222
|
+
if (newValues[0] !== oldValues[0] || newValues[1] !== oldValues[1] || newValues[2] !== oldValues[2]) {
|
|
223
|
+
initEasyMDE()
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
props.statefulLayout.events.on('autofocus', () => {
|
|
228
|
+
console.log('focus code mirror ?')
|
|
229
|
+
if (props.modelValue.autofocus && easymde) {
|
|
230
|
+
easymde.codemirror.focus()
|
|
231
|
+
}
|
|
208
232
|
})
|
|
209
233
|
|
|
210
|
-
|
|
234
|
+
const theme = useTheme()
|
|
235
|
+
const darkStyle = computed(() => getDarkStyle(theme))
|
|
236
|
+
|
|
237
|
+
return () => [
|
|
238
|
+
h('style', { innerHTML: darkStyle.value }),
|
|
239
|
+
h(VInput, fieldProps.value, fieldSlots.value)
|
|
240
|
+
]
|
|
211
241
|
}
|
|
212
242
|
})
|
|
213
243
|
|
|
244
|
+
const getDarkStyle = (/** @type {import('vuetify').ThemeInstance} */theme) => {
|
|
245
|
+
// Inspired by https://github.com/Ionaru/easy-markdown-editor/issues/131#issuecomment-1738202589
|
|
246
|
+
return `
|
|
247
|
+
.vjsf-node-markdown.vjsf-dark .EasyMDEContainer .CodeMirror {
|
|
248
|
+
color: white;
|
|
249
|
+
border-color: ${theme.current.value.variables['border-color']};
|
|
250
|
+
background-color: ${theme.current.value.colors.surface};
|
|
251
|
+
}
|
|
252
|
+
.vjsf-node-markdown.vjsf-dark .EasyMDEContainer .cm-s-easymde .CodeMirror-cursor {
|
|
253
|
+
border-color: white;
|
|
254
|
+
}
|
|
255
|
+
.vjsf-node-markdown.vjsf-dark .CodeMirror-cursor {
|
|
256
|
+
border-left:1px solid white;
|
|
257
|
+
border-right:none;width:0;
|
|
258
|
+
}
|
|
259
|
+
.vjsf-node-markdown.vjsf-dark .EasyMDEContainer .editor-toolbar > * {
|
|
260
|
+
border-color: ${theme.current.value.colors.surface};
|
|
261
|
+
}
|
|
262
|
+
.vjsf-node-markdown.vjsf-dark .editor-toolbar {
|
|
263
|
+
border-top: 1px solid ${theme.current.value.variables['border-color']};
|
|
264
|
+
border-left: 1px solid ${theme.current.value.variables['border-color']};
|
|
265
|
+
border-right: 1px solid ${theme.current.value.variables['border-color']};
|
|
266
|
+
}
|
|
267
|
+
.vjsf-node-markdown.vjsf-dark .editor-toolbar i.separator {
|
|
268
|
+
border-left: 1px solid ${theme.current.value.variables['border-color']};
|
|
269
|
+
border-right: 1px solid ${theme.current.value.variables['border-color']};
|
|
270
|
+
}
|
|
271
|
+
.vjsf-node-markdown.vjsf-dark .EasyMDEContainer .editor-toolbar > .active, .editor-toolbar > button:hover, .editor-preview pre, .cm-s-easymde .cm-comment {
|
|
272
|
+
background-color: ${theme.current.value.colors.surface};
|
|
273
|
+
}
|
|
274
|
+
.vjsf-node-markdown.vjsf-dark .EasyMDEContainer .CodeMirror-fullscreen {
|
|
275
|
+
background: ${theme.current.value.colors.surface};
|
|
276
|
+
}
|
|
277
|
+
.vjsf-node-markdown.vjsf-dark .editor-toolbar.fullscreen {
|
|
278
|
+
background: ${theme.current.value.colors.surface};
|
|
279
|
+
}
|
|
280
|
+
.vjsf-node-markdown.vjsf-dark .editor-preview {
|
|
281
|
+
background: ${theme.current.value.colors.surface};
|
|
282
|
+
}
|
|
283
|
+
.vjsf-node-markdown.vjsf-dark .editor-preview-side {
|
|
284
|
+
border-color: ${theme.current.value.variables['border-color']};
|
|
285
|
+
}
|
|
286
|
+
.vjsf-node-markdown.vjsf-dark .CodeMirror-selected {
|
|
287
|
+
background: ${theme.current.value.colors.secondary};
|
|
288
|
+
}
|
|
289
|
+
.vjsf-node-markdown.vjsf-dark .CodeMirror-focused .CodeMirror-selected {
|
|
290
|
+
background: ${theme.current.value.colors.secondary};
|
|
291
|
+
}
|
|
292
|
+
.vjsf-node-markdown.vjsf-dark .CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection {
|
|
293
|
+
background:${theme.current.value.colors.secondary}
|
|
294
|
+
}
|
|
295
|
+
.vjsf-node-markdown.vjsf-dark .CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection {
|
|
296
|
+
background:${theme.current.value.colors.secondary}
|
|
297
|
+
}
|
|
298
|
+
.vjsf-node-markdown.vjsf-dark .EasyMDEContainer .CodeMirror-focused .CodeMirror-selected {
|
|
299
|
+
background: ${theme.current.value.colors.secondary}
|
|
300
|
+
}
|
|
301
|
+
`
|
|
302
|
+
}
|
|
303
|
+
|
|
214
304
|
</script>
|
|
215
305
|
|
|
216
306
|
<style>
|
|
307
|
+
.vjsf-node-markdown .vjsf-node-markdown-content {
|
|
308
|
+
width: 100%;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* adjust to density */
|
|
217
312
|
.vjsf-node-markdown .v-input--density-compact .editor-toolbar {
|
|
218
313
|
padding: 0;
|
|
219
314
|
}
|
|
220
315
|
.vjsf-node-markdown .v-input--density-comfortable .editor-toolbar {
|
|
221
316
|
padding: 4px;
|
|
222
317
|
}
|
|
223
|
-
|
|
224
318
|
.vjsf-node-markdown .v-input--density-compact .CodeMirror-wrap {
|
|
225
319
|
padding-top: 2px;
|
|
226
320
|
padding-bottom: 2px;
|
|
@@ -230,4 +324,18 @@ export default defineComponent({
|
|
|
230
324
|
padding-bottom: 6px;
|
|
231
325
|
}
|
|
232
326
|
|
|
327
|
+
/* adjust to readOnly/summary mode */
|
|
328
|
+
.vjsf-node-markdown.vjsf-readonly .EasyMDEContainer .CodeMirror {
|
|
329
|
+
border-width: 0;
|
|
330
|
+
padding: 0;
|
|
331
|
+
}
|
|
332
|
+
.vjsf-node-markdown.vjsf-summary .vjsf-node-markdown-content {
|
|
333
|
+
height: 96px;
|
|
334
|
+
overflow: hidden;
|
|
335
|
+
mask-image: linear-gradient(180deg, #000 66%, transparent 90%);
|
|
336
|
+
}
|
|
337
|
+
.vjsf-node-markdown.vjsf-readonly .vjsf-node-markdown-content textarea {
|
|
338
|
+
display: none;
|
|
339
|
+
}
|
|
340
|
+
|
|
233
341
|
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, computed } from 'vue'
|
|
3
|
-
import { VStepper, VStepperHeader, VStepperItem, VContainer } from 'vuetify/components'
|
|
3
|
+
import { VStepper, VStepperHeader, VStepperItem, VStepperWindow, VStepperWindowItem, VStepperActions, VContainer, VRow, VSpacer, VBtn, VDivider } from 'vuetify/components'
|
|
4
4
|
import { isSection } from '@json-layout/core'
|
|
5
5
|
import Node from '../node.vue'
|
|
6
6
|
import SectionHeader from '../fragments/section-header.vue'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { VTabs, VTab, VContainer } from 'vuetify/components'
|
|
2
|
+
import { VTabs, VTab, VContainer, VSheet, VWindow, VWindowItem, VRow, VIcon } from 'vuetify/components'
|
|
3
3
|
import { ref } from 'vue'
|
|
4
4
|
import { isSection } from '@json-layout/core'
|
|
5
5
|
import Node from '../node.vue'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { defineComponent, h, computed } from 'vue'
|
|
2
|
+
import { defineComponent, h, computed, ref, watch } from 'vue'
|
|
3
3
|
import { VTextarea } from 'vuetify/components'
|
|
4
4
|
import { getInputProps } from '../../utils/props.js'
|
|
5
5
|
import { getCompSlots } from '../../utils/slots.js'
|
|
@@ -18,12 +18,33 @@ export default defineComponent({
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
setup (props) {
|
|
21
|
-
|
|
21
|
+
/** @type {import('vue').Ref<null | HTMLElement>} */
|
|
22
|
+
const textarea = ref(null)
|
|
23
|
+
|
|
24
|
+
const fieldProps = computed(() => {
|
|
25
|
+
const inputProps = getInputProps(props.modelValue, props.statefulLayout, ['placeholder'])
|
|
26
|
+
inputProps.ref = textarea
|
|
27
|
+
if (props.modelValue.options.readOnly && props.modelValue.options.summary) inputProps.rows = 3
|
|
28
|
+
return inputProps
|
|
29
|
+
})
|
|
22
30
|
const fieldSlots = computed(() => getCompSlots(props.modelValue, props.statefulLayout))
|
|
23
31
|
|
|
32
|
+
watch(() => props.modelValue.options.readOnly, (readOnly) => {
|
|
33
|
+
if (readOnly && textarea.value) {
|
|
34
|
+
textarea.value.scrollTop = 0
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
24
38
|
// @ts-ignore
|
|
25
39
|
return () => h(VTextarea, fieldProps.value, fieldSlots.value)
|
|
26
40
|
}
|
|
27
41
|
})
|
|
28
42
|
|
|
29
43
|
</script>
|
|
44
|
+
|
|
45
|
+
<style>
|
|
46
|
+
.vjsf-node-textarea.vjsf-readonly.vjsf-summary textarea {
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
mask-image: linear-gradient(180deg, #000 66%, transparent 90%);
|
|
49
|
+
}
|
|
50
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { isSection } from '@json-layout/core'
|
|
3
|
-
import { VTabs, VTab, VContainer } from 'vuetify/components'
|
|
3
|
+
import { VTabs, VTab, VContainer, VSheet, VWindow, VWindowItem, VRow, VIcon } from 'vuetify/components'
|
|
4
4
|
import { ref } from 'vue'
|
|
5
5
|
import Node from '../node.vue'
|
|
6
6
|
import SectionHeader from '../fragments/section-header.vue'
|
package/src/components/vjsf.vue
CHANGED
|
@@ -18,10 +18,22 @@ export default function useDnd (array, callback) {
|
|
|
18
18
|
|
|
19
19
|
const sortableArray = shallowRef(array)
|
|
20
20
|
|
|
21
|
+
const hovered = ref(-1)
|
|
21
22
|
const draggable = ref(-1)
|
|
22
23
|
const dragging = ref(-1)
|
|
23
24
|
|
|
25
|
+
hovered.value = 1
|
|
26
|
+
|
|
24
27
|
const itemBind = (/** @type {number} */itemIndex) => ({
|
|
28
|
+
// hover the item
|
|
29
|
+
onMouseenter: () => {
|
|
30
|
+
hovered.value = itemIndex
|
|
31
|
+
},
|
|
32
|
+
onMouseleave: () => {
|
|
33
|
+
hovered.value = -1
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// drag the item
|
|
25
37
|
onDragstart: () => {
|
|
26
38
|
dragging.value = itemIndex
|
|
27
39
|
},
|
|
@@ -36,6 +48,7 @@ export default function useDnd (array, callback) {
|
|
|
36
48
|
})
|
|
37
49
|
|
|
38
50
|
const handleBind = (/** @type {number} */itemIndex) => ({
|
|
51
|
+
// hover the handle
|
|
39
52
|
onMouseover () {
|
|
40
53
|
draggable.value = itemIndex
|
|
41
54
|
},
|
|
@@ -47,7 +60,9 @@ export default function useDnd (array, callback) {
|
|
|
47
60
|
return {
|
|
48
61
|
activeDnd,
|
|
49
62
|
sortableArray,
|
|
63
|
+
hovered,
|
|
50
64
|
draggable,
|
|
65
|
+
dragging,
|
|
51
66
|
itemBind,
|
|
52
67
|
handleBind
|
|
53
68
|
}
|
|
@@ -19,8 +19,8 @@ export const emits = {
|
|
|
19
19
|
* @param {import('vue').Ref<any>} modelValue
|
|
20
20
|
* @param {import('vue').Ref<import("../components/types.js").PartialVjsfOptions>} options
|
|
21
21
|
* @param {any} emit
|
|
22
|
-
* @param {typeof import('@json-layout/core').compile} compile
|
|
23
|
-
* @param {import('@json-layout/core').CompiledLayout} [precompiledLayout]
|
|
22
|
+
* @param {typeof import('@json-layout/core').compile} [compile]
|
|
23
|
+
* @param {import('vue').Ref<import('@json-layout/core').CompiledLayout>} [precompiledLayout]
|
|
24
24
|
*/
|
|
25
25
|
export const useVjsf = (schema, modelValue, options, emit, compile, precompiledLayout) => {
|
|
26
26
|
const el = ref(null)
|
|
@@ -50,7 +50,8 @@ export const useVjsf = (schema, modelValue, options, emit, compile, precompiledL
|
|
|
50
50
|
const fullOptions = computed(() => getFullOptions(options.value, form, width.value, slots))
|
|
51
51
|
|
|
52
52
|
const compiledLayout = computed(() => {
|
|
53
|
-
if (precompiledLayout) return precompiledLayout
|
|
53
|
+
if (precompiledLayout?.value) return precompiledLayout?.value
|
|
54
|
+
if (!compile) throw new Error('compile function is not available')
|
|
54
55
|
const compiledLayout = compile(schema.value, fullOptions.value)
|
|
55
56
|
return compiledLayout
|
|
56
57
|
})
|
|
@@ -83,17 +84,21 @@ export const useVjsf = (schema, modelValue, options, emit, compile, precompiledL
|
|
|
83
84
|
})
|
|
84
85
|
emit('update:state', _statefulLayout)
|
|
85
86
|
_statefulLayout.events.on('autofocus', () => {
|
|
87
|
+
console.log('autofocus ?')
|
|
86
88
|
if (!el.value) return
|
|
87
89
|
// @ts-ignore
|
|
88
90
|
const autofocusNodeElement = el.value.querySelector('.vjsf-input--autofocus')
|
|
89
91
|
if (autofocusNodeElement) {
|
|
90
|
-
const autofocusInputElement = autofocusNodeElement.querySelector('input')
|
|
92
|
+
const autofocusInputElement = autofocusNodeElement.querySelector('input') ?? autofocusNodeElement.querySelector('textarea:not([style*="display: none"]')
|
|
91
93
|
if (autofocusInputElement) autofocusInputElement.focus()
|
|
92
94
|
}
|
|
93
95
|
})
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
watch(fullOptions, (newOptions) => {
|
|
99
|
+
// in case of runtime compilation the watch on compiledLayout will be triggered
|
|
100
|
+
if (!precompiledLayout?.value) return
|
|
101
|
+
|
|
97
102
|
if (statefulLayout.value) {
|
|
98
103
|
statefulLayout.value.options = newOptions
|
|
99
104
|
} else {
|
|
@@ -109,7 +114,7 @@ export const useVjsf = (schema, modelValue, options, emit, compile, precompiledL
|
|
|
109
114
|
// case where schema is updated from outside
|
|
110
115
|
watch(compiledLayout, (newCompiledLayout) => {
|
|
111
116
|
initStatefulLayout()
|
|
112
|
-
}
|
|
117
|
+
})
|
|
113
118
|
|
|
114
119
|
return { el, statefulLayout, stateTree }
|
|
115
120
|
}
|
package/types/compat/v2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"AA8FA;;;;;;GAMG;AACH,kCALW,MAAM,+CAEN,MAAM,0BAkBhB;sBApHqB,KAAK"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/markdown.vue.js"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"markdown.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/markdown.vue.js"],"names":[],"mappings":";;QAWM,2EAA2E;cAAjE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,aAAa,EAAE,gBAAgB,CAAC;;;;QAKxE,+EAA+E;cAArE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EAAE,cAAc,CAAC;;;;;;;QAL5E,2EAA2E;cAAjE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,aAAa,EAAE,gBAAgB,CAAC;;;;QAKxE,+EAA+E;cAArE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,mBAAmB,EAAE,cAAc,CAAC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
declare const _default: import("vue").DefineComponent<{}, {
|
|
2
2
|
$emit: ((event: "update:modelValue", data: any) => void) & ((event: "update:state", state: import("@json-layout/core").StatefulLayout) => void);
|
|
3
|
-
modelValue: any;
|
|
4
3
|
options: Partial<Omit<import("./types.js").VjsfOptions, "width">>;
|
|
4
|
+
modelValue: any;
|
|
5
5
|
schema: Record<string, any>;
|
|
6
6
|
precompiledLayout: import("../../../node_modules/@json-layout/core/types/compile/types.js").CompiledLayout;
|
|
7
7
|
$props: {
|
|
8
|
-
readonly modelValue?: any;
|
|
9
8
|
readonly options?: Partial<Omit<import("./types.js").VjsfOptions, "width">> | undefined;
|
|
9
|
+
readonly modelValue?: any;
|
|
10
10
|
readonly schema?: Record<string, any> | undefined;
|
|
11
11
|
readonly precompiledLayout?: import("../../../node_modules/@json-layout/core/types/compile/types.js").CompiledLayout | undefined;
|
|
12
12
|
};
|
|
@@ -8,7 +8,7 @@ export const emits: {
|
|
|
8
8
|
*/
|
|
9
9
|
'update:state': (state: StatefulLayout) => boolean;
|
|
10
10
|
};
|
|
11
|
-
export function useVjsf(schema: import('vue').Ref<Object>, modelValue: import('vue').Ref<any>, options: import('vue').Ref<import("../components/types.js").PartialVjsfOptions>, emit: any, compile
|
|
11
|
+
export function useVjsf(schema: import('vue').Ref<Object>, modelValue: import('vue').Ref<any>, options: import('vue').Ref<import("../components/types.js").PartialVjsfOptions>, emit: any, compile?: typeof import("@json-layout/core").compile | undefined, precompiledLayout?: import("vue").Ref<import("../../../node_modules/@json-layout/core/types/compile/types.js").CompiledLayout> | undefined): {
|
|
12
12
|
el: import("vue").Ref<null>;
|
|
13
13
|
statefulLayout: import("vue").ShallowRef<StatefulLayout | null>;
|
|
14
14
|
stateTree: import("vue").ShallowRef<import("../../../node_modules/@json-layout/core/types/state/types.js").StateTree | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-vjsf.d.ts","sourceRoot":"","sources":["../../src/composables/use-vjsf.js"],"names":[],"mappings":"AAKA;IACE;;MAEE;gCADO,GAAG;IAGZ;;MAEE;4BADO,cAAc;EAGxB;AAUM,gCAPI,OAAO,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,cACzB,OAAO,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,WACtB,OAAO,KAAK,EAAE,GAAG,CAAC,OAAO,wBAAwB,EAAE,kBAAkB,CAAC,QACtE,GAAG
|
|
1
|
+
{"version":3,"file":"use-vjsf.d.ts","sourceRoot":"","sources":["../../src/composables/use-vjsf.js"],"names":[],"mappings":"AAKA;IACE;;MAEE;gCADO,GAAG;IAGZ;;MAEE;4BADO,cAAc;EAGxB;AAUM,gCAPI,OAAO,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,cACzB,OAAO,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,WACtB,OAAO,KAAK,EAAE,GAAG,CAAC,OAAO,wBAAwB,EAAE,kBAAkB,CAAC,QACtE,GAAG;;;;EAkGb;+BAtH8B,mBAAmB"}
|
package/src/utils/clone.js
DELETED