@ditojs/admin 2.73.4 → 2.74.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.73.4",
3
+ "version": "2.74.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",
@@ -42,8 +42,8 @@
42
42
  "not ie_mob > 0"
43
43
  ],
44
44
  "dependencies": {
45
- "@ditojs/ui": "^2.73.2",
46
- "@ditojs/utils": "^2.73.0",
45
+ "@ditojs/ui": "^2.74.0",
46
+ "@ditojs/utils": "^2.74.0",
47
47
  "@kyvg/vue3-notification": "^3.4.2",
48
48
  "@lk77/vue3-color": "^3.0.6",
49
49
  "@tiptap/core": "^3.15.3",
@@ -89,9 +89,9 @@
89
89
  "vue-upload-component": "^3.1.17"
90
90
  },
91
91
  "devDependencies": {
92
- "@ditojs/build": "^2.73.0",
92
+ "@ditojs/build": "^2.74.0",
93
93
  "typescript": "^5.9.3",
94
94
  "vite": "^7.3.1"
95
95
  },
96
- "gitHead": "bbb41fce56cdd91db7a090a2237bd130a699159a"
96
+ "gitHead": "26c5b4bd33d1bc469c8b621a214ae3c8f0927c11"
97
97
  }
@@ -1,7 +1,9 @@
1
1
  <template lang="pug">
2
2
  .dito-affixes(
3
- v-if="visibleItems.length > 0"
3
+ v-if="hasContent"
4
+ :class="classes"
4
5
  )
6
+ slot(name="prepend")
5
7
  DitoAffix.dito-affix(
6
8
  v-for="(item, index) in visibleItems"
7
9
  :key="index"
@@ -10,19 +12,33 @@
10
12
  :item="item"
11
13
  :parentContext="parentContext"
12
14
  )
15
+ button.dito-affixes__clear(
16
+ v-if="clearable"
17
+ type="button"
18
+ :disabled="disabled"
19
+ @click.stop="$emit('clear')"
20
+ @mousedown.stop
21
+ )
22
+ slot(name="append")
13
23
  </template>
14
24
 
15
25
  <script>
16
26
  import DitoComponent from '../DitoComponent.js'
17
27
  import DitoAffix from './DitoAffix.vue'
18
28
  import { asArray, isString } from '@ditojs/utils'
29
+ import { hasSlotContent } from '@ditojs/ui/src'
19
30
  import { shouldRenderSchema } from '../utils/schema.js'
20
31
 
21
32
  export default DitoComponent.component('DitoAffixes', {
22
33
  components: { DitoAffix },
34
+ emits: ['clear'],
23
35
 
24
36
  props: {
25
37
  items: { type: [String, Object, Array], default: null },
38
+ position: { type: String, default: null },
39
+ absolute: { type: Boolean, default: false },
40
+ clearable: { type: Boolean, default: false },
41
+ disabled: { type: Boolean, default: false },
26
42
  parentContext: { type: Object, required: true }
27
43
  },
28
44
 
@@ -52,6 +68,23 @@ export default DitoComponent.component('DitoAffixes', {
52
68
  return this.processedItems.filter(item =>
53
69
  shouldRenderSchema(item, this.context)
54
70
  )
71
+ },
72
+
73
+ classes() {
74
+ const prefix = 'dito-affixes'
75
+ return {
76
+ [`${prefix}--${this.position}`]: this.position,
77
+ [`${prefix}--absolute`]: this.absolute
78
+ }
79
+ },
80
+
81
+ hasContent() {
82
+ return (
83
+ this.visibleItems.length > 0 ||
84
+ this.clearable ||
85
+ hasSlotContent(this.$slots.prepend) ||
86
+ hasSlotContent(this.$slots.append)
87
+ )
55
88
  }
56
89
  }
57
90
  })
@@ -61,13 +94,34 @@ export default DitoComponent.component('DitoAffixes', {
61
94
  @import '../styles/_imports';
62
95
 
63
96
  .dito-affixes {
97
+ $self: &;
98
+
64
99
  @include user-select(none);
65
100
 
66
101
  display: flex;
67
102
  align-items: center;
68
103
  gap: $input-padding-hor;
69
104
 
70
- @at-root .dito-input & {
105
+ &--absolute {
106
+ position: absolute;
107
+ inset: $border-width;
108
+ margin: $input-padding-ver $input-padding-hor;
109
+ pointer-events: none;
110
+
111
+ > * {
112
+ pointer-events: auto;
113
+ }
114
+
115
+ &#{$self}--prefix {
116
+ right: unset;
117
+ }
118
+
119
+ &#{$self}--suffix {
120
+ left: unset;
121
+ }
122
+ }
123
+
124
+ @at-root .dito-component & {
71
125
  color: $color-grey;
72
126
 
73
127
  .dito-icon--disabled {
@@ -75,8 +129,44 @@ export default DitoComponent.component('DitoAffixes', {
75
129
  }
76
130
  }
77
131
 
78
- @at-root .dito-input:focus-within & {
132
+ @at-root .dito-component:focus-within:not(:has(.dito-component)) & {
79
133
  color: $color-active;
80
134
  }
135
+
136
+ &__clear {
137
+ --size: #{calc($input-height - 4 * $border-width)};
138
+
139
+ display: none;
140
+ position: relative;
141
+ cursor: pointer;
142
+ width: var(--size);
143
+ height: var(--size);
144
+ border-radius: $border-radius;
145
+ margin: (-$input-padding-ver + $border-width)
146
+ (-$input-padding-hor + $border-width);
147
+ padding: 0;
148
+ border: 0;
149
+
150
+ @at-root .dito-component:hover:not(:has(.dito-component)) & {
151
+ display: block;
152
+ }
153
+
154
+ &::before {
155
+ @extend %icon-clear;
156
+
157
+ color: $color-grey;
158
+ }
159
+
160
+ &:hover::before {
161
+ color: $color-black;
162
+ }
163
+ }
164
+
165
+ // Hide other affixes when clear button is shown
166
+ // prettier-ignore
167
+ @at-root .dito-component:hover:not(:has(.dito-component))
168
+ #{$self}:has(#{$self}__clear) > *:not(#{$self}__clear) {
169
+ display: none;
170
+ }
81
171
  }
82
172
  </style>
@@ -15,6 +15,7 @@ component.dito-label(
15
15
  )
16
16
  DitoAffixes.dito-label__prefix(
17
17
  :items="prefixes"
18
+ position="prefix"
18
19
  :parentContext="context"
19
20
  )
20
21
  label(
@@ -24,6 +25,7 @@ component.dito-label(
24
25
  )
25
26
  DitoAffixes.dito-label__suffix(
26
27
  :items="suffixes"
28
+ position="suffix"
27
29
  :parentContext="context"
28
30
  )
29
31
  .dito-info(
@@ -213,6 +215,7 @@ export default DitoComponent.component('DitoLabel', {
213
215
  @at-root button#{&} {
214
216
  border: 0;
215
217
  padding: 0;
218
+ background: none;
216
219
  text-align: start;
217
220
 
218
221
  &:hover {
@@ -109,37 +109,6 @@
109
109
  cursor: grabbing;
110
110
  }
111
111
  }
112
-
113
- &--overlay {
114
- $overlay-inset: calc(2 * $border-width);
115
-
116
- position: absolute;
117
- inset: $overlay-inset;
118
- left: unset;
119
- padding: 0;
120
- border: 0;
121
- border-radius: $border-radius;
122
- cursor: pointer;
123
-
124
- &.dito-button--clear {
125
- width: calc(2em - 2 * ($border-width + $overlay-inset));
126
- display: none;
127
-
128
- @at-root .dito-component:hover:not(:has(.dito-component)) & {
129
- display: block;
130
- }
131
-
132
- &::before {
133
- @extend %icon-clear;
134
-
135
- color: $color-grey;
136
- }
137
-
138
- &:hover::before {
139
- color: $color-black;
140
- }
141
- }
142
- }
143
112
  }
144
113
 
145
114
  .dito-buttons {
@@ -13,12 +13,16 @@ DitoButton.dito-button(
13
13
  DitoAffixes(
14
14
  v-if="prefixes.length > 0"
15
15
  :items="prefixes"
16
+ position="prefix"
17
+ :disabled="disabled"
16
18
  :parentContext="context"
17
19
  )
18
20
  template(#suffix)
19
21
  DitoAffixes(
20
22
  v-if="suffixes.length > 0"
21
23
  :items="suffixes"
24
+ position="suffix"
25
+ :disabled="disabled"
22
26
  :parentContext="context"
23
27
  )
24
28
  .dito-info(
@@ -4,24 +4,36 @@ DitoTrigger.dito-color(
4
4
  trigger="focus"
5
5
  )
6
6
  template(#trigger)
7
- .dito-input(:class="{ 'dito-input--focus': showPopup }")
8
- input(
9
- :id="dataPath"
10
- ref="element"
11
- v-model="hexValue"
12
- type="input"
13
- size="8"
14
- v-bind="attributes"
15
- )
16
- .dito-color__preview(
17
- v-if="value"
18
- )
19
- div(:style="{ background: `#${hexValue || '00000000'}` }")
20
- button.dito-button--clear.dito-button--overlay(
21
- v-if="showClearButton"
22
- :disabled="disabled"
23
- @click.stop="clear"
24
- )
7
+ DitoInput(
8
+ :id="dataPath"
9
+ ref="element"
10
+ v-model="hexValue"
11
+ type="input"
12
+ size="8"
13
+ :focused="showPopup"
14
+ v-bind="attributes"
15
+ )
16
+ template(#prefix)
17
+ DitoAffixes(
18
+ :items="schema.prefix"
19
+ position="prefix"
20
+ :disabled="disabled"
21
+ :parentContext="context"
22
+ )
23
+ template(#suffix)
24
+ DitoAffixes(
25
+ :items="schema.suffix"
26
+ position="suffix"
27
+ :clearable="showClearButton"
28
+ :disabled="disabled"
29
+ :parentContext="context"
30
+ @clear="clear"
31
+ )
32
+ template(#append)
33
+ .dito-color__preview(
34
+ v-if="value"
35
+ )
36
+ div(:style="{ background: `#${hexValue || '00000000'}` }")
25
37
  template(#popup)
26
38
  SketchPicker.dito-color__picker(
27
39
  v-model="colorValue"
@@ -35,8 +47,9 @@ DitoTrigger.dito-color(
35
47
  import tinycolor from 'tinycolor2'
36
48
  import { Sketch as SketchPicker } from '@lk77/vue3-color'
37
49
  import { isObject, isString } from '@ditojs/utils'
38
- import { DitoTrigger } from '@ditojs/ui/src'
50
+ import { DitoTrigger, DitoInput } from '@ditojs/ui/src'
39
51
  import DitoTypeComponent from '../DitoTypeComponent.js'
52
+ import DitoAffixes from '../components/DitoAffixes.vue'
40
53
  import { getSchemaAccessor } from '../utils/accessor.js'
41
54
 
42
55
  // Monkey-patch the `SketchPicker's` `hex` computed property to return lowercase
@@ -48,7 +61,7 @@ SketchPicker.computed.hex = function () {
48
61
 
49
62
  // @vue/component
50
63
  export default DitoTypeComponent.register('color', {
51
- components: { DitoTrigger, SketchPicker },
64
+ components: { DitoTrigger, DitoInput, DitoAffixes, SketchPicker },
52
65
 
53
66
  data() {
54
67
  return {
@@ -214,7 +227,7 @@ $color-swatch-radius: $border-radius - $border-width;
214
227
 
215
228
  .dito-color {
216
229
  .dito-input {
217
- display: block;
230
+ display: flex;
218
231
  position: relative;
219
232
 
220
233
  input {
@@ -224,10 +237,6 @@ $color-swatch-radius: $border-radius - $border-width;
224
237
  }
225
238
  }
226
239
 
227
- .dito-button--clear {
228
- margin-right: $color-swatch-width;
229
- }
230
-
231
240
  &__picker {
232
241
  margin: $popup-margin;
233
242
  border: $border-style;
@@ -238,15 +247,14 @@ $color-swatch-radius: $border-radius - $border-width;
238
247
 
239
248
  &__preview {
240
249
  background: $pattern-transparency;
250
+ margin: (-$input-padding-ver) (-$input-padding-hor);
251
+ margin-left: 0;
241
252
  border-left: $border-style;
242
253
 
243
254
  &,
244
255
  div {
245
- position: absolute;
246
256
  width: $color-swatch-width;
247
- top: 0;
248
- right: 0;
249
- bottom: 0;
257
+ height: calc($input-height - 2 * $border-width);
250
258
  border-top-right-radius: $color-swatch-radius;
251
259
  border-bottom-right-radius: $color-swatch-radius;
252
260
  }
@@ -12,18 +12,20 @@
12
12
  template(#prefix)
13
13
  DitoAffixes(
14
14
  :items="schema.prefix"
15
+ position="prefix"
16
+ absolute
17
+ :disabled="disabled"
15
18
  :parentContext="context"
16
19
  )
17
20
  template(#suffix)
18
21
  DitoAffixes(
19
22
  :items="schema.suffix"
20
- :parentContext="context"
21
- )
22
- button.dito-button--clear.dito-button--overlay(
23
- v-if="showClearButton"
23
+ position="suffix"
24
+ absolute
25
+ :clearable="showClearButton"
24
26
  :disabled="disabled"
25
- @click.stop="clear"
26
- @mousedown.stop
27
+ :parentContext="context"
28
+ @clear="clear"
27
29
  )
28
30
  </template>
29
31
 
@@ -1,14 +1,17 @@
1
1
  <template lang="pug">
2
2
  .dito-multiselect
3
3
  .dito-multiselect__inner
4
+ DitoAffixes(
5
+ :items="schema.prefix"
6
+ position="prefix"
7
+ absolute
8
+ :disabled="disabled"
9
+ :parentContext="context"
10
+ )
4
11
  VueMultiselect(
5
12
  ref="element"
6
13
  v-model="selectedOptions"
7
- :class=`{
8
- 'multiselect--multiple': multiple,
9
- 'multiselect--loading': isLoading,
10
- 'multiselect--highlight': showHighlight
11
- }`
14
+ :class="multiselectClasses"
12
15
  :showLabels="false"
13
16
  :placeholder="placeholder"
14
17
  tagPlaceholder="Press enter to add new tag"
@@ -31,11 +34,14 @@
31
34
  @tag="onAddTag"
32
35
  @search-change="onSearchChange"
33
36
  )
34
- button.dito-button--clear.dito-button--overlay(
35
- v-if="showClearButton"
36
- type="button"
37
+ DitoAffixes(
38
+ :items="schema.suffix"
39
+ position="suffix"
40
+ absolute
41
+ :clearable="showClearButton"
37
42
  :disabled="disabled"
38
- @click="clear"
43
+ :parentContext="context"
44
+ @clear="clear"
39
45
  )
40
46
  //- Edit button is never disabled, even if the field is disabled.
41
47
  DitoEditButtons(
@@ -56,6 +62,7 @@ import DitoTypeComponent from '../DitoTypeComponent.js'
56
62
  import DitoContext from '../DitoContext.js'
57
63
  import TypeMixin from '../mixins/TypeMixin.js'
58
64
  import OptionsMixin from '../mixins/OptionsMixin.js'
65
+ import DitoAffixes from '../components/DitoAffixes.vue'
59
66
  import VueMultiselect from 'vue-multiselect'
60
67
  import { getSchemaAccessor } from '../utils/accessor.js'
61
68
  import { isBoolean } from '@ditojs/utils'
@@ -63,7 +70,7 @@ import { isBoolean } from '@ditojs/utils'
63
70
  // @vue/component
64
71
  export default DitoTypeComponent.register('multiselect', {
65
72
  mixins: [OptionsMixin],
66
- components: { VueMultiselect },
73
+ components: { DitoAffixes, VueMultiselect },
67
74
 
68
75
  data() {
69
76
  return {
@@ -125,6 +132,15 @@ export default DitoTypeComponent.register('multiselect', {
125
132
  default: false
126
133
  }),
127
134
 
135
+ multiselectClasses() {
136
+ const prefix = 'multiselect'
137
+ return {
138
+ [`${prefix}--multiple`]: this.multiple,
139
+ [`${prefix}--loading`]: this.isLoading,
140
+ [`${prefix}--highlight`]: this.showHighlight
141
+ }
142
+ },
143
+
128
144
  placeholder() {
129
145
  let { placeholder, searchable, taggable } = this.schema
130
146
  if (isBoolean(placeholder)) {
@@ -239,6 +255,8 @@ $tag-line-height: 1em;
239
255
  &__inner {
240
256
  flex: 1;
241
257
  position: relative;
258
+ display: flex;
259
+ align-items: center;
242
260
  }
243
261
 
244
262
  .dito-edit-buttons {
@@ -12,17 +12,18 @@ DitoInput.dito-number(
12
12
  template(#prefix)
13
13
  DitoAffixes(
14
14
  :items="schema.prefix"
15
+ position="prefix"
16
+ :disabled="disabled"
15
17
  :parentContext="context"
16
18
  )
17
19
  template(#suffix)
18
20
  DitoAffixes(
19
21
  :items="schema.suffix"
20
- :parentContext="context"
21
- )
22
- button.dito-button--clear.dito-button--overlay(
23
- v-if="showClearButton"
22
+ position="suffix"
23
+ :clearable="showClearButton"
24
24
  :disabled="disabled"
25
- @click.stop="clear"
25
+ :parentContext="context"
26
+ @clear="clear"
26
27
  )
27
28
  </template>
28
29
 
@@ -2,6 +2,13 @@
2
2
  //- Nesting is needed to make an arrow appear over the select item:
3
3
  .dito-select
4
4
  .dito-select__inner
5
+ DitoAffixes(
6
+ :items="schema.prefix"
7
+ position="prefix"
8
+ absolute
9
+ :disabled="disabled"
10
+ :parentContext="context"
11
+ )
5
12
  select(
6
13
  :id="dataPath"
7
14
  ref="element"
@@ -32,10 +39,14 @@
32
39
  v-else-if="selectedOption"
33
40
  )
34
41
  option(:value="selectedValue") {{ getLabelForOption(selectedOption) }}
35
- button.dito-button--clear.dito-button--overlay(
36
- v-if="showClearButton"
42
+ DitoAffixes(
43
+ :items="schema.suffix"
44
+ position="suffix"
45
+ absolute
46
+ :clearable="showClearButton"
37
47
  :disabled="disabled"
38
- @click="clear"
48
+ :parentContext="context"
49
+ @clear="clear"
39
50
  )
40
51
  //- Edit button is never disabled, even if the field is disabled.
41
52
  DitoEditButtons(
@@ -54,10 +65,12 @@
54
65
  <script>
55
66
  import DitoTypeComponent from '../DitoTypeComponent.js'
56
67
  import OptionsMixin from '../mixins/OptionsMixin.js'
68
+ import DitoAffixes from '../components/DitoAffixes.vue'
57
69
 
58
70
  // @vue/component
59
71
  export default DitoTypeComponent.register('select', {
60
72
  mixins: [OptionsMixin],
73
+ components: { DitoAffixes },
61
74
 
62
75
  nativeField: true,
63
76
 
@@ -9,17 +9,18 @@ DitoInput.dito-text(
9
9
  template(#prefix)
10
10
  DitoAffixes(
11
11
  :items="schema.prefix"
12
+ position="prefix"
13
+ :disabled="disabled"
12
14
  :parentContext="context"
13
15
  )
14
16
  template(#suffix)
15
17
  DitoAffixes(
16
18
  :items="schema.suffix"
17
- :parentContext="context"
18
- )
19
- button.dito-button--clear.dito-button--overlay(
20
- v-if="showClearButton"
19
+ position="suffix"
20
+ :clearable="showClearButton"
21
21
  :disabled="disabled"
22
- @click.stop="clear"
22
+ :parentContext="context"
23
+ @clear="clear"
23
24
  )
24
25
  </template>
25
26