@ditojs/admin 0.265.0 → 0.269.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.
Files changed (39) hide show
  1. package/LICENSE +1 -1
  2. package/dist/dito-admin.common.js +1193 -968
  3. package/dist/dito-admin.common.js.map +1 -1
  4. package/dist/dito-admin.umd.js +1193 -968
  5. package/dist/dito-admin.umd.js.map +1 -1
  6. package/dist/dito-admin.umd.min.js +3 -3
  7. package/dist/dito-admin.umd.min.js.map +1 -1
  8. package/package.json +13 -13
  9. package/src/DitoAdmin.js +3 -2
  10. package/src/components/DitoAccount.vue +4 -3
  11. package/src/components/DitoButtons.vue +4 -6
  12. package/src/components/DitoClipboard.vue +20 -9
  13. package/src/components/{DitoComponentContainer.vue → DitoContainer.vue} +4 -8
  14. package/src/components/DitoForm.vue +30 -16
  15. package/src/components/DitoLabel.vue +2 -2
  16. package/src/components/{DitoComponents.vue → DitoPane.vue} +18 -28
  17. package/src/components/DitoPanel.vue +10 -5
  18. package/src/components/DitoPanels.vue +4 -2
  19. package/src/components/DitoSchema.vue +59 -51
  20. package/src/components/DitoSchemaInlined.vue +1 -1
  21. package/src/components/index.js +5 -6
  22. package/src/mixins/DitoMixin.js +13 -4
  23. package/src/mixins/ResourceMixin.js +6 -1
  24. package/src/mixins/RouteMixin.js +1 -1
  25. package/src/mixins/SchemaParentMixin.js +6 -0
  26. package/src/mixins/SourceMixin.js +63 -33
  27. package/src/mixins/TypeMixin.js +32 -17
  28. package/src/mixins/ValidatorMixin.js +0 -4
  29. package/src/styles/_button.sass +1 -1
  30. package/src/types/TypeButton.vue +12 -11
  31. package/src/types/TypeCode.vue +1 -1
  32. package/src/types/TypeMarkup.vue +1 -1
  33. package/src/types/TypeMultiselect.vue +11 -10
  34. package/src/types/TypeSection.vue +1 -1
  35. package/src/types/TypeTreeList.vue +1 -3
  36. package/src/types/TypeUpload.vue +1 -1
  37. package/src/utils/accessor.js +2 -0
  38. package/src/utils/schema.js +17 -7
  39. package/src/components/DitoButtonContainer.vue +0 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/admin",
3
- "version": "0.265.0",
3
+ "version": "0.269.0",
4
4
  "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",
5
5
  "main": "dist/dito-admin.umd.min.js",
6
6
  "repository": "https://github.com/ditojs/dito/tree/master/packages/admin",
@@ -33,13 +33,13 @@
33
33
  }
34
34
  },
35
35
  "dependencies": {
36
- "@babel/runtime": "^7.15.3",
37
- "@ditojs/ui": "^0.265.0",
38
- "@ditojs/utils": "^0.265.0",
39
- "axios": "^0.21.1",
36
+ "@babel/runtime": "^7.15.4",
37
+ "@ditojs/ui": "^0.269.0",
38
+ "@ditojs/utils": "^0.269.0",
39
+ "axios": "^0.21.4",
40
40
  "codeflask": "^1.4.1",
41
- "core-js": "^3.16.4",
42
- "filesize": "^8.0.0",
41
+ "core-js": "^3.18.0",
42
+ "filesize": "^8.0.3",
43
43
  "filesize-parser": "^1.5.0",
44
44
  "hint.css": "^2.6.0",
45
45
  "nanoid": "^3.1.25",
@@ -61,16 +61,16 @@
61
61
  "vuedraggable": "^2.24.3"
62
62
  },
63
63
  "devDependencies": {
64
- "@babel/core": "^7.15.0",
65
- "@babel/preset-env": "^7.15.0",
66
- "@ditojs/babel-preset": "^0.265.0",
67
- "@ditojs/build": "^0.265.0",
64
+ "@babel/core": "^7.15.5",
65
+ "@babel/preset-env": "^7.15.6",
66
+ "@ditojs/babel-preset": "^0.269.0",
67
+ "@ditojs/build": "^0.269.0",
68
68
  "@vue/cli-plugin-babel": "^4.5.13",
69
69
  "@vue/cli-service": "^4.5.13",
70
70
  "autoprefixer": "^9.8.6",
71
71
  "babel-loader": "^8.2.2",
72
72
  "fibers": "^5.0.0",
73
- "postcss": "^8.3.6",
73
+ "postcss": "^8.3.8",
74
74
  "postcss-loader": "^4.3.0",
75
75
  "pug": "^3.0.2",
76
76
  "pug-plain-loader": "^1.1.0",
@@ -78,5 +78,5 @@
78
78
  "sass-loader": "^10.2.0",
79
79
  "webpack": "^4.46.0"
80
80
  },
81
- "gitHead": "43c28125d771e6d264b7d2b97e29b9b2642e93f6"
81
+ "gitHead": "74b214afd5691f27312afbfd034c72de8871eac5"
82
82
  }
package/src/DitoAdmin.js CHANGED
@@ -182,7 +182,8 @@ export default class DitoAdmin {
182
182
  $sourceComponent: () => null,
183
183
  $resourceComponent: () => null,
184
184
  $dialogComponent: () => null,
185
- $panelComponent: () => null
185
+ $panelComponent: () => null,
186
+ $tabComponent: () => null
186
187
  },
187
188
 
188
189
  render: createElement => createElement(DitoRoot, {
@@ -213,8 +214,8 @@ export default class DitoAdmin {
213
214
  return axios.request({
214
215
  url,
215
216
  method,
216
- data: data !== null ? JSON.stringify(data) : null,
217
217
  params,
218
+ ...(data && { data }),
218
219
  baseURL: isApiRequest ? this.api.url : null,
219
220
  headers: {
220
221
  ...(isApiRequest && this.api.headers),
@@ -9,10 +9,11 @@
9
9
  )
10
10
  li(
11
11
  v-for="(label, value) of items"
12
- @mousedown.stop="onPulldownMouseDown(value)"
13
- @mouseup="onPulldownMouseUp(value)"
14
12
  )
15
- a {{ label }}
13
+ a(
14
+ @mousedown.stop="onPulldownMouseDown(value)"
15
+ @mouseup="onPulldownMouseUp(value)"
16
+ ) {{ label }}
16
17
  </template>
17
18
 
18
19
  <style lang="sass">
@@ -1,12 +1,10 @@
1
1
  <template lang="pug">
2
- // NOTE: This is similar to DitoComponents, but uses the DitoButtonContainer
3
- // sub-class as the component container for different layout:
4
2
  .dito-buttons(
5
3
  v-if="buttonSchemas || $slots.default"
6
4
  /* Pass on $listeners so that dito-edit-buttons can pass events. */
7
5
  v-on="$listeners"
8
6
  )
9
- dito-button-container(
7
+ dito-container(
10
8
  v-for="(buttonSchema, buttonDataPath) in buttonSchemas"
11
9
  v-if="shouldRender(buttonSchema)"
12
10
  :key="buttonDataPath"
@@ -19,8 +17,8 @@
19
17
  :generateLabels="false"
20
18
  )
21
19
  // Render each node in the default slot through `dito-vnode`, so it can be
22
- // wrapped in a `.dito-button-container` class.
23
- .dito-button-container(
20
+ // wrapped in a `.dito-container` class.
21
+ .dito-container(
24
22
  v-for="node in $slots.default"
25
23
  v-if="node.tag"
26
24
  )
@@ -34,7 +32,7 @@ import { appendDataPath } from '@/utils/data'
34
32
  // @vue/component
35
33
  export default DitoComponent.component('dito-buttons', {
36
34
  provide: {
37
- tabComponent: null
35
+ $tabComponent: () => null
38
36
  },
39
37
 
40
38
  props: {
@@ -36,17 +36,18 @@ export default DitoComponent.component('dito-clipboard', {
36
36
  data() {
37
37
  return {
38
38
  copyEnabled: false,
39
- pasteEnabled: false
39
+ pasteEnabled: false,
40
+ fixClipboard: true
40
41
  }
41
42
  },
42
43
 
43
44
  computed: {
44
- clipboardSettings() {
45
+ clipboardOptions() {
45
46
  return isObject(this.clipboard) ? this.clipboard : {}
46
47
  },
47
48
 
48
49
  copyData() {
49
- const { copy } = this.clipboardSettings
50
+ const { copy } = this.clipboardOptions
50
51
  return copy
51
52
  ? clipboardData => copy.call(this, new DitoContext(this, {
52
53
  clipboardData
@@ -55,7 +56,7 @@ export default DitoComponent.component('dito-clipboard', {
55
56
  },
56
57
 
57
58
  pasteData() {
58
- const { paste } = this.clipboardSettings
59
+ const { paste } = this.clipboardOptions
59
60
  return paste
60
61
  ? clipboardData => paste.call(this, new DitoContext(this, {
61
62
  clipboardData
@@ -73,11 +74,15 @@ export default DitoComponent.component('dito-clipboard', {
73
74
  this.domOn(window, {
74
75
  focus: this.checkClipboard
75
76
  })
76
- this.$watch('data', this.checkClipboard)
77
- // If we already have data (e.g. create form), then check right away also:
78
- if (this.data) {
79
- this.checkClipboard()
80
- }
77
+ this.$watch('data', {
78
+ // Check right away also in case there's already data (e.g. create form).
79
+ immediate: true,
80
+ handler: (to, from) => {
81
+ if (to !== from) {
82
+ this.checkClipboard()
83
+ }
84
+ }
85
+ })
81
86
  },
82
87
 
83
88
  methods: {
@@ -86,6 +91,12 @@ export default DitoComponent.component('dito-clipboard', {
86
91
  let { clipboardData } = this.appState
87
92
  try {
88
93
  const json = await navigator.clipboard?.readText?.()
94
+ if (this.fixClipboard && json) {
95
+ // This appears to be needed on Safari to prevent a strange "Paste"
96
+ // button from appearing when the clipboard is accessed (why?!).
97
+ await navigator.clipboard?.writeText?.(json)
98
+ this.fixClipboard = false
99
+ }
89
100
  if (json) {
90
101
  clipboardData = JSON.parse(json)
91
102
  }
@@ -1,6 +1,5 @@
1
1
  <template lang="pug">
2
- // See `containerClass` for the assignment of the class-names to this div:
3
- div(
2
+ .dito-container(
4
3
  v-show="componentVisible"
5
4
  :class="containerClass"
6
5
  :style="containerStyle"
@@ -30,7 +29,7 @@
30
29
  </template>
31
30
 
32
31
  <style lang="sass">
33
- .dito-component-container
32
+ .dito-container
34
33
  // Needed for better vertical alignment:
35
34
  align-self: stretch
36
35
  box-sizing: border-box
@@ -47,7 +46,7 @@
47
46
  margin: $form-spacing $form-spacing-half 0
48
47
  &.dito-single
49
48
  height: 100% // So that list buttons can be sticky at the bottom
50
- // NOTE: This is not nested inside .dito-component-container so that other
49
+ // NOTE: This is not nested inside `.dito-container` so that other
51
50
  // type components can override `.dito-width-fill` class (filter precedence).
52
51
  .dito-component
53
52
  &.dito-width-fill
@@ -68,7 +67,7 @@ import { parseFraction } from '@/utils/math'
68
67
  import { isString, isNumber } from '@ditojs/utils'
69
68
 
70
69
  // @vue/component
71
- export default DitoComponent.component('dito-component-container', {
70
+ export default DitoComponent.component('dito-container', {
72
71
  props: {
73
72
  schema: { type: Object, required: true },
74
73
  dataPath: { type: String, default: '' },
@@ -156,9 +155,6 @@ export default DitoComponent.component('dito-component-container', {
156
155
  containerClass() {
157
156
  const { class: containerClass } = this.schema
158
157
  return {
159
- // Use the component name as its class, so the extended
160
- // dito-button-container automatically works too.
161
- [this.$options.name]: true,
162
158
  'dito-single': this.single,
163
159
  'dito-omit-padding': shouldOmitPadding(this.schema),
164
160
  ...(
@@ -23,7 +23,6 @@
23
23
  :meta="meta"
24
24
  :store="store"
25
25
  :disabled="isLoading"
26
- :selectedTab="selectedTab"
27
26
  :menuHeader="true"
28
27
  )
29
28
  dito-buttons.dito-buttons-round.dito-buttons-main.dito-buttons-large(
@@ -266,6 +265,10 @@ export default DitoComponent.component('dito-form', {
266
265
  },
267
266
 
268
267
  methods: {
268
+ emitSchemaEvent(event, params) {
269
+ return this.mainSchemaComponent.emitEvent(event, params)
270
+ },
271
+
269
272
  getDataPathFrom(route) {
270
273
  // Get the data path by denormalizePath the relative route path
271
274
  return this.api.denormalizePath(this.path
@@ -305,11 +308,11 @@ export default DitoComponent.component('dito-form', {
305
308
 
306
309
  // @override ResourceMixin.clearData()
307
310
  clearData() {
308
- this.setData(null, true)
311
+ this.setData(null)
309
312
  },
310
313
 
311
314
  // @override ResourceMixin.setData()
312
- setData(data, clearing = false) {
315
+ setData(data) {
313
316
  // setData() is called after submit when data has changed.
314
317
  if (this.isTransient) {
315
318
  // For components with transient data, modify this.sourceData.
@@ -318,9 +321,6 @@ export default DitoComponent.component('dito-form', {
318
321
  this.createdData = null
319
322
  this.loadedData = data
320
323
  }
321
- if (!clearing) {
322
- this.mainSchemaComponent.onLoad()
323
- }
324
324
  },
325
325
 
326
326
  clearClonedData(newValue, oldValue) {
@@ -338,17 +338,18 @@ export default DitoComponent.component('dito-form', {
338
338
  return this.navigate(this.parentRouteComponent.path)
339
339
  },
340
340
 
341
+ getSubmitVerb(present = true) {
342
+ return this.isCreating
343
+ ? present ? 'create' : 'created'
344
+ : present ? 'submit' : 'submitted'
345
+ },
346
+
341
347
  async submit(button, { validate = true, closeForm = false } = {}) {
342
348
  if (validate && !this.validateAll()) {
343
349
  return false
344
350
  }
345
351
 
346
- const getVerb = present => {
347
- const verb = this.isCreating
348
- ? present ? 'create' : 'created'
349
- : present ? 'submit' : 'submitted'
350
- return this.verbs[verb]
351
- }
352
+ const getVerb = present => this.verbs[this.getSubmitVerb(present)]
352
353
 
353
354
  // Allow buttons to override both method and resource path to submit to:
354
355
  const butttonResource = getResource(button.schema.resource, {
@@ -360,6 +361,10 @@ export default DitoComponent.component('dito-form', {
360
361
  let success
361
362
  if (!butttonResource && this.isTransient) {
362
363
  success = await this.submitTransient(button, resource, method, data, {
364
+ onSuccess: () => this.emitSchemaEvent(this.getSubmitVerb()),
365
+ onError: error => this.emitSchemaEvent('error', {
366
+ context: { error }
367
+ }),
363
368
  notifySuccess: () => {
364
369
  const verb = getVerb(false)
365
370
  this.notify({
@@ -387,6 +392,10 @@ export default DitoComponent.component('dito-form', {
387
392
  } else {
388
393
  success = await this.submitResource(button, resource, method, data, {
389
394
  setData: true,
395
+ onSuccess: () => this.emitSchemaEvent(this.getSubmitVerb()),
396
+ onError: error => this.emitSchemaEvent('error', {
397
+ context: { error }
398
+ }),
390
399
  notifySuccess: () => {
391
400
  const verb = getVerb(false)
392
401
  this.notify({
@@ -421,22 +430,27 @@ export default DitoComponent.component('dito-form', {
421
430
  return success
422
431
  },
423
432
 
424
- async submitTransient(button, resource, method, data, {
425
- notifyError,
426
- notifySuccess
433
+ async submitTransient(button, _resource, _method, data, {
434
+ onSuccess,
435
+ onError,
436
+ notifySuccess,
437
+ notifyError
427
438
  }) {
428
439
  // Handle the default "submitting" of transient, nested data:
429
440
  const success = this.isCreating
430
441
  ? this.addSourceData(data)
431
442
  : this.setSourceData(data)
432
443
  if (success) {
444
+ onSuccess?.()
433
445
  await this.emitButtonEvent(button, 'success', {
434
446
  notify: notifySuccess
435
447
  })
436
448
  } else {
449
+ const error = 'Could not submit transient item'
450
+ onError?.(error)
437
451
  await this.emitButtonEvent(button, 'error', {
438
452
  notify: notifyError,
439
- error: 'Could not submit transient item'
453
+ error
440
454
  })
441
455
  }
442
456
  return success
@@ -90,8 +90,8 @@
90
90
  // TODO: Find a better way to control this behavior.
91
91
  .dito-schema-compact
92
92
  > .dito-schema-content
93
- > .dito-components
94
- > .dito-component-container
93
+ > .dito-pane
94
+ > .dito-container
95
95
  > .dito-label:not(.dito-label-component)
96
96
  display: inline-block
97
97
  </style>
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- .dito-components(
2
+ .dito-pane(
3
3
  v-if="isPopulated && componentSchemas.length > 0"
4
4
  v-show="visible"
5
5
  )
@@ -15,7 +15,7 @@
15
15
  .dito-break(
16
16
  v-if="schema.break === 'before'"
17
17
  )
18
- dito-component-container(
18
+ dito-container(
19
19
  v-if="shouldRender(schema)"
20
20
  :key="nestedDataPath"
21
21
  :schema="schema"
@@ -34,30 +34,28 @@
34
34
  </template>
35
35
 
36
36
  <style lang="sass">
37
- // TODO: Consider renaming to `DitoContainer`? That's how it's called in
38
- // `DitoSchema`
39
- .dito-components
37
+ .dito-pane
40
38
  display: flex
41
39
  position: relative
42
40
  flex-flow: row wrap
43
41
  align-content: flex-start
44
42
  align-items: baseline
45
- // Remove padding added by .dito-component-container
43
+ // Remove padding added by `.dito-container`
46
44
  margin: (-$form-spacing) (-$form-spacing-half)
47
45
  // Add removed horizontal margin again to max-width:
48
46
  max-width: $content-width + 2 * $form-spacing-half
49
- // Use `flex: 0%` for all `.dito-components` except `.dito-components-main`,
47
+ // Use `flex: 0%` for all `.dito-pane` except `.dito-pane-main`,
50
48
  // so that the `.dito-buttons-main` can be moved all the way to the bottom.
51
49
  flex: 0%
52
50
 
53
- &.dito-components-main
51
+ &.dito-pane-main
54
52
  flex: 100%
55
53
 
56
54
  .dito-schema-header:not(.dito-schema-menu-header) + &
57
55
  // Clear top-margin if the components are preceded by a schema header.
58
56
  margin-top: 0
59
57
 
60
- .dito-component-container.dito-omit-padding > &
58
+ .dito-container.dito-omit-padding > &
61
59
  // Clear margins set above again if parent is omitting padding.
62
60
  margin: 0
63
61
  max-width: unset
@@ -73,10 +71,10 @@ import { appendDataPath } from '@/utils/data'
73
71
  import { getAllPanelSchemas, isNested } from '@/utils/schema'
74
72
 
75
73
  // @vue/component
76
- export default DitoComponent.component('dito-components', {
74
+ export default DitoComponent.component('dito-pane', {
77
75
  provide() {
78
76
  return {
79
- tabComponent: this.tabComponent
77
+ $tabComponent: () => this.tabComponent
80
78
  }
81
79
  },
82
80
 
@@ -95,7 +93,7 @@ export default DitoComponent.component('dito-components', {
95
93
 
96
94
  computed: {
97
95
  tabComponent() {
98
- return this.tab ? this : null
96
+ return this.tab ? this : this.$tabComponent()
99
97
  },
100
98
 
101
99
  componentSchemas() {
@@ -130,21 +128,13 @@ export default DitoComponent.component('dito-components', {
130
128
  panelSchemas() {
131
129
  // Gather all panel schemas from all component schemas, by finding those
132
130
  // that want to provide a panel. See `getAllPanelSchemas()` for details.
133
- return this.componentSchemas.reduce(
134
- (schemas, { schema, nestedDataPath: dataPath }) => {
135
- for (const panel of getAllPanelSchemas(
136
- schema,
137
- dataPath,
138
- this.schemaComponent
139
- )) {
140
- schemas.push({
141
- ...panel,
142
- tabComponent: this.tabComponent
143
- })
144
- }
145
- return schemas
146
- },
147
- []
131
+ return this.componentSchemas.flatMap(
132
+ ({ schema, nestedDataPath: dataPath }) => getAllPanelSchemas(
133
+ schema,
134
+ dataPath,
135
+ this.schemaComponent,
136
+ this.tabComponent
137
+ )
148
138
  )
149
139
  },
150
140
 
@@ -163,7 +153,7 @@ export default DitoComponent.component('dito-components', {
163
153
 
164
154
  methods: {
165
155
  _register(add) {
166
- this.schemaComponent._registerContainer(this, add)
156
+ this.schemaComponent._registerPane(this, add)
167
157
  },
168
158
 
169
159
  focus() {
@@ -2,7 +2,7 @@
2
2
  // Only show panels in tabs when the tabs are also visible.
3
3
  component.dito-panel(
4
4
  :is="panelTag"
5
- v-show="visible && (!tabComponent || tabComponent.visible)"
5
+ v-show="visible && (!panelTabComponent || panelTabComponent.visible)"
6
6
  @submit.prevent
7
7
  )
8
8
  label.dito-panel-title {{ getLabel(schema) }}
@@ -56,9 +56,9 @@
56
56
  margin: 0
57
57
  label
58
58
  font-weight: normal
59
- .dito-components
59
+ .dito-pane
60
60
  margin: 0 (-$form-spacing-half)
61
- .dito-component-container
61
+ .dito-container
62
62
  padding: $form-spacing-half
63
63
  </style>
64
64
 
@@ -75,7 +75,8 @@ export default DitoComponent.component('dito-panel', {
75
75
 
76
76
  provide() {
77
77
  return {
78
- $panelComponent: () => this
78
+ $panelComponent: () => this,
79
+ $tabComponent: () => this.panelTabComponent
79
80
  }
80
81
  },
81
82
 
@@ -86,7 +87,7 @@ export default DitoComponent.component('dito-panel', {
86
87
  meta: { type: Object, required: true },
87
88
  store: { type: Object, required: true },
88
89
  disabled: { type: Boolean, required: true },
89
- tabComponent: { type: DitoComponent, default: null }
90
+ panelTabComponent: { type: DitoComponent, default: null }
90
91
  },
91
92
 
92
93
  data() {
@@ -100,6 +101,10 @@ export default DitoComponent.component('dito-panel', {
100
101
  return this
101
102
  },
102
103
 
104
+ tabComponent() {
105
+ return this.panelTabComponent
106
+ },
107
+
103
108
  buttonSchemas() {
104
109
  return getButtonSchemas(this.schema.buttons)
105
110
  },
@@ -1,5 +1,7 @@
1
1
  <template lang="pug">
2
- .dito-panels
2
+ .dito-panels(
3
+ v-if="panels.length > 0"
4
+ )
3
5
  dito-panel(
4
6
  v-for="{ schema, dataPath, tabComponent } in panels"
5
7
  v-if="shouldRender(schema)"
@@ -10,7 +12,7 @@
10
12
  :meta="meta"
11
13
  :store="getChildStore(schema.name)"
12
14
  :disabled="schema.disabled != null ? schema.disabled : disabled"
13
- :tabComponent="tabComponent"
15
+ :panelTabComponent="tabComponent"
14
16
  )
15
17
  </template>
16
18