@ditojs/admin 2.8.2 → 2.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/admin",
3
- "version": "2.8.2",
3
+ "version": "2.9.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,8 +33,8 @@
33
33
  "not ie_mob > 0"
34
34
  ],
35
35
  "dependencies": {
36
- "@ditojs/ui": "^2.8.0",
37
- "@ditojs/utils": "^2.8.0",
36
+ "@ditojs/ui": "^2.9.0",
37
+ "@ditojs/utils": "^2.9.0",
38
38
  "@kyvg/vue3-notification": "^2.9.0",
39
39
  "@lk77/vue3-color": "^3.0.6",
40
40
  "@tiptap/core": "^2.0.3",
@@ -74,7 +74,7 @@
74
74
  "vue-upload-component": "^3.1.8"
75
75
  },
76
76
  "devDependencies": {
77
- "@ditojs/build": "^2.8.0",
77
+ "@ditojs/build": "^2.9.0",
78
78
  "@vitejs/plugin-vue": "^4.2.1",
79
79
  "@vue/compiler-sfc": "^3.2.47",
80
80
  "pug": "^3.0.2",
@@ -83,7 +83,7 @@
83
83
  "vite": "^4.3.5"
84
84
  },
85
85
  "types": "types",
86
- "gitHead": "d9a0905b7c93d509a3710b36c749cc6e651401d3",
86
+ "gitHead": "002f3c713f9d3b10e348d2e9fd07d3a5bea426e6",
87
87
  "scripts": {
88
88
  "build": "vite build",
89
89
  "watch": "yarn build --mode 'development' --watch",
package/src/appState.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { reactive } from 'vue'
2
+ import { parseUserAgent } from './utils/agent'
2
3
 
3
4
  export default reactive({
4
5
  title: '',
5
6
  routeComponents: [],
6
7
  user: null,
8
+ agent: parseUserAgent(navigator.userAgent || ''),
7
9
  loadCache: {}, // See TypeMixin.load()
8
10
  activeLabel: null,
9
11
  clipboardData: null
@@ -20,15 +20,16 @@
20
20
  <script>
21
21
  import { isObject, clone, deindent } from '@ditojs/utils'
22
22
  import DitoComponent from '../DitoComponent.js'
23
- import DitoContext from '../DitoContext.js'
24
23
  import DomMixin from '../mixins/DomMixin.js'
24
+ import DitoContext from '../DitoContext.js'
25
25
 
26
26
  // @vue/component
27
27
  export default DitoComponent.component('DitoClipboard', {
28
28
  mixins: [DomMixin],
29
29
 
30
30
  props: {
31
- clipboard: { type: [Boolean, Object], default: false },
31
+ clipboard: { type: [Boolean, Object], required: true },
32
+ schema: { type: Object, required: true },
32
33
  dataPath: { type: String, required: true },
33
34
  data: { type: [Object, Array], default: null }
34
35
  },
@@ -36,8 +37,7 @@ export default DitoComponent.component('DitoClipboard', {
36
37
  data() {
37
38
  return {
38
39
  copyEnabled: false,
39
- pasteEnabled: false,
40
- fixClipboard: true
40
+ pasteEnabled: false
41
41
  }
42
42
  },
43
43
 
@@ -50,12 +50,7 @@ export default DitoComponent.component('DitoClipboard', {
50
50
  const { copy } = this.clipboardOptions
51
51
  return copy
52
52
  ? clipboardData =>
53
- copy.call(
54
- this,
55
- new DitoContext(this, {
56
- clipboardData
57
- })
58
- )
53
+ copy.call(this, new DitoContext(this, { clipboardData }))
59
54
  : clipboardData => clone(clipboardData)
60
55
  },
61
56
 
@@ -63,38 +58,27 @@ export default DitoComponent.component('DitoClipboard', {
63
58
  const { paste } = this.clipboardOptions
64
59
  return paste
65
60
  ? clipboardData =>
66
- paste.call(
67
- this,
68
- new DitoContext(this, {
69
- clipboardData
70
- })
71
- )
61
+ paste.call(this, new DitoContext(this, { clipboardData }))
72
62
  : clipboardData => clipboardData
73
63
  }
74
64
  },
75
65
 
76
66
  watch: {
77
- 'appState.clipboardData': 'checkClipboard'
67
+ 'parentComponent.hasData': {
68
+ // Check right away also in case there's already data (e.g. create form).
69
+ immediate: true,
70
+ handler(hasData) {
71
+ this.copyEnabled = hasData
72
+ }
73
+ },
74
+ 'appState.clipboardData': 'updatePaste'
78
75
  },
79
76
 
80
77
  mounted() {
81
78
  // Check clipboard content whenever something gets copied or the window gets
82
79
  // (re)activated, as those are the moments when the clipboard can change:
83
- this.domOn(document, {
84
- copy: this.checkClipboard
85
- })
86
- this.domOn(window, {
87
- focus: this.checkClipboard
88
- })
89
- this.$watch('data', {
90
- // Check right away also in case there's already data (e.g. create form).
91
- immediate: true,
92
- handler: (to, from) => {
93
- if (to !== from) {
94
- this.checkClipboard()
95
- }
96
- }
97
- })
80
+ this.domOn(document, { copy: this.updatePaste })
81
+ this.domOn(window, { focus: this.updatePaste })
98
82
  },
99
83
 
100
84
  methods: {
@@ -103,12 +87,6 @@ export default DitoComponent.component('DitoClipboard', {
103
87
  let { clipboardData } = this.appState
104
88
  try {
105
89
  const json = await navigator.clipboard?.readText?.()
106
- if (this.fixClipboard && json) {
107
- // This appears to be needed on Safari to prevent a strange "Paste"
108
- // button from appearing when the clipboard is accessed (why?!).
109
- await navigator.clipboard?.writeText?.(json)
110
- this.fixClipboard = false
111
- }
112
90
  if (json) {
113
91
  clipboardData = JSON.parse(json)
114
92
  }
@@ -124,27 +102,34 @@ export default DitoComponent.component('DitoClipboard', {
124
102
  }
125
103
  }
126
104
  const { $schema, ...data } = clipboardData || {}
127
- return $schema === this.schemaComponent?.schema.name ? data : null
105
+ return $schema === this.schema.name ? data : null
128
106
  },
129
107
 
130
- async checkClipboard() {
131
- this.copyEnabled = !!this.data
132
- // See if the clipboard content is valid JSON data that is compatible
133
- // with the current target schema, and only then activate the pasting:
134
- this.pasteEnabled = !!(await this.getClipboardData(false)) // don't report
108
+ async updatePaste() {
109
+ this.pasteEnabled = !!this.appState.clipboardData
110
+ if (!this.pasteEnabled && this.appState.agent.chrome) {
111
+ // See if the clipboard content is valid JSON data that is compatible
112
+ // with the current target schema, and only then activate the pasting:
113
+ const data = await this.getClipboardData(false) // Don't report
114
+ this.pasteEnabled = !!data
115
+ }
135
116
  },
136
117
 
137
118
  async onCopy() {
138
- let data = this.schemaComponent?.clipboardData
119
+ let data = this.parentComponent.clipboardData
139
120
  try {
140
- data = data && this.copyData(data)
121
+ if (data) {
122
+ data = {
123
+ $schema: this.schema.name,
124
+ ...this.copyData(data)
125
+ }
126
+ }
141
127
  // Keep an internal clipboard as fallback.
142
128
  this.appState.clipboardData = data
129
+ this.pasteEnabled = true
143
130
  try {
144
131
  const json = JSON.stringify(data, null, 2)
145
132
  await navigator.clipboard?.writeText?.(json)
146
- // See if we can activate the paste button, depending on browsers:
147
- await this.checkClipboard()
148
133
  } catch (err) {
149
134
  console.error(err, err.name, err.message)
150
135
  }
@@ -155,15 +140,10 @@ export default DitoComponent.component('DitoClipboard', {
155
140
  },
156
141
 
157
142
  async onPaste() {
158
- let data = await this.getClipboardData(true) // report
159
- try {
160
- data = data && this.pasteData(data)
161
- if (data) {
162
- this.schemaComponent.setData(data)
163
- }
164
- } catch (error) {
165
- console.error(error)
166
- alert(error.message)
143
+ let data = await this.getClipboardData(true) // Report
144
+ data = data && this.pasteData(data)
145
+ if (data) {
146
+ this.parentComponent.clipboardData = data
167
147
  }
168
148
  }
169
149
  }
@@ -255,26 +255,24 @@ export default DitoComponent.component('DitoContainer', {
255
255
  // percentages in flex-basis to work.
256
256
  padding: $form-spacing-half;
257
257
 
258
- .dito-page .dito-pane > & {
259
- @container (width < #{calc($content-width * 0.8)}) {
258
+ .dito-pane > & {
259
+ .dito-page--width-80 & {
260
260
  flex-grow: 1;
261
261
  flex-basis: var(--basis-mobile, var(--basis));
262
262
  // DEBUG: background: yellow;
263
263
  }
264
264
 
265
- @container (width < #{calc($content-width * 0.6)}) {
265
+ .dito-page--width-60 & {
266
266
  flex-basis: calc(2 * var(--basis));
267
267
  // DEBUG: background: orange;
268
268
  }
269
- }
270
269
 
271
- .dito-sidebar .dito-pane > & {
272
- @container (width < #{$sidebar-max-width}) {
270
+ .dito-sidebar--width-99 & {
273
271
  flex-grow: 1;
274
272
  // DEBUG: background: yellow;
275
273
  }
276
274
 
277
- @container (width < #{calc($sidebar-max-width * 0.6)}) {
275
+ .dito-sidebar--width-60 & {
278
276
  flex-basis: calc(2 * var(--basis));
279
277
  // DEBUG: background: orange;
280
278
  }
@@ -31,13 +31,14 @@
31
31
 
32
32
  <script>
33
33
  import { clone } from '@ditojs/utils'
34
- import { addEvents } from '@ditojs/ui/src'
35
34
  import DitoComponent from '../DitoComponent.js'
35
+ import DomMixin from '../mixins/DomMixin.js'
36
36
  import { getButtonSchemas } from '../utils/schema.js'
37
37
  import { UseFocusTrap } from '@vueuse/integrations/useFocusTrap/component'
38
38
 
39
39
  // @vue/component
40
40
  export default DitoComponent.component('DitoDialog', {
41
+ mixins: [DomMixin],
41
42
  components: { UseFocusTrap },
42
43
  emits: ['remove'],
43
44
 
@@ -70,7 +71,6 @@ export default DitoComponent.component('DitoDialog', {
70
71
  }
71
72
  }
72
73
  return {
73
- windowEvents: null,
74
74
  dialogData
75
75
  }
76
76
  },
@@ -125,7 +125,7 @@ export default DitoComponent.component('DitoDialog', {
125
125
  },
126
126
 
127
127
  mounted() {
128
- this.windowEvents = addEvents(window, {
128
+ this.domOn(window, {
129
129
  keyup: event => {
130
130
  if ((this.hasCancel || !this.hasButtons) && event.keyCode === 27) {
131
131
  this.cancel()
@@ -134,10 +134,6 @@ export default DitoComponent.component('DitoDialog', {
134
134
  })
135
135
  },
136
136
 
137
- unmounted() {
138
- this.windowEvents.remove()
139
- },
140
-
141
137
  methods: {
142
138
  remove() {
143
139
  this.$emit('remove')
@@ -16,12 +16,13 @@ component(
16
16
  </template>
17
17
 
18
18
  <script>
19
- import { addEvents } from '@ditojs/ui'
20
19
  import DitoComponent from '../DitoComponent'
20
+ import DomMixin from '../mixins/DomMixin.js'
21
21
  import { UseSortable } from '@vueuse/integrations/useSortable/component'
22
22
 
23
23
  // @vue/component
24
24
  export default DitoComponent.component('DitoDraggable', {
25
+ mixins: [DomMixin],
25
26
  components: { UseSortable },
26
27
  emits: ['update:modelValue'],
27
28
 
@@ -53,8 +54,8 @@ export default DitoComponent.component('DitoDraggable', {
53
54
 
54
55
  methods: {
55
56
  onStart(event) {
56
- this.isDragging = true
57
57
  this.options.onStart?.(event)
58
+ this.isDragging = true
58
59
  this.mouseEvents?.remove()
59
60
  },
60
61
 
@@ -62,7 +63,7 @@ export default DitoComponent.component('DitoDraggable', {
62
63
  this.options.onEnd?.(event)
63
64
  // Keep `isDragging` true until the next mouse interaction so that
64
65
  // confused hover states are cleared before removing the hover catcher.
65
- this.mouseEvents = addEvents(this.$el, {
66
+ this.mouseEvents = this.domOn(document, {
66
67
  mousedown: this.onMouse,
67
68
  mousemove: this.onMouse,
68
69
  mouseleave: this.onMouse
@@ -71,7 +72,7 @@ export default DitoComponent.component('DitoDraggable', {
71
72
 
72
73
  onMouse() {
73
74
  this.isDragging = false
74
- this.mouseEvents.remove()
75
+ this.mouseEvents?.remove()
75
76
  this.mouseEvents = null
76
77
  }
77
78
  }
@@ -83,16 +84,18 @@ export default DitoComponent.component('DitoDraggable', {
83
84
 
84
85
  .dito-draggable {
85
86
  // Overlay a hover catcher while we're dragging to prevent hover states from
86
- // getting stuck / confused.
87
- &:has(&__chosen),
88
- &--dragging {
89
- > * {
90
- position: relative;
87
+ // getting stuck / confused. Safari struggles with this, so disable it there.
88
+ @include browser-query(('chrome', 'firefox')) {
89
+ &:has(&__chosen),
90
+ &--dragging {
91
+ > * {
92
+ position: relative;
91
93
 
92
- > :first-child::after {
93
- content: '';
94
- position: absolute;
95
- inset: 0;
94
+ > :first-child::after {
95
+ content: '';
96
+ position: absolute;
97
+ inset: 0;
98
+ }
96
99
  }
97
100
  }
98
101
  }
@@ -115,8 +115,9 @@ export default DitoComponent.component('DitoLabel', {
115
115
  }
116
116
 
117
117
  &__inner {
118
- display: flex;
119
118
  flex: 1 0 0%;
119
+ display: flex;
120
+ align-items: center;
120
121
  overflow: hidden;
121
122
  }
122
123
 
@@ -2,6 +2,7 @@
2
2
  <template lang="pug">
3
3
  .dito-pane(
4
4
  v-if="isPopulated && componentSchemas.length > 0"
5
+ v-resize="onResizePane"
5
6
  :class=`{
6
7
  'dito-pane--single': isSingleComponent
7
8
  }`
@@ -22,8 +23,9 @@
22
23
  )
23
24
  DitoContainer(
24
25
  v-if="shouldRenderSchema(schema)"
26
+ ref="containers"
25
27
  :key="nestedDataPath"
26
- v-resize="event => onResizeContainer(index, event)"
28
+ :data-index="index"
27
29
  :schema="schema"
28
30
  :dataPath="dataPath"
29
31
  :data="data"
@@ -196,15 +198,26 @@ export default DitoComponent.component('DitoPane', {
196
198
  }
197
199
  },
198
200
 
199
- onResizeContainer(index, { target, contentRect: { width, height } }) {
200
- this.positions[index] =
201
- width > 0 && height > 0
202
- ? {
203
- top: target.getBoundingClientRect().y,
204
- height: height / parseFloat(getComputedStyle(target).fontSize),
205
- node: target
206
- }
207
- : null
201
+ onResizePane() {
202
+ this.$nextTick(() => {
203
+ for (const container of this.$refs.containers) {
204
+ const node = container.$el
205
+ const index = +node.dataset.index
206
+ const bounds = node.getBoundingClientRect()
207
+ const style = getComputedStyle(node)
208
+ const padding = parseFloat(style.padding)
209
+ const fontSize = parseFloat(style.fontSize)
210
+ const height = bounds.height - 2 * padding
211
+ this.positions[index] =
212
+ height <= 0
213
+ ? null
214
+ : {
215
+ top: bounds.y,
216
+ height: height / fontSize,
217
+ node
218
+ }
219
+ }
220
+ })
208
221
  },
209
222
 
210
223
  isInLabeledRow(index) {
@@ -1,5 +1,9 @@
1
1
  <template lang="pug">
2
- .dito-root
2
+ .dito-root(
3
+ :data-agent-browser="appState.agent.browser"
4
+ :data-agent-platform="appState.agent.platform"
5
+ :data-agent-version="appState.agent.versionNumber"
6
+ )
3
7
  VueNotifications.dito-notifications(
4
8
  ref="notifications"
5
9
  position="top right"
@@ -21,7 +25,10 @@
21
25
  @remove="removeDialog(key)"
22
26
  )
23
27
  DitoNavigation
24
- main.dito-page.dito-scroll-parent(:class="appState.pageClass")
28
+ main.dito-page.dito-scroll-parent(
29
+ v-resize="onResizePage"
30
+ :class="pageClasses"
31
+ )
25
32
  DitoHeader(
26
33
  :spinner="options.spinner"
27
34
  :isLoading="isLoading"
@@ -43,10 +50,10 @@
43
50
  import { delegate as tippyDelegate } from 'tippy.js'
44
51
  import { asArray, mapConcurrently, stripTags } from '@ditojs/utils'
45
52
  import DitoComponent from '../DitoComponent.js'
53
+ import DomMixin from '../mixins/DomMixin.js'
46
54
  import DitoUser from '../DitoUser.js'
47
55
  import DitoView from '../components/DitoView.vue'
48
56
  import DitoDialog from './DitoDialog.vue'
49
- import DomMixin from '../mixins/DomMixin.js'
50
57
  import {
51
58
  processView,
52
59
  resolveViews,
@@ -74,8 +81,9 @@ export default DitoComponent.component('DitoRoot', {
74
81
  resolvedViews: {},
75
82
  removeRoutes: null,
76
83
  dialogs: {},
77
- allowLogin: false,
84
+ pageWidth: 0,
78
85
  loadingCount: 0,
86
+ allowLogin: false,
79
87
  isDraggingFiles: false
80
88
  }
81
89
  },
@@ -87,6 +95,19 @@ export default DitoComponent.component('DitoRoot', {
87
95
 
88
96
  isLoading() {
89
97
  return this.loadingCount > 0
98
+ },
99
+
100
+ pageClasses() {
101
+ const prefix = 'dito-page'
102
+ // NOTE: Keep synced with $content-width in SCSS:
103
+ const contentWidth = 900
104
+ return [
105
+ this.appState.pageClass,
106
+ {
107
+ [`${prefix}--width-80`]: this.pageWidth <= contentWidth * 0.8,
108
+ [`${prefix}--width-60`]: this.pageWidth <= contentWidth * 0.6
109
+ }
110
+ ]
90
111
  }
91
112
  },
92
113
 
@@ -103,7 +124,7 @@ export default DitoComponent.component('DitoRoot', {
103
124
  tippyDelegate(this.$el, {
104
125
  target: '.dito-info',
105
126
  theme: 'info',
106
- appendTo: 'parent',
127
+ appendTo: node => node.closest('.dito-pane'),
107
128
  animation: 'shift-away-subtle',
108
129
  interactive: true,
109
130
  delay: 250,
@@ -438,6 +459,10 @@ export default DitoComponent.component('DitoRoot', {
438
459
  ...routes.flat()
439
460
  ])
440
461
  this.$router.replace(fullPath)
462
+ },
463
+
464
+ onResizePage({ contentRect: { width } }) {
465
+ this.pageWidth = width
441
466
  }
442
467
  }
443
468
  })
@@ -475,10 +500,9 @@ function addRoutes(router, routes) {
475
500
 
476
501
  flex: 0 1 var(--max-page-width);
477
502
  background: $content-color-background;
503
+ min-width: 0%;
478
504
  max-width: var(--max-page-width);
479
505
  overflow: visible; // For .dito-header full-width background.
480
- // For the `@container` rule in `.dito-container` to work:
481
- container-type: inline-size;
482
506
 
483
507
  &--wide {
484
508
  --max-content-width: #{$content-width-wide};
@@ -505,9 +529,9 @@ function addRoutes(router, routes) {
505
529
  position: fixed;
506
530
  top: 0;
507
531
  left: 0;
508
- z-index: $z-index-drag-overlay;
509
532
  width: 100%;
510
533
  height: 100%;
534
+ z-index: $z-index-drag-overlay;
511
535
  background: rgba(0, 0, 0, 0.25);
512
536
  pointer-events: none;
513
537
  backdrop-filter: blur(8px);
@@ -39,7 +39,9 @@ slot(name="before")
39
39
  :tabs="tabs"
40
40
  )
41
41
  DitoClipboard(
42
+ v-if="clipboard"
42
43
  :clipboard="clipboard"
44
+ :schema="schema"
43
45
  :dataPath="dataPath"
44
46
  :data="data"
45
47
  )
@@ -236,10 +238,13 @@ export default DitoComponent.component('DitoSchema', {
236
238
  return this.processData({ target: 'server', schemaOnly: true })
237
239
  },
238
240
 
239
- clipboardData() {
240
- return {
241
- $schema: this.schema.name,
242
- ...this.processData({ target: 'clipboard', schemaOnly: true })
241
+ clipboardData: {
242
+ get() {
243
+ return this.processData({ target: 'clipboard', schemaOnly: true })
244
+ },
245
+
246
+ set(data) {
247
+ this.setData(data)
243
248
  }
244
249
  },
245
250
 
@@ -1,5 +1,8 @@
1
1
  <template lang="pug">
2
- aside.dito-sidebar.dito-scroll-parent
2
+ aside.dito-sidebar.dito-scroll-parent(
3
+ v-resize="onResizeSidebar"
4
+ :class="classes"
5
+ )
3
6
  nav.dito-header
4
7
  slot
5
8
  .dito-sidebar__teleport.dito-scroll
@@ -9,7 +12,31 @@ aside.dito-sidebar.dito-scroll-parent
9
12
  import DitoComponent from '../DitoComponent.js'
10
13
 
11
14
  // @vue/component
12
- export default DitoComponent.component('DitoSidebar', {})
15
+ export default DitoComponent.component('DitoSidebar', {
16
+ data() {
17
+ return {
18
+ sidebarWidth: 0
19
+ }
20
+ },
21
+
22
+ computed: {
23
+ classes() {
24
+ const prefix = 'dito-sidebar'
25
+ // NOTE: Keep synced with $sidebar-max-width in SCSS:
26
+ const sidebarWidth = 360
27
+ return {
28
+ [`${prefix}--width-99`]: this.sidebarWidth < sidebarWidth,
29
+ [`${prefix}--width-60`]: this.sidebarWidth <= sidebarWidth * 0.6
30
+ }
31
+ }
32
+ },
33
+
34
+ methods: {
35
+ onResizeSidebar({ contentRect: { width } }) {
36
+ this.sidebarWidth = width
37
+ }
38
+ }
39
+ })
13
40
  </script>
14
41
 
15
42
  <style lang="scss">
@@ -19,7 +46,5 @@ export default DitoComponent.component('DitoSidebar', {})
19
46
  flex: 0 1 $sidebar-max-width;
20
47
  max-width: $sidebar-max-width;
21
48
  min-width: $sidebar-min-width;
22
- // For the `@container` rule in `.dito-container` to work:
23
- container-type: inline-size;
24
49
  }
25
50
  </style>
@@ -51,7 +51,7 @@ export default DitoComponent.component('DitoUploadFile', {
51
51
  'file.upload.file': {
52
52
  immediate: true,
53
53
  handler(file) {
54
- if (file && this.thumbnail) {
54
+ if (this.thumbnail && file?.type.startsWith('image/')) {
55
55
  const reader = new FileReader()
56
56
  reader.onload = () => {
57
57
  this.uploadUrl = reader.result
@@ -39,13 +39,14 @@ export default {
39
39
  ],
40
40
 
41
41
  provide() {
42
+ const self = () => this
42
43
  return this.providesData
43
44
  ? {
44
- $parentComponent: () => this,
45
- $dataComponent: () => this
45
+ $parentComponent: self,
46
+ $dataComponent: self
46
47
  }
47
48
  : {
48
- $parentComponent: () => this
49
+ $parentComponent: self
49
50
  }
50
51
  },
51
52
 
@@ -1,11 +1,12 @@
1
1
  <template lang="pug">
2
- input.dito-checkbox(
3
- :id="dataPath"
4
- ref="element"
5
- v-model="value"
6
- type="checkbox"
7
- v-bind="attributes"
8
- )
2
+ .dito-checkbox
3
+ input(
4
+ :id="dataPath"
5
+ ref="element"
6
+ v-model="value"
7
+ type="checkbox"
8
+ v-bind="attributes"
9
+ )
9
10
  </template>
10
11
 
11
12
  <script>