@ditojs/admin 2.3.2 → 2.4.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 +1371 -1315
- package/dist/dito-admin.umd.js +4 -4
- package/dist/style.css +1 -1
- package/package.json +5 -5
- package/src/components/DitoContainer.vue +7 -1
- package/src/components/DitoHeader.vue +1 -0
- package/src/components/DitoMenu.vue +9 -4
- package/src/components/DitoRoot.vue +108 -1
- package/src/components/DitoTableHead.vue +1 -0
- package/src/mixins/TypeMixin.js +9 -11
- package/src/styles/_button.scss +3 -3
- package/src/styles/_settings.scss +3 -0
- package/src/types/DitoTypeButton.vue +0 -1
- package/src/types/DitoTypeCheckbox.vue +0 -1
- package/src/types/DitoTypeComputed.vue +0 -1
- package/src/types/DitoTypeMarkup.vue +1 -1
- package/src/types/DitoTypeNumber.vue +0 -1
- package/src/types/DitoTypeProgress.vue +0 -1
- package/src/types/DitoTypeSwitch.vue +0 -1
- package/src/types/DitoTypeText.vue +0 -1
- package/src/types/DitoTypeTextarea.vue +0 -1
- package/src/types/DitoTypeUpload.vue +21 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/admin",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
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",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"not ie_mob > 0"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ditojs/ui": "^2.
|
|
37
|
-
"@ditojs/utils": "^2.
|
|
36
|
+
"@ditojs/ui": "^2.4.0",
|
|
37
|
+
"@ditojs/utils": "^2.4.0",
|
|
38
38
|
"@kyvg/vue3-notification": "^2.9.0",
|
|
39
39
|
"@lk77/vue3-color": "^3.0.6",
|
|
40
40
|
"@tiptap/core": "^2.0.3",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"vue-upload-component": "^3.1.8"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@ditojs/build": "^2.
|
|
77
|
+
"@ditojs/build": "^2.4.0",
|
|
78
78
|
"@vitejs/plugin-vue": "^4.1.0",
|
|
79
79
|
"@vue/compiler-sfc": "^3.2.47",
|
|
80
80
|
"pug": "^3.0.2",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"vite": "^4.3.1"
|
|
84
84
|
},
|
|
85
85
|
"types": "types",
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "11402f86e31da1b8ebd0a6ea5c984d3abf12d7be",
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "vite build",
|
|
89
89
|
"watch": "yarn build --mode 'development' --watch",
|
|
@@ -248,7 +248,7 @@ export default DitoComponent.component('DitoContainer', {
|
|
|
248
248
|
--justify: flex-end;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
&:not(#{$self}--
|
|
251
|
+
&:not(#{$self}--alone-in-row) {
|
|
252
252
|
// Now only apply alignment if there are neighbouring components no the
|
|
253
253
|
// same row that also align.
|
|
254
254
|
// Look ahead:
|
|
@@ -260,6 +260,12 @@ export default DitoComponent.component('DitoContainer', {
|
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
&--drop-target {
|
|
264
|
+
background: $content-color-background;
|
|
265
|
+
border-radius: $border-radius;
|
|
266
|
+
z-index: $drag-overlay-z-index + 1;
|
|
267
|
+
}
|
|
268
|
+
|
|
263
269
|
&--omit-padding {
|
|
264
270
|
padding: 0;
|
|
265
271
|
|
|
@@ -53,23 +53,28 @@ export default DitoComponent.component('DitoMenu', {
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
getItemPath(item) {
|
|
57
57
|
return item?.path
|
|
58
58
|
? `/${item.path}`
|
|
59
59
|
: item.items
|
|
60
|
-
? this.
|
|
60
|
+
? this.getItemPath(Object.values(item.items)[0])
|
|
61
61
|
: null
|
|
62
62
|
},
|
|
63
63
|
|
|
64
|
+
getItemHref(item) {
|
|
65
|
+
const path = this.getItemPath(item)
|
|
66
|
+
return path ? this.$router.resolve(path).href : null
|
|
67
|
+
},
|
|
68
|
+
|
|
64
69
|
isActiveItem(item) {
|
|
65
70
|
return (
|
|
66
|
-
this.$route.path.startsWith(this.
|
|
71
|
+
this.$route.path.startsWith(this.getItemPath(item)) ||
|
|
67
72
|
item.items && Object.values(item.items).some(this.isActiveItem)
|
|
68
73
|
)
|
|
69
74
|
},
|
|
70
75
|
|
|
71
76
|
onClickItem(item) {
|
|
72
|
-
const path = this.
|
|
77
|
+
const path = this.getItemPath(item)
|
|
73
78
|
if (path) {
|
|
74
79
|
this.$router.push({ path, force: true })
|
|
75
80
|
}
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
position="top right"
|
|
6
6
|
classes="dito-notification"
|
|
7
7
|
)
|
|
8
|
+
Transition(name="dito-drag")
|
|
9
|
+
.dito-drag-overlay(
|
|
10
|
+
v-if="isDraggingFiles"
|
|
11
|
+
)
|
|
8
12
|
TransitionGroup(name="dito-dialog")
|
|
9
13
|
DitoDialog(
|
|
10
14
|
v-for="(dialog, key) in dialogs"
|
|
@@ -69,7 +73,8 @@ export default DitoComponent.component('DitoRoot', {
|
|
|
69
73
|
removeRoutes: null,
|
|
70
74
|
dialogs: {},
|
|
71
75
|
allowLogin: false,
|
|
72
|
-
loadingCount: 0
|
|
76
|
+
loadingCount: 0,
|
|
77
|
+
isDraggingFiles: false
|
|
73
78
|
}
|
|
74
79
|
},
|
|
75
80
|
|
|
@@ -91,6 +96,8 @@ export default DitoComponent.component('DitoRoot', {
|
|
|
91
96
|
},
|
|
92
97
|
|
|
93
98
|
async mounted() {
|
|
99
|
+
this.setupDragAndDrop()
|
|
100
|
+
|
|
94
101
|
tippyDelegate(this.$el, {
|
|
95
102
|
target: '.dito-info',
|
|
96
103
|
onCreate(instance) {
|
|
@@ -103,6 +110,7 @@ export default DitoComponent.component('DitoRoot', {
|
|
|
103
110
|
})
|
|
104
111
|
}
|
|
105
112
|
})
|
|
113
|
+
|
|
106
114
|
// Clear the label marked as active on all mouse and keyboard events, except
|
|
107
115
|
// the ones that DitoLabel itself intercepts.
|
|
108
116
|
this.domOn(document, {
|
|
@@ -118,6 +126,7 @@ export default DitoComponent.component('DitoRoot', {
|
|
|
118
126
|
}
|
|
119
127
|
}
|
|
120
128
|
})
|
|
129
|
+
|
|
121
130
|
try {
|
|
122
131
|
this.allowLogin = false
|
|
123
132
|
if (await this.fetchUser()) {
|
|
@@ -132,6 +141,81 @@ export default DitoComponent.component('DitoRoot', {
|
|
|
132
141
|
},
|
|
133
142
|
|
|
134
143
|
methods: {
|
|
144
|
+
setupDragAndDrop() {
|
|
145
|
+
// This code only happens the visual effects around dragging and dropping
|
|
146
|
+
// files into a `DitoTypeUpload` component. The actual uploading is
|
|
147
|
+
// handled by the `DitoTypeUpload` component itself.
|
|
148
|
+
|
|
149
|
+
let dragCount = 0
|
|
150
|
+
let uploads = []
|
|
151
|
+
|
|
152
|
+
const toggleDropTargetClass = enabled => {
|
|
153
|
+
for (const upload of uploads) {
|
|
154
|
+
upload.closest('.dito-container').classList.toggle(
|
|
155
|
+
'dito-container--drop-target',
|
|
156
|
+
enabled
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
if (!enabled) {
|
|
160
|
+
uploads = []
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const setDraggingFiles = enabled => {
|
|
165
|
+
this.isDraggingFiles = enabled
|
|
166
|
+
if (enabled) {
|
|
167
|
+
toggleDropTargetClass(true)
|
|
168
|
+
} else {
|
|
169
|
+
setTimeout(() => toggleDropTargetClass(false), 250)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
this.domOn(document, {
|
|
174
|
+
dragenter: event => {
|
|
175
|
+
if (!dragCount && event.dataTransfer) {
|
|
176
|
+
uploads = document.querySelectorAll('.dito-upload')
|
|
177
|
+
const hasUploads = uploads.length > 0
|
|
178
|
+
event.dataTransfer.effectAllowed = hasUploads ? 'copy' : 'none'
|
|
179
|
+
if (hasUploads) {
|
|
180
|
+
setDraggingFiles(true)
|
|
181
|
+
} else {
|
|
182
|
+
event.preventDefault()
|
|
183
|
+
event.stopPropagation()
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
dragCount++
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
dragleave: event => {
|
|
191
|
+
dragCount--
|
|
192
|
+
if (!dragCount && event.dataTransfer) {
|
|
193
|
+
setDraggingFiles(false)
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
dragover: event => {
|
|
198
|
+
if (event.dataTransfer) {
|
|
199
|
+
const canDrop = event.target.closest(
|
|
200
|
+
'.dito-container:has(.dito-upload)'
|
|
201
|
+
)
|
|
202
|
+
event.dataTransfer.dropEffect = canDrop ? 'copy' : 'none'
|
|
203
|
+
if (!canDrop) {
|
|
204
|
+
event.preventDefault()
|
|
205
|
+
event.stopPropagation()
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
drop: event => {
|
|
211
|
+
dragCount = 0
|
|
212
|
+
if (event.dataTransfer) {
|
|
213
|
+
setDraggingFiles(false)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
},
|
|
218
|
+
|
|
135
219
|
notify({ type = 'info', title, text } = {}) {
|
|
136
220
|
title ||= (
|
|
137
221
|
{
|
|
@@ -389,4 +473,27 @@ function addRoutes(router, routes) {
|
|
|
389
473
|
background: $content-color-background;
|
|
390
474
|
}
|
|
391
475
|
}
|
|
476
|
+
|
|
477
|
+
.dito-drag-overlay {
|
|
478
|
+
position: fixed;
|
|
479
|
+
top: 0;
|
|
480
|
+
left: 0;
|
|
481
|
+
z-index: $drag-overlay-z-index;
|
|
482
|
+
width: 100%;
|
|
483
|
+
height: 100%;
|
|
484
|
+
background: rgba(0, 0, 0, 0.25);
|
|
485
|
+
pointer-events: none;
|
|
486
|
+
backdrop-filter: blur(8px);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.dito-drag-enter-active,
|
|
490
|
+
.dito-drag-leave-active {
|
|
491
|
+
transition: opacity 0.25s, backdrop-filter 0.25s;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.dito-drag-enter-from,
|
|
495
|
+
.dito-drag-leave-to {
|
|
496
|
+
opacity: 0;
|
|
497
|
+
backdrop-filter: blur(0);
|
|
498
|
+
}
|
|
392
499
|
</style>
|
package/src/mixins/TypeMixin.js
CHANGED
|
@@ -239,17 +239,15 @@ export default {
|
|
|
239
239
|
|
|
240
240
|
// @overridable
|
|
241
241
|
focusElement() {
|
|
242
|
-
const [element] = asArray(this.$refs.element)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
})
|
|
252
|
-
}
|
|
242
|
+
const [element = this.$el] = asArray(this.$refs.element)
|
|
243
|
+
this.$nextTick(() => {
|
|
244
|
+
element.focus?.()
|
|
245
|
+
// If the element is disabled, `focus()` will likely not have the
|
|
246
|
+
// desired effect. Use `scrollIntoView()` if available:
|
|
247
|
+
if (!element.focus || this.disabled) {
|
|
248
|
+
;(element.$el || element).scrollIntoView?.()
|
|
249
|
+
}
|
|
250
|
+
})
|
|
253
251
|
},
|
|
254
252
|
|
|
255
253
|
focus() {
|
package/src/styles/_button.scss
CHANGED
|
@@ -149,9 +149,9 @@
|
|
|
149
149
|
// For now, nothing for these:
|
|
150
150
|
// .dito-button-create:empty::before,
|
|
151
151
|
// .dito-button-add:empty::before
|
|
152
|
-
|
|
153
|
-
.dito-button-
|
|
154
|
-
@extend %icon-
|
|
152
|
+
|
|
153
|
+
.dito-button-upload:empty::before {
|
|
154
|
+
@extend %icon-upload;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
.dito-button-delete:empty::before,
|
|
@@ -66,6 +66,9 @@ $menu-padding-ver: $header-padding-ver - $menu-spacing;
|
|
|
66
66
|
$menu-padding-hor: $header-padding-hor - $menu-spacing;
|
|
67
67
|
$menu-padding: $menu-padding-ver $menu-padding-hor;
|
|
68
68
|
|
|
69
|
+
// Drag & Drop
|
|
70
|
+
$drag-overlay-z-index: 2000;
|
|
71
|
+
|
|
69
72
|
// Patterns
|
|
70
73
|
$pattern-transparency-size: ($font-size - $border-width) * $input-height-factor;
|
|
71
74
|
$pattern-transparency: repeating-conic-gradient(
|
|
@@ -73,13 +73,8 @@
|
|
|
73
73
|
v-if="isUploadActive"
|
|
74
74
|
type="button"
|
|
75
75
|
@click.prevent="upload.active = false"
|
|
76
|
-
) Cancel
|
|
77
|
-
|
|
78
|
-
v-else-if="isUploadReady"
|
|
79
|
-
type="button"
|
|
80
|
-
@click.prevent="upload.active = true"
|
|
81
|
-
) Upload All
|
|
82
|
-
VueUpload.dito-button.dito-button-add-upload(
|
|
76
|
+
) Cancel
|
|
77
|
+
VueUpload.dito-button(
|
|
83
78
|
ref="upload"
|
|
84
79
|
v-model="uploads"
|
|
85
80
|
:inputId="dataPath"
|
|
@@ -90,10 +85,13 @@
|
|
|
90
85
|
:accept="accept"
|
|
91
86
|
:multiple="multiple"
|
|
92
87
|
:size="maxSize"
|
|
93
|
-
title="Upload Files"
|
|
88
|
+
:title="multiple ? 'Upload Files' : 'Upload File'"
|
|
89
|
+
:drop="$el?.closest('.dito-container')"
|
|
90
|
+
:dropDirectory="true"
|
|
94
91
|
@input-filter="inputFilter"
|
|
95
92
|
@input-file="inputFile"
|
|
96
93
|
)
|
|
94
|
+
.dito-button-upload
|
|
97
95
|
</template>
|
|
98
96
|
|
|
99
97
|
<script>
|
|
@@ -169,7 +167,7 @@ export default DitoTypeComponent.register('upload', {
|
|
|
169
167
|
|
|
170
168
|
isUploadReady() {
|
|
171
169
|
return (
|
|
172
|
-
this.uploads.length &&
|
|
170
|
+
this.uploads.length > 0 &&
|
|
173
171
|
!(this.upload.active || this.upload.uploaded)
|
|
174
172
|
)
|
|
175
173
|
},
|
|
@@ -180,7 +178,7 @@ export default DitoTypeComponent.register('upload', {
|
|
|
180
178
|
|
|
181
179
|
uploadProgress() {
|
|
182
180
|
return (
|
|
183
|
-
this.uploads.reduce((total, file) =>
|
|
181
|
+
this.uploads.reduce((total, file) => +file.progress + total, 0) /
|
|
184
182
|
this.uploads.length
|
|
185
183
|
)
|
|
186
184
|
},
|
|
@@ -193,6 +191,17 @@ export default DitoTypeComponent.register('upload', {
|
|
|
193
191
|
}
|
|
194
192
|
},
|
|
195
193
|
|
|
194
|
+
watch: {
|
|
195
|
+
isUploadReady(ready) {
|
|
196
|
+
if (ready) {
|
|
197
|
+
// Auto-upload.
|
|
198
|
+
this.$nextTick(() => {
|
|
199
|
+
this.upload.active = true
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
|
|
196
205
|
methods: {
|
|
197
206
|
formatFileSize,
|
|
198
207
|
|
|
@@ -293,6 +302,7 @@ export default DitoTypeComponent.register('upload', {
|
|
|
293
302
|
this.removeFile(newFile)
|
|
294
303
|
}
|
|
295
304
|
} else if (error) {
|
|
305
|
+
this.removeFile(newFile)
|
|
296
306
|
const text = (
|
|
297
307
|
{
|
|
298
308
|
abort: 'Upload aborted',
|
|
@@ -310,7 +320,6 @@ export default DitoTypeComponent.register('upload', {
|
|
|
310
320
|
title: 'File Upload Error',
|
|
311
321
|
text
|
|
312
322
|
})
|
|
313
|
-
this.removeFile(newFile)
|
|
314
323
|
}
|
|
315
324
|
}
|
|
316
325
|
},
|
|
@@ -347,7 +356,7 @@ function asFiles(value) {
|
|
|
347
356
|
}
|
|
348
357
|
}
|
|
349
358
|
|
|
350
|
-
.dito-button-
|
|
359
|
+
.dito-button-upload {
|
|
351
360
|
padding: 0;
|
|
352
361
|
|
|
353
362
|
> * {
|