@ditojs/admin 2.26.0 → 2.26.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 +1420 -1389
- package/dist/dito-admin.umd.js +5 -5
- package/dist/style.css +1 -1
- package/package.json +29 -29
- package/src/DitoContext.js +6 -0
- package/src/components/DitoForm.vue +60 -44
- package/src/components/{DitoFormNested.vue → DitoFormInlined.vue} +3 -3
- package/src/components/index.js +1 -1
- package/src/mixins/DitoMixin.js +24 -18
- package/src/mixins/EmitterMixin.js +2 -0
- package/src/mixins/RouteMixin.js +0 -4
- package/src/mixins/SourceMixin.js +1 -1
- package/src/styles/_table.scss +1 -1
- package/src/utils/schema.js +26 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/admin",
|
|
3
|
-
"version": "2.26.
|
|
3
|
+
"version": "2.26.1",
|
|
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,31 +33,31 @@
|
|
|
33
33
|
"not ie_mob > 0"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ditojs/ui": "^2.26.
|
|
37
|
-
"@ditojs/utils": "^2.26.
|
|
36
|
+
"@ditojs/ui": "^2.26.1",
|
|
37
|
+
"@ditojs/utils": "^2.26.1",
|
|
38
38
|
"@kyvg/vue3-notification": "^3.2.1",
|
|
39
39
|
"@lk77/vue3-color": "^3.0.6",
|
|
40
|
-
"@tiptap/core": "^2.
|
|
41
|
-
"@tiptap/extension-blockquote": "^2.
|
|
42
|
-
"@tiptap/extension-bold": "^2.
|
|
43
|
-
"@tiptap/extension-bullet-list": "^2.
|
|
44
|
-
"@tiptap/extension-code": "^2.
|
|
45
|
-
"@tiptap/extension-code-block": "^2.
|
|
46
|
-
"@tiptap/extension-document": "^2.
|
|
47
|
-
"@tiptap/extension-hard-break": "^2.
|
|
48
|
-
"@tiptap/extension-heading": "^2.
|
|
49
|
-
"@tiptap/extension-history": "^2.
|
|
50
|
-
"@tiptap/extension-horizontal-rule": "^2.
|
|
51
|
-
"@tiptap/extension-italic": "^2.
|
|
52
|
-
"@tiptap/extension-link": "^2.
|
|
53
|
-
"@tiptap/extension-list-item": "^2.
|
|
54
|
-
"@tiptap/extension-ordered-list": "^2.
|
|
55
|
-
"@tiptap/extension-paragraph": "^2.
|
|
56
|
-
"@tiptap/extension-strike": "^2.
|
|
57
|
-
"@tiptap/extension-text": "^2.
|
|
58
|
-
"@tiptap/extension-underline": "^2.
|
|
59
|
-
"@tiptap/pm": "^2.
|
|
60
|
-
"@tiptap/vue-3": "^2.
|
|
40
|
+
"@tiptap/core": "^2.3.0",
|
|
41
|
+
"@tiptap/extension-blockquote": "^2.3.0",
|
|
42
|
+
"@tiptap/extension-bold": "^2.3.0",
|
|
43
|
+
"@tiptap/extension-bullet-list": "^2.3.0",
|
|
44
|
+
"@tiptap/extension-code": "^2.3.0",
|
|
45
|
+
"@tiptap/extension-code-block": "^2.3.0",
|
|
46
|
+
"@tiptap/extension-document": "^2.3.0",
|
|
47
|
+
"@tiptap/extension-hard-break": "^2.3.0",
|
|
48
|
+
"@tiptap/extension-heading": "^2.3.0",
|
|
49
|
+
"@tiptap/extension-history": "^2.3.0",
|
|
50
|
+
"@tiptap/extension-horizontal-rule": "^2.3.0",
|
|
51
|
+
"@tiptap/extension-italic": "^2.3.0",
|
|
52
|
+
"@tiptap/extension-link": "^2.3.0",
|
|
53
|
+
"@tiptap/extension-list-item": "^2.3.0",
|
|
54
|
+
"@tiptap/extension-ordered-list": "^2.3.0",
|
|
55
|
+
"@tiptap/extension-paragraph": "^2.3.0",
|
|
56
|
+
"@tiptap/extension-strike": "^2.3.0",
|
|
57
|
+
"@tiptap/extension-text": "^2.3.0",
|
|
58
|
+
"@tiptap/extension-underline": "^2.3.0",
|
|
59
|
+
"@tiptap/pm": "^2.3.0",
|
|
60
|
+
"@tiptap/vue-3": "^2.3.0",
|
|
61
61
|
"@vueuse/integrations": "^10.9.0",
|
|
62
62
|
"codeflask": "^1.4.1",
|
|
63
63
|
"filesize": "^10.1.1",
|
|
@@ -69,21 +69,21 @@
|
|
|
69
69
|
"tippy.js": "^6.3.7",
|
|
70
70
|
"type-fest": "^4.15.0",
|
|
71
71
|
"vue": "3.4.10",
|
|
72
|
-
"vue-multiselect": "^3.0.0
|
|
72
|
+
"vue-multiselect": "^3.0.0",
|
|
73
73
|
"vue-router": "^4.3.0",
|
|
74
74
|
"vue-upload-component": "^3.1.15"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@ditojs/build": "^2.26.
|
|
77
|
+
"@ditojs/build": "^2.26.1",
|
|
78
78
|
"@vitejs/plugin-vue": "^5.0.4",
|
|
79
79
|
"@vue/compiler-sfc": "3.4.21",
|
|
80
80
|
"pug": "^3.0.2",
|
|
81
|
-
"sass": "1.
|
|
82
|
-
"typescript": "^5.4.
|
|
81
|
+
"sass": "1.75.0",
|
|
82
|
+
"typescript": "^5.4.5",
|
|
83
83
|
"vite": "^5.2.8"
|
|
84
84
|
},
|
|
85
85
|
"types": "types",
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "8f2e07e3774d0e03b24ee78cffe60c5edacb93ae",
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "vite build",
|
|
89
89
|
"watch": "yarn build --mode 'development' --watch",
|
package/src/DitoContext.js
CHANGED
|
@@ -61,6 +61,12 @@ export default class DitoContext {
|
|
|
61
61
|
: new DitoContext(component, context)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
extend(object) {
|
|
65
|
+
// Create a copy of this context that inherits from the real one, but
|
|
66
|
+
// overrides some properties with the ones from the passed `object`.
|
|
67
|
+
return Object.setPrototypeOf(object, this)
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
// `nested` is `true` when the data-path points a value inside an item, and
|
|
65
71
|
// `false` when it points to the item itself.
|
|
66
72
|
get nested() {
|
|
@@ -1,66 +1,82 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
+
DefineTemplate
|
|
3
|
+
//- Prevent implicit submission of the form, for example when typing enter
|
|
4
|
+
//- in an input field.
|
|
5
|
+
//- https://stackoverflow.com/a/51507806
|
|
6
|
+
button(
|
|
7
|
+
v-show="false"
|
|
8
|
+
type="submit"
|
|
9
|
+
disabled
|
|
10
|
+
)
|
|
11
|
+
DitoSchema(
|
|
12
|
+
:schema="schema"
|
|
13
|
+
:dataPath="dataPath"
|
|
14
|
+
:data="data"
|
|
15
|
+
:meta="meta"
|
|
16
|
+
:store="store"
|
|
17
|
+
:padding="isInlinedSource ? 'nested' : 'root'"
|
|
18
|
+
:disabled="isLoading"
|
|
19
|
+
:scrollable="!isInlinedSource"
|
|
20
|
+
generateLabels
|
|
21
|
+
)
|
|
22
|
+
template(#buttons)
|
|
23
|
+
DitoButtons.dito-buttons-round.dito-buttons-large.dito-buttons-main(
|
|
24
|
+
:class="{ 'dito-buttons-sticky': !isInlinedSource }"
|
|
25
|
+
:buttons="buttonSchemas"
|
|
26
|
+
:dataPath="dataPath"
|
|
27
|
+
:data="data"
|
|
28
|
+
:meta="meta"
|
|
29
|
+
:store="store"
|
|
30
|
+
:disabled="isLoading"
|
|
31
|
+
)
|
|
32
|
+
|
|
2
33
|
.dito-form.dito-scroll-parent(
|
|
3
|
-
:class="{ 'dito-form-
|
|
34
|
+
:class="{ 'dito-form-inlined': isInlinedSource }"
|
|
4
35
|
:data-resource="sourceSchema.path"
|
|
5
36
|
)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
//- editing. Only render a router-view here if this isn't the last data route
|
|
9
|
-
//- and not a nested form route, which will appear elsewhere in its own view.
|
|
10
|
-
RouterView(
|
|
11
|
-
v-if="!(isLastUnnestedRoute || isNestedRoute)"
|
|
12
|
-
v-show="!isActive"
|
|
37
|
+
template(
|
|
38
|
+
v-if="isInlinedSource"
|
|
13
39
|
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
v-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
40
|
+
//- Use a <div> for inlined forms, as we shouldn't nest actual <form> tags.
|
|
41
|
+
//- NOTE: inlined form components are kept alive by using `v-show` instead
|
|
42
|
+
//- of `v-if` here, so event handling and other things still work with
|
|
43
|
+
//- inlined editing.
|
|
44
|
+
div(
|
|
45
|
+
v-show="isActive"
|
|
46
|
+
)
|
|
47
|
+
ReuseTemplate
|
|
48
|
+
template(
|
|
49
|
+
v-else
|
|
20
50
|
)
|
|
21
|
-
//-
|
|
22
|
-
//- in
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
v-show="
|
|
26
|
-
type="submit"
|
|
27
|
-
disabled
|
|
51
|
+
//- Only render a router-view here if this isn't the last data route and not
|
|
52
|
+
//- an inlined form route, which will appear elsewhere in its own view.
|
|
53
|
+
RouterView(
|
|
54
|
+
v-if="!isLastUnnestedRoute"
|
|
55
|
+
v-show="!isActive"
|
|
28
56
|
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
:data="data"
|
|
33
|
-
:meta="meta"
|
|
34
|
-
:store="store"
|
|
35
|
-
:padding="isNestedRoute ? 'nested' : 'root'"
|
|
36
|
-
:disabled="isLoading"
|
|
37
|
-
:scrollable="isRootForm"
|
|
38
|
-
generateLabels
|
|
57
|
+
form.dito-scroll-parent(
|
|
58
|
+
v-show="isActive"
|
|
59
|
+
@submit.prevent
|
|
39
60
|
)
|
|
40
|
-
|
|
41
|
-
DitoButtons.dito-buttons-round.dito-buttons-large.dito-buttons-main(
|
|
42
|
-
:class="{ 'dito-buttons-sticky': isRootForm }"
|
|
43
|
-
:buttons="buttonSchemas"
|
|
44
|
-
:dataPath="dataPath"
|
|
45
|
-
:data="data"
|
|
46
|
-
:meta="meta"
|
|
47
|
-
:store="store"
|
|
48
|
-
:disabled="isLoading"
|
|
49
|
-
)
|
|
61
|
+
ReuseTemplate
|
|
50
62
|
</template>
|
|
51
63
|
|
|
52
64
|
<script>
|
|
65
|
+
import { createReusableTemplate } from '@vueuse/core'
|
|
53
66
|
import { clone, capitalize, parseDataPath, assignDeeply } from '@ditojs/utils'
|
|
54
67
|
import DitoComponent from '../DitoComponent.js'
|
|
55
68
|
import RouteMixin from '../mixins/RouteMixin.js'
|
|
56
69
|
import ResourceMixin from '../mixins/ResourceMixin.js'
|
|
57
70
|
import { getResource, getMemberResource } from '../utils/resource.js'
|
|
58
|
-
import { getButtonSchemas, isObjectSource } from '../utils/schema.js'
|
|
71
|
+
import { getButtonSchemas, isInlined, isObjectSource } from '../utils/schema.js'
|
|
59
72
|
import { resolvePath } from '../utils/path.js'
|
|
60
73
|
|
|
74
|
+
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
|
|
75
|
+
|
|
61
76
|
// @vue/component
|
|
62
77
|
export default DitoComponent.component('DitoForm', {
|
|
63
78
|
mixins: [RouteMixin, ResourceMixin],
|
|
79
|
+
components: { DefineTemplate, ReuseTemplate },
|
|
64
80
|
|
|
65
81
|
data() {
|
|
66
82
|
return {
|
|
@@ -127,8 +143,8 @@ export default DitoComponent.component('DitoForm', {
|
|
|
127
143
|
)
|
|
128
144
|
},
|
|
129
145
|
|
|
130
|
-
|
|
131
|
-
return this.
|
|
146
|
+
isInlinedSource() {
|
|
147
|
+
return isInlined(this.sourceSchema)
|
|
132
148
|
},
|
|
133
149
|
|
|
134
150
|
isActive() {
|
|
@@ -3,14 +3,14 @@ import DitoComponent from '../DitoComponent.js'
|
|
|
3
3
|
import DitoForm from './DitoForm.vue'
|
|
4
4
|
|
|
5
5
|
// @vue/component
|
|
6
|
-
export default DitoComponent.component('
|
|
6
|
+
export default DitoComponent.component('DitoFormInlined', {
|
|
7
7
|
extends: DitoForm
|
|
8
8
|
})
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<style lang="scss">
|
|
12
|
-
.dito-form-
|
|
13
|
-
// No scrolling
|
|
12
|
+
.dito-form-inlined {
|
|
13
|
+
// No scrolling in inlined forms, and prevent open .multiselect from
|
|
14
14
|
// being cropped.
|
|
15
15
|
overflow: visible;
|
|
16
16
|
}
|
package/src/components/index.js
CHANGED
|
@@ -27,7 +27,7 @@ export { default as DitoCreateButton } from './DitoCreateButton.vue'
|
|
|
27
27
|
export { default as DitoClipboard } from './DitoClipboard.vue'
|
|
28
28
|
export { default as DitoView } from './DitoView.vue'
|
|
29
29
|
export { default as DitoForm } from './DitoForm.vue'
|
|
30
|
-
export { default as
|
|
30
|
+
export { default as DitoFormInlined } from './DitoFormInlined.vue'
|
|
31
31
|
export { default as DitoErrors } from './DitoErrors.vue'
|
|
32
32
|
export { default as DitoScopes } from './DitoScopes.vue'
|
|
33
33
|
export { default as DitoPagination } from './DitoPagination.vue'
|
package/src/mixins/DitoMixin.js
CHANGED
|
@@ -534,30 +534,36 @@ export default {
|
|
|
534
534
|
}
|
|
535
535
|
},
|
|
536
536
|
|
|
537
|
-
|
|
537
|
+
emitEvent(event, {
|
|
538
538
|
context = null,
|
|
539
539
|
parent = null
|
|
540
540
|
} = {}) {
|
|
541
541
|
const hasListeners = this.hasListeners(event)
|
|
542
542
|
const parentHasListeners = parent?.hasListeners(event)
|
|
543
543
|
if (hasListeners || parentHasListeners) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
544
|
+
const emitEvent = target =>
|
|
545
|
+
target.emit(event, (context = DitoContext.get(this, context)))
|
|
546
|
+
|
|
547
|
+
const handleParentListeners = result =>
|
|
548
|
+
// Don't bubble to parent if handled event returned `false`
|
|
549
|
+
parentHasListeners && result !== false
|
|
550
|
+
? emitEvent(parent).then(() => result)
|
|
551
|
+
: result
|
|
552
|
+
|
|
553
|
+
const handleListeners = () =>
|
|
554
|
+
hasListeners
|
|
555
|
+
? emitEvent(this).then(handleParentListeners)
|
|
556
|
+
: handleParentListeners(undefined)
|
|
557
|
+
|
|
558
|
+
return ['load', 'change'].includes(event)
|
|
559
|
+
? // The effects of some events need time to propagate through Vue.
|
|
560
|
+
// Use $nextTick() to make sure our handlers see these changes.
|
|
561
|
+
// For example, `processedItem` is only correct after components
|
|
562
|
+
// that are newly rendered due to data changes have registered.
|
|
563
|
+
// NOTE: The result of `handleListeners()` makes it through the
|
|
564
|
+
// `$nextTick()` call and will be returned as expected.
|
|
565
|
+
this.$nextTick(handleListeners)
|
|
566
|
+
: handleListeners()
|
|
561
567
|
}
|
|
562
568
|
},
|
|
563
569
|
|
package/src/mixins/RouteMixin.js
CHANGED
package/src/styles/_table.scss
CHANGED
package/src/utils/schema.js
CHANGED
|
@@ -122,7 +122,12 @@ export function getSchemaIdentifier(schema) {
|
|
|
122
122
|
return JSON.stringify(schema)
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
const resolvedSchemas = new WeakMap()
|
|
126
|
+
export async function resolveSchema(value, unwrapModule = false) {
|
|
127
|
+
if (resolvedSchemas.has(value)) {
|
|
128
|
+
return resolvedSchemas.get(value)
|
|
129
|
+
}
|
|
130
|
+
let schema = value
|
|
126
131
|
if (isFunction(schema)) {
|
|
127
132
|
schema = schema()
|
|
128
133
|
}
|
|
@@ -147,6 +152,7 @@ export async function resolveSchema(schema, unwrapModule = false) {
|
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
154
|
}
|
|
155
|
+
resolvedSchemas.set(value, schema)
|
|
150
156
|
return schema
|
|
151
157
|
}
|
|
152
158
|
|
|
@@ -236,29 +242,34 @@ export async function resolveSchemaComponents(schemas) {
|
|
|
236
242
|
await mapConcurrently(Object.values(schemas || {}), resolveSchemaComponent)
|
|
237
243
|
}
|
|
238
244
|
|
|
245
|
+
const processedSchemas = new WeakSet()
|
|
239
246
|
export async function processSchemaComponents(
|
|
240
247
|
api,
|
|
241
248
|
schema,
|
|
242
249
|
routes = null,
|
|
243
250
|
level = 0
|
|
244
251
|
) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
if (schema && !processedSchemas.has(schema)) {
|
|
253
|
+
processedSchemas.add(schema)
|
|
254
|
+
|
|
255
|
+
const promises = []
|
|
256
|
+
const process = (component, name, relativeLevel) => {
|
|
257
|
+
promises.push(
|
|
258
|
+
processSchemaComponent(
|
|
259
|
+
api,
|
|
260
|
+
component,
|
|
261
|
+
name,
|
|
262
|
+
routes,
|
|
263
|
+
level + relativeLevel
|
|
264
|
+
)
|
|
254
265
|
)
|
|
255
|
-
|
|
256
|
-
}
|
|
266
|
+
}
|
|
257
267
|
|
|
258
|
-
|
|
259
|
-
|
|
268
|
+
iterateNestedSchemaComponents(schema, process)
|
|
269
|
+
iterateSchemaComponents(getPanelSchemas(schema), process)
|
|
260
270
|
|
|
261
|
-
|
|
271
|
+
await Promise.all(promises)
|
|
272
|
+
}
|
|
262
273
|
}
|
|
263
274
|
|
|
264
275
|
export async function processSchemaComponent(
|