@ditojs/admin 2.92.0 → 2.94.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.
Files changed (50) hide show
  1. package/dist/dito-admin.es.js +1668 -1518
  2. package/dist/dito-admin.umd.js +6 -6
  3. package/package.json +14 -15
  4. package/src/components/DitoAccount.vue +7 -1
  5. package/src/components/DitoCreateButton.vue +6 -2
  6. package/src/components/DitoDialog.vue +1 -1
  7. package/src/components/DitoErrors.vue +1 -0
  8. package/src/components/DitoForm.vue +1 -0
  9. package/src/components/DitoFormInner.vue +5 -0
  10. package/src/components/DitoHeader.vue +1 -1
  11. package/src/components/DitoLabel.vue +6 -1
  12. package/src/components/DitoMenu.vue +1 -0
  13. package/src/components/DitoNavigation.vue +1 -1
  14. package/src/components/DitoNotifications.vue +1 -1
  15. package/src/components/DitoPane.vue +3 -0
  16. package/src/components/DitoPanel.vue +2 -0
  17. package/src/components/DitoSchema.vue +1 -0
  18. package/src/components/DitoScopes.vue +1 -0
  19. package/src/components/DitoSpinner.vue +2 -0
  20. package/src/components/DitoTableHead.vue +10 -0
  21. package/src/components/DitoTabs.vue +18 -2
  22. package/src/components/DitoTrail.vue +2 -1
  23. package/src/components/DitoTreeItem.vue +10 -0
  24. package/src/mixins/DitoMixin.js +4 -2
  25. package/src/mixins/PulldownMixin.js +9 -0
  26. package/src/mixins/SortableMixin.js +3 -1
  27. package/src/types/DitoTypeCheckboxes.vue +2 -0
  28. package/src/types/DitoTypeList.vue +2 -0
  29. package/src/types/DitoTypeMultiselect.vue +2 -0
  30. package/src/types/DitoTypeRadio.vue +2 -0
  31. package/src/types/DitoTypeUpload.vue +4 -4
  32. package/src/utils/ui/dist/dito-ui.es.js +2729 -0
  33. package/src/utils/ui/dist/dito-ui.umd.js +2744 -0
  34. package/src/utils/ui/dist/style.css +1139 -0
  35. package/types/index.d.ts +74 -11
  36. package/types/tests/admin.test-d.ts +0 -27
  37. package/types/tests/component-buttons.test-d.ts +0 -44
  38. package/types/tests/component-list.test-d.ts +0 -159
  39. package/types/tests/component-misc.test-d.ts +0 -137
  40. package/types/tests/component-object.test-d.ts +0 -69
  41. package/types/tests/component-section.test-d.ts +0 -174
  42. package/types/tests/component-select.test-d.ts +0 -107
  43. package/types/tests/components.test-d.ts +0 -81
  44. package/types/tests/context.test-d.ts +0 -31
  45. package/types/tests/fixtures.ts +0 -24
  46. package/types/tests/form.test-d.ts +0 -109
  47. package/types/tests/instance.test-d.ts +0 -20
  48. package/types/tests/schema-features.test-d.ts +0 -402
  49. package/types/tests/variance.test-d.ts +0 -125
  50. package/types/tests/view.test-d.ts +0 -146
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/admin",
3
- "version": "2.92.0",
3
+ "version": "2.94.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/main/packages/admin",
@@ -24,14 +24,8 @@
24
24
  "dist/",
25
25
  "types/"
26
26
  ],
27
- "scripts": {
28
- "build": "vite build",
29
- "watch": "pnpm run build --mode 'development' --watch",
30
- "prepare": "pnpm run build",
31
- "types": "tsc --noEmit types/index.d.ts"
32
- },
33
27
  "engines": {
34
- "node": ">= 20.0.0"
28
+ "node": ">= 24.0.0"
35
29
  },
36
30
  "browserslist": [
37
31
  "> 1%",
@@ -40,8 +34,8 @@
40
34
  "not ie_mob > 0"
41
35
  ],
42
36
  "dependencies": {
43
- "@ditojs/ui": "^2.92.0",
44
- "@ditojs/utils": "^2.92.0"
37
+ "@ditojs/ui": "^2.94.0",
38
+ "@ditojs/utils": "^2.94.0"
45
39
  },
46
40
  "peerDependencies": {
47
41
  "@kyvg/vue3-notification": "^3.4.2",
@@ -89,9 +83,14 @@
89
83
  "vue-upload-component": "^3.1.17"
90
84
  },
91
85
  "devDependencies": {
92
- "@ditojs/build": "^2.92.0",
93
- "typescript": "^5.9.3",
94
- "vite": "^8.0.0"
86
+ "@ditojs/build": "^2.94.0",
87
+ "typescript": "^6.0.2",
88
+ "vite": "^8.0.3"
95
89
  },
96
- "gitHead": "7d2288aa487ec5c8607b2079a29e2d8793fbd07f"
97
- }
90
+ "gitHead": "7bb40e9e490632e20ceaf92907b4ff7b27484e26",
91
+ "scripts": {
92
+ "build": "vite build",
93
+ "watch": "pnpm run build --mode 'development' --watch",
94
+ "types": "tsc --noEmit types/index.d.ts"
95
+ }
96
+ }
@@ -1,14 +1,20 @@
1
1
  <template lang="pug">
2
2
  .dito-account
3
3
  a(
4
+ role="button"
5
+ v-bind="pulldownTriggerAttributes"
4
6
  @mousedown.stop="onPulldownMouseDown()"
5
7
  )
6
8
  span {{ user.username }}
7
- ul.dito-pulldown(:class="{ 'dito-pulldown--open': pulldown.open }")
9
+ ul.dito-pulldown(
10
+ role="menu"
11
+ :class="{ 'dito-pulldown--open': pulldown.open }"
12
+ )
8
13
  li(
9
14
  v-for="(label, value) of items"
10
15
  )
11
16
  a.dito-pulldown__item(
17
+ role="menuitem"
12
18
  @mousedown.stop="onPulldownMouseDown(value)"
13
19
  @mouseup="onPulldownMouseUp(value)"
14
20
  ) {{ label }}
@@ -13,15 +13,19 @@
13
13
  button.dito-button(
14
14
  type="button"
15
15
  :disabled="disabled"
16
- v-bind="getButtonAttributes(verb)"
16
+ v-bind="{ ...pulldownTriggerAttributes, ...getButtonAttributes(verb) }"
17
17
  @mousedown.stop="onPulldownMouseDown()"
18
18
  ) {{ text }}
19
- ul.dito-pulldown(:class="{ 'dito-pulldown--open': pulldown.open }")
19
+ ul.dito-pulldown(
20
+ role="menu"
21
+ :class="{ 'dito-pulldown--open': pulldown.open }"
22
+ )
20
23
  li(
21
24
  v-for="(form, type) in creatableForms"
22
25
  v-show="shouldShowSchema(form)"
23
26
  )
24
27
  a.dito-pulldown__item(
28
+ role="menuitem"
25
29
  :class=`{
26
30
  'dito-pulldown__item--disabled': shouldDisableSchema(form)
27
31
  }`
@@ -2,7 +2,7 @@
2
2
  .dito-dialog(
3
3
  ref="dialog"
4
4
  role="dialog"
5
- aria-expanded="true"
5
+ :aria-label="schema.label || 'Dialog'"
6
6
  aria-modal="true"
7
7
  :style="{ '--width': settings.width ? `${settings.width}px` : null }"
8
8
  @mouseup="onMouseUp"
@@ -1,6 +1,7 @@
1
1
  <template lang="pug">
2
2
  .dito-errors(
3
3
  v-if="errors"
4
+ role="alert"
4
5
  )
5
6
  ul
6
7
  li(
@@ -15,6 +15,7 @@
15
15
  DitoFormInner(
16
16
  v-show="isActiveRoute"
17
17
  :nested="isNestedRoute"
18
+ :label="itemLabel"
18
19
  )
19
20
  //- Prevent implicit submission of the form, for example when typing enter
20
21
  //- in an input field.
@@ -6,6 +6,7 @@ div(
6
6
  slot
7
7
  form.dito-scroll-parent(
8
8
  v-else
9
+ :aria-label="label"
9
10
  @submit.prevent
10
11
  )
11
12
  slot
@@ -20,6 +21,10 @@ export default DitoComponent.component('DitoFormInner', {
20
21
  nested: {
21
22
  type: Boolean,
22
23
  default: false
24
+ },
25
+ label: {
26
+ type: String,
27
+ default: null
23
28
  }
24
29
  }
25
30
  })
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- nav.dito-header
2
+ nav.dito-header(:aria-busy="isLoading")
3
3
  DitoTrail
4
4
  DitoSpinner(
5
5
  v-if="isLoading"
@@ -69,7 +69,12 @@ export default DitoComponent.component('DitoLabel', {
69
69
  },
70
70
 
71
71
  attributes() {
72
- return this.collapsible ? { onClick: this.onClick } : {}
72
+ return this.collapsible
73
+ ? {
74
+ 'onClick': this.onClick,
75
+ 'aria-expanded': !this.collapsed
76
+ }
77
+ : {}
73
78
  },
74
79
 
75
80
  isActive() {
@@ -12,6 +12,7 @@ ul.dito-menu(
12
12
  a.dito-menu__link(
13
13
  :href="getItemHref(item)"
14
14
  :class="{ 'dito-menu__link--active': isActiveItem(item) }"
15
+ :aria-current="isActiveItem(item) ? 'page' : null"
15
16
  @click.prevent.stop="onClickItem(item)"
16
17
  ) {{ getLabel(item) }}
17
18
  DitoMenu.dito-menu__sub(
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- nav.dito-navigation.dito-scroll-parent
2
+ nav.dito-navigation.dito-scroll-parent(aria-label="Main navigation")
3
3
  h1
4
4
  RouterLink.dito-link(to="/") {{ appState.title }}
5
5
  DitoMenu.dito-scroll(:items="views")
@@ -1,5 +1,5 @@
1
1
  <template lang="pug">
2
- .dito-notifications
2
+ .dito-notifications(aria-live="polite")
3
3
  .dito-header
4
4
  span
5
5
  .dito-notifications__inner
@@ -2,7 +2,10 @@
2
2
  <template lang="pug">
3
3
  .dito-pane(
4
4
  v-if="isPopulated && componentSchemas.length > 0"
5
+ :id="tab ? `${dataPath}-tabpanel-${tab}` : null"
5
6
  v-resize="onResizePane"
7
+ :role="tab ? 'tabpanel' : null"
8
+ :aria-labelledby="tab ? `${dataPath}-tab-${tab}` : null"
6
9
  :class="classes"
7
10
  )
8
11
  template(
@@ -4,6 +4,8 @@ component.dito-panel(
4
4
  v-if="shouldRenderSchema(panelSchema)"
5
5
  v-show="visible && (!panelTabComponent || panelTabComponent.visible)"
6
6
  :is="panelTag"
7
+ role="region"
8
+ :aria-label="getLabel(schema)"
7
9
  @submit.prevent
8
10
  )
9
11
  DitoSchema.dito-panel__schema(
@@ -41,6 +41,7 @@ slot(name="prepend")
41
41
  v-if="opened"
42
42
  v-model="selectedTab"
43
43
  :tabs="tabs"
44
+ :dataPath="dataPath"
44
45
  )
45
46
  DitoClipboard(
46
47
  v-if="clipboard"
@@ -10,6 +10,7 @@
10
10
  button.dito-button(
11
11
  type="button"
12
12
  :class="{ 'dito-button--selected': scope.name === query.scope }"
13
+ :aria-pressed="scope.name === query.scope"
13
14
  :title="scope.hint || getLabel(scope)"
14
15
  @click="navigate"
15
16
  ) {{ getLabel(scope) }}
@@ -1,6 +1,8 @@
1
1
  <template lang="pug">
2
2
  .dito-spinner(
3
3
  v-show="loading"
4
+ role="status"
5
+ aria-label="Loading"
4
6
  :style="{ '--color': color, '--size': size, '--margin': margin }"
5
7
  )
6
8
  .dito-spinner__pulse.dito-spinner__pulse1
@@ -6,6 +6,7 @@ thead.dito-table-head
6
6
  )
7
7
  th(
8
8
  v-if="shouldRenderSchema(column)"
9
+ scope="col"
9
10
  :class="getColumnClass(column)"
10
11
  )
11
12
  RouterLink(
@@ -17,6 +18,7 @@ thead.dito-table-head
17
18
  button.dito-button(
18
19
  type="button"
19
20
  :class="getSortClass(column)"
21
+ :aria-sort="getSortState(column)"
20
22
  @click="navigate"
21
23
  )
22
24
  .dito-button__order-arrows
@@ -64,6 +66,14 @@ export default DitoComponent.component('DitoTableHead', {
64
66
  : null
65
67
  },
66
68
 
69
+ getSortState(column) {
70
+ const states = { asc: 'ascending', desc: 'descending' }
71
+ return (
72
+ states[this.sort.name === column.name && this.sort.order] ||
73
+ 'none'
74
+ )
75
+ },
76
+
67
77
  getSortLink(column) {
68
78
  // Toggle order if the same column is clicked again.
69
79
  const order =
@@ -1,11 +1,16 @@
1
1
  <template lang="pug">
2
- .dito-tabs
2
+ .dito-tabs(role="tablist")
3
3
  template(
4
4
  v-for="(tabSchema, key) in tabs"
5
5
  )
6
6
  a.dito-tabs__link(
7
7
  v-if="shouldRenderSchema(tabSchema)"
8
+ :id="getTabId(key)"
8
9
  :key="key"
10
+ role="tab"
11
+ :aria-selected="modelValue === key"
12
+ :aria-controls="getPanelId(key)"
13
+ :tabindex="modelValue === key ? 0 : -1"
9
14
  :class="{ 'dito-tabs__link--active': modelValue === key }"
10
15
  @click="$emit('update:modelValue', key)"
11
16
  ) {{ getLabel(tabSchema, key) }}
@@ -19,7 +24,18 @@ export default DitoComponent.component('DitoTabs', {
19
24
  emits: ['update:modelValue'],
20
25
  props: {
21
26
  tabs: { type: Object, default: null },
22
- modelValue: { type: String, default: null }
27
+ modelValue: { type: String, default: null },
28
+ dataPath: { type: String, default: '' }
29
+ },
30
+
31
+ methods: {
32
+ getTabId(key) {
33
+ return `${this.dataPath}-tab-${key}`
34
+ },
35
+
36
+ getPanelId(key) {
37
+ return `${this.dataPath}-tabpanel-${key}`
38
+ }
23
39
  }
24
40
  })
25
41
  </script>
@@ -1,11 +1,12 @@
1
1
  <template lang="pug">
2
- .dito-trail
2
+ .dito-trail(aria-label="Breadcrumb")
3
3
  ul
4
4
  li(
5
5
  v-for="component in trail"
6
6
  )
7
7
  a.dito-trail__link(
8
8
  :class="{ 'dito-trail__link--active': component.path === $route.path }"
9
+ :aria-current="component.path === $route.path ? 'page' : null"
9
10
  :href="getComponentHref(component)"
10
11
  @click.prevent.stop="onClickComponent(component)"
11
12
  )
@@ -13,7 +13,12 @@
13
13
  )
14
14
  .dito-tree-branch(
15
15
  v-if="numEntries"
16
+ role="button"
17
+ tabindex="0"
18
+ :aria-label="ariaLabel"
19
+ :aria-expanded="opened"
16
20
  @click.stop="opened = !opened"
21
+ @keydown.enter.space.prevent="opened = !opened"
17
22
  )
18
23
  .dito-chevron(
19
24
  v-if="numEntries"
@@ -105,6 +110,7 @@ import SortableMixin from '../mixins/SortableMixin.js'
105
110
  import { appendDataPath } from '../utils/data.js'
106
111
  import { getSchemaAccessor } from '../utils/accessor.js'
107
112
  import { getNamedSchemas, hasFormSchema } from '../utils/schema.js'
113
+ import { stripHtml } from '@ditojs/utils'
108
114
 
109
115
  // @vue/component
110
116
  export default DitoComponent.component('DitoTreeItem', {
@@ -222,6 +228,10 @@ export default DitoComponent.component('DitoTreeItem', {
222
228
  return []
223
229
  },
224
230
 
231
+ ariaLabel() {
232
+ return this.label ? stripHtml(this.label) : null
233
+ },
234
+
225
235
  details() {
226
236
  const { numChildren } = this
227
237
  return (
@@ -277,9 +277,11 @@ export default {
277
277
  },
278
278
 
279
279
  getButtonAttributes(verb) {
280
+ const label = labelize(verb)
280
281
  return {
281
- class: `dito-button--${verb}`,
282
- title: labelize(verb)
282
+ 'class': `dito-button--${verb}`,
283
+ 'title': label,
284
+ 'aria-label': label
283
285
  }
284
286
  },
285
287
 
@@ -27,6 +27,15 @@ export default {
27
27
  }
28
28
  },
29
29
 
30
+ computed: {
31
+ pulldownTriggerAttributes() {
32
+ return {
33
+ 'aria-haspopup': 'menu',
34
+ 'aria-expanded': this.pulldown.open
35
+ }
36
+ }
37
+ },
38
+
30
39
  methods: {
31
40
  onPulldownMouseDown(value = null) {
32
41
  if (value === null) {
@@ -7,7 +7,9 @@ export default {
7
7
  },
8
8
 
9
9
  methods: {
10
- getDraggableOptions(forceFallback = false) {
10
+ getDraggableOptions(
11
+ forceFallback = window.dito.settings?.forceDragFallback ?? false
12
+ ) {
11
13
  const prefix = 'dito-draggable'
12
14
  return {
13
15
  animation: 150,
@@ -1,6 +1,8 @@
1
1
  <template lang="pug">
2
2
  ul.dito-checkboxes(
3
3
  :id="dataPath"
4
+ role="group"
5
+ :aria-label="label"
4
6
  :class="`dito-layout--${schema.layout || 'vertical'}`"
5
7
  )
6
8
  li(
@@ -2,6 +2,8 @@
2
2
  .dito-list(
3
3
  v-if="isReady"
4
4
  :id="dataPath"
5
+ role="region"
6
+ :aria-label="label || labelize(schema.name)"
5
7
  )
6
8
  .dito-list__header(
7
9
  v-if="scopes || hasPagination"
@@ -10,8 +10,10 @@
10
10
  :parentContext="context"
11
11
  )
12
12
  VueMultiselect(
13
+ :id="dataPath"
13
14
  ref="element"
14
15
  v-model="selectedOptions"
16
+ :aria-label="label"
15
17
  :class="multiselectClasses"
16
18
  :showLabels="false"
17
19
  :placeholder="placeholder"
@@ -1,6 +1,8 @@
1
1
  <template lang="pug">
2
2
  ul.dito-radio-buttons(
3
3
  :id="dataPath"
4
+ role="radiogroup"
5
+ :aria-label="label"
4
6
  :class="`dito-layout--${schema.layout || 'vertical'}`"
5
7
  )
6
8
  li(
@@ -24,13 +24,13 @@
24
24
  //- Styling comes from `DitoTableHead`
25
25
  thead.dito-table-head
26
26
  tr
27
- th
27
+ th(scope="col")
28
28
  span File
29
- th
29
+ th(scope="col")
30
30
  span Size
31
- th
31
+ th(scope="col")
32
32
  span Status
33
- th
33
+ th(scope="col")
34
34
  span
35
35
  DitoDraggable(
36
36
  v-model="files"