@ditojs/admin 2.2.1 → 2.2.3
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 +1432 -1327
- package/dist/dito-admin.umd.js +4 -4
- package/dist/style.css +1 -1
- package/package.json +5 -5
- package/src/DitoAdmin.js +1 -23
- package/src/components/DitoMenu.vue +16 -4
- package/src/components/DitoPane.vue +2 -1
- package/src/components/DitoPanel.vue +72 -15
- package/src/components/DitoRoot.vue +10 -2
- package/src/components/DitoSchema.vue +6 -2
- package/src/mixins/DataMixin.js +4 -1
- package/src/mixins/DitoMixin.js +4 -5
- package/src/mixins/DomMixin.js +1 -1
- package/src/mixins/NumberMixin.js +7 -1
- package/src/mixins/SourceMixin.js +5 -9
- package/src/mixins/TypeMixin.js +1 -1
- package/src/styles/_button.scss +8 -0
- package/src/types/DitoTypeButton.vue +6 -5
- package/src/types/DitoTypeCode.vue +0 -4
- package/src/types/DitoTypeList.vue +16 -21
- package/src/types/DitoTypeMarkup.vue +1 -1
- package/src/types/DitoTypeNumber.vue +6 -0
- package/src/types/DitoTypePanel.vue +1 -1
- package/src/types/DitoTypeText.vue +6 -0
- package/src/utils/filter.js +72 -30
- package/src/utils/route.js +46 -0
- package/src/utils/schema.js +51 -23
- package/src/verbs.js +3 -1
package/src/utils/filter.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isArray, asArray, labelize } from '@ditojs/utils'
|
|
2
|
+
import { getNamedSchemas, processNestedSchemaDefaults } from './schema'
|
|
2
3
|
|
|
3
4
|
export const filterComponents = {
|
|
4
5
|
'text'(filter) {
|
|
@@ -24,17 +25,19 @@ export const filterComponents = {
|
|
|
24
25
|
operator: filter.operators
|
|
25
26
|
? {
|
|
26
27
|
type: 'select',
|
|
28
|
+
width: '2/5',
|
|
27
29
|
options: isArray(filter.operators)
|
|
28
30
|
? options.filter(
|
|
29
31
|
option => filter.operators.includes(option.value)
|
|
30
32
|
)
|
|
31
33
|
: options,
|
|
32
|
-
|
|
34
|
+
clearable: true
|
|
33
35
|
}
|
|
34
36
|
: null,
|
|
35
37
|
text: {
|
|
36
38
|
type: 'text',
|
|
37
|
-
width: filter.operators ? '
|
|
39
|
+
width: filter.operators ? '3/5' : 'fill',
|
|
40
|
+
clearable: true
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
},
|
|
@@ -49,19 +52,22 @@ export const filterComponents = {
|
|
|
49
52
|
return {
|
|
50
53
|
from: {
|
|
51
54
|
type: 'datetime',
|
|
52
|
-
width: '
|
|
53
|
-
dateFormat
|
|
55
|
+
width: '1/2',
|
|
56
|
+
dateFormat,
|
|
57
|
+
clearable: true
|
|
54
58
|
},
|
|
55
59
|
to: {
|
|
56
60
|
type: 'datetime',
|
|
57
|
-
width: '
|
|
58
|
-
dateFormat
|
|
61
|
+
width: '1/2',
|
|
62
|
+
dateFormat,
|
|
63
|
+
clearable: true
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
export function
|
|
69
|
+
export function createFiltersPanel(api, filters, dataPath, proxy) {
|
|
70
|
+
const { sticky, ...filterSchemas } = filters
|
|
65
71
|
const panel = {
|
|
66
72
|
type: 'panel',
|
|
67
73
|
label: 'Filters',
|
|
@@ -69,6 +75,8 @@ export function getFiltersPanel(filters, dataPath, proxy) {
|
|
|
69
75
|
target: dataPath,
|
|
70
76
|
// Override the default value
|
|
71
77
|
disabled: false,
|
|
78
|
+
sticky,
|
|
79
|
+
|
|
72
80
|
// NOTE: On panels, the data() callback does something else than on normal
|
|
73
81
|
// schema: It produces the `data` property to be passed to the panel's
|
|
74
82
|
// schema, not the data to be used for the panel component directly.
|
|
@@ -78,50 +86,83 @@ export function getFiltersPanel(filters, dataPath, proxy) {
|
|
|
78
86
|
proxy.query
|
|
79
87
|
)
|
|
80
88
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
|
|
90
|
+
components: createFiltersComponents(filterSchemas),
|
|
91
|
+
buttons: createFiltersButtons(false),
|
|
92
|
+
panelButtons: createFiltersButtons(true),
|
|
93
|
+
|
|
94
|
+
events: {
|
|
95
|
+
change() {
|
|
96
|
+
this.applyFilters()
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
computed: {
|
|
101
|
+
filters() {
|
|
102
|
+
return formatFiltersData(this.schema, this.data)
|
|
91
103
|
},
|
|
92
104
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
text: 'Filter',
|
|
96
|
-
events: {
|
|
97
|
-
click({ schemaComponent }) {
|
|
98
|
-
schemaComponent.applyFilters()
|
|
99
|
-
}
|
|
100
|
-
}
|
|
105
|
+
hasFilters() {
|
|
106
|
+
return this.filters.length > 0
|
|
101
107
|
}
|
|
102
108
|
},
|
|
109
|
+
|
|
103
110
|
methods: {
|
|
104
111
|
applyFilters() {
|
|
105
112
|
proxy.query = {
|
|
106
113
|
...proxy.query,
|
|
107
|
-
filter:
|
|
114
|
+
filter: this.filters,
|
|
108
115
|
// Clear pagination when applying or clearing filters:
|
|
109
116
|
page: undefined
|
|
110
117
|
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
clearFilters() {
|
|
121
|
+
this.resetData()
|
|
122
|
+
this.applyFilters()
|
|
111
123
|
}
|
|
112
124
|
}
|
|
113
125
|
}
|
|
126
|
+
processNestedSchemaDefaults(api, panel)
|
|
114
127
|
return panel
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
function
|
|
130
|
+
function createFiltersButtons(small) {
|
|
131
|
+
return {
|
|
132
|
+
clear: {
|
|
133
|
+
type: 'button',
|
|
134
|
+
text: small ? null : 'Clear',
|
|
135
|
+
disabled: ({ schemaComponent }) => !schemaComponent.hasFilters,
|
|
136
|
+
events: {
|
|
137
|
+
click({ schemaComponent }) {
|
|
138
|
+
// Since panel buttons are outside of the schema, we need to use the
|
|
139
|
+
// schema component received from the initialize event below:
|
|
140
|
+
schemaComponent.clearFilters()
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
submit: {
|
|
146
|
+
type: 'submit',
|
|
147
|
+
text: small ? null : 'Filter',
|
|
148
|
+
visible: !small,
|
|
149
|
+
events: {
|
|
150
|
+
click({ schemaComponent }) {
|
|
151
|
+
schemaComponent.applyFilters()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function createFiltersComponents(filters) {
|
|
118
159
|
const comps = {}
|
|
119
|
-
for (const filter of Object.values(filters || {})) {
|
|
160
|
+
for (const filter of Object.values(getNamedSchemas(filters) || {})) {
|
|
120
161
|
// Support both custom forms and default filter components, through the
|
|
121
162
|
// `filterComponents` registry. Even for default filters, still use the
|
|
122
163
|
// properties in `filter` as the base for `form`, so things like `label`
|
|
123
164
|
// can be changed on the resulting form.
|
|
124
|
-
const { filter: type, ...form } = filter
|
|
165
|
+
const { filter: type, width, ...form } = filter
|
|
125
166
|
const components = type
|
|
126
167
|
? filterComponents[type]?.(filter)
|
|
127
168
|
: filter.components
|
|
@@ -142,6 +183,7 @@ function getFiltersComponents(filters) {
|
|
|
142
183
|
comps[filter.name] = {
|
|
143
184
|
label: form.label,
|
|
144
185
|
type: 'object',
|
|
186
|
+
width,
|
|
145
187
|
default: () => ({}),
|
|
146
188
|
form,
|
|
147
189
|
inlined: true
|
|
@@ -175,7 +217,7 @@ function formatFiltersData(schema, data) {
|
|
|
175
217
|
}
|
|
176
218
|
}
|
|
177
219
|
}
|
|
178
|
-
return filters
|
|
220
|
+
return filters
|
|
179
221
|
}
|
|
180
222
|
|
|
181
223
|
function parseFiltersData(schema, query) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isArray, asArray } from '@ditojs/utils'
|
|
2
|
+
|
|
3
|
+
export function formatQuery(query) {
|
|
4
|
+
const entries = query
|
|
5
|
+
? isArray(query)
|
|
6
|
+
? query
|
|
7
|
+
: Object.entries(query)
|
|
8
|
+
: []
|
|
9
|
+
return (
|
|
10
|
+
new URLSearchParams(
|
|
11
|
+
// Expand array values into multiple entries under the same key, so
|
|
12
|
+
// `formatQuery({ foo: [1, 2], bar: 3 })` => 'foo=1&foo=2&bar=3'.
|
|
13
|
+
entries.reduce(
|
|
14
|
+
(entries, [key, value]) => {
|
|
15
|
+
for (const val of asArray(value)) {
|
|
16
|
+
entries.push([key, val])
|
|
17
|
+
}
|
|
18
|
+
return entries
|
|
19
|
+
},
|
|
20
|
+
[]
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
.toString()
|
|
24
|
+
// decode all these encoded characters to have the same behavior as
|
|
25
|
+
// vue-router's own query encoding.
|
|
26
|
+
.replaceAll(/%(?:21|24|28|29|2C|2F|3A|3B|3D|3F|40)/g, decodeURIComponent)
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function replaceRoute({ path, query, hash }) {
|
|
31
|
+
// Preserve `history.state`, see:
|
|
32
|
+
// https://router.vuejs.org/guide/migration/#usage-of-history-state
|
|
33
|
+
history.replaceState(
|
|
34
|
+
history.state,
|
|
35
|
+
null,
|
|
36
|
+
`${
|
|
37
|
+
location.origin
|
|
38
|
+
}${
|
|
39
|
+
path ?? location.pathname
|
|
40
|
+
}?${
|
|
41
|
+
query ? formatQuery(query) : location.search.slice(1)
|
|
42
|
+
}${
|
|
43
|
+
hash ?? location.hash
|
|
44
|
+
}`
|
|
45
|
+
)
|
|
46
|
+
}
|
package/src/utils/schema.js
CHANGED
|
@@ -55,7 +55,7 @@ export function iterateSchemaComponents(schemas, callback) {
|
|
|
55
55
|
|
|
56
56
|
export function iterateNestedSchemaComponents(schema, callback) {
|
|
57
57
|
return schema
|
|
58
|
-
? iterateSchemaComponents([schema, ...
|
|
58
|
+
? iterateSchemaComponents([schema, ...getTabSchemas(schema)], callback)
|
|
59
59
|
: undefined
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -191,7 +191,12 @@ export async function resolveSchemaComponents(schemas) {
|
|
|
191
191
|
await mapConcurrently(Object.values(schemas || {}), resolveSchemaComponent)
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
export async function processSchemaComponents(
|
|
194
|
+
export async function processSchemaComponents(
|
|
195
|
+
api,
|
|
196
|
+
schema,
|
|
197
|
+
routes = null,
|
|
198
|
+
level = 0
|
|
199
|
+
) {
|
|
195
200
|
const promises = []
|
|
196
201
|
const process = (component, name, relativeLevel) => {
|
|
197
202
|
promises.push(
|
|
@@ -206,21 +211,28 @@ export async function processSchemaComponents(api, schema, routes, level) {
|
|
|
206
211
|
}
|
|
207
212
|
|
|
208
213
|
iterateNestedSchemaComponents(schema, process)
|
|
209
|
-
iterateSchemaComponents(
|
|
214
|
+
iterateSchemaComponents(getPanelSchemas(schema), process)
|
|
210
215
|
|
|
211
216
|
await Promise.all(promises)
|
|
212
217
|
}
|
|
213
218
|
|
|
214
|
-
export async function processSchemaComponent(
|
|
215
|
-
|
|
219
|
+
export async function processSchemaComponent(
|
|
220
|
+
api,
|
|
221
|
+
schema,
|
|
222
|
+
name,
|
|
223
|
+
routes = null,
|
|
224
|
+
level = 0
|
|
225
|
+
) {
|
|
226
|
+
processSchemaDefaults(api, schema)
|
|
216
227
|
|
|
217
228
|
await Promise.all([
|
|
218
229
|
// Also process nested panel schemas.
|
|
219
|
-
mapConcurrently(
|
|
220
|
-
|
|
230
|
+
mapConcurrently(
|
|
231
|
+
getPanelSchemas(schema),
|
|
232
|
+
panel => processSchemaComponents(api, panel, routes, level)
|
|
221
233
|
),
|
|
222
234
|
// Delegate schema processing to the actual type components.
|
|
223
|
-
|
|
235
|
+
getTypeOptions(schema)?.processSchema?.(
|
|
224
236
|
api,
|
|
225
237
|
schema,
|
|
226
238
|
name,
|
|
@@ -235,8 +247,8 @@ export async function processView(component, api, schema, name) {
|
|
|
235
247
|
throw new Error(`Invalid view schema: '${getSchemaIdentifier(schema)}'`)
|
|
236
248
|
}
|
|
237
249
|
processRouteSchema(api, schema, name)
|
|
238
|
-
|
|
239
|
-
await
|
|
250
|
+
processSchemaDefaults(api, schema)
|
|
251
|
+
await processNestedSchemas(api, schema)
|
|
240
252
|
const children = []
|
|
241
253
|
await processSchemaComponents(api, schema, children, 0)
|
|
242
254
|
return {
|
|
@@ -250,7 +262,7 @@ export async function processView(component, api, schema, name) {
|
|
|
250
262
|
}
|
|
251
263
|
}
|
|
252
264
|
|
|
253
|
-
export function
|
|
265
|
+
export function processSchemaDefaults(api, schema) {
|
|
254
266
|
let defaults = api.defaults[schema.type]
|
|
255
267
|
if (defaults) {
|
|
256
268
|
if (isFunction(defaults)) {
|
|
@@ -268,6 +280,20 @@ export function processDefaults(api, schema) {
|
|
|
268
280
|
}
|
|
269
281
|
}
|
|
270
282
|
|
|
283
|
+
export function processNestedSchemaDefaults(api, schema) {
|
|
284
|
+
// Process defaults for nested schemas. Note that this is also done when
|
|
285
|
+
// calling `processSchemaComponents()`, but that function is async, and we
|
|
286
|
+
// need a sync version that only handles the defaults for filters, see
|
|
287
|
+
// `getFiltersPanel()`.
|
|
288
|
+
iterateNestedSchemaComponents(schema, component => {
|
|
289
|
+
processSchemaDefaults(api, component)
|
|
290
|
+
const forms = getFormSchemas(component)
|
|
291
|
+
for (const form of Object.values(forms)) {
|
|
292
|
+
processNestedSchemaDefaults(api, form)
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
|
|
271
297
|
export function processRouteSchema(api, schema, name) {
|
|
272
298
|
// Used for view and source schemas, see SourceMixin.
|
|
273
299
|
schema.name = name
|
|
@@ -304,8 +330,8 @@ export async function processForm(api, schema) {
|
|
|
304
330
|
if (!isForm(schema)) {
|
|
305
331
|
throw new Error(`Invalid form schema: '${getSchemaIdentifier(schema)}'`)
|
|
306
332
|
}
|
|
307
|
-
|
|
308
|
-
await
|
|
333
|
+
processSchemaDefaults(api, schema)
|
|
334
|
+
await processNestedSchemas(api, schema)
|
|
309
335
|
return schema
|
|
310
336
|
}
|
|
311
337
|
|
|
@@ -314,7 +340,7 @@ export async function processTab(api, schema) {
|
|
|
314
340
|
if (!isTab(schema)) {
|
|
315
341
|
throw new Error(`Invalid tab schema: '${getSchemaIdentifier(schema)}'`)
|
|
316
342
|
}
|
|
317
|
-
|
|
343
|
+
processSchemaDefaults(api, schema)
|
|
318
344
|
return schema
|
|
319
345
|
}
|
|
320
346
|
|
|
@@ -323,11 +349,11 @@ export async function processPanel(api, schema) {
|
|
|
323
349
|
if (!isPanel(schema)) {
|
|
324
350
|
throw new Error(`Invalid panel schema: '${getSchemaIdentifier(schema)}'`)
|
|
325
351
|
}
|
|
326
|
-
|
|
352
|
+
processSchemaDefaults(api, schema)
|
|
327
353
|
return schema
|
|
328
354
|
}
|
|
329
355
|
|
|
330
|
-
export async function
|
|
356
|
+
export async function processNestedSchemas(api, schema) {
|
|
331
357
|
const { tabs, panels } = schema
|
|
332
358
|
if (tabs) {
|
|
333
359
|
schema.tabs = await resolveSchemas(
|
|
@@ -392,7 +418,7 @@ export function getViewEditPath(schema, context) {
|
|
|
392
418
|
}
|
|
393
419
|
|
|
394
420
|
export function getFormSchemas(schema, context, modifyForm) {
|
|
395
|
-
const viewSchema = getViewFormSchema(schema, context)
|
|
421
|
+
const viewSchema = context && getViewFormSchema(schema, context)
|
|
396
422
|
if (viewSchema) {
|
|
397
423
|
schema = viewSchema
|
|
398
424
|
} else if (schema.view) {
|
|
@@ -413,7 +439,7 @@ export function getFormSchemas(schema, context, modifyForm) {
|
|
|
413
439
|
return Object.fromEntries(
|
|
414
440
|
Object.entries(forms).map(([type, form]) => {
|
|
415
441
|
// Support `schema.components` callbacks to create components on the fly.
|
|
416
|
-
if (isFunction(form.components)) {
|
|
442
|
+
if (context && isFunction(form.components)) {
|
|
417
443
|
form = {
|
|
418
444
|
...form,
|
|
419
445
|
components: form.components(context)
|
|
@@ -760,10 +786,10 @@ export function processSchemaData(
|
|
|
760
786
|
}
|
|
761
787
|
|
|
762
788
|
processComponents(schema.components)
|
|
763
|
-
for (const tab of
|
|
789
|
+
for (const tab of getTabSchemas(schema)) {
|
|
764
790
|
processComponents(tab.components)
|
|
765
791
|
}
|
|
766
|
-
for (const panel of
|
|
792
|
+
for (const panel of getPanelSchemas(schema)) {
|
|
767
793
|
processComponents(panel.components)
|
|
768
794
|
}
|
|
769
795
|
|
|
@@ -871,21 +897,23 @@ export function getPanelEntries(
|
|
|
871
897
|
return panelEntries
|
|
872
898
|
}
|
|
873
899
|
|
|
874
|
-
export function
|
|
900
|
+
export function getTabSchemas(schema) {
|
|
875
901
|
return schema?.tabs ? Object.values(schema.tabs) : []
|
|
876
902
|
}
|
|
877
903
|
|
|
878
|
-
export function
|
|
879
|
-
return
|
|
904
|
+
export function getPanelSchemas(schema) {
|
|
905
|
+
return schema?.panels ? Object.values(schema.panels) : []
|
|
880
906
|
}
|
|
881
907
|
|
|
882
908
|
export function getAllPanelEntries(
|
|
909
|
+
api,
|
|
883
910
|
schema,
|
|
884
911
|
dataPath = null,
|
|
885
912
|
schemaComponent = null,
|
|
886
913
|
tabComponent = null
|
|
887
914
|
) {
|
|
888
915
|
const panelSchema = getTypeOptions(schema)?.getPanelSchema?.(
|
|
916
|
+
api,
|
|
889
917
|
schema,
|
|
890
918
|
dataPath,
|
|
891
919
|
schemaComponent
|
package/src/verbs.js
CHANGED
|
@@ -4,9 +4,11 @@ export default [
|
|
|
4
4
|
'submit', 'submitted',
|
|
5
5
|
'delete', 'deleted',
|
|
6
6
|
'edit', 'edited',
|
|
7
|
+
'clear', 'cleared',
|
|
7
8
|
'close', 'closed',
|
|
8
9
|
'cancel', 'cancelled',
|
|
9
|
-
'drag', 'dragged'
|
|
10
|
+
'drag', 'dragged',
|
|
11
|
+
'login', 'logged in'
|
|
10
12
|
].reduce((verbs, verb) => {
|
|
11
13
|
verbs[verb] = verb
|
|
12
14
|
return verbs
|