@radio-garden/ditojs-admin 2.85.2-0.5067ad799

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 (153) hide show
  1. package/README.md +180 -0
  2. package/dist/dito-admin.css +1 -0
  3. package/dist/dito-admin.es.js +12106 -0
  4. package/dist/dito-admin.umd.js +7 -0
  5. package/package.json +96 -0
  6. package/src/DitoAdmin.js +293 -0
  7. package/src/DitoComponent.js +34 -0
  8. package/src/DitoContext.js +318 -0
  9. package/src/DitoTypeComponent.js +42 -0
  10. package/src/DitoUser.js +12 -0
  11. package/src/appState.js +12 -0
  12. package/src/components/DitoAccount.vue +60 -0
  13. package/src/components/DitoAffix.vue +68 -0
  14. package/src/components/DitoAffixes.vue +200 -0
  15. package/src/components/DitoButtons.vue +80 -0
  16. package/src/components/DitoClipboard.vue +186 -0
  17. package/src/components/DitoContainer.vue +374 -0
  18. package/src/components/DitoCreateButton.vue +146 -0
  19. package/src/components/DitoDialog.vue +242 -0
  20. package/src/components/DitoDraggable.vue +117 -0
  21. package/src/components/DitoEditButtons.vue +135 -0
  22. package/src/components/DitoErrors.vue +83 -0
  23. package/src/components/DitoForm.vue +521 -0
  24. package/src/components/DitoFormInner.vue +26 -0
  25. package/src/components/DitoFormNested.vue +17 -0
  26. package/src/components/DitoHeader.vue +84 -0
  27. package/src/components/DitoLabel.vue +200 -0
  28. package/src/components/DitoMenu.vue +186 -0
  29. package/src/components/DitoNavigation.vue +40 -0
  30. package/src/components/DitoNotifications.vue +170 -0
  31. package/src/components/DitoPagination.vue +42 -0
  32. package/src/components/DitoPane.vue +334 -0
  33. package/src/components/DitoPanel.vue +256 -0
  34. package/src/components/DitoPanels.vue +61 -0
  35. package/src/components/DitoRoot.vue +524 -0
  36. package/src/components/DitoSchema.vue +846 -0
  37. package/src/components/DitoSchemaInlined.vue +97 -0
  38. package/src/components/DitoScopes.vue +76 -0
  39. package/src/components/DitoSidebar.vue +50 -0
  40. package/src/components/DitoSpinner.vue +95 -0
  41. package/src/components/DitoTableCell.vue +64 -0
  42. package/src/components/DitoTableHead.vue +121 -0
  43. package/src/components/DitoTabs.vue +103 -0
  44. package/src/components/DitoTrail.vue +124 -0
  45. package/src/components/DitoTreeItem.vue +420 -0
  46. package/src/components/DitoUploadFile.vue +199 -0
  47. package/src/components/DitoVNode.vue +14 -0
  48. package/src/components/DitoView.vue +143 -0
  49. package/src/components/index.js +42 -0
  50. package/src/directives/resize.js +83 -0
  51. package/src/index.js +1 -0
  52. package/src/mixins/ContextMixin.js +68 -0
  53. package/src/mixins/DataMixin.js +131 -0
  54. package/src/mixins/DitoMixin.js +591 -0
  55. package/src/mixins/DomMixin.js +29 -0
  56. package/src/mixins/EmitterMixin.js +158 -0
  57. package/src/mixins/ItemMixin.js +144 -0
  58. package/src/mixins/LoadingMixin.js +23 -0
  59. package/src/mixins/NumberMixin.js +118 -0
  60. package/src/mixins/OptionsMixin.js +304 -0
  61. package/src/mixins/PulldownMixin.js +63 -0
  62. package/src/mixins/ResourceMixin.js +398 -0
  63. package/src/mixins/RouteMixin.js +190 -0
  64. package/src/mixins/SchemaParentMixin.js +33 -0
  65. package/src/mixins/SortableMixin.js +49 -0
  66. package/src/mixins/SourceMixin.js +734 -0
  67. package/src/mixins/TextMixin.js +26 -0
  68. package/src/mixins/TypeMixin.js +280 -0
  69. package/src/mixins/ValidationMixin.js +119 -0
  70. package/src/mixins/ValidatorMixin.js +57 -0
  71. package/src/mixins/ValueMixin.js +31 -0
  72. package/src/styles/_base.scss +17 -0
  73. package/src/styles/_button.scss +191 -0
  74. package/src/styles/_imports.scss +3 -0
  75. package/src/styles/_info.scss +19 -0
  76. package/src/styles/_layout.scss +19 -0
  77. package/src/styles/_pulldown.scss +38 -0
  78. package/src/styles/_scroll.scss +13 -0
  79. package/src/styles/_settings.scss +88 -0
  80. package/src/styles/_table.scss +223 -0
  81. package/src/styles/_tippy.scss +45 -0
  82. package/src/styles/style.scss +9 -0
  83. package/src/types/DitoTypeButton.vue +143 -0
  84. package/src/types/DitoTypeCheckbox.vue +27 -0
  85. package/src/types/DitoTypeCheckboxes.vue +65 -0
  86. package/src/types/DitoTypeCode.vue +199 -0
  87. package/src/types/DitoTypeColor.vue +272 -0
  88. package/src/types/DitoTypeComponent.vue +31 -0
  89. package/src/types/DitoTypeComputed.vue +50 -0
  90. package/src/types/DitoTypeDate.vue +99 -0
  91. package/src/types/DitoTypeLabel.vue +23 -0
  92. package/src/types/DitoTypeList.vue +364 -0
  93. package/src/types/DitoTypeMarkup.vue +700 -0
  94. package/src/types/DitoTypeMultiselect.vue +522 -0
  95. package/src/types/DitoTypeNumber.vue +66 -0
  96. package/src/types/DitoTypeObject.vue +136 -0
  97. package/src/types/DitoTypePanel.vue +18 -0
  98. package/src/types/DitoTypeProgress.vue +40 -0
  99. package/src/types/DitoTypeRadio.vue +45 -0
  100. package/src/types/DitoTypeSection.vue +80 -0
  101. package/src/types/DitoTypeSelect.vue +133 -0
  102. package/src/types/DitoTypeSlider.vue +66 -0
  103. package/src/types/DitoTypeSpacer.vue +11 -0
  104. package/src/types/DitoTypeSwitch.vue +40 -0
  105. package/src/types/DitoTypeText.vue +101 -0
  106. package/src/types/DitoTypeTextarea.vue +48 -0
  107. package/src/types/DitoTypeTreeList.vue +193 -0
  108. package/src/types/DitoTypeUpload.vue +503 -0
  109. package/src/types/index.js +30 -0
  110. package/src/utils/SchemaGraph.js +147 -0
  111. package/src/utils/accessor.js +75 -0
  112. package/src/utils/agent.js +47 -0
  113. package/src/utils/data.js +92 -0
  114. package/src/utils/filter.js +266 -0
  115. package/src/utils/math.js +14 -0
  116. package/src/utils/options.js +48 -0
  117. package/src/utils/path.js +5 -0
  118. package/src/utils/resource.js +44 -0
  119. package/src/utils/route.js +53 -0
  120. package/src/utils/schema.js +1121 -0
  121. package/src/utils/type.js +81 -0
  122. package/src/utils/uid.js +15 -0
  123. package/src/utils/units.js +5 -0
  124. package/src/validators/_creditcard.js +6 -0
  125. package/src/validators/_decimals.js +11 -0
  126. package/src/validators/_domain.js +6 -0
  127. package/src/validators/_email.js +6 -0
  128. package/src/validators/_hostname.js +6 -0
  129. package/src/validators/_integer.js +6 -0
  130. package/src/validators/_max.js +6 -0
  131. package/src/validators/_min.js +6 -0
  132. package/src/validators/_password.js +5 -0
  133. package/src/validators/_range.js +6 -0
  134. package/src/validators/_required.js +9 -0
  135. package/src/validators/_url.js +6 -0
  136. package/src/validators/index.js +12 -0
  137. package/src/verbs.js +17 -0
  138. package/types/index.d.ts +3298 -0
  139. package/types/tests/admin.test-d.ts +27 -0
  140. package/types/tests/component-buttons.test-d.ts +44 -0
  141. package/types/tests/component-list.test-d.ts +159 -0
  142. package/types/tests/component-misc.test-d.ts +137 -0
  143. package/types/tests/component-object.test-d.ts +69 -0
  144. package/types/tests/component-section.test-d.ts +174 -0
  145. package/types/tests/component-select.test-d.ts +107 -0
  146. package/types/tests/components.test-d.ts +81 -0
  147. package/types/tests/context.test-d.ts +31 -0
  148. package/types/tests/fixtures.ts +24 -0
  149. package/types/tests/form.test-d.ts +109 -0
  150. package/types/tests/instance.test-d.ts +20 -0
  151. package/types/tests/schema-features.test-d.ts +402 -0
  152. package/types/tests/variance.test-d.ts +125 -0
  153. package/types/tests/view.test-d.ts +146 -0
@@ -0,0 +1,522 @@
1
+ <template lang="pug">
2
+ .dito-multiselect
3
+ .dito-multiselect__inner
4
+ DitoAffixes(
5
+ :items="schema.prefix"
6
+ position="prefix"
7
+ mode="input"
8
+ absolute
9
+ :disabled="disabled"
10
+ :parentContext="context"
11
+ )
12
+ VueMultiselect(
13
+ ref="element"
14
+ v-model="selectedOptions"
15
+ :class="multiselectClasses"
16
+ :showLabels="false"
17
+ :placeholder="placeholder"
18
+ tagPlaceholder="Press enter to add new tag"
19
+ :options="populate && activeOptions || []"
20
+ :customLabel="getLabelForOption"
21
+ :trackBy="optionValue"
22
+ :groupLabel="groupByLabel"
23
+ :groupValues="groupByOptions"
24
+ :multiple="multiple"
25
+ :taggable="taggable"
26
+ :searchable="searchable"
27
+ :internalSearch="!searchFilter"
28
+ :preserveSearch="!!searchFilter"
29
+ :clearOnSelect="!searchFilter"
30
+ :closeOnSelect="!stayOpen"
31
+ :loading="isLoading"
32
+ v-bind="attributes"
33
+ @open="onOpen"
34
+ @close="onClose"
35
+ @tag="onAddTag"
36
+ @search-change="onSearchChange"
37
+ )
38
+ DitoAffixes(
39
+ :items="schema.suffix"
40
+ position="suffix"
41
+ mode="input"
42
+ absolute
43
+ :clearable="showClearButton"
44
+ :disabled="disabled"
45
+ :inlineInfo="inlineInfo"
46
+ :parentContext="context"
47
+ @clear="clear"
48
+ )
49
+ //- Edit button is never disabled, even if the field is disabled.
50
+ DitoEditButtons(
51
+ v-if="editable"
52
+ :schema="schema"
53
+ :dataPath="dataPath"
54
+ :data="data"
55
+ :meta="meta"
56
+ :store="store"
57
+ :disabled="false"
58
+ :editable="editable"
59
+ :editPath="editPath"
60
+ )
61
+ </template>
62
+
63
+ <script>
64
+ import DitoTypeComponent from '../DitoTypeComponent.js'
65
+ import DitoContext from '../DitoContext.js'
66
+ import TypeMixin from '../mixins/TypeMixin.js'
67
+ import OptionsMixin from '../mixins/OptionsMixin.js'
68
+ import DitoAffixes from '../components/DitoAffixes.vue'
69
+ import VueMultiselect from 'vue-multiselect'
70
+ import { getSchemaAccessor } from '../utils/accessor.js'
71
+ import { isBoolean } from '@ditojs/utils'
72
+
73
+ // @vue/component
74
+ export default DitoTypeComponent.register('multiselect', {
75
+ mixins: [OptionsMixin],
76
+ components: { DitoAffixes, VueMultiselect },
77
+
78
+ data() {
79
+ return {
80
+ searchedOptions: null,
81
+ populate: false
82
+ }
83
+ },
84
+
85
+ computed: {
86
+ selectedOptions: {
87
+ get() {
88
+ return this.multiple
89
+ ? (this.selectedValue || [])
90
+ .map(
91
+ // If an option cannot be found, we may be in taggable mode and
92
+ // can add it.
93
+ value => (
94
+ this.getOptionForValue(value) || this.addTagOption(value)
95
+ )
96
+ )
97
+ // Filter out options that we couldn't match.
98
+ // TODO: Should we display an error instead?
99
+ .filter(Boolean)
100
+ : this.selectedOption
101
+ },
102
+
103
+ set(option) {
104
+ // Convert value to options object, since vue-multiselect can't map that
105
+ // itself unfortunately. `track-by` is used for :key mapping it seems.
106
+ this.selectedValue = this.multiple
107
+ ? (option || []).map(value => this.getValueForOption(value))
108
+ : this.getValueForOption(option)
109
+ this.onChange()
110
+ }
111
+ },
112
+
113
+ activeOptions() {
114
+ return this.searchedOptions || this.options
115
+ },
116
+
117
+ // @override
118
+ multiple: getSchemaAccessor('multiple', {
119
+ type: Boolean,
120
+ default: false
121
+ }),
122
+
123
+ searchable: getSchemaAccessor('searchable', {
124
+ type: Boolean,
125
+ default: false
126
+ }),
127
+
128
+ taggable: getSchemaAccessor('taggable', {
129
+ type: Boolean,
130
+ default: false
131
+ }),
132
+
133
+ stayOpen: getSchemaAccessor('stayOpen', {
134
+ type: Boolean,
135
+ default: false
136
+ }),
137
+
138
+ multiselectClasses() {
139
+ const prefix = 'multiselect'
140
+ return {
141
+ [`${prefix}--multiple`]: this.multiple,
142
+ [`${prefix}--loading`]: this.isLoading,
143
+ [`${prefix}--highlight`]: this.showHighlight
144
+ }
145
+ },
146
+
147
+ placeholder() {
148
+ let { placeholder, searchable, taggable } = this.schema
149
+ if (isBoolean(placeholder)) {
150
+ placeholder = placeholder ? undefined : null
151
+ }
152
+ return placeholder === undefined
153
+ ? searchable && taggable
154
+ ? `Search or add a ${this.label}`
155
+ : searchable
156
+ ? `Select or search ${this.label}`
157
+ : undefined
158
+ : placeholder
159
+ },
160
+
161
+ showHighlight() {
162
+ return this.isMounted && this.$refs.element.pointerDirty
163
+ }
164
+ },
165
+
166
+ mounted() {
167
+ if (this.autofocus) {
168
+ // vue-multiselect doesn't support the autofocus attribute. We need to
169
+ // handle it here.
170
+ this.focus()
171
+ }
172
+ },
173
+
174
+ methods: {
175
+ addTagOption(tag) {
176
+ if (this.taggable) {
177
+ const { optionLabel, optionValue } = this
178
+ const option =
179
+ optionLabel && optionValue
180
+ ? {
181
+ [optionLabel]: tag,
182
+ // TODO: Define a simple schema option to convert the tag value
183
+ // to something else, e.g. `toTag: tag => underscore(tag)`
184
+ [optionValue]: tag
185
+ }
186
+ : tag
187
+ this.options.push(option)
188
+ return option
189
+ }
190
+ },
191
+
192
+ focusElement() {
193
+ this.$refs.element.activate()
194
+ },
195
+
196
+ blurElement() {
197
+ this.$refs.element.deactivate()
198
+ },
199
+
200
+ onOpen() {
201
+ this.populate = true
202
+ },
203
+
204
+ onClose() {
205
+ // Since we don't fire blur events while the multiselect is open (see
206
+ // below), we need to do it here, when it's actually closed.
207
+ if (this.focused) {
208
+ this.onBlur()
209
+ }
210
+ },
211
+
212
+ onBlur() {
213
+ if (!this.$refs.element.isOpen) {
214
+ TypeMixin.methods.onBlur.call(this)
215
+ }
216
+ },
217
+
218
+ onAddTag(tag) {
219
+ const option = this.addTagOption(tag)
220
+ if (option) {
221
+ this.value ??= []
222
+ this.value.push(this.getValueForOption(option))
223
+ }
224
+ },
225
+
226
+ async onSearchChange(searchTerm) {
227
+ if (this.searchFilter) {
228
+ if (searchTerm) {
229
+ // Set `searchedOptions` to an empty array, before it will be
230
+ // populated asynchronously with the actual results.
231
+ this.searchedOptions = []
232
+ this.searchedOptions = await this.resolveData(
233
+ () => this.searchFilter(new DitoContext(this, { searchTerm }))
234
+ )
235
+ } else {
236
+ // Clear `searchedOptions` when the query is cleared.
237
+ this.searchedOptions = null
238
+ }
239
+ }
240
+ }
241
+ }
242
+ })
243
+ </script>
244
+
245
+ <style lang="scss">
246
+ @import '../styles/_imports';
247
+ @import 'vue-multiselect/dist/vue-multiselect.css';
248
+
249
+ $spinner-width: $select-arrow-width;
250
+ $tag-icon-width: 1.8em;
251
+ $tag-margin: 2px;
252
+ $tag-padding: 3px;
253
+ $tag-line-height: 1em;
254
+
255
+ .dito-multiselect {
256
+ display: inline-flex;
257
+ position: relative;
258
+
259
+ &__inner {
260
+ flex: 1;
261
+ position: relative;
262
+ display: flex;
263
+ align-items: center;
264
+ }
265
+
266
+ .dito-edit-buttons {
267
+ margin-left: $form-spacing-half;
268
+ }
269
+
270
+ .multiselect {
271
+ $self: last-selector(&);
272
+
273
+ --input-width: 100%;
274
+
275
+ font-size: inherit;
276
+ min-height: inherit;
277
+ color: $color-black;
278
+
279
+ &--multiple {
280
+ --input-width: auto;
281
+ }
282
+
283
+ &__tags {
284
+ display: flex;
285
+ font-size: inherit;
286
+ min-height: inherit;
287
+ overflow: auto;
288
+ padding: 0 $spinner-width 0 0;
289
+ // So tags can float on multiple lines and have proper margins:
290
+ padding-bottom: $tag-margin;
291
+
292
+ .dito-container--has-errors & {
293
+ border-color: $color-error;
294
+ }
295
+ }
296
+
297
+ &__tag {
298
+ float: left;
299
+ margin: $tag-margin 0 0 $tag-margin;
300
+ border-radius: 1em;
301
+ padding: $tag-padding $tag-icon-width $tag-padding 0.8em;
302
+ line-height: $tag-line-height;
303
+ height: calc($input-height - 2 * $tag-padding);
304
+ }
305
+
306
+ &__tags-wrap {
307
+ overflow: auto;
308
+ line-height: 0;
309
+ }
310
+
311
+ &__single,
312
+ &__placeholder,
313
+ &__input {
314
+ @include ellipsis;
315
+
316
+ flex: 1 0 0%;
317
+ width: 0;
318
+ min-height: 0;
319
+ margin: 0 0 1px 0;
320
+ font-size: inherit;
321
+ line-height: inherit;
322
+ // Sadly, vue-select sets style="padding: ...;" in addition to using
323
+ // classes, so `!important` is necessary:
324
+ padding: $input-padding !important;
325
+ // So input can float next to tags and have proper margins with
326
+ // &__tags:
327
+ padding-bottom: 0 !important;
328
+ background: none;
329
+ }
330
+
331
+ &__placeholder,
332
+ &__input::placeholder {
333
+ color: $color-placeholder;
334
+ }
335
+
336
+ &__placeholder {
337
+ &::after {
338
+ // Enforce actual line-height for positioning.
339
+ content: '\200b';
340
+ }
341
+ }
342
+
343
+ &__select,
344
+ &__spinner {
345
+ padding: 0;
346
+ // $border-width to prevent masking border with &__spinner
347
+ top: $border-width;
348
+ right: $border-width;
349
+ bottom: $border-width;
350
+ height: inherit;
351
+ border-radius: $border-radius;
352
+ }
353
+
354
+ &__select {
355
+ width: $select-arrow-width;
356
+
357
+ &::before {
358
+ @include arrow($select-arrow-size);
359
+
360
+ bottom: $select-arrow-bottom;
361
+ right: $select-arrow-right;
362
+ }
363
+ }
364
+
365
+ &__spinner {
366
+ width: $spinner-width;
367
+
368
+ &::before,
369
+ &::after {
370
+ // Change the width of the loading spinner
371
+ border-width: 3px;
372
+ border-top-color: $color-active;
373
+ inset: 0;
374
+ margin: auto;
375
+ }
376
+ }
377
+
378
+ &__option {
379
+ $option: last-selector(&);
380
+
381
+ min-height: unset;
382
+ height: unset;
383
+ line-height: $line-height;
384
+ padding: $input-padding;
385
+
386
+ &::after {
387
+ // Instruction text for options (e.g. "Press enter to add new tag")
388
+ position: static;
389
+ height: auto;
390
+ line-height: inherit;
391
+ padding-left: $input-padding-hor;
392
+ }
393
+
394
+ // Only show the highlight once the pulldown has received mouse or
395
+ // keyboard interaction, in which case `&--highlight` will be set,
396
+ // which is controlled by `pointerDirty` in vue-multiselect.
397
+ // Until then, clear the highlight style, but only if it isn't also
398
+ // disabled or selected, in which case we want to keep the style.
399
+ @at-root #{$self}:not(#{$self}--highlight)
400
+ #{$option}:not(#{$option}--disabled):not(#{$option}--selected) {
401
+ color: $color-text;
402
+ background: transparent;
403
+ }
404
+
405
+ &--highlight {
406
+ &::after {
407
+ background: transparent;
408
+ color: $color-white;
409
+ }
410
+
411
+ @at-root #{$self}#{$self}--highlight #{last-selector(&)} {
412
+ color: $color-text-inverted;
413
+ background: $color-active;
414
+ }
415
+ }
416
+
417
+ &--selected {
418
+ font-weight: normal;
419
+ color: $color-text;
420
+ background: $color-highlight;
421
+
422
+ @at-root #{$self}#{$self}--highlight &#{$option}--highlight {
423
+ color: $color-text-inverted;
424
+ }
425
+ }
426
+
427
+ &--disabled {
428
+ background: none;
429
+ color: $color-disabled;
430
+ }
431
+ }
432
+
433
+ &__tag {
434
+ color: $color-text-inverted;
435
+ background: $color-active;
436
+ }
437
+
438
+ &__tag-icon {
439
+ background: none;
440
+ border-radius: 1em;
441
+ width: $tag-icon-width;
442
+ margin: 0;
443
+
444
+ &::after {
445
+ @extend %icon-clear;
446
+
447
+ font-size: 0.9em;
448
+ color: $color-text-inverted;
449
+ }
450
+
451
+ &:hover::after {
452
+ color: $color-text;
453
+ }
454
+ }
455
+
456
+ &__tags,
457
+ &__content-wrapper {
458
+ border: $border-style;
459
+ border-radius: $border-radius;
460
+ }
461
+
462
+ &__content-wrapper {
463
+ z-index: $z-index-popup;
464
+ border-color: $color-active;
465
+ }
466
+
467
+ &:not(&--above) #{$self}__content-wrapper {
468
+ margin: (-$border-width) 0 0;
469
+ border-top-color: $border-color;
470
+ border-top-left-radius: 0;
471
+ border-top-right-radius: 0;
472
+ }
473
+
474
+ &--above #{$self}__content-wrapper {
475
+ margin: 0 0 (-$border-width);
476
+ border-bottom-color: $border-color;
477
+ border-bottom-left-radius: 0;
478
+ border-bottom-right-radius: 0;
479
+ }
480
+
481
+ &--active {
482
+ #{$self}__placeholder {
483
+ // Don't use `display: none` to hide place-holder, as the layout would
484
+ // collapse.
485
+ display: inline-block;
486
+ visibility: hidden;
487
+ }
488
+
489
+ #{$self}__single,
490
+ #{$self}__input {
491
+ // Sadly, vue-select sets `style="width"` in addition to using classes
492
+ // so `!important` is necessary:
493
+ width: var(--input-width) !important;
494
+ }
495
+
496
+ #{$self}__tags {
497
+ border-color: $color-active;
498
+ border-bottom-left-radius: 0;
499
+ border-bottom-right-radius: 0;
500
+ }
501
+
502
+ &#{$self}--above {
503
+ #{$self}__tags {
504
+ border-radius: $border-radius;
505
+ border-top-left-radius: 0;
506
+ border-top-right-radius: 0;
507
+ }
508
+ }
509
+ }
510
+
511
+ &--loading {
512
+ #{$self}__tags {
513
+ border-radius: $border-radius;
514
+ }
515
+
516
+ #{$self}__content-wrapper {
517
+ display: none;
518
+ }
519
+ }
520
+ }
521
+ }
522
+ </style>
@@ -0,0 +1,66 @@
1
+ <template lang="pug">
2
+ DitoInput.dito-number(
3
+ :id="dataPath"
4
+ ref="element"
5
+ v-model="inputValue"
6
+ type="number"
7
+ v-bind="attributes"
8
+ :min="min"
9
+ :max="max"
10
+ :step="stepValue"
11
+ )
12
+ template(#prefix)
13
+ DitoAffixes(
14
+ :items="schema.prefix"
15
+ position="prefix"
16
+ mode="input"
17
+ :disabled="disabled"
18
+ :parentContext="context"
19
+ )
20
+ template(#suffix)
21
+ DitoAffixes(
22
+ :items="schema.suffix"
23
+ position="suffix"
24
+ mode="input"
25
+ :clearable="showClearButton"
26
+ :disabled="disabled"
27
+ :inlineInfo="inlineInfo"
28
+ :parentContext="context"
29
+ @clear="clear"
30
+ )
31
+ </template>
32
+
33
+ <script>
34
+ import DitoTypeComponent from '../DitoTypeComponent.js'
35
+ import NumberMixin from '../mixins/NumberMixin.js'
36
+ import DitoAffixes from '../components/DitoAffixes.vue'
37
+ import { DitoInput } from '@ditojs/ui/src'
38
+
39
+ export default DitoTypeComponent.register(
40
+ ['number', 'integer'],
41
+ // @vue/component
42
+ {
43
+ mixins: [NumberMixin],
44
+ components: { DitoInput, DitoAffixes },
45
+ nativeField: true,
46
+ textField: true,
47
+
48
+ computed: {
49
+ isInteger() {
50
+ return this.type === 'integer'
51
+ }
52
+ }
53
+ }
54
+ )
55
+ </script>
56
+
57
+ <style lang="scss">
58
+ // Only show spin buttons if the number component defines a step size.
59
+ input[type='number']:not([step]) {
60
+ &::-webkit-inner-spin-button,
61
+ &::-webkit-outer-spin-button {
62
+ -webkit-appearance: none;
63
+ margin: 0;
64
+ }
65
+ }
66
+ </style>
@@ -0,0 +1,136 @@
1
+ <template lang="pug">
2
+ .dito-object(
3
+ v-if="isReady"
4
+ :id="dataPath"
5
+ )
6
+ .dito-object-content(
7
+ v-if="objectData"
8
+ )
9
+ //- Support the same rendering options as TypeList:
10
+ DitoSchemaInlined(
11
+ v-if="isInlined"
12
+ :label="objectLabel"
13
+ :schema="getItemFormSchema(schema, objectData, context)"
14
+ :dataPath="dataPath"
15
+ :data="objectData"
16
+ :meta="nestedMeta"
17
+ :store="store"
18
+ :disabled="disabled || isLoading"
19
+ :collapsed="collapsed"
20
+ :collapsible="collapsible"
21
+ :deletable="objectData && deletable"
22
+ :editable="objectData && editable"
23
+ :editPath="path"
24
+ :accumulatedBasis="accumulatedBasis"
25
+ @delete="deleteItem(objectData)"
26
+ )
27
+ component(
28
+ v-else-if="schema.component"
29
+ :is="schema.component"
30
+ :dataPath="dataPath"
31
+ :data="objectData"
32
+ :nested="false"
33
+ )
34
+ span(
35
+ v-else-if="render"
36
+ v-html="render(getContext())"
37
+ )
38
+ span(
39
+ v-else
40
+ v-html="getItemLabel(schema, objectData)"
41
+ )
42
+ //- NOTE: `DitoEditButtons` here only handle the create button outside of the
43
+ //- schema, the edit buttons inside are handled by `DitoSchemaInlined`.
44
+ DitoEditButtons(
45
+ :buttons="buttonSchemas"
46
+ :schema="schema"
47
+ :dataPath="dataPath"
48
+ :data="objectData"
49
+ :path="path"
50
+ :meta="meta"
51
+ :store="store"
52
+ :disabled="disabled || isLoading"
53
+ :creatable="creatable"
54
+ :createPath="createPath"
55
+ )
56
+ </template>
57
+
58
+ <script>
59
+ import DitoTypeComponent from '../DitoTypeComponent.js'
60
+ import DitoContext from '../DitoContext.js'
61
+ import SourceMixin from '../mixins/SourceMixin.js'
62
+ import { resolveSchemaComponent } from '../utils/schema.js'
63
+
64
+ // @vue/component
65
+ export default DitoTypeComponent.register('object', {
66
+ mixins: [SourceMixin],
67
+
68
+ getSourceType(type) {
69
+ // No need for transformation here. See TypeTreeList for details.
70
+ return type
71
+ },
72
+
73
+ computed: {
74
+ objectLabel() {
75
+ // Only show a label if the object is collapsible.
76
+ return this.collapsible
77
+ ? this.getItemLabel(this.schema, this.objectData, { asObject: true })
78
+ : null
79
+ }
80
+ },
81
+
82
+ methods: {
83
+ getContext() {
84
+ return new DitoContext(this, { data: this.objectData })
85
+ }
86
+ },
87
+
88
+ async processSchema(
89
+ api,
90
+ schema,
91
+ name,
92
+ routes,
93
+ level,
94
+ nested = false,
95
+ flatten = false,
96
+ process = null
97
+ ) {
98
+ await Promise.all([
99
+ resolveSchemaComponent(schema),
100
+ SourceMixin.processSchema(
101
+ api,
102
+ schema,
103
+ name,
104
+ routes,
105
+ level,
106
+ nested,
107
+ flatten,
108
+ process
109
+ )
110
+ ])
111
+ }
112
+ })
113
+ </script>
114
+
115
+ <style lang="scss">
116
+ @import '../styles/_imports';
117
+
118
+ .dito-object {
119
+ display: flex;
120
+ border: $border-style;
121
+ border-radius: $border-radius;
122
+ margin: 0;
123
+ padding: $form-spacing;
124
+ box-sizing: border-box;
125
+ min-width: min-content;
126
+
127
+ .dito-object-content {
128
+ flex: 0 1 100%;
129
+ }
130
+
131
+ > .dito-edit-buttons {
132
+ flex: 1 0 0%;
133
+ margin-left: $form-spacing;
134
+ }
135
+ }
136
+ </style>