@ditojs/admin 2.6.8 → 2.6.10

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.6.8",
3
+ "version": "2.6.10",
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,7 +33,7 @@
33
33
  "not ie_mob > 0"
34
34
  ],
35
35
  "dependencies": {
36
- "@ditojs/ui": "^2.6.3",
36
+ "@ditojs/ui": "^2.6.10",
37
37
  "@ditojs/utils": "^2.6.0",
38
38
  "@kyvg/vue3-notification": "^2.9.0",
39
39
  "@lk77/vue3-color": "^3.0.6",
@@ -83,7 +83,7 @@
83
83
  "vite": "^4.3.4"
84
84
  },
85
85
  "types": "types",
86
- "gitHead": "6c09a5f0fe96aeadf73ca07daa427e39a7f1462d",
86
+ "gitHead": "12d6a71efe6eb89b514b7ca9737fefc3afc319f2",
87
87
  "scripts": {
88
88
  "build": "vite build",
89
89
  "watch": "yarn build --mode 'development' --watch",
@@ -4,6 +4,17 @@
4
4
  :class="containerClass"
5
5
  :style="containerStyle"
6
6
  )
7
+ Teleport(
8
+ v-if="isMounted && panelEntries.length > 0"
9
+ to=".dito-sidebar__teleport"
10
+ )
11
+ DitoPanels(
12
+ :panels="panelEntries"
13
+ :data="data"
14
+ :meta="meta"
15
+ :store="store"
16
+ :disabled="disabled"
17
+ )
7
18
  DitoLabel(
8
19
  v-if="hasLabel"
9
20
  :class="componentClass"
@@ -13,6 +24,7 @@
13
24
  )
14
25
  component.dito-component(
15
26
  :is="typeComponent"
27
+ ref="component"
16
28
  :class="componentClass"
17
29
  :schema="schema"
18
30
  :dataPath="dataPath"
@@ -35,7 +47,12 @@ import { isString, isNumber } from '@ditojs/utils'
35
47
  import DitoComponent from '../DitoComponent.js'
36
48
  import DitoContext from '../DitoContext.js'
37
49
  import { getSchemaAccessor } from '../utils/accessor.js'
38
- import { getTypeComponent, hasLabel, omitPadding } from '../utils/schema.js'
50
+ import {
51
+ getAllPanelEntries,
52
+ getTypeComponent,
53
+ hasLabel,
54
+ omitPadding
55
+ } from '../utils/schema.js'
39
56
  import { parseFraction } from '../utils/math.js'
40
57
 
41
58
  // @vue/component
@@ -56,7 +73,8 @@ export default DitoComponent.component('DitoContainer', {
56
73
 
57
74
  data() {
58
75
  return {
59
- errors: null
76
+ errors: null,
77
+ isMounted: false
60
78
  }
61
79
  },
62
80
 
@@ -196,9 +214,23 @@ export default DitoComponent.component('DitoContainer', {
196
214
  'dito-width-shrink': this.flexShrink,
197
215
  'dito-has-errors': !!this.errors
198
216
  }
217
+ },
218
+
219
+ panelEntries() {
220
+ return getAllPanelEntries(
221
+ this.api,
222
+ this.schema,
223
+ this.dataPath,
224
+ this.$refs.component,
225
+ this.tabComponent
226
+ )
199
227
  }
200
228
  },
201
229
 
230
+ mounted() {
231
+ this.isMounted = true
232
+ },
233
+
202
234
  methods: {
203
235
  onErrors(errors) {
204
236
  this.errors = errors
@@ -263,9 +295,10 @@ export default DitoComponent.component('DitoContainer', {
263
295
  }
264
296
 
265
297
  &--label-vertical {
266
- // For components without labels in rows with other components that have
267
- // labels, add some spacing to the top to align with the other components:
268
- > .dito-component:first-child {
298
+ // For plain components without labels in rows with other components that
299
+ // have labels, add some spacing to the top to align with the other
300
+ // components (e.g. buttons):
301
+ > .dito-component:first-child:not(.dito-section, .dito-list, .dito-object) {
269
302
  margin-top: $line-height * $font-size + $form-spacing-half;
270
303
  }
271
304
  }
@@ -283,17 +283,22 @@ export default DitoComponent.component('DitoForm', {
283
283
  },
284
284
 
285
285
  watch: {
286
- $route(to, from) {
287
- // Reload form data when navigating to a different entity in same form.
288
- const param = this.meta?.param
289
- if (
290
- param &&
291
- this.providesData &&
292
- from.matched[0].path === to.matched[0].path && // Staying on same form?
293
- from.params[param] !== 'create' && // But haven't been creating?
294
- to.params[param] !== from.params[param] // Going to a different entity?
295
- ) {
296
- this.loadData(true)
286
+ $route: {
287
+ // https://github.com/vuejs/vue-router/issues/3393#issuecomment-1158470149
288
+ flush: 'post',
289
+ handler(to, from) {
290
+ // Reload form data when navigating to a different entity in same form.
291
+ const param = this.meta?.param
292
+ if (
293
+ param &&
294
+ this.providesData &&
295
+ // TODO: See if we can remove this due to `flush: 'post'`.
296
+ from.matched[0].path === to.matched[0].path && // Staying on same form
297
+ from.params[param] !== 'create' && // But haven't been creating
298
+ to.params[param] !== from.params[param] // Going to a different entity
299
+ ) {
300
+ this.loadData(true)
301
+ }
297
302
  }
298
303
  },
299
304
 
@@ -20,7 +20,7 @@ nav.dito-header
20
20
  v-if="isLoading"
21
21
  )
22
22
  //- Teleport target for `.dito-schema-header`:
23
- .dito-title
23
+ .dito-header__teleport
24
24
  slot
25
25
  </template>
26
26
 
@@ -84,6 +84,19 @@ export default DitoComponent.component('DitoHeader', {
84
84
  z-index: $header-z-index;
85
85
  @include user-select(none);
86
86
 
87
+ &::after {
88
+ // Cover the gaps between the different headers that might appear due to
89
+ // layout rounding imprecisions.
90
+ content: '';
91
+ top: 0;
92
+ bottom: 0;
93
+ left: calc(100% - 1px);
94
+ width: 2px;
95
+ position: absolute;
96
+ background: inherit;
97
+ z-index: -1;
98
+ }
99
+
87
100
  span {
88
101
  display: inline-block;
89
102
  padding: $header-padding;
@@ -91,16 +91,16 @@ export default DitoComponent.component('DitoLabel', {
91
91
  .dito-label {
92
92
  $self: &;
93
93
 
94
- --label-padding: 0;
95
94
  // For buttons and chevron to align right:
96
95
  display: flex;
97
96
  position: relative;
98
97
  // Vertically center all items in the label, e.g. chevron, edit-buttons.
99
98
  align-items: center;
100
- padding: var(--label-padding);
101
99
  margin: 0 $form-spacing-half $form-spacing-half 0;
102
100
 
103
101
  .dito-container:not(.dito-container--label-vertical) > & {
102
+ // When labels are not vertical (e.g. compact layout, next to component),
103
+ // clear bottom padding for better vertical alignment.
104
104
  margin-bottom: 0;
105
105
  }
106
106
 
@@ -43,7 +43,7 @@
43
43
  <script>
44
44
  import DitoComponent from '../DitoComponent.js'
45
45
  import { appendDataPath } from '../utils/data.js'
46
- import { getAllPanelEntries, isNested } from '../utils/schema.js'
46
+ import { isNested } from '../utils/schema.js'
47
47
 
48
48
  // @vue/component
49
49
  export default DitoComponent.component('DitoPane', {
@@ -108,21 +108,6 @@ export default DitoComponent.component('DitoPane', {
108
108
  )
109
109
  },
110
110
 
111
- panelEntries() {
112
- // Gather all panel schemas from all component schemas, by finding those
113
- // that want to provide a panel. See `getAllPanelEntries()` for details.
114
- return this.componentSchemas.flatMap(
115
- ({ schema, nestedDataPath: dataPath }) =>
116
- getAllPanelEntries(
117
- this.api,
118
- schema,
119
- dataPath,
120
- this.schemaComponent,
121
- this.tabComponent
122
- )
123
- )
124
- },
125
-
126
111
  isSingleComponent() {
127
112
  return this.single && this.componentSchemas.length === 1
128
113
  },
@@ -184,7 +184,7 @@ export default DitoComponent.component('DitoPanel', {
184
184
  $form-spacing;
185
185
 
186
186
  position: sticky;
187
- top: 0;
187
+ top: $content-padding;
188
188
  margin-bottom: $margin;
189
189
  z-index: 1;
190
190
 
@@ -223,16 +223,19 @@ export default DitoComponent.component('DitoPanel', {
223
223
 
224
224
  > .dito-schema-content {
225
225
  > .dito-pane {
226
- padding: (2 * $form-spacing - $input-padding-ver)
227
- (2 * $form-spacing - $input-padding-hor);
226
+ padding: $form-spacing;
228
227
  }
229
228
 
230
- .dito-label {
231
- margin: $form-spacing 0;
232
- }
229
+ .dito-pane {
230
+ margin: -$form-spacing-half;
233
231
 
234
- .dito-container {
235
- padding: $form-spacing-half;
232
+ &.dito-pane--single {
233
+ margin: 0;
234
+ }
235
+
236
+ > .dito-container:not(.dito-container--single) {
237
+ padding: $form-spacing-half;
238
+ }
236
239
  }
237
240
 
238
241
  .dito-object {
@@ -45,7 +45,7 @@ export default DitoComponent.component('DitoPanels', {
45
45
  @import '../styles/_imports';
46
46
 
47
47
  .dito-panels {
48
- padding: $content-padding;
49
- padding-left: $form-spacing;
48
+ margin: $content-padding;
49
+ margin-left: $form-spacing;
50
50
  }
51
51
  </style>
@@ -3,16 +3,27 @@ slot(name="before")
3
3
  .dito-schema(
4
4
  v-bind="$attrs"
5
5
  )
6
+ Teleport(
7
+ v-if="isPopulated && panelEntries.length > 0"
8
+ to=".dito-sidebar__teleport"
9
+ )
10
+ DitoPanels(
11
+ :panels="panelEntries"
12
+ :data="data"
13
+ :meta="meta"
14
+ :store="store"
15
+ :disabled="disabled"
16
+ )
6
17
  .dito-schema-content(
7
18
  ref="content"
8
19
  :class="{ 'dito-scroll': scrollable }"
9
20
  )
10
21
  Teleport(
11
- to=".dito-title"
22
+ v-if="hasLabel || hasTabs || clipboard"
23
+ to=".dito-header__teleport"
12
24
  :disabled="!headerInMenu"
13
25
  )
14
26
  .dito-schema-header(
15
- v-if="hasLabel || hasTabs || clipboard"
16
27
  :class="{ 'dito-schema-header--menu': headerInMenu }"
17
28
  )
18
29
  DitoLabel(
@@ -83,18 +94,6 @@ slot(name="before")
83
94
  v-if="!hasLabel"
84
95
  name="edit-buttons"
85
96
  )
86
- Teleport(
87
- v-else-if="isPopulated"
88
- to=".dito-sidebar"
89
- )
90
- DitoPanels(
91
- :class="{ 'dito-scroll': scrollable }"
92
- :panels="panelEntries"
93
- :data="data"
94
- :meta="meta"
95
- :store="store"
96
- :disabled="disabled"
97
- )
98
97
  slot(name="after")
99
98
  </template>
100
99
 
@@ -116,8 +115,7 @@ import {
116
115
  getNamedSchemas,
117
116
  getPanelEntries,
118
117
  setDefaultValues,
119
- processData,
120
- isForm
118
+ processData
121
119
  } from '../utils/schema.js'
122
120
  import { getSchemaAccessor, getStoreAccessor } from '../utils/accessor.js'
123
121
 
@@ -164,7 +162,6 @@ export default DitoComponent.component('DitoSchema', {
164
162
  ? data(this.context)
165
163
  : data
166
164
  ),
167
- currentPath: null,
168
165
  currentTab: null,
169
166
  componentsRegistry: {},
170
167
  panesRegistry: {},
@@ -186,11 +183,7 @@ export default DitoComponent.component('DitoSchema', {
186
183
  },
187
184
 
188
185
  panelEntries() {
189
- const panelEntries = getPanelEntries(this.schema.panels, '')
190
- for (const pane of this.panes) {
191
- panelEntries.push(...pane.panelEntries)
192
- }
193
- return panelEntries
186
+ return getPanelEntries(this.schema.panels, '')
194
187
  },
195
188
 
196
189
  tabs() {
@@ -349,21 +342,13 @@ export default DitoComponent.component('DitoSchema', {
349
342
  watch: {
350
343
  '$route.hash': {
351
344
  immediate: true,
345
+ // https://github.com/vuejs/vue-router/issues/3393#issuecomment-1158470149
346
+ flush: 'post',
352
347
  handler(hash) {
353
348
  // Remember the current path to know if tab changes should still be
354
349
  // handled, but remove the trailing `/create` or `/:id` from it so that
355
350
  // tabs informs that stay open after creation still work.
356
- const getPath = () =>
357
- isForm(this.schema)
358
- ? this.$route.path.replace(/\/.\w*$/, '')
359
- : this.$route.path
360
- if (
361
- this.hasTabs && (
362
- !this.currentPath ||
363
- this.currentPath === getPath()
364
- )
365
- ) {
366
- this.currentPath = getPath()
351
+ if (this.hasTabs) {
367
352
  this.currentTab = hash?.slice(1) || null
368
353
  if (this.hasErrors) {
369
354
  this.repositionErrors()
@@ -408,48 +393,44 @@ export default DitoComponent.component('DitoSchema', {
408
393
  },
409
394
 
410
395
  methods: {
411
- getComponentsByDataPath(dataPath, match) {
412
- return this._getEntriesByDataPath(
413
- this.componentsByDataPath,
414
- dataPath,
415
- match
416
- )
396
+ getComponentsByDataPath(dataPath) {
397
+ return this._getEntriesByDataPath(this.componentsByDataPath, dataPath)
417
398
  },
418
399
 
419
- getComponentByDataPath(dataPath, match) {
420
- return this.getComponentsByDataPath(dataPath, match)[0] || null
400
+ getComponentByDataPath(dataPath) {
401
+ return this.getComponentsByDataPath(dataPath)[0] || null
421
402
  },
422
403
 
423
- getComponentsByName(dataPath, match) {
424
- return this._getEntriesByName(this.componentsByDataPath, dataPath, match)
404
+ getComponentsByName(dataPath) {
405
+ return this._getEntriesByName(this.componentsByDataPath, dataPath)
425
406
  },
426
407
 
427
- getComponentByName(name, match) {
428
- return this.getComponentsByName(name, match)[0] || null
408
+ getComponentByName(name) {
409
+ return this.getComponentsByName(name)[0] || null
429
410
  },
430
411
 
431
- getComponents(dataPathOrName, match) {
432
- return this._getEntries(this.componentsByDataPath, dataPathOrName, match)
412
+ getComponents(dataPathOrName) {
413
+ return this._getEntries(this.componentsByDataPath, dataPathOrName)
433
414
  },
434
415
 
435
- getComponent(dataPathOrName, match) {
436
- return this.getComponents(dataPathOrName, match)[0] || null
416
+ getComponent(dataPathOrName) {
417
+ return this.getComponents(dataPathOrName)[0] || null
437
418
  },
438
419
 
439
- getPanelsByDataPath(dataPath, match) {
440
- return this._getEntriesByDataPath(this.panelsByDataPath, dataPath, match)
420
+ getPanelsByDataPath(dataPath) {
421
+ return this._getEntriesByDataPath(this.panelsByDataPath, dataPath)
441
422
  },
442
423
 
443
- getPanelByDataPath(dataPath, match) {
444
- return this.getPanelsByDataPath(dataPath, match)[0] || null
424
+ getPanelByDataPath(dataPath) {
425
+ return this.getPanelsByDataPath(dataPath)[0] || null
445
426
  },
446
427
 
447
- getPanels(dataPathOrName, match) {
448
- return this._getEntries(this.panelsByDataPath, dataPathOrName, match)
428
+ getPanels(dataPathOrName) {
429
+ return this._getEntries(this.panelsByDataPath, dataPathOrName)
449
430
  },
450
431
 
451
- getPanel(dataPathOrName, match) {
452
- return this.getPanels(dataPathOrName, match)[0] || null
432
+ getPanel(dataPathOrName) {
433
+ return this.getPanels(dataPathOrName)[0] || null
453
434
  },
454
435
 
455
436
  someComponent(callback) {
@@ -728,28 +709,18 @@ export default DitoComponent.component('DitoSchema', {
728
709
  }, {})
729
710
  },
730
711
 
731
- _getEntries(entriesByDataPath, dataPath, match) {
712
+ _getEntries(entriesByDataPath, dataPath) {
732
713
  return normalizeDataPath(dataPath).startsWith(this.dataPath)
733
- ? this._getEntriesByDataPath(entriesByDataPath, dataPath, match)
734
- : this._getEntriesByName(entriesByDataPath, dataPath, match)
735
- },
736
-
737
- _getEntriesByDataPath(entriesByDataPath, dataPath, match) {
738
- return this._filterEntries(
739
- entriesByDataPath[normalizeDataPath(dataPath)] || [],
740
- match
741
- )
714
+ ? this._getEntriesByDataPath(entriesByDataPath, dataPath)
715
+ : this._getEntriesByName(entriesByDataPath, dataPath)
742
716
  },
743
717
 
744
- _getEntriesByName(entriesByDataPath, name, match) {
745
- return this._filterEntries(
746
- entriesByDataPath[appendDataPath(this.dataPath, name)] || [],
747
- match
748
- )
718
+ _getEntriesByDataPath(entriesByDataPath, dataPath) {
719
+ return entriesByDataPath[normalizeDataPath(dataPath)] || []
749
720
  },
750
721
 
751
- _filterEntries(entries, match) {
752
- return match ? entries.filter(match) : entries
722
+ _getEntriesByName(entriesByDataPath, name) {
723
+ return entriesByDataPath[appendDataPath(this.dataPath, name)] || []
753
724
  }
754
725
  }
755
726
  })
@@ -78,10 +78,8 @@ export default DitoComponent.component('DitoSchemaInlined', {
78
78
  margin: -$form-spacing;
79
79
 
80
80
  .dito-label {
81
- // Add removed $form-spacing again
82
- --label-padding: #{$form-spacing};
83
-
84
- margin: 0;
81
+ // Add removed $form-spacing again.
82
+ margin: $form-spacing;
85
83
  width: 100%;
86
84
  box-sizing: content-box;
87
85
  // Prevent collapsing to min-height when alone in
@@ -2,6 +2,7 @@
2
2
  aside.dito-sidebar.dito-scroll-parent
3
3
  nav.dito-header
4
4
  slot
5
+ .dito-sidebar__teleport.dito-scroll
5
6
  </template>
6
7
 
7
8
  <script>
@@ -99,6 +99,7 @@
99
99
 
100
100
  <script>
101
101
  import DitoComponent from '../DitoComponent.js'
102
+ import ItemMixin from '../mixins/ItemMixin'
102
103
  import SortableMixin from '../mixins/SortableMixin.js'
103
104
  import { appendDataPath } from '../utils/data.js'
104
105
  import { getSchemaAccessor } from '../utils/accessor.js'
@@ -106,7 +107,7 @@ import { getNamedSchemas, hasFormSchema } from '../utils/schema.js'
106
107
 
107
108
  // @vue/component
108
109
  export default DitoComponent.component('DitoTreeItem', {
109
- mixins: [SortableMixin],
110
+ mixins: [ItemMixin, SortableMixin],
110
111
  inject: ['container'],
111
112
 
112
113
  props: {
@@ -235,6 +235,13 @@ export default {
235
235
  return store
236
236
  },
237
237
 
238
+ removeChildStore(key, index) {
239
+ // GEt the child-store first, so that indices can be transferred over
240
+ // temporary id changes during persistence.
241
+ this.getChildStore(key, index)
242
+ this.removeStore(key)
243
+ },
244
+
238
245
  getSchemaValue(
239
246
  keyOrDataPath,
240
247
  {
@@ -32,6 +32,14 @@ export default {
32
32
  return dataPath
33
33
  },
34
34
 
35
+ getItemStore(sourceSchema, item, index) {
36
+ return this.getChildStore(this.getItemUid(sourceSchema, item), index)
37
+ },
38
+
39
+ removeItemStore(sourceSchema, item, index) {
40
+ this.removeChildStore(this.getItemUid(sourceSchema, item), index)
41
+ },
42
+
35
43
  findItemIdIndex(sourceSchema, data, itemId) {
36
44
  const index = this.isTransient
37
45
  ? // For transient data, the index is used as the id
@@ -1,9 +1,5 @@
1
- import ItemMixin from './ItemMixin.js'
2
-
3
1
  // @vue/component
4
2
  export default {
5
- mixins: [ItemMixin],
6
-
7
3
  data() {
8
4
  return {
9
5
  dragging: false
@@ -1,4 +1,5 @@
1
1
  import DitoComponent from '../DitoComponent.js'
2
+ import ItemMixin from './ItemMixin.js'
2
3
  import ResourceMixin from './ResourceMixin.js'
3
4
  import SchemaParentMixin from '../mixins/SchemaParentMixin.js'
4
5
  import { getSchemaAccessor, getStoreAccessor } from '../utils/accessor.js'
@@ -29,7 +30,7 @@ import {
29
30
 
30
31
  // @vue/component
31
32
  export default {
32
- mixins: [ResourceMixin, SchemaParentMixin],
33
+ mixins: [ItemMixin, ResourceMixin, SchemaParentMixin],
33
34
 
34
35
  defaultValue(schema) {
35
36
  return isListSource(schema) ? [] : null
@@ -404,18 +405,22 @@ export default {
404
405
  return item
405
406
  },
406
407
 
407
- removeItem(item) {
408
+ removeItem(item, index) {
409
+ let removed = false
408
410
  if (this.isObjectSource) {
409
411
  this.objectData = null
410
- this.onChange()
412
+ removed = true
411
413
  } else {
412
414
  const { listData } = this
413
- const index = listData && listData.indexOf(item)
414
415
  if (index >= 0) {
415
416
  listData.splice(index, 1)
416
- this.onChange()
417
+ removed = true
417
418
  }
418
419
  }
420
+ if (removed) {
421
+ this.removeItemStore(this.schema, item, index)
422
+ this.onChange()
423
+ }
419
424
  },
420
425
 
421
426
  deleteItem(item, index) {
@@ -444,15 +449,15 @@ export default {
444
449
  )
445
450
  ) {
446
451
  if (this.isTransient) {
447
- this.removeItem(item)
452
+ this.removeItem(item, index)
448
453
  notify()
449
454
  } else {
450
- const itemId = this.getItemId(this.schema, item)
455
+ const itemId = this.getItemId(this.schema, item, index)
451
456
  const resource = getMemberResource(itemId, this.resource)
452
457
  if (resource) {
453
458
  this.handleRequest({ method: 'delete', resource }, err => {
454
459
  if (!err) {
455
- this.removeItem(item)
460
+ this.removeItem(item, index)
456
461
  notify()
457
462
  }
458
463
  this.reloadData()
@@ -520,8 +525,8 @@ export default {
520
525
  this.api.normalizePath(normalizeDataPath(dataPathParts))
521
526
  )
522
527
  // See if there actually is a route for this sub-component:
523
- const { matched } = this.$router.match(path)
524
- if (matched.length) {
528
+ const { matched } = this.$router.resolve(path)
529
+ if (matched.length && matched[0].name !== 'catch-all') {
525
530
  if (this.$route.path === path) {
526
531
  // We're already there, so just call `onComplete()`:
527
532
  callOnComplete()
@@ -241,8 +241,9 @@ export default {
241
241
  // @overridable
242
242
  async scrollIntoView() {
243
243
  await this.focusSchema()
244
- const { element = this } = this.$refs
245
- ;(element.$el || element).scrollIntoView?.({
244
+ let { element = this } = this.$refs
245
+ element = element.scrollIntoView ? element : element.$el
246
+ element.scrollIntoView?.({
246
247
  behavior: 'smooth',
247
248
  block: 'center'
248
249
  })
@@ -252,14 +253,14 @@ export default {
252
253
  focusElement() {
253
254
  let { element = this } = this.$refs
254
255
  element = element.focus ? element : element.$el
255
- element?.focus?.()
256
+ element.focus?.()
256
257
  },
257
258
 
258
259
  // @overridable
259
260
  blurElement() {
260
261
  let { element = this } = this.$refs
261
262
  element = element.blur ? element : element.$el
262
- element?.blur?.()
263
+ element.blur?.()
263
264
  },
264
265
 
265
266
  async focusSchema() {