@ditojs/admin 2.6.5 → 2.6.7

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.5",
3
+ "version": "2.6.7",
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",
@@ -83,7 +83,7 @@
83
83
  "vite": "^4.3.4"
84
84
  },
85
85
  "types": "types",
86
- "gitHead": "01511e6f5082c5f63c6fb17959559b3e28a111b2",
86
+ "gitHead": "b192b2ffad1ff1c9e2e228a7b4a0ca86c8184028",
87
87
  "scripts": {
88
88
  "build": "vite build",
89
89
  "watch": "yarn build --mode 'development' --watch",
@@ -24,6 +24,7 @@
24
24
  :single="single"
25
25
  :nested="nested"
26
26
  :disabled="componentDisabled"
27
+ :accumulatedBasis="combinedBasis"
27
28
  @errors="onErrors"
28
29
  )
29
30
  DitoErrors(:errors="errors")
@@ -49,7 +50,8 @@ export default DitoComponent.component('DitoContainer', {
49
50
  nested: { type: Boolean, default: true },
50
51
  disabled: { type: Boolean, required: true },
51
52
  generateLabels: { type: Boolean, default: false },
52
- verticalLabels: { type: Boolean, default: false }
53
+ verticalLabels: { type: Boolean, default: false },
54
+ accumulatedBasis: { type: Number, default: null }
53
55
  },
54
56
 
55
57
  data() {
@@ -140,14 +142,20 @@ export default DitoComponent.component('DitoContainer', {
140
142
  flexBasis() {
141
143
  const width = this.width
142
144
  // 'auto' = no fitting:
143
- const basis = [null, 'auto', 'fill'].includes(width)
145
+ return [null, 'auto', 'fill'].includes(width)
144
146
  ? 'auto'
145
147
  : /%$/.test(width)
146
- ? parseFloat(width) // percentage
148
+ ? parseFloat(width) / 100 // percentage -> fraction
147
149
  : /[a-z]/.test(width)
148
150
  ? width // native units
149
- : parseFraction(width) * 100 // fraction
150
- return isNumber(basis) ? `${basis}%` : basis
151
+ : parseFraction(width) // fraction
152
+ },
153
+
154
+ combinedBasis() {
155
+ const { accumulatedBasis, flexBasis } = this
156
+ return isNumber(accumulatedBasis) && isNumber(flexBasis)
157
+ ? accumulatedBasis * flexBasis
158
+ : null
151
159
  },
152
160
 
153
161
  containerClass() {
@@ -166,10 +174,15 @@ export default DitoComponent.component('DitoContainer', {
166
174
  },
167
175
 
168
176
  containerStyle() {
177
+ const { flexBasis, combinedBasis } = this
169
178
  return {
170
179
  '--grow': this.flexGrow ? 1 : 0,
171
180
  '--shrink': this.flexShrink ? 1 : 0,
172
- '--basis': this.flexBasis
181
+ '--basis': isNumber(flexBasis) ? `${flexBasis * 100}%` : flexBasis,
182
+ '--basis-mobile':
183
+ isNumber(combinedBasis) && combinedBasis <= 0.25
184
+ ? `${flexBasis * 200}%`
185
+ : null
173
186
  }
174
187
  },
175
188
 
@@ -207,8 +220,6 @@ export default DitoComponent.component('DitoContainer', {
207
220
  flex: var(--grow) var(--shrink) var(--basis);
208
221
  flex-flow: column;
209
222
  align-items: flex-start;
210
- // Needed for better vertical alignment:
211
- align-self: stretch;
212
223
  box-sizing: border-box;
213
224
  // To prevent list tables from blowing out of their flex box containers.
214
225
  max-width: 100%;
@@ -216,31 +227,28 @@ export default DitoComponent.component('DitoContainer', {
216
227
  // percentages in flex-basis to work.
217
228
  padding: $form-spacing $form-spacing-half;
218
229
 
219
- .dito-page & {
220
- @container (width < #{$content-width}) {
221
- flex-grow: 1;
222
- }
223
-
230
+ .dito-page .dito-pane > & {
224
231
  @container (width < #{calc($content-width * 0.8)}) {
225
- &:not(:has(> .dito-list, > .dito-object)) {
226
- flex-basis: calc(var(--basis) * 1.5);
227
- }
232
+ flex-grow: 1;
233
+ flex-basis: var(--basis-mobile, var(--basis));
234
+ // DEBUG: background: yellow;
228
235
  }
229
236
 
230
237
  @container (width < #{calc($content-width * 0.6)}) {
231
- &:not(:has(> .dito-list, > .dito-object)) {
232
- flex-basis: calc(var(--basis) * 2);
233
- }
238
+ flex-basis: calc(2 * var(--basis));
239
+ // DEBUG: background: orange;
234
240
  }
235
241
  }
236
242
 
237
- .dito-sidebar & {
243
+ .dito-sidebar .dito-pane > & {
238
244
  @container (width < #{$sidebar-max-width}) {
239
245
  flex-grow: 1;
246
+ // DEBUG: background: yellow;
240
247
  }
241
248
 
242
249
  @container (width < #{calc($sidebar-max-width * 0.6)}) {
243
- flex-basis: calc(var(--basis) * 2);
250
+ flex-basis: calc(2 * var(--basis));
251
+ // DEBUG: background: orange;
244
252
  }
245
253
  }
246
254
 
@@ -18,6 +18,7 @@ ul.dito-menu(
18
18
  v-if="item.items"
19
19
  :class="getItemClass(item, 'dito-sub-menu')"
20
20
  :items="item.items"
21
+ :path="getItemPath(item, false)"
21
22
  )
22
23
  </template>
23
24
 
@@ -30,6 +31,10 @@ export default DitoComponent.component('DitoMenu', {
30
31
  items: {
31
32
  type: [Object, Array],
32
33
  default: () => []
34
+ },
35
+ path: {
36
+ type: String,
37
+ default: ''
33
38
  }
34
39
  },
35
40
 
@@ -53,28 +58,27 @@ export default DitoComponent.component('DitoMenu', {
53
58
  }
54
59
  },
55
60
 
56
- getItemPath(item) {
57
- return item?.path
58
- ? `/${item.path}`
59
- : item.items
60
- ? this.getItemPath(Object.values(item.items)[0])
61
- : null
61
+ getItemPath(item, firstChild) {
62
+ const path = item.path ? `${this.path}/${item.path}` : null
63
+ return firstChild && path && item.items
64
+ ? `${path}${this.getItemPath(Object.values(item.items)[0], false)}`
65
+ : path
62
66
  },
63
67
 
64
68
  getItemHref(item) {
65
- const path = this.getItemPath(item)
69
+ const path = this.getItemPath(item, true)
66
70
  return path ? this.$router.resolve(path).href : null
67
71
  },
68
72
 
69
73
  isActiveItem(item) {
70
74
  return (
71
- this.$route.path.startsWith(this.getItemPath(item)) ||
75
+ this.$route.path.startsWith(this.getItemPath(item, false)) ||
72
76
  item.items && Object.values(item.items).some(this.isActiveItem)
73
77
  )
74
78
  },
75
79
 
76
80
  onClickItem(item) {
77
- const path = this.getItemPath(item)
81
+ const path = this.getItemPath(item, true)
78
82
  if (path) {
79
83
  this.$router.push({ path, force: true })
80
84
  }
@@ -33,6 +33,7 @@
33
33
  :disabled="disabled"
34
34
  :generateLabels="generateLabels"
35
35
  :verticalLabels="isInLabeledRow(index)"
36
+ :accumulatedBasis="accumulatedBasis"
36
37
  )
37
38
  .dito-break(
38
39
  v-if="schema.break === 'after'"
@@ -62,7 +63,8 @@ export default DitoComponent.component('DitoPane', {
62
63
  single: { type: Boolean, default: false },
63
64
  visible: { type: Boolean, default: true },
64
65
  disabled: { type: Boolean, default: false },
65
- generateLabels: { type: Boolean, default: false }
66
+ generateLabels: { type: Boolean, default: false },
67
+ accumulatedBasis: { type: Number, default: null }
66
68
  },
67
69
 
68
70
  data() {
@@ -162,7 +164,7 @@ export default DitoComponent.component('DitoPane', {
162
164
  for (const index of row) {
163
165
  const position = this.positions[index]
164
166
  if (
165
- position?.height > 4 &&
167
+ position?.height > 3 &&
166
168
  position.node.querySelector(':scope > .dito-label')
167
169
  ) {
168
170
  // TODO: Handle nested schemas, e.g. 'section' or 'object' and
@@ -205,12 +207,11 @@ export default DitoComponent.component('DitoPane', {
205
207
  }
206
208
  },
207
209
 
208
- onResizeContainer(index, { target }) {
209
- const { y, width, height } = target.getBoundingClientRect()
210
+ onResizeContainer(index, { target, contentRect: { width, height } }) {
210
211
  this.positions[index] =
211
212
  width > 0 && height > 0
212
213
  ? {
213
- top: y,
214
+ top: target.getBoundingClientRect().y,
214
215
  height: height / parseFloat(getComputedStyle(target).fontSize),
215
216
  node: target
216
217
  }
@@ -233,8 +234,8 @@ export default DitoComponent.component('DitoPane', {
233
234
  display: flex;
234
235
  position: relative;
235
236
  flex-flow: row wrap;
237
+ align-items: flex-start;
236
238
  align-content: flex-start;
237
- align-items: baseline;
238
239
  padding: $content-padding;
239
240
  // Remove the padding added by `.dito-container` inside `.dito-pane`:
240
241
  margin: (-$form-spacing) (-$form-spacing-half);
@@ -473,10 +473,11 @@ function addRoutes(router, routes) {
473
473
 
474
474
  .dito-page {
475
475
  --max-content-width: #{$content-width};
476
+ --max-page-width: calc(var(--max-content-width) + 2 * #{$content-padding});
476
477
 
477
- flex: 1 1 var(--max-content-width);
478
+ flex: 0 1 var(--max-page-width);
478
479
  background: $content-color-background;
479
- max-width: calc(var(--max-content-width) + 2 * #{$content-padding});
480
+ max-width: var(--max-page-width);
480
481
  // For the `@container` rule in `.dito-container` to work:
481
482
  container-type: inline-size;
482
483
 
@@ -56,6 +56,7 @@ slot(name="before")
56
56
  :single="!inlined && !hasMainPane"
57
57
  :disabled="disabled"
58
58
  :generateLabels="generateLabels"
59
+ :accumulatedBasis="accumulatedBasis"
59
60
  )
60
61
  TransitionHeight(:enabled="inlined")
61
62
  DitoPane.dito-pane-main(
@@ -69,6 +70,7 @@ slot(name="before")
69
70
  :single="!inlined && !hasTabs"
70
71
  :disabled="disabled"
71
72
  :generateLabels="generateLabels"
73
+ :accumulatedBasis="accumulatedBasis"
72
74
  )
73
75
  slot(
74
76
  v-if="!inlined && isPopulated"
@@ -149,7 +151,8 @@ export default DitoComponent.component('DitoSchema', {
149
151
  scrollable: { type: Boolean, default: false },
150
152
  hasOwnData: { type: Boolean, default: false },
151
153
  headerInMenu: { type: Boolean, default: false },
152
- generateLabels: { type: Boolean, default: false }
154
+ generateLabels: { type: Boolean, default: false },
155
+ accumulatedBasis: { type: Number, default: 1 }
153
156
  },
154
157
 
155
158
  data() {
@@ -12,6 +12,7 @@ DitoSchema.dito-schema-inlined(
12
12
  :collapsed="collapsed"
13
13
  :collapsible="collapsible"
14
14
  :generateLabels="!isCompact"
15
+ :accumulatedBasis="accumulatedBasis"
15
16
  )
16
17
  //- Render dito-edit-buttons for inlined schemas separately from all
17
18
  //- others in `TypeList` as a scope, for better handling of layout.
@@ -51,7 +52,8 @@ export default DitoComponent.component('DitoSchemaInlined', {
51
52
  draggable: { type: Boolean, default: false },
52
53
  editable: { type: Boolean, default: false },
53
54
  deletable: { type: Boolean, default: false },
54
- editPath: { type: String, default: null }
55
+ editPath: { type: String, default: null },
56
+ accumulatedBasis: { type: Number, default: null }
55
57
  },
56
58
 
57
59
  computed: {
@@ -15,7 +15,7 @@ export default DitoComponent.component('DitoSidebar', {})
15
15
  @import '../styles/_imports';
16
16
 
17
17
  .dito-sidebar {
18
- flex: 0 4 $sidebar-max-width;
18
+ flex: 0 1 $sidebar-max-width;
19
19
  max-width: $sidebar-max-width;
20
20
  min-width: $sidebar-min-width;
21
21
  // For the `@container` rule in `.dito-container` to work:
@@ -73,7 +73,7 @@ export default {
73
73
  },
74
74
 
75
75
  path() {
76
- return this.getRoutePath(this.routeRecord.path)
76
+ return this.getRoutePath(this.routeRecord?.path)
77
77
  },
78
78
 
79
79
  label() {
@@ -168,10 +168,13 @@ export default {
168
168
  // Maps the route's actual path to the matched routes by counting its
169
169
  // parts separated by '/', splitting the path into the mapped parts
170
170
  // containing actual parameters.
171
- return this.$route.path
172
- .split('/')
173
- .slice(0, templatePath.split('/').length)
174
- .join('/')
171
+ const { path } = this.$route
172
+ return templatePath
173
+ ? path
174
+ .split('/')
175
+ .slice(0, templatePath.split('/').length)
176
+ .join('/')
177
+ : path
175
178
  },
176
179
 
177
180
  getChildPath(path) {
@@ -21,7 +21,8 @@ export default {
21
21
  label: { type: String, default: null },
22
22
  single: { type: Boolean, default: false },
23
23
  nested: { type: Boolean, default: true },
24
- disabled: { type: Boolean, default: false }
24
+ disabled: { type: Boolean, default: false },
25
+ accumulatedBasis: { type: Number, default: null }
25
26
  },
26
27
 
27
28
  data() {
@@ -3,7 +3,7 @@
3
3
  display: flex;
4
4
 
5
5
  > * {
6
- flex: 1;
6
+ flex: 1 0 auto;
7
7
  display: flex;
8
8
  }
9
9
  }
@@ -28,8 +28,8 @@ export default DitoTypeComponent.register(
28
28
  {
29
29
  defaultValue: () => undefined, // Callback to override `defaultValue: null`
30
30
  excludeValue: true,
31
- generateLabel: false,
32
31
  defaultWidth: 'auto',
32
+ generateLabel: false,
33
33
 
34
34
  computed: {
35
35
  verb() {
@@ -27,6 +27,7 @@ export default DitoTypeComponent.register('checkboxes', {
27
27
 
28
28
  nativeField: true,
29
29
  defaultValue: [],
30
+ defaultWidth: 'auto',
30
31
 
31
32
  computed: {
32
33
  selectedOptions: {
@@ -316,9 +316,10 @@ $tag-line-height: 1em;
316
316
  }
317
317
 
318
318
  &__tags {
319
+ display: flex;
319
320
  font-size: inherit;
320
- overflow: auto;
321
321
  min-height: inherit;
322
+ overflow: auto;
322
323
  padding: 0 $spinner-width 0 0;
323
324
  // So tags can float on multiple lines and have proper margins:
324
325
  padding-bottom: $tag-margin;
@@ -341,11 +342,14 @@ $tag-line-height: 1em;
341
342
  &__single,
342
343
  &__placeholder,
343
344
  &__input {
344
- font-size: inherit;
345
- line-height: inherit;
346
- white-space: nowrap;
345
+ @include ellipsis;
346
+
347
+ flex: 1 0 0%;
348
+ width: 0;
347
349
  min-height: 0;
348
350
  margin: 0 0 1px 0;
351
+ font-size: inherit;
352
+ line-height: inherit;
349
353
  // Sadly, vue-select sets style="padding: ...;" in addition to using
350
354
  // classes, so `!important` is necessary:
351
355
  padding: $input-padding !important;
@@ -18,6 +18,7 @@
18
18
  :disabled="disabled || isLoading"
19
19
  :collapsed="collapsed"
20
20
  :collapsible="collapsible"
21
+ :accumulatedBasis="accumulatedBasis"
21
22
  )
22
23
  component(
23
24
  v-else-if="schema.component"
@@ -25,7 +25,8 @@ import OptionsMixin from '../mixins/OptionsMixin.js'
25
25
  export default DitoTypeComponent.register('radio', {
26
26
  mixins: [OptionsMixin],
27
27
 
28
- nativeField: true
28
+ nativeField: true,
29
+ defaultWidth: 'auto'
29
30
  })
30
31
  </script>
31
32
 
@@ -283,32 +283,32 @@ export async function processSchemaComponent(
283
283
  ])
284
284
  }
285
285
 
286
- export async function processView(component, api, schema, name) {
286
+ export async function processView(component, api, schema, name, menuLevel = 0) {
287
287
  processSchemaDefaults(api, schema)
288
+ let children = []
288
289
  if (isView(schema)) {
289
290
  processRouteSchema(api, schema, name)
290
291
  await processNestedSchemas(api, schema)
291
- const children = []
292
- await processSchemaComponents(api, schema, children, 0)
293
- return {
294
- path: `/${schema.path}`,
295
- children,
296
- component,
297
- meta: {
298
- api,
299
- schema
300
- }
301
- }
292
+ await processSchemaComponents(api, schema, children)
302
293
  } else if (isMenu(schema)) {
303
- return Promise.all(
294
+ processRouteSchema(api, schema, name)
295
+ children = await Promise.all(
304
296
  Object.entries(schema.items).map(async ([name, item]) =>
305
- processView(component, api, item, name)
297
+ processView(component, api, item, name, menuLevel + 1)
306
298
  )
307
299
  )
308
- //
309
300
  } else {
310
301
  throw new Error(`Invalid view schema: '${getSchemaIdentifier(schema)}'`)
311
302
  }
303
+ return {
304
+ path: menuLevel === 0 ? `/${schema.path}` : schema.path,
305
+ children,
306
+ component,
307
+ meta: {
308
+ api,
309
+ schema
310
+ }
311
+ }
312
312
  }
313
313
 
314
314
  export function processSchemaDefaults(api, schema) {