@ditojs/admin 2.3.2 → 2.4.1
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 +1778 -1491
- package/dist/dito-admin.umd.js +5 -4
- package/dist/style.css +1 -1
- package/package.json +5 -5
- package/src/components/DitoContainer.vue +2 -1
- package/src/components/DitoErrors.vue +52 -4
- package/src/components/DitoHeader.vue +2 -1
- package/src/components/DitoMenu.vue +9 -4
- package/src/components/DitoPane.vue +1 -1
- package/src/components/DitoRoot.vue +126 -1
- package/src/components/DitoSchema.vue +22 -3
- package/src/components/DitoTableHead.vue +1 -0
- package/src/components/DitoUploadFile.vue +197 -0
- package/src/components/index.js +1 -0
- package/src/mixins/DitoMixin.js +24 -5
- package/src/mixins/TypeMixin.js +22 -16
- package/src/mixins/ValidationMixin.js +9 -1
- package/src/mixins/ValidatorMixin.js +1 -5
- package/src/styles/_button.scss +3 -3
- package/src/styles/_info.scss +0 -16
- package/src/styles/_settings.scss +3 -0
- package/src/styles/_tippy.scss +39 -0
- package/src/styles/style.scss +2 -1
- package/src/types/DitoTypeList.vue +2 -2
- package/src/types/DitoTypeMarkup.vue +7 -4
- package/src/types/DitoTypeUpload.vue +209 -81
- package/src/utils/schema.js +1 -1
- package/src/utils/uid.js +7 -4
package/src/mixins/TypeMixin.js
CHANGED
|
@@ -3,7 +3,7 @@ import ValidationMixin from './ValidationMixin.js'
|
|
|
3
3
|
import { getSchemaAccessor } from '../utils/accessor.js'
|
|
4
4
|
import { computeValue } from '../utils/schema.js'
|
|
5
5
|
import { getItem, getParentItem } from '../utils/data.js'
|
|
6
|
-
import {
|
|
6
|
+
import { camelize } from '@ditojs/utils'
|
|
7
7
|
|
|
8
8
|
// @vue/component
|
|
9
9
|
export default {
|
|
@@ -238,24 +238,30 @@ export default {
|
|
|
238
238
|
},
|
|
239
239
|
|
|
240
240
|
// @overridable
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (this.disabled) {
|
|
249
|
-
;(element.$el || element).scrollIntoView?.()
|
|
250
|
-
}
|
|
251
|
-
})
|
|
252
|
-
}
|
|
241
|
+
async scrollIntoView() {
|
|
242
|
+
await this.focusSchema()
|
|
243
|
+
const { element = this } = this.$refs
|
|
244
|
+
;(element.$el || element).scrollIntoView?.({
|
|
245
|
+
behavior: 'smooth',
|
|
246
|
+
block: 'center'
|
|
247
|
+
})
|
|
253
248
|
},
|
|
254
249
|
|
|
255
|
-
|
|
250
|
+
// @overridable
|
|
251
|
+
async focusElement() {
|
|
252
|
+
await this.focusSchema()
|
|
253
|
+
const { element = this } = this.$refs
|
|
254
|
+
;(element.$el || element).focus?.()
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
async focusSchema() {
|
|
256
258
|
// Also focus this component's schema and panel in case it's a tab.
|
|
257
|
-
this.schemaComponent.focus()
|
|
258
|
-
this.tabComponent?.focus()
|
|
259
|
+
await this.schemaComponent.focus()
|
|
260
|
+
await this.tabComponent?.focus()
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
focus() {
|
|
264
|
+
this.scrollIntoView()
|
|
259
265
|
this.focusElement()
|
|
260
266
|
},
|
|
261
267
|
|
|
@@ -15,6 +15,12 @@ export default {
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
|
|
18
|
+
computed: {
|
|
19
|
+
hasErrors() {
|
|
20
|
+
return !!this.errors
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
|
|
18
24
|
methods: {
|
|
19
25
|
resetValidation() {
|
|
20
26
|
this.isTouched = false
|
|
@@ -60,6 +66,8 @@ export default {
|
|
|
60
66
|
|
|
61
67
|
markTouched() {
|
|
62
68
|
this.isTouched = true
|
|
69
|
+
// Clear currently displayed errors when focusing input.
|
|
70
|
+
this.clearErrors()
|
|
63
71
|
},
|
|
64
72
|
|
|
65
73
|
markDirty() {
|
|
@@ -87,7 +95,7 @@ export default {
|
|
|
87
95
|
this.addError(message, true)
|
|
88
96
|
}
|
|
89
97
|
if (focus) {
|
|
90
|
-
this.
|
|
98
|
+
this.scrollIntoView()
|
|
91
99
|
}
|
|
92
100
|
return true
|
|
93
101
|
},
|
|
@@ -6,11 +6,7 @@ export default {
|
|
|
6
6
|
|
|
7
7
|
computed: {
|
|
8
8
|
errors() {
|
|
9
|
-
return this.schemaComponents.
|
|
10
|
-
(result, { errors }) =>
|
|
11
|
-
errors && result ? result.concat(errors) : errors,
|
|
12
|
-
null
|
|
13
|
-
)
|
|
9
|
+
return this.schemaComponents.flatMap(({ errors }) => errors || [])
|
|
14
10
|
},
|
|
15
11
|
|
|
16
12
|
isTouched() {
|
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,
|
package/src/styles/_info.scss
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
@import 'tippy.js/animations/shift-away-subtle.css';
|
|
2
|
-
|
|
3
1
|
.dito-info {
|
|
4
2
|
--size: calc(1em * var(--line-height));
|
|
5
3
|
|
|
@@ -19,17 +17,3 @@
|
|
|
19
17
|
width: var(--size);
|
|
20
18
|
}
|
|
21
19
|
}
|
|
22
|
-
|
|
23
|
-
.tippy-box[data-theme~='info'] {
|
|
24
|
-
background-color: $color-active;
|
|
25
|
-
color: $color-white;
|
|
26
|
-
filter: drop-shadow(0 2px 4px $color-shadow);
|
|
27
|
-
|
|
28
|
-
> .tippy-arrow::before {
|
|
29
|
-
color: $color-active;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.tippy-content {
|
|
33
|
-
white-space: pre-line;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -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(
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@use 'sass:color';
|
|
2
|
+
@import 'tippy.js/animations/shift-away-subtle.css';
|
|
3
|
+
|
|
4
|
+
.tippy-box {
|
|
5
|
+
&[data-theme] {
|
|
6
|
+
--color: #{$color-active};
|
|
7
|
+
|
|
8
|
+
font-size: unset;
|
|
9
|
+
line-height: unset;
|
|
10
|
+
background-color: var(--color);
|
|
11
|
+
color: $color-white;
|
|
12
|
+
filter: drop-shadow(0 2px 4px $color-shadow);
|
|
13
|
+
|
|
14
|
+
.tippy-content {
|
|
15
|
+
white-space: pre-line;
|
|
16
|
+
padding: ($input-padding-ver + 2 * $border-width)
|
|
17
|
+
($input-padding-hor + $border-width);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
> .tippy-arrow::before {
|
|
21
|
+
color: var(--color);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&[data-theme~='info'] {
|
|
26
|
+
--color: #{color.adjust($color-active, $lightness: 5%)};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&[data-theme~='error'] {
|
|
30
|
+
--color: #{color.adjust($color-error, $lightness: 5%)};
|
|
31
|
+
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
|
|
34
|
+
> .tippy-arrow {
|
|
35
|
+
transform: unset !important;
|
|
36
|
+
left: 16px !important;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/styles/style.scss
CHANGED
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
:dataPath="getDataPath(index)"
|
|
81
81
|
:data="item"
|
|
82
82
|
:meta="nestedMeta"
|
|
83
|
-
:store="getChildStore(index)"
|
|
83
|
+
:store="getChildStore(getItemUid(schema, item), index)"
|
|
84
84
|
:disabled="disabled || isLoading"
|
|
85
85
|
:collapsed="collapsed"
|
|
86
86
|
:collapsible="collapsible"
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
:dataPath="getDataPath(index)"
|
|
118
118
|
:data="item"
|
|
119
119
|
:meta="nestedMeta"
|
|
120
|
-
:store="getChildStore(index)"
|
|
120
|
+
:store="getChildStore(getItemUid(schema, item), index)"
|
|
121
121
|
@delete="deleteItem(item, index)"
|
|
122
122
|
)
|
|
123
123
|
//- Render create buttons inside table when not in a single component view:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
.dito-markup(:id="dataPath")
|
|
3
|
-
.dito-markup-toolbar
|
|
3
|
+
.dito-markup-toolbar
|
|
4
4
|
.dito-buttons.dito-buttons-toolbar(
|
|
5
5
|
v-if="groupedButtons.length > 0"
|
|
6
6
|
)
|
|
@@ -379,6 +379,7 @@ export default DitoTypeComponent.register('markup', {
|
|
|
379
379
|
|
|
380
380
|
getButtons(settingsName, descriptions) {
|
|
381
381
|
const list = []
|
|
382
|
+
const { commands } = this.editor
|
|
382
383
|
|
|
383
384
|
const addButton = ({ name, icon, attributes, ignoreActive, onClick }) => {
|
|
384
385
|
list.push({
|
|
@@ -389,8 +390,11 @@ export default DitoTypeComponent.register('markup', {
|
|
|
389
390
|
(ignoreActive == null || !ignoreActive())
|
|
390
391
|
),
|
|
391
392
|
onClick: () => {
|
|
392
|
-
const key =
|
|
393
|
-
|
|
393
|
+
const key =
|
|
394
|
+
name in commands
|
|
395
|
+
? name
|
|
396
|
+
: `toggle${camelize(name, true)}`
|
|
397
|
+
if (key in commands) {
|
|
394
398
|
const command = attributes =>
|
|
395
399
|
this.editor.chain()[key](attributes).focus().run()
|
|
396
400
|
onClick
|
|
@@ -439,7 +443,6 @@ export default DitoTypeComponent.register('markup', {
|
|
|
439
443
|
},
|
|
440
444
|
|
|
441
445
|
focusElement() {
|
|
442
|
-
this.$el.scrollIntoView?.()
|
|
443
446
|
this.editor.focus()
|
|
444
447
|
}
|
|
445
448
|
}
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
.dito-upload
|
|
3
|
+
//- In order to handle upload buttons in multiple possible places, depending
|
|
4
|
+
//- on whether they handle single or multiple uploads, render the upload
|
|
5
|
+
//- component invisibly at the root, and delegate the click events to it from
|
|
6
|
+
//- the buttons rendered further below. Luckily this works surprisingly well.
|
|
7
|
+
VueUpload.dito-upload-input(
|
|
8
|
+
ref="upload"
|
|
9
|
+
v-model="uploads"
|
|
10
|
+
:inputId="dataPath"
|
|
11
|
+
:name="dataPath"
|
|
12
|
+
:disabled="disabled"
|
|
13
|
+
:postAction="uploadPath"
|
|
14
|
+
:extensions="extensions"
|
|
15
|
+
:accept="accept"
|
|
16
|
+
:multiple="multiple"
|
|
17
|
+
:size="maxSize"
|
|
18
|
+
:drop="$el?.closest('.dito-container')"
|
|
19
|
+
:dropDirectory="true"
|
|
20
|
+
@input-filter="onInputFilter"
|
|
21
|
+
@input-file="onInputFile"
|
|
22
|
+
)
|
|
3
23
|
table.dito-table.dito-table-separators.dito-table-background
|
|
4
|
-
//- Styling comes from DitoTableHead
|
|
24
|
+
//- Styling comes from `DitoTableHead`
|
|
5
25
|
thead.dito-table-head
|
|
6
26
|
tr
|
|
7
27
|
th
|
|
8
|
-
span
|
|
28
|
+
span File
|
|
9
29
|
th
|
|
10
30
|
span Size
|
|
11
31
|
th
|
|
@@ -18,48 +38,81 @@
|
|
|
18
38
|
:options="getSortableOptions(false)"
|
|
19
39
|
:draggable="draggable"
|
|
20
40
|
)
|
|
21
|
-
|
|
22
|
-
v-
|
|
23
|
-
:key="file.key"
|
|
41
|
+
template(
|
|
42
|
+
v-if="multiple || !isUploadActive"
|
|
24
43
|
)
|
|
25
|
-
|
|
26
|
-
v-
|
|
44
|
+
tr(
|
|
45
|
+
v-for="(file, index) in files"
|
|
46
|
+
:key="file.key"
|
|
27
47
|
)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
v-if="file.upload"
|
|
48
|
+
td(
|
|
49
|
+
v-if="render"
|
|
50
|
+
v-html="renderFile(file, index)"
|
|
32
51
|
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
template(
|
|
42
|
-
v-else-if="file.upload.success"
|
|
52
|
+
td(
|
|
53
|
+
v-else-if="downloadUrls[index]"
|
|
54
|
+
)
|
|
55
|
+
a(
|
|
56
|
+
:download="file.name"
|
|
57
|
+
:href="downloadUrls[index]"
|
|
58
|
+
target="_blank"
|
|
59
|
+
@click.prevent="onClickDownload(file, index)"
|
|
43
60
|
)
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
DitoUploadFile(
|
|
62
|
+
:file="file"
|
|
63
|
+
:thumbnail="thumbnails"
|
|
64
|
+
:thumbnailUrl="thumbnailUrls[index]"
|
|
65
|
+
)
|
|
66
|
+
td(
|
|
46
67
|
v-else
|
|
47
68
|
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
a.dito-button(
|
|
53
|
-
v-if="draggable"
|
|
54
|
-
v-bind="getButtonAttributes(verbs.drag)"
|
|
69
|
+
DitoUploadFile(
|
|
70
|
+
:file="file"
|
|
71
|
+
:thumbnail="thumbnails"
|
|
72
|
+
:thumbnailUrl="thumbnailUrls[index]"
|
|
55
73
|
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
v-
|
|
60
|
-
@click="deleteFile(file, index)"
|
|
74
|
+
td {{ formatFileSize(file.size) }}
|
|
75
|
+
td
|
|
76
|
+
template(
|
|
77
|
+
v-if="file.upload"
|
|
61
78
|
)
|
|
62
|
-
|
|
79
|
+
template(
|
|
80
|
+
v-if="file.upload.error"
|
|
81
|
+
)
|
|
82
|
+
| Error: {{ file.upload.error }}
|
|
83
|
+
template(
|
|
84
|
+
v-else-if="file.upload.active"
|
|
85
|
+
)
|
|
86
|
+
| Uploading...
|
|
87
|
+
template(
|
|
88
|
+
v-else-if="file.upload.success"
|
|
89
|
+
)
|
|
90
|
+
| Uploaded
|
|
91
|
+
template(
|
|
92
|
+
v-else
|
|
93
|
+
)
|
|
94
|
+
| Stored
|
|
95
|
+
td.dito-cell-edit-buttons
|
|
96
|
+
.dito-buttons.dito-buttons-round
|
|
97
|
+
button.dito-button.dito-button-upload(
|
|
98
|
+
v-if="!multiple"
|
|
99
|
+
:title="uploadTitle"
|
|
100
|
+
@click="onClickUpload"
|
|
101
|
+
)
|
|
102
|
+
//- Firefox doesn't like <button> here, so use <a> instead:
|
|
103
|
+
a.dito-button(
|
|
104
|
+
v-if="draggable"
|
|
105
|
+
v-bind="getButtonAttributes(verbs.drag)"
|
|
106
|
+
)
|
|
107
|
+
button.dito-button(
|
|
108
|
+
v-if="deletable"
|
|
109
|
+
type="button"
|
|
110
|
+
v-bind="getButtonAttributes(verbs.delete)"
|
|
111
|
+
@click="deleteFile(file, index)"
|
|
112
|
+
)
|
|
113
|
+
tfoot(
|
|
114
|
+
v-if="multiple || isUploadActive || !hasFiles"
|
|
115
|
+
)
|
|
63
116
|
tr
|
|
64
117
|
td(:colspan="4")
|
|
65
118
|
.dito-upload-footer
|
|
@@ -73,26 +126,11 @@
|
|
|
73
126
|
v-if="isUploadActive"
|
|
74
127
|
type="button"
|
|
75
128
|
@click.prevent="upload.active = false"
|
|
76
|
-
) Cancel
|
|
77
|
-
button.dito-button(
|
|
78
|
-
v-
|
|
79
|
-
|
|
80
|
-
@click
|
|
81
|
-
) Upload All
|
|
82
|
-
VueUpload.dito-button.dito-button-add-upload(
|
|
83
|
-
ref="upload"
|
|
84
|
-
v-model="uploads"
|
|
85
|
-
:inputId="dataPath"
|
|
86
|
-
:name="dataPath"
|
|
87
|
-
:disabled="disabled"
|
|
88
|
-
:postAction="uploadPath"
|
|
89
|
-
:extensions="extensions"
|
|
90
|
-
:accept="accept"
|
|
91
|
-
:multiple="multiple"
|
|
92
|
-
:size="maxSize"
|
|
93
|
-
title="Upload Files"
|
|
94
|
-
@input-filter="inputFilter"
|
|
95
|
-
@input-file="inputFile"
|
|
129
|
+
) Cancel
|
|
130
|
+
button.dito-button.dito-button-upload(
|
|
131
|
+
v-if="multiple || !hasFiles"
|
|
132
|
+
:title="uploadTitle"
|
|
133
|
+
@click="onClickUpload"
|
|
96
134
|
)
|
|
97
135
|
</template>
|
|
98
136
|
|
|
@@ -104,7 +142,7 @@ import parseFileSize from 'filesize-parser'
|
|
|
104
142
|
import { getSchemaAccessor } from '../utils/accessor.js'
|
|
105
143
|
import { formatFileSize } from '../utils/units.js'
|
|
106
144
|
import { appendDataPath } from '../utils/data.js'
|
|
107
|
-
import { isArray, asArray
|
|
145
|
+
import { isArray, asArray } from '@ditojs/utils'
|
|
108
146
|
import VueUpload from 'vue-upload-component'
|
|
109
147
|
|
|
110
148
|
// @vue/component
|
|
@@ -125,10 +163,22 @@ export default DitoTypeComponent.register('upload', {
|
|
|
125
163
|
return this.$refs.upload
|
|
126
164
|
},
|
|
127
165
|
|
|
166
|
+
uploadTitle() {
|
|
167
|
+
return this.multiple ? 'Upload Files' : 'Upload File'
|
|
168
|
+
},
|
|
169
|
+
|
|
128
170
|
files() {
|
|
129
171
|
return asFiles(this.value)
|
|
130
172
|
},
|
|
131
173
|
|
|
174
|
+
downloadUrls() {
|
|
175
|
+
return this.files.map((file, index) => this.getDownloadUrl(file, index))
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
thumbnailUrls() {
|
|
179
|
+
return this.files.map((file, index) => this.getThumbnailUrl(file, index))
|
|
180
|
+
},
|
|
181
|
+
|
|
132
182
|
multiple: getSchemaAccessor('multiple', {
|
|
133
183
|
type: Boolean,
|
|
134
184
|
default: false,
|
|
@@ -167,20 +217,43 @@ export default DitoTypeComponent.register('upload', {
|
|
|
167
217
|
default: false
|
|
168
218
|
}),
|
|
169
219
|
|
|
220
|
+
render: getSchemaAccessor('render', {
|
|
221
|
+
type: Function,
|
|
222
|
+
default: null
|
|
223
|
+
}),
|
|
224
|
+
|
|
225
|
+
thumbnails: getSchemaAccessor('thumbnails', {
|
|
226
|
+
type: [Boolean, String],
|
|
227
|
+
default(thumbnails) {
|
|
228
|
+
return thumbnails ?? !!this.schema.thumbnailUrl
|
|
229
|
+
},
|
|
230
|
+
get(thumbnails) {
|
|
231
|
+
return thumbnails === true ? 'medium' : thumbnails || null
|
|
232
|
+
}
|
|
233
|
+
}),
|
|
234
|
+
|
|
235
|
+
hasFiles() {
|
|
236
|
+
return this.files.length > 0
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
hasUploads() {
|
|
240
|
+
return this.uploads.length > 0
|
|
241
|
+
},
|
|
242
|
+
|
|
170
243
|
isUploadReady() {
|
|
171
244
|
return (
|
|
172
|
-
this.
|
|
245
|
+
this.hasUploads &&
|
|
173
246
|
!(this.upload.active || this.upload.uploaded)
|
|
174
247
|
)
|
|
175
248
|
},
|
|
176
249
|
|
|
177
250
|
isUploadActive() {
|
|
178
|
-
return this.
|
|
251
|
+
return this.hasUploads && this.upload.active
|
|
179
252
|
},
|
|
180
253
|
|
|
181
254
|
uploadProgress() {
|
|
182
255
|
return (
|
|
183
|
-
this.uploads.reduce((total, file) => total + file.progress, 0) /
|
|
256
|
+
this.uploads.reduce((total, file) => total + +file.progress, 0) /
|
|
184
257
|
this.uploads.length
|
|
185
258
|
)
|
|
186
259
|
},
|
|
@@ -193,22 +266,59 @@ export default DitoTypeComponent.register('upload', {
|
|
|
193
266
|
}
|
|
194
267
|
},
|
|
195
268
|
|
|
269
|
+
watch: {
|
|
270
|
+
isUploadReady(ready) {
|
|
271
|
+
if (ready) {
|
|
272
|
+
// Auto-upload.
|
|
273
|
+
this.$nextTick(() => {
|
|
274
|
+
this.upload.active = true
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
|
|
196
280
|
methods: {
|
|
197
281
|
formatFileSize,
|
|
198
282
|
|
|
283
|
+
getFileContext(file, index) {
|
|
284
|
+
return this.multiple
|
|
285
|
+
? new DitoContext(this, {
|
|
286
|
+
value: file,
|
|
287
|
+
data: this.files,
|
|
288
|
+
index,
|
|
289
|
+
dataPath: appendDataPath(this.dataPath, index)
|
|
290
|
+
})
|
|
291
|
+
: this.context
|
|
292
|
+
},
|
|
293
|
+
|
|
199
294
|
renderFile(file, index) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
295
|
+
return this.render(this.getFileContext(file, index))
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
getDownloadUrl(file, index) {
|
|
299
|
+
return file.url
|
|
300
|
+
? file.url
|
|
301
|
+
: !file.upload || file.upload.success
|
|
302
|
+
? this.getSchemaValue('downloadUrl', {
|
|
303
|
+
type: 'String',
|
|
304
|
+
default: null,
|
|
305
|
+
context: this.getFileContext(file, index)
|
|
209
306
|
})
|
|
307
|
+
: null
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
getThumbnailUrl(file, index) {
|
|
311
|
+
return !file.upload || file.upload.success
|
|
312
|
+
? this.getSchemaValue('thumbnailUrl', {
|
|
313
|
+
type: 'String',
|
|
314
|
+
default: null,
|
|
315
|
+
context: this.getFileContext(file, index)
|
|
316
|
+
}) || (
|
|
317
|
+
file.type.startsWith('image/')
|
|
318
|
+
? file.url
|
|
319
|
+
: null
|
|
210
320
|
)
|
|
211
|
-
:
|
|
321
|
+
: null
|
|
212
322
|
},
|
|
213
323
|
|
|
214
324
|
deleteFile(file, index) {
|
|
@@ -274,7 +384,7 @@ export default DitoTypeComponent.register('upload', {
|
|
|
274
384
|
this.replaceFile(file, null)
|
|
275
385
|
},
|
|
276
386
|
|
|
277
|
-
|
|
387
|
+
onInputFile(newFile, oldFile) {
|
|
278
388
|
if (newFile && !oldFile) {
|
|
279
389
|
const { id, name, size } = newFile
|
|
280
390
|
this.addFile({ id, name, size, upload: newFile })
|
|
@@ -293,6 +403,7 @@ export default DitoTypeComponent.register('upload', {
|
|
|
293
403
|
this.removeFile(newFile)
|
|
294
404
|
}
|
|
295
405
|
} else if (error) {
|
|
406
|
+
this.removeFile(newFile)
|
|
296
407
|
const text = (
|
|
297
408
|
{
|
|
298
409
|
abort: 'Upload aborted',
|
|
@@ -310,16 +421,35 @@ export default DitoTypeComponent.register('upload', {
|
|
|
310
421
|
title: 'File Upload Error',
|
|
311
422
|
text
|
|
312
423
|
})
|
|
313
|
-
this.removeFile(newFile)
|
|
314
424
|
}
|
|
315
425
|
}
|
|
316
426
|
},
|
|
317
427
|
|
|
318
|
-
|
|
428
|
+
onInputFilter(newFile /*, oldFile, prevent */) {
|
|
319
429
|
const xhr = newFile?.xhr
|
|
320
430
|
if (this.api.cors?.credentials && xhr && !xhr.withCredentials) {
|
|
321
431
|
xhr.withCredentials = true
|
|
322
432
|
}
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
async onClickDownload(file, index) {
|
|
436
|
+
try {
|
|
437
|
+
const response = await fetch(this.downloadUrls[index])
|
|
438
|
+
const blob = await response.blob()
|
|
439
|
+
this.download({
|
|
440
|
+
filename: file.name,
|
|
441
|
+
url: URL.createObjectURL(blob)
|
|
442
|
+
})
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error(error)
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
onClickUpload(event) {
|
|
449
|
+
// Delegate the click event to the hidden file input.
|
|
450
|
+
this.upload.$el.querySelector('input').dispatchEvent(
|
|
451
|
+
new event.constructor(event.type, event)
|
|
452
|
+
)
|
|
323
453
|
}
|
|
324
454
|
},
|
|
325
455
|
|
|
@@ -342,18 +472,16 @@ function asFiles(value) {
|
|
|
342
472
|
|
|
343
473
|
.dito-upload {
|
|
344
474
|
.dito-table {
|
|
345
|
-
tr
|
|
475
|
+
tr,
|
|
476
|
+
.dito-cell-edit-buttons {
|
|
346
477
|
vertical-align: middle;
|
|
347
478
|
}
|
|
348
479
|
}
|
|
349
480
|
|
|
350
|
-
.dito-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
position: absolute;
|
|
355
|
-
cursor: pointer;
|
|
356
|
-
}
|
|
481
|
+
.dito-upload-input {
|
|
482
|
+
// See `onClickUpload()` method for details.
|
|
483
|
+
position: absolute;
|
|
484
|
+
pointer-events: none;
|
|
357
485
|
}
|
|
358
486
|
|
|
359
487
|
.dito-upload-footer {
|
package/src/utils/schema.js
CHANGED
|
@@ -1042,5 +1042,5 @@ export function getItemUid(sourceSchema, item) {
|
|
|
1042
1042
|
// either way, pass through `getUid()` so that the ids are associated with the
|
|
1043
1043
|
// item through a weak map, as the ids can be filtered out in `processData()`
|
|
1044
1044
|
// while the components that use the uids as key are still visible.
|
|
1045
|
-
return getUid(item, getItemId(sourceSchema, item))
|
|
1045
|
+
return getUid(item, item => getItemId(sourceSchema, item))
|
|
1046
1046
|
}
|
package/src/utils/uid.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { toRaw } from 'vue'
|
|
2
|
+
|
|
1
3
|
const uidMap = new WeakMap()
|
|
2
4
|
|
|
3
5
|
// Generated and remembers unique ids per passed object using a weak map.
|
|
4
6
|
let uid = 0
|
|
5
|
-
export function getUid(
|
|
6
|
-
|
|
7
|
+
export function getUid(item, getItemId = null) {
|
|
8
|
+
const raw = toRaw(item)
|
|
9
|
+
let id = uidMap.get(raw)
|
|
7
10
|
if (!id) {
|
|
8
|
-
id =
|
|
9
|
-
uidMap.set(
|
|
11
|
+
id = getItemId?.(item) || `@${++uid}`
|
|
12
|
+
uidMap.set(raw, id)
|
|
10
13
|
}
|
|
11
14
|
return id
|
|
12
15
|
}
|