@ditojs/admin 2.23.0 → 2.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/admin",
3
- "version": "2.23.0",
3
+ "version": "2.24.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.23.0",
37
- "@ditojs/utils": "^2.23.0",
36
+ "@ditojs/ui": "^2.24.0",
37
+ "@ditojs/utils": "^2.24.0",
38
38
  "@kyvg/vue3-notification": "^3.1.4",
39
39
  "@lk77/vue3-color": "^3.0.6",
40
40
  "@tiptap/core": "^2.2.2",
@@ -67,23 +67,23 @@
67
67
  "sortablejs": "^1.15.2",
68
68
  "tinycolor2": "^1.6.0",
69
69
  "tippy.js": "^6.3.7",
70
- "type-fest": "^4.9.0",
71
- "vue": "3.4.10",
70
+ "type-fest": "^4.10.2",
71
+ "vue": "^3.4.18",
72
72
  "vue-multiselect": "^3.0.0-beta.3",
73
73
  "vue-router": "^4.2.5",
74
74
  "vue-upload-component": "^3.1.8"
75
75
  },
76
76
  "devDependencies": {
77
- "@ditojs/build": "^2.23.0",
77
+ "@ditojs/build": "^2.24.0",
78
78
  "@vitejs/plugin-vue": "^5.0.3",
79
- "@vue/compiler-sfc": "3.4.10",
79
+ "@vue/compiler-sfc": "^3.4.18",
80
80
  "pug": "^3.0.2",
81
81
  "sass": "1.70.0",
82
82
  "typescript": "^5.3.3",
83
- "vite": "^5.1.0"
83
+ "vite": "^5.1.1"
84
84
  },
85
85
  "types": "types",
86
- "gitHead": "6aee1fd0e7569c8eb21f46138012c532b9ac8db7",
86
+ "gitHead": "cd3083bf66a2f1b3fdb568fbb85e58ba0c202287",
87
87
  "scripts": {
88
88
  "build": "vite build",
89
89
  "watch": "yarn build --mode 'development' --watch",
@@ -431,10 +431,11 @@ export default DitoComponent.component('DitoForm', {
431
431
  ]
432
432
  })
433
433
  },
434
- notifyError: () => {
434
+ notifyError: error => {
435
435
  const verb = getVerb(true)
436
436
  this.notify({
437
437
  type: 'error',
438
+ error,
438
439
  title: 'Request Error',
439
440
  text: `Unable to ${verb} ${this.itemLabel}.`
440
441
  })
@@ -460,6 +461,7 @@ export default DitoComponent.component('DitoForm', {
460
461
  const verb = getVerb(true)
461
462
  this.notify({
462
463
  type: 'error',
464
+ error,
463
465
  title: 'Request Error',
464
466
  text: [
465
467
  `Unable to ${verb} ${this.itemLabel}${error ? ':' : ''}`,
@@ -23,7 +23,7 @@ export default DitoComponent.component('DitoNotifications', {
23
23
  },
24
24
 
25
25
  methods: {
26
- notify({ type = 'info', title, text } = {}) {
26
+ notify({ type = 'info', title, text, error } = {}) {
27
27
  title ||= (
28
28
  {
29
29
  warning: 'Warning',
@@ -46,7 +46,12 @@ export default DitoComponent.component('DitoNotifications', {
46
46
  'error'
47
47
  )
48
48
  // eslint-disable-next-line no-console
49
- console[log](stripTags(text))
49
+ console[log](
50
+ ...[
51
+ stripTags(text),
52
+ ...(type === 'error' && error ? [error] : [])
53
+ ]
54
+ )
50
55
  const { notifications = true } = this.api
51
56
  if (notifications) {
52
57
  // Calculate display-duration for the notification based on its content
@@ -233,8 +233,8 @@ export default DitoComponent.component('DitoRoot', {
233
233
  })
234
234
  },
235
235
 
236
- notify({ type = 'info', title, text } = {}) {
237
- this.notifications.notify({ type, title, text })
236
+ notify({ type = 'info', title, text, error } = {}) {
237
+ this.notifications.notify({ type, title, text, error })
238
238
  },
239
239
 
240
240
  closeNotifications() {
@@ -325,15 +325,13 @@ export default DitoComponent.component('DitoRoot', {
325
325
  await this.resolveViews()
326
326
  }
327
327
  } catch (err) {
328
- const error = err.response?.data?.error
328
+ const error = err.response?.data?.error || err
329
329
  this.notify({
330
330
  type: 'error',
331
+ error,
331
332
  title: 'Authentication Error',
332
- text: error || err
333
+ text: error
333
334
  })
334
- if (!error) {
335
- console.error(err, err.response)
336
- }
337
335
  this.login()
338
336
  }
339
337
  }
@@ -367,10 +365,12 @@ export default DitoComponent.component('DitoRoot', {
367
365
  })
368
366
  user = response.data.user || null
369
367
  } catch (err) {
368
+ const error = err.response?.data?.error || err
370
369
  this.notify({
371
370
  type: 'error',
371
+ error,
372
372
  title: 'Authentication Error',
373
- text: err
373
+ text: error
374
374
  })
375
375
  }
376
376
  this.setUser(user)
@@ -209,6 +209,10 @@ export default DitoComponent.component('DitoSchema', {
209
209
  return first
210
210
  },
211
211
 
212
+ routeTab() {
213
+ return this.$route.hash?.slice(1) || null
214
+ },
215
+
212
216
  clipboard() {
213
217
  return this.schema?.clipboard ?? null
214
218
  },
@@ -357,21 +361,21 @@ export default DitoComponent.component('DitoSchema', {
357
361
  },
358
362
 
359
363
  watch: {
360
- '$route.hash': {
364
+ routeTab: {
361
365
  immediate: true,
362
366
  // https://github.com/vuejs/vue-router/issues/3393#issuecomment-1158470149
363
367
  flush: 'post',
364
- handler(hash) {
368
+ handler(routeTab) {
365
369
  // Remember the current path to know if tab changes should still be
366
370
  // handled, but remove the trailing `/create` or `/:id` from it so that
367
371
  // tabs informs that stay open after creation still work.
368
372
  if (this.hasRootTabs) {
369
- this.selectedTab = hash?.slice(1) || null
373
+ this.selectedTab = routeTab
370
374
  }
371
375
  }
372
376
  },
373
377
 
374
- 'selectedTab'(newTab, oldTab) {
378
+ selectedTab(newTab, oldTab) {
375
379
  if (this.scrollable) {
376
380
  const { content } = this.$refs
377
381
  this.scrollPositions[oldTab] = content.scrollTop
@@ -403,7 +407,7 @@ export default DitoComponent.component('DitoSchema', {
403
407
  },
404
408
 
405
409
  mounted() {
406
- this.selectedTab = this.defaultTab
410
+ this.selectedTab = this.routeTab || this.defaultTab
407
411
  },
408
412
 
409
413
  unmounted() {
@@ -5,7 +5,7 @@ import {
5
5
  normalizeDataPath,
6
6
  getValueAtDataPath
7
7
  } from '@ditojs/utils'
8
- import { reactive } from 'vue'
8
+ import { markRaw, ref } from 'vue'
9
9
  import LoadingMixin from './LoadingMixin.js'
10
10
 
11
11
  // @vue/component
@@ -25,8 +25,12 @@ export default {
25
25
  schema = { data: schema }
26
26
  }
27
27
  let { data = undefined, dataPath = null } = schema
28
- // See if there is async data loading already in process.
29
- const asyncEntry = (this.asyncDataEntries[name] ||= reactive({
28
+ // Create a reactive entry for the async data, if it doesn't exist yet.
29
+ // NOTE: Use `markRaw()` to avoid reactivity on the entry itself, as
30
+ // reactivity is only desired on the `reactiveVersion` property, which is
31
+ // used to trigger controlled reevaluation of the computed getter.
32
+ const asyncEntry = (this.asyncDataEntries[name] ||= markRaw({
33
+ reactiveVersion: ref(1),
30
34
  dependencyFunction: null,
31
35
  resolvedData: undefined,
32
36
  resolving: false,
@@ -34,7 +38,9 @@ export default {
34
38
  }))
35
39
  // If the data callback provided a dependency function when it was called,
36
40
  // cal it in every call of `handleDataSchema()` to force Vue to keep track
37
- // of the async dependencies.
41
+ // of the async dependencies. Also access `reactiveVersion.value` right
42
+ // away, to ensure that the reactive property is tracked as a dependency:
43
+ asyncEntry.reactiveVersion.value &&
38
44
  asyncEntry.dependencyFunction?.(this.context)
39
45
 
40
46
  if (asyncEntry.resolved) {
@@ -80,6 +86,9 @@ export default {
80
86
  asyncEntry.resolvedData = data
81
87
  asyncEntry.resolving = false
82
88
  asyncEntry.resolved = true
89
+ // Trigger reevaluation of the computed getter by increasing the
90
+ // `reactiveVersion` value.
91
+ asyncEntry.reactiveVersion.value++
83
92
  })
84
93
  .catch(error => {
85
94
  console.error(error)
@@ -261,7 +261,7 @@ export default {
261
261
  ? labelize(data.type)
262
262
  : 'Error'
263
263
  const text = data?.message ?? error
264
- this.notify({ type: 'error', title, text })
264
+ this.notify({ type: 'error', error, title, text })
265
265
  }
266
266
  }
267
267
  }
@@ -309,6 +309,7 @@ export default {
309
309
  notifyError = error =>
310
310
  this.notify({
311
311
  type: 'error',
312
+ error,
312
313
  title: 'Request Error',
313
314
  text: [
314
315
  `Unable to send request${error ? ':' : ''}`,
@@ -27,6 +27,7 @@ import {
27
27
  parseDataPath,
28
28
  normalizeDataPath
29
29
  } from '@ditojs/utils'
30
+ import { raw } from '@ditojs/ui'
30
31
 
31
32
  // @vue/component
32
33
  export default {
@@ -45,7 +46,7 @@ export default {
45
46
  data() {
46
47
  return {
47
48
  wrappedPrimitives: null,
48
- unwrappingPrimitives: false
49
+ unwrappingPrimitives: raw(false)
49
50
  }
50
51
  },
51
52
 
@@ -95,16 +96,18 @@ export default {
95
96
  data ||= []
96
97
  const { wrapPrimitives } = this
97
98
  if (wrapPrimitives) {
98
- if (this.unwrappingPrimitives) {
99
+ if (this.unwrappingPrimitives.value) {
99
100
  // We're done unwrapping once `listData` is reevaluated, so set
100
101
  // this to `false` again. See `wrappedPrimitives` watcher above.
101
102
  // TODO: Fix side-effects
102
- // eslint-disable-next-line
103
- this.unwrappingPrimitives = false
103
+ // eslint-disable-next-line max-len
104
+ // eslint-disable-next-line vue/no-side-effects-in-computed-properties
105
+ this.unwrappingPrimitives.value = false
104
106
  } else {
105
107
  // Convert data to a list of wrapped primitives, and return it.
106
108
  // TODO: Fix side-effects
107
- // eslint-disable-next-line
109
+ // eslint-disable-next-line max-len
110
+ // eslint-disable-next-line vue/no-side-effects-in-computed-properties
108
111
  this.wrappedPrimitives = data.map(value => ({
109
112
  [wrapPrimitives]: value
110
113
  }))
@@ -337,11 +340,11 @@ export default {
337
340
  const { wrapPrimitives } = this
338
341
  // Skip the initial setting of wrappedPrimitives array
339
342
  if (wrapPrimitives && from !== null) {
340
- // Whenever the wrappedPrimitives change, map their values back to
341
- // the array of primitives, in a primitive way :)
342
- // But set `unwrappingPrimitives = true`, so the `listData` computed
343
+ // Whenever the wrappedPrimitives change, map their values back to the
344
+ // array of primitives, in a primitive way :)
345
+ // But set `unwrappingPrimitives` to true, so the `listData` computed
343
346
  // property knows about it, which sets it to `false` again.
344
- this.unwrappingPrimitives = true
347
+ this.unwrappingPrimitives.value = true
345
348
  this.value = to.map(object => object[wrapPrimitives])
346
349
  }
347
350
  }
@@ -416,6 +416,7 @@ export default DitoTypeComponent.register('upload', {
416
416
  )
417
417
  this.notify({
418
418
  type: 'error',
419
+ error,
419
420
  title: 'File Upload Error',
420
421
  text
421
422
  })