@ditojs/admin 2.8.1 → 2.9.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/dito-admin.es.js +2799 -2702
- package/dist/dito-admin.umd.js +6 -6
- package/dist/style.css +1 -1
- package/package.json +5 -5
- package/src/appState.js +2 -0
- package/src/components/DitoClipboard.vue +40 -56
- package/src/components/DitoContainer.vue +5 -7
- package/src/components/DitoDialog.vue +3 -7
- package/src/components/DitoDraggable.vue +74 -2
- package/src/components/DitoEditButtons.vue +6 -6
- package/src/components/DitoLabel.vue +2 -1
- package/src/components/DitoPane.vue +23 -10
- package/src/components/DitoRoot.vue +32 -8
- package/src/components/DitoSchema.vue +12 -6
- package/src/components/DitoSchemaInlined.vue +3 -10
- package/src/components/DitoSidebar.vue +29 -4
- package/src/components/DitoTreeItem.vue +2 -2
- package/src/components/DitoUploadFile.vue +1 -1
- package/src/mixins/DitoMixin.js +4 -3
- package/src/mixins/SortableMixin.js +9 -8
- package/src/styles/_layout.scss +1 -1
- package/src/styles/style.scss +0 -1
- package/src/types/DitoTypeCheckbox.vue +8 -7
- package/src/types/DitoTypeCheckboxes.vue +10 -9
- package/src/types/DitoTypeList.vue +1 -1
- package/src/types/DitoTypeMultiselect.vue +26 -21
- package/src/types/DitoTypeUpload.vue +1 -1
- package/src/utils/agent.js +47 -0
- package/src/utils/schema.js +12 -9
- package/src/styles/_sortable.scss +0 -13
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
aside.dito-sidebar.dito-scroll-parent
|
|
2
|
+
aside.dito-sidebar.dito-scroll-parent(
|
|
3
|
+
v-resize="onResizeSidebar"
|
|
4
|
+
:class="classes"
|
|
5
|
+
)
|
|
3
6
|
nav.dito-header
|
|
4
7
|
slot
|
|
5
8
|
.dito-sidebar__teleport.dito-scroll
|
|
@@ -9,7 +12,31 @@ aside.dito-sidebar.dito-scroll-parent
|
|
|
9
12
|
import DitoComponent from '../DitoComponent.js'
|
|
10
13
|
|
|
11
14
|
// @vue/component
|
|
12
|
-
export default DitoComponent.component('DitoSidebar', {
|
|
15
|
+
export default DitoComponent.component('DitoSidebar', {
|
|
16
|
+
data() {
|
|
17
|
+
return {
|
|
18
|
+
sidebarWidth: 0
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
computed: {
|
|
23
|
+
classes() {
|
|
24
|
+
const prefix = 'dito-sidebar'
|
|
25
|
+
// NOTE: Keep synced with $sidebar-max-width in SCSS:
|
|
26
|
+
const sidebarWidth = 360
|
|
27
|
+
return {
|
|
28
|
+
[`${prefix}--width-99`]: this.sidebarWidth < sidebarWidth,
|
|
29
|
+
[`${prefix}--width-60`]: this.sidebarWidth <= sidebarWidth * 0.6
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
methods: {
|
|
35
|
+
onResizeSidebar({ contentRect: { width } }) {
|
|
36
|
+
this.sidebarWidth = width
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
13
40
|
</script>
|
|
14
41
|
|
|
15
42
|
<style lang="scss">
|
|
@@ -19,7 +46,5 @@ export default DitoComponent.component('DitoSidebar', {})
|
|
|
19
46
|
flex: 0 1 $sidebar-max-width;
|
|
20
47
|
max-width: $sidebar-max-width;
|
|
21
48
|
min-width: $sidebar-min-width;
|
|
22
|
-
// For the `@container` rule in `.dito-container` to work:
|
|
23
|
-
container-type: inline-size;
|
|
24
49
|
}
|
|
25
50
|
</style>
|
|
@@ -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
|
)
|
|
@@ -51,7 +51,7 @@ export default DitoComponent.component('DitoUploadFile', {
|
|
|
51
51
|
'file.upload.file': {
|
|
52
52
|
immediate: true,
|
|
53
53
|
handler(file) {
|
|
54
|
-
if (
|
|
54
|
+
if (this.thumbnail && file?.type.startsWith('image/')) {
|
|
55
55
|
const reader = new FileReader()
|
|
56
56
|
reader.onload = () => {
|
|
57
57
|
this.uploadUrl = reader.result
|
package/src/mixins/DitoMixin.js
CHANGED
|
@@ -39,13 +39,14 @@ export default {
|
|
|
39
39
|
],
|
|
40
40
|
|
|
41
41
|
provide() {
|
|
42
|
+
const self = () => this
|
|
42
43
|
return this.providesData
|
|
43
44
|
? {
|
|
44
|
-
$parentComponent:
|
|
45
|
-
$dataComponent:
|
|
45
|
+
$parentComponent: self,
|
|
46
|
+
$dataComponent: self
|
|
46
47
|
}
|
|
47
48
|
: {
|
|
48
|
-
$parentComponent:
|
|
49
|
+
$parentComponent: self
|
|
49
50
|
}
|
|
50
51
|
},
|
|
51
52
|
|
|
@@ -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,11 +1,12 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
.dito-checkbox
|
|
3
|
+
input(
|
|
4
|
+
:id="dataPath"
|
|
5
|
+
ref="element"
|
|
6
|
+
v-model="value"
|
|
7
|
+
type="checkbox"
|
|
8
|
+
v-bind="attributes"
|
|
9
|
+
)
|
|
9
10
|
</template>
|
|
10
11
|
|
|
11
12
|
<script>
|
|
@@ -7,14 +7,15 @@ ul.dito-checkboxes(
|
|
|
7
7
|
v-for="option in options"
|
|
8
8
|
)
|
|
9
9
|
label
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
.dito-checkbox
|
|
11
|
+
input(
|
|
12
|
+
ref="element"
|
|
13
|
+
v-model="selectedOptions"
|
|
14
|
+
type="checkbox"
|
|
15
|
+
:value="getValueForOption(option)"
|
|
16
|
+
v-bind="attributes"
|
|
17
|
+
)
|
|
18
|
+
span {{ getLabelForOption(option) }}
|
|
18
19
|
</template>
|
|
19
20
|
|
|
20
21
|
<script>
|
|
@@ -51,7 +52,7 @@ export default DitoTypeComponent.register('checkboxes', {
|
|
|
51
52
|
@extend %input-borderless;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
input {
|
|
55
56
|
margin-right: $form-spacing;
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -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>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// This user-agent parser was lifted from Paper.js:
|
|
2
|
+
// https://github.com/paperjs/paper.js/blob/cc15696750035ab00e00c64c7c95daa2c85efe01/src/core/PaperScope.js#L75-L107
|
|
3
|
+
|
|
4
|
+
export function parseUserAgent(userAgent = '') {
|
|
5
|
+
const agent = {}
|
|
6
|
+
// Use replace() to get all matches, and deal with Chrome/Webkit overlap:
|
|
7
|
+
const ua = userAgent.toLowerCase()
|
|
8
|
+
const [os] =
|
|
9
|
+
/(iphone|ipad|linux; android|darwin|win|mac|linux|freebsd|sunos)/.exec(
|
|
10
|
+
ua
|
|
11
|
+
) || []
|
|
12
|
+
const platform = (
|
|
13
|
+
{
|
|
14
|
+
'darwin': 'mac',
|
|
15
|
+
'iphone': 'ios',
|
|
16
|
+
'ipad': 'ios',
|
|
17
|
+
'linux; android': 'android'
|
|
18
|
+
}[os] ||
|
|
19
|
+
os
|
|
20
|
+
)
|
|
21
|
+
if (platform) {
|
|
22
|
+
agent.platform = platform
|
|
23
|
+
agent[platform] = true
|
|
24
|
+
}
|
|
25
|
+
ua.replace(
|
|
26
|
+
/(opera|chrome|safari|webkit|firefox|msie|trident)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv:v?([.\d]+))?/g,
|
|
27
|
+
(match, browser, v1, v2, rv) => {
|
|
28
|
+
// Do not set additional browsers once chrome is detected.
|
|
29
|
+
if (!agent.chrome) {
|
|
30
|
+
const version = rv || v2 || v1
|
|
31
|
+
if (!agent.version || browser !== 'safari') {
|
|
32
|
+
// Use the version we get for webkit for Safari, which is actually
|
|
33
|
+
// The Safari version, e.g. 16.0
|
|
34
|
+
agent.version = version
|
|
35
|
+
agent.versionNumber = parseFloat(version)
|
|
36
|
+
}
|
|
37
|
+
agent.browser = browser
|
|
38
|
+
agent[browser] = true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
if (agent.chrome) {
|
|
43
|
+
// Can't have it both ways, Chrome.
|
|
44
|
+
delete agent.webkit
|
|
45
|
+
}
|
|
46
|
+
return agent
|
|
47
|
+
}
|
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,
|