@ditojs/admin 2.8.1 → 2.8.2
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/dito-admin.es.js +1609 -1579
- package/dist/dito-admin.umd.js +5 -5
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/components/DitoClipboard.vue +4 -0
- package/src/components/DitoDraggable.vue +71 -2
- package/src/components/DitoEditButtons.vue +6 -6
- package/src/components/DitoSchema.vue +3 -2
- package/src/components/DitoSchemaInlined.vue +3 -10
- package/src/components/DitoTreeItem.vue +2 -2
- package/src/mixins/SortableMixin.js +9 -8
- package/src/styles/_layout.scss +1 -1
- package/src/styles/style.scss +0 -1
- package/src/types/DitoTypeList.vue +1 -1
- package/src/types/DitoTypeMultiselect.vue +26 -21
- package/src/types/DitoTypeUpload.vue +1 -1
- package/src/utils/schema.js +12 -9
- package/src/styles/_sortable.scss +0 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/admin",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Admin is a schema based admin interface for Dito.js Server, featuring auto-generated views and forms and built with Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/master/packages/admin",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"vite": "^4.3.5"
|
|
84
84
|
},
|
|
85
85
|
"types": "types",
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "d9a0905b7c93d509a3710b36c749cc6e651401d3",
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "vite build",
|
|
89
89
|
"watch": "yarn build --mode 'development' --watch",
|
|
@@ -73,6 +73,10 @@ export default DitoComponent.component('DitoClipboard', {
|
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
|
|
76
|
+
watch: {
|
|
77
|
+
'appState.clipboardData': 'checkClipboard'
|
|
78
|
+
},
|
|
79
|
+
|
|
76
80
|
mounted() {
|
|
77
81
|
// Check clipboard content whenever something gets copied or the window gets
|
|
78
82
|
// (re)activated, as those are the moments when the clipboard can change:
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
UseSortable(
|
|
2
|
+
UseSortable.dito-draggable(
|
|
3
3
|
v-if="draggable"
|
|
4
|
+
:class="{ 'dito-draggable--dragging': isDragging }"
|
|
4
5
|
:tag="tag"
|
|
5
6
|
:modelValue="modelValue"
|
|
6
|
-
:options="options"
|
|
7
|
+
:options="{ ...options, onStart, onEnd }"
|
|
7
8
|
@update:modelValue="$emit('update:modelValue', $event)"
|
|
8
9
|
)
|
|
9
10
|
slot
|
|
@@ -15,6 +16,7 @@ component(
|
|
|
15
16
|
</template>
|
|
16
17
|
|
|
17
18
|
<script>
|
|
19
|
+
import { addEvents } from '@ditojs/ui'
|
|
18
20
|
import DitoComponent from '../DitoComponent'
|
|
19
21
|
import { UseSortable } from '@vueuse/integrations/useSortable/component'
|
|
20
22
|
|
|
@@ -40,6 +42,73 @@ export default DitoComponent.component('DitoDraggable', {
|
|
|
40
42
|
type: Boolean,
|
|
41
43
|
default: true
|
|
42
44
|
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
data() {
|
|
48
|
+
return {
|
|
49
|
+
mouseEvents: null,
|
|
50
|
+
isDragging: false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
methods: {
|
|
55
|
+
onStart(event) {
|
|
56
|
+
this.isDragging = true
|
|
57
|
+
this.options.onStart?.(event)
|
|
58
|
+
this.mouseEvents?.remove()
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
onEnd(event) {
|
|
62
|
+
this.options.onEnd?.(event)
|
|
63
|
+
// Keep `isDragging` true until the next mouse interaction so that
|
|
64
|
+
// confused hover states are cleared before removing the hover catcher.
|
|
65
|
+
this.mouseEvents = addEvents(this.$el, {
|
|
66
|
+
mousedown: this.onMouse,
|
|
67
|
+
mousemove: this.onMouse,
|
|
68
|
+
mouseleave: this.onMouse
|
|
69
|
+
})
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
onMouse() {
|
|
73
|
+
this.isDragging = false
|
|
74
|
+
this.mouseEvents.remove()
|
|
75
|
+
this.mouseEvents = null
|
|
76
|
+
}
|
|
43
77
|
}
|
|
44
78
|
})
|
|
45
79
|
</script>
|
|
80
|
+
|
|
81
|
+
<style lang="scss">
|
|
82
|
+
@import '../styles/_imports';
|
|
83
|
+
|
|
84
|
+
.dito-draggable {
|
|
85
|
+
// Overlay a hover catcher while we're dragging to prevent hover states from
|
|
86
|
+
// getting stuck / confused.
|
|
87
|
+
&:has(&__chosen),
|
|
88
|
+
&--dragging {
|
|
89
|
+
> * {
|
|
90
|
+
position: relative;
|
|
91
|
+
|
|
92
|
+
> :first-child::after {
|
|
93
|
+
content: '';
|
|
94
|
+
position: absolute;
|
|
95
|
+
inset: 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&__fallback {
|
|
101
|
+
filter: drop-shadow(0 2px 4px $color-shadow);
|
|
102
|
+
|
|
103
|
+
// Nested <td> need to also switch to `display: flex` style during dragging.
|
|
104
|
+
&,
|
|
105
|
+
td {
|
|
106
|
+
display: flex;
|
|
107
|
+
|
|
108
|
+
> * {
|
|
109
|
+
flex: 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
@@ -78,19 +78,19 @@ export default DitoComponent.component('DitoEditButtons', {
|
|
|
78
78
|
return this.disabled || !this.hasSchemaOption('deletable')
|
|
79
79
|
},
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
isEditableDisabled() {
|
|
82
82
|
return (
|
|
83
83
|
this.disabled ||
|
|
84
|
-
!this.
|
|
85
|
-
!this.hasSchemaOption('
|
|
84
|
+
!this.editPath ||
|
|
85
|
+
!this.hasSchemaOption('editable')
|
|
86
86
|
)
|
|
87
87
|
},
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
isCreatableDisabled() {
|
|
90
90
|
return (
|
|
91
91
|
this.disabled ||
|
|
92
|
-
!this.
|
|
93
|
-
!this.hasSchemaOption('
|
|
92
|
+
!this.createPath ||
|
|
93
|
+
!this.hasSchemaOption('creatable')
|
|
94
94
|
)
|
|
95
95
|
},
|
|
96
96
|
|
|
@@ -214,7 +214,7 @@ export default DitoComponent.component('DitoSchema', {
|
|
|
214
214
|
},
|
|
215
215
|
|
|
216
216
|
clipboard() {
|
|
217
|
-
return this.schema?.clipboard
|
|
217
|
+
return this.schema?.clipboard ?? null
|
|
218
218
|
},
|
|
219
219
|
|
|
220
220
|
hasHeader() {
|
|
@@ -676,6 +676,7 @@ export default DitoComponent.component('DitoSchema', {
|
|
|
676
676
|
{
|
|
677
677
|
// Needed for DitoContext handling inside `processData` and
|
|
678
678
|
// `processSchemaData()`:
|
|
679
|
+
rootData: this.rootData,
|
|
679
680
|
component: this,
|
|
680
681
|
schemaOnly,
|
|
681
682
|
target
|
|
@@ -772,7 +773,7 @@ export default DitoComponent.component('DitoSchema', {
|
|
|
772
773
|
grid-template-rows: min-content;
|
|
773
774
|
grid-template-columns: 100%;
|
|
774
775
|
|
|
775
|
-
>
|
|
776
|
+
> :only-child {
|
|
776
777
|
grid-row-end: none;
|
|
777
778
|
}
|
|
778
779
|
}
|
|
@@ -82,17 +82,10 @@ export default DitoComponent.component('DitoSchemaInlined', {
|
|
|
82
82
|
grid-template-rows: min-content;
|
|
83
83
|
grid-template-columns: 100%;
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
&:not(:hover, .dito-schema--open) {
|
|
86
86
|
> .dito-schema-header {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
.dito-label {
|
|
91
|
-
width: 100%;
|
|
92
|
-
margin: 0;
|
|
93
|
-
// Prevent collapsing to min-height when alone in
|
|
94
|
-
// .dito-schema-content, due to grid-template-rows: min-content
|
|
95
|
-
min-height: $input-height;
|
|
87
|
+
> .dito-clipboard {
|
|
88
|
+
display: none;
|
|
96
89
|
}
|
|
97
90
|
}
|
|
98
91
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
.dito-tree-item(
|
|
3
3
|
:id="dataPath"
|
|
4
4
|
:class=`{
|
|
5
|
-
'dito-dragging':
|
|
5
|
+
'dito-dragging': isDragging,
|
|
6
6
|
'dito-active': active
|
|
7
7
|
}`
|
|
8
8
|
:style="level > 0 && { '--level': level }"
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
v-if="childrenSchema"
|
|
77
77
|
v-show="opened"
|
|
78
78
|
:modelValue="updateOrder(childrenSchema, childrenList)"
|
|
79
|
-
:options="
|
|
79
|
+
:options="getDraggableOptions(true)"
|
|
80
80
|
:draggable="childrenDraggable"
|
|
81
81
|
@update:modelValue="value => (childrenList = value)"
|
|
82
82
|
)
|
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
export default {
|
|
3
3
|
data() {
|
|
4
4
|
return {
|
|
5
|
-
|
|
5
|
+
isDragging: false
|
|
6
6
|
}
|
|
7
7
|
},
|
|
8
8
|
|
|
9
9
|
methods: {
|
|
10
|
-
|
|
10
|
+
getDraggableOptions(forceFallback = false) {
|
|
11
|
+
const prefix = 'dito-draggable'
|
|
11
12
|
return {
|
|
12
13
|
animation: 150,
|
|
13
14
|
handle: '.dito-button-drag',
|
|
14
|
-
dragClass:
|
|
15
|
-
chosenClass:
|
|
16
|
-
ghostClass:
|
|
17
|
-
fallbackClass:
|
|
15
|
+
dragClass: `${prefix}__drag`,
|
|
16
|
+
chosenClass: `${prefix}__chosen`,
|
|
17
|
+
ghostClass: `${prefix}__ghost`,
|
|
18
|
+
fallbackClass: `${prefix}__fallback`,
|
|
18
19
|
forceFallback,
|
|
19
20
|
onStart: this.onStartDrag,
|
|
20
21
|
onEnd: this.onEndDrag
|
|
@@ -22,11 +23,11 @@ export default {
|
|
|
22
23
|
},
|
|
23
24
|
|
|
24
25
|
onStartDrag() {
|
|
25
|
-
this.
|
|
26
|
+
this.isDragging = true
|
|
26
27
|
},
|
|
27
28
|
|
|
28
29
|
onEndDrag({ oldIndex, newIndex }) {
|
|
29
|
-
this.
|
|
30
|
+
this.isDragging = false
|
|
30
31
|
if (oldIndex !== newIndex) {
|
|
31
32
|
this.onChange()
|
|
32
33
|
}
|
package/src/styles/_layout.scss
CHANGED
package/src/styles/style.scss
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.dito-multiselect
|
|
3
|
-
:class=`{
|
|
4
|
-
'dito-multiselect-single': !multiple,
|
|
5
|
-
'dito-multiselect-multiple': multiple
|
|
6
|
-
}`
|
|
7
|
-
)
|
|
2
|
+
.dito-multiselect
|
|
8
3
|
.dito-multiselect__inner
|
|
9
4
|
VueMultiselect(
|
|
10
5
|
ref="element"
|
|
11
6
|
v-model="selectedOptions"
|
|
12
|
-
:class
|
|
7
|
+
:class=`{
|
|
8
|
+
'multiselect--multiple': multiple,
|
|
9
|
+
'multiselect--loading': isLoading,
|
|
10
|
+
'multiselect--highlight': showHighlight
|
|
11
|
+
}`
|
|
13
12
|
:showLabels="false"
|
|
14
13
|
:placeholder="placeholder"
|
|
15
14
|
tagPlaceholder="Press enter to add new tag"
|
|
@@ -241,16 +240,6 @@ $tag-line-height: 1em;
|
|
|
241
240
|
position: relative;
|
|
242
241
|
}
|
|
243
242
|
|
|
244
|
-
// TODO: BEM
|
|
245
|
-
&.dito-multiselect-single {
|
|
246
|
-
--input-width: 100%;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// TODO: BEM
|
|
250
|
-
&.dito-multiselect-multiple {
|
|
251
|
-
--input-width: auto;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
243
|
.dito-edit-buttons {
|
|
255
244
|
margin-left: $form-spacing-half;
|
|
256
245
|
}
|
|
@@ -258,10 +247,16 @@ $tag-line-height: 1em;
|
|
|
258
247
|
.multiselect {
|
|
259
248
|
$self: last-selector(&);
|
|
260
249
|
|
|
250
|
+
--input-width: 100%;
|
|
251
|
+
|
|
261
252
|
font-size: inherit;
|
|
262
253
|
min-height: inherit;
|
|
263
254
|
color: $color-black;
|
|
264
255
|
|
|
256
|
+
&--multiple {
|
|
257
|
+
--input-width: auto;
|
|
258
|
+
}
|
|
259
|
+
|
|
265
260
|
&__tags {
|
|
266
261
|
display: flex;
|
|
267
262
|
font-size: inherit;
|
|
@@ -373,11 +368,11 @@ $tag-line-height: 1em;
|
|
|
373
368
|
}
|
|
374
369
|
|
|
375
370
|
// Only show the highlight once the pulldown has received mouse or
|
|
376
|
-
// keyboard interaction, in which case `&--
|
|
371
|
+
// keyboard interaction, in which case `&--highlight` will be set,
|
|
377
372
|
// which is controlled by `pointerDirty` in vue-multiselect.
|
|
378
373
|
// Until then, clear the highlight style, but only if it isn't also
|
|
379
374
|
// disabled or selected, in which case we want to keep the style.
|
|
380
|
-
@at-root #{$self}:not(#{$self}--
|
|
375
|
+
@at-root #{$self}:not(#{$self}--highlight)
|
|
381
376
|
#{$option}:not(#{$option}--disabled):not(#{$option}--selected) {
|
|
382
377
|
color: $color-text;
|
|
383
378
|
background: transparent;
|
|
@@ -391,7 +386,7 @@ $tag-line-height: 1em;
|
|
|
391
386
|
color: $color-white;
|
|
392
387
|
}
|
|
393
388
|
|
|
394
|
-
@at-root #{$self}#{$self}--
|
|
389
|
+
@at-root #{$self}#{$self}--highlight #{last-selector(&)} {
|
|
395
390
|
color: $color-text-inverted;
|
|
396
391
|
background: $color-active;
|
|
397
392
|
}
|
|
@@ -402,7 +397,7 @@ $tag-line-height: 1em;
|
|
|
402
397
|
color: $color-text;
|
|
403
398
|
background: $color-highlight;
|
|
404
399
|
|
|
405
|
-
@at-root #{$self}#{$self}--
|
|
400
|
+
@at-root #{$self}#{$self}--highlight &#{$option}--highlight {
|
|
406
401
|
color: $color-text-inverted;
|
|
407
402
|
}
|
|
408
403
|
}
|
|
@@ -490,6 +485,16 @@ $tag-line-height: 1em;
|
|
|
490
485
|
}
|
|
491
486
|
}
|
|
492
487
|
}
|
|
488
|
+
|
|
489
|
+
&--loading {
|
|
490
|
+
#{$self}__tags {
|
|
491
|
+
border-radius: $border-radius;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
#{$self}__content-wrapper {
|
|
495
|
+
display: none;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
493
498
|
}
|
|
494
499
|
}
|
|
495
500
|
</style>
|
package/src/utils/schema.js
CHANGED
|
@@ -485,11 +485,11 @@ export function getFormSchemas(schema, context, modifyForm) {
|
|
|
485
485
|
|
|
486
486
|
let { form, forms } = schema
|
|
487
487
|
if (!form && !forms) {
|
|
488
|
-
const {
|
|
488
|
+
const { name, compact, clipboard, tabs, components } = schema
|
|
489
489
|
if (components || tabs) {
|
|
490
|
-
// Convert inlined
|
|
491
|
-
//
|
|
492
|
-
form = {
|
|
490
|
+
// Convert inlined forms to stand-alone forms, supporting `name`,
|
|
491
|
+
// `compact`, `clipboard`, `tabs` and `components` settings.
|
|
492
|
+
form = { type: 'form', name, compact, clipboard, tabs, components }
|
|
493
493
|
} else {
|
|
494
494
|
// No `forms`, `form` or `components`, return and empty `forms` object.
|
|
495
495
|
return {}
|
|
@@ -702,10 +702,11 @@ function cloneItem(sourceSchema, item, options) {
|
|
|
702
702
|
|
|
703
703
|
export function processData(schema, sourceSchema, data, dataPath, {
|
|
704
704
|
component,
|
|
705
|
+
rootData,
|
|
705
706
|
schemaOnly, // whether to only include data covered by the schema, or all data
|
|
706
707
|
target
|
|
707
708
|
} = {}) {
|
|
708
|
-
const options = { component, schemaOnly, target
|
|
709
|
+
const options = { component, rootData, schemaOnly, target }
|
|
709
710
|
const processedData = cloneItem(sourceSchema, data, options)
|
|
710
711
|
const graph = new SchemaGraph()
|
|
711
712
|
|
|
@@ -837,15 +838,15 @@ export function processSchemaData(
|
|
|
837
838
|
const componentDataPath = getDataPath(dataPath, name)
|
|
838
839
|
|
|
839
840
|
const processItem = (item, index = null) => {
|
|
840
|
-
const
|
|
841
|
+
const itemDataPath =
|
|
841
842
|
index !== null
|
|
842
843
|
? getDataPath(componentDataPath, index)
|
|
843
844
|
: componentDataPath
|
|
844
845
|
const context = new DitoContext(options.component, {
|
|
845
846
|
schema: componentSchema,
|
|
846
|
-
data,
|
|
847
|
+
data: item,
|
|
847
848
|
value: item,
|
|
848
|
-
dataPath,
|
|
849
|
+
dataPath: itemDataPath,
|
|
849
850
|
index,
|
|
850
851
|
rootData: options.rootData
|
|
851
852
|
})
|
|
@@ -862,7 +863,7 @@ export function processSchemaData(
|
|
|
862
863
|
return processSchemaData(
|
|
863
864
|
form,
|
|
864
865
|
item,
|
|
865
|
-
|
|
866
|
+
itemDataPath,
|
|
866
867
|
processedItem,
|
|
867
868
|
processBefore,
|
|
868
869
|
processAfter,
|
|
@@ -882,6 +883,7 @@ export function processSchemaData(
|
|
|
882
883
|
componentDataPath,
|
|
883
884
|
processedData
|
|
884
885
|
)
|
|
886
|
+
|
|
885
887
|
let value = processedData ? processedData[name] : data[name]
|
|
886
888
|
if (value != null && hasFormSchema(componentSchema)) {
|
|
887
889
|
// Recursively process data on nested form items.
|
|
@@ -897,6 +899,7 @@ export function processSchemaData(
|
|
|
897
899
|
processedData[name] = value
|
|
898
900
|
}
|
|
899
901
|
}
|
|
902
|
+
|
|
900
903
|
processAfter?.(
|
|
901
904
|
componentSchema,
|
|
902
905
|
data,
|