@ditojs/admin 3.2.7 → 3.3.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.
Files changed (112) hide show
  1. package/dist/dito-admin.css +1 -1
  2. package/dist/dito-admin.es.js +6011 -5826
  3. package/dist/dito-admin.umd.js +3 -7
  4. package/package.json +18 -17
  5. package/src/DitoAdmin.js +44 -24
  6. package/src/DitoContext.js +28 -2
  7. package/src/DitoTypeComponent.js +1 -0
  8. package/src/appState.js +6 -3
  9. package/src/components/DitoAccount.vue +35 -27
  10. package/src/components/DitoAffixes.vue +9 -2
  11. package/src/components/DitoClipboard.vue +59 -93
  12. package/src/components/DitoConfirmMessage.vue +18 -0
  13. package/src/components/DitoContainer.vue +56 -18
  14. package/src/components/DitoCreateButton.vue +36 -84
  15. package/src/components/DitoDialog.vue +20 -15
  16. package/src/components/DitoDragHandle.vue +44 -0
  17. package/src/components/DitoDraggable.vue +43 -16
  18. package/src/components/DitoEditButtons.vue +48 -24
  19. package/src/components/DitoErrors.vue +9 -56
  20. package/src/components/DitoForm.vue +82 -42
  21. package/src/components/DitoInputAffixes.vue +50 -0
  22. package/src/components/DitoLabel.vue +1 -15
  23. package/src/components/DitoMenu.vue +45 -52
  24. package/src/components/DitoNotifications.vue +38 -14
  25. package/src/components/DitoOptionList.vue +81 -0
  26. package/src/components/DitoOptionsEditButtons.vue +57 -0
  27. package/src/components/DitoPagination.vue +1 -8
  28. package/src/components/DitoPane.vue +51 -39
  29. package/src/components/DitoPanel.vue +12 -26
  30. package/src/components/DitoPanels.vue +11 -5
  31. package/src/components/DitoResizeHandle.vue +47 -0
  32. package/src/components/DitoRoot.vue +113 -320
  33. package/src/components/DitoSchema.vue +72 -63
  34. package/src/components/DitoSchemaInlined.vue +8 -9
  35. package/src/components/DitoScopes.vue +14 -18
  36. package/src/components/DitoSidebar.vue +4 -29
  37. package/src/components/DitoSourceItem.vue +114 -0
  38. package/src/components/DitoTableCell.vue +4 -0
  39. package/src/components/DitoTableHead.vue +15 -17
  40. package/src/components/DitoTabs.vue +25 -0
  41. package/src/components/DitoTrail.vue +25 -25
  42. package/src/components/DitoTreeItem.vue +102 -141
  43. package/src/components/DitoUploadFile.vue +5 -2
  44. package/src/components/DitoView.vue +33 -24
  45. package/src/index.js +4 -1
  46. package/src/mixins/DitoMixin.js +77 -110
  47. package/src/mixins/DomMixin.js +12 -14
  48. package/src/mixins/EmitterMixin.js +3 -2
  49. package/src/mixins/ItemMixin.js +38 -94
  50. package/src/mixins/OptionsMixin.js +38 -25
  51. package/src/mixins/ResourceMixin.js +182 -111
  52. package/src/mixins/RouteMixin.js +61 -44
  53. package/src/mixins/SchemaParentMixin.js +4 -1
  54. package/src/mixins/SourceMixin.js +118 -100
  55. package/src/mixins/TypeMixin.js +42 -40
  56. package/src/mixins/ValidationMixin.js +16 -4
  57. package/src/mixins/ValidatorMixin.js +1 -1
  58. package/src/styles/_settings.scss +4 -0
  59. package/src/styles/_tippy.scss +0 -11
  60. package/src/styles/style.scss +0 -1
  61. package/src/types/DitoTypeButton.vue +11 -14
  62. package/src/types/DitoTypeCheckboxes.vue +35 -17
  63. package/src/types/DitoTypeCode.vue +19 -41
  64. package/src/types/DitoTypeColor.vue +67 -19
  65. package/src/types/DitoTypeDate.vue +11 -14
  66. package/src/types/DitoTypeList.vue +50 -62
  67. package/src/types/DitoTypeMarkup.vue +41 -54
  68. package/src/types/DitoTypeMultiselect.vue +82 -52
  69. package/src/types/DitoTypeNumber.vue +12 -15
  70. package/src/types/DitoTypeObject.vue +4 -38
  71. package/src/types/DitoTypeRadio.vue +19 -6
  72. package/src/types/DitoTypeSection.vue +23 -9
  73. package/src/types/DitoTypeSelect.vue +39 -23
  74. package/src/types/DitoTypeSlider.vue +9 -1
  75. package/src/types/DitoTypeSwitch.vue +1 -1
  76. package/src/types/DitoTypeText.vue +11 -14
  77. package/src/types/DitoTypeTreeList.vue +46 -8
  78. package/src/types/DitoTypeUpload.vue +190 -67
  79. package/src/utils/DataModel.js +368 -146
  80. package/src/utils/FileDropTargets.js +97 -0
  81. package/src/utils/ItemStores.js +31 -0
  82. package/src/utils/ListQuery.js +115 -0
  83. package/src/utils/LoadingTracker.js +76 -0
  84. package/src/utils/SchemaGraph.js +3 -2
  85. package/src/utils/Session.js +151 -0
  86. package/src/utils/ViewRegistry.js +85 -0
  87. package/src/utils/accessor.js +12 -4
  88. package/src/utils/affixes.js +33 -0
  89. package/src/utils/changeOnceEdited.js +33 -0
  90. package/src/utils/clipboard.js +55 -0
  91. package/src/utils/data.js +10 -5
  92. package/src/utils/dialogs.js +59 -0
  93. package/src/utils/dragResize.js +61 -0
  94. package/src/utils/filter.js +36 -2
  95. package/src/utils/html.js +9 -0
  96. package/src/utils/list.js +10 -0
  97. package/src/utils/options.js +4 -0
  98. package/src/utils/registration.js +18 -0
  99. package/src/utils/request.js +108 -13
  100. package/src/utils/route.js +6 -17
  101. package/src/utils/schema/data.js +24 -2
  102. package/src/utils/schema/events.js +19 -0
  103. package/src/utils/schema/item.js +114 -0
  104. package/src/utils/schema/lookup.js +39 -14
  105. package/src/utils/schema/setup.js +5 -5
  106. package/src/utils/schema/validation.js +16 -2
  107. package/src/utils/uid.js +105 -8
  108. package/src/validators/_decimals.js +16 -5
  109. package/types/index.d.ts +168 -41
  110. package/src/mixins/LoadingMixin.js +0 -23
  111. package/src/mixins/PulldownMixin.js +0 -72
  112. package/src/styles/_pulldown.scss +0 -38
@@ -1,51 +1,69 @@
1
1
  <template lang="pug">
2
- ul.dito-checkboxes(
2
+ DitoOptionList.dito-checkboxes(
3
3
  :id="componentPath"
4
4
  role="group"
5
5
  :aria-label="label"
6
- :class="`dito-layout--${schema.layout || 'vertical'}`"
6
+ :options="options"
7
+ :isGrouped="!!groupBy"
8
+ :layout="schema.layout"
7
9
  )
8
- li(
9
- v-for="option in options"
10
- )
10
+ template(#option="{ option, isFirstOption }")
11
11
  label
12
12
  .dito-checkbox
13
13
  input(
14
- ref="element"
15
- v-model="selectedOptions"
14
+ :ref="isFirstOption ? 'element' : undefined"
15
+ v-model="checkedValues"
16
16
  type="checkbox"
17
17
  :value="getValueForOption(option)"
18
18
  v-bind="attributes"
19
19
  )
20
20
  span {{ getLabelForOption(option) }}
21
+ DitoOptionsEditButtons(
22
+ v-if="editable"
23
+ :schema="schema"
24
+ :dataPath="dataPath"
25
+ :data="data"
26
+ :meta="meta"
27
+ :store="store"
28
+ :parentContext="context"
29
+ :optionValue="getValueForOption(option)"
30
+ )
21
31
  </template>
22
32
 
23
33
  <script>
24
34
  import DitoTypeComponent from '../DitoTypeComponent.js'
25
35
  import OptionsMixin from '../mixins/OptionsMixin.js'
36
+ import DitoOptionList from '../components/DitoOptionList.vue'
37
+ import DitoOptionsEditButtons from '../components/DitoOptionsEditButtons.vue'
38
+ import { getMultipleValue } from '../utils/schema/data.js'
26
39
 
27
40
  // @vue/component
28
41
  export default DitoTypeComponent.register('checkboxes', {
29
42
  mixins: [OptionsMixin],
43
+ components: { DitoOptionList, DitoOptionsEditButtons },
30
44
 
31
45
  nativeField: true,
32
- defaultValue: [],
46
+ defaultValue: ({ schema }) => (getMultipleValue(schema) ? [] : null),
33
47
  defaultWidth: 'auto',
34
48
  defaultMultiple: true,
35
49
 
36
50
  computed: {
37
- // @override
38
- multiple() {
39
- return true
40
- },
41
-
42
- selectedOptions: {
51
+ // The values of the checked checkboxes, as an array for their `v-model`,
52
+ // also with `multiple: false`.
53
+ checkedValues: {
43
54
  get() {
44
- return (this.selectedValue || []).filter(value => value)
55
+ const { selectedValue } = this
56
+ return this.multiple
57
+ ? (selectedValue || []).filter(value => value != null)
58
+ : selectedValue != null
59
+ ? [selectedValue]
60
+ : []
45
61
  },
46
62
 
47
- set(option) {
48
- this.selectedValue = option || []
63
+ set(values) {
64
+ // With `multiple: false`, checking a checkbox unchecks the checked
65
+ // one, as the `v-model` of checkboxes appends checked values.
66
+ this.selectedValue = this.multiple ? values : (values.at(-1) ?? null)
49
67
  }
50
68
  }
51
69
  }
@@ -4,21 +4,23 @@
4
4
  :style="style"
5
5
  )
6
6
  .dito-code__editor(ref="editor")
7
- .dito-resize(
7
+ DitoResizeHandle(
8
8
  v-if="resizable"
9
- @mousedown.stop.prevent="onDragResize"
9
+ :getElement="() => $el"
10
+ @resize="height = `${$event}px`"
10
11
  )
11
12
  </template>
12
13
 
13
14
  <script>
14
15
  import DitoTypeComponent from '../DitoTypeComponent.js'
15
- import DomMixin from '../mixins/DomMixin.js'
16
16
  import { getSchemaAccessor } from '../utils/accessor.js'
17
+ import { createChangeOnceEdited } from '../utils/changeOnceEdited.js'
18
+ import DitoResizeHandle from '../components/DitoResizeHandle.vue'
17
19
  import CodeFlask from 'codeflask'
18
20
 
19
21
  // @vue/component
20
22
  export default DitoTypeComponent.register('code', {
21
- mixins: [DomMixin],
23
+ components: { DitoResizeHandle },
22
24
 
23
25
  data() {
24
26
  return {
@@ -61,28 +63,26 @@ export default DitoTypeComponent.register('code', {
61
63
  lineNumbers: false
62
64
  })
63
65
 
64
- let changed = false
65
66
  let ignoreWatch = false
66
67
  let ignoreUpdate = false
67
68
 
68
- const onChange = () => {
69
- if (!this.focused && changed) {
70
- changed = false
71
- this.onChange()
72
- }
73
- }
69
+ // Emits the change of the value once the editor isn't focused anymore.
70
+ const changeOnceEdited = createChangeOnceEdited({
71
+ isEditing: () => this.focused,
72
+ emitChange: () => this.onChange()
73
+ })
74
74
 
75
75
  const onFocus = () => this.onFocus()
76
76
 
77
77
  const onBlur = () => {
78
78
  this.onBlur()
79
- onChange()
79
+ changeOnceEdited.emitIfDone()
80
80
  }
81
81
 
82
- this.domOn(this.$refs.editor.querySelector('textarea'), {
83
- focus: onFocus,
84
- blur: onBlur
85
- })
82
+ // The textarea is owned by the editor, so its handlers go with it:
83
+ const textarea = this.$refs.editor.querySelector('textarea')
84
+ textarea.addEventListener('focus', onFocus)
85
+ textarea.addEventListener('blur', onBlur)
86
86
 
87
87
  const setCode = code => {
88
88
  if (code !== flask.code) {
@@ -95,8 +95,9 @@ export default DitoTypeComponent.register('code', {
95
95
  if (value !== this.value) {
96
96
  ignoreWatch = true
97
97
  this.value = value
98
- changed = true
99
- onChange()
98
+ changeOnceEdited.markEdited()
99
+ this.onInput()
100
+ changeOnceEdited.emitIfDone()
100
101
  }
101
102
  }
102
103
 
@@ -137,29 +138,6 @@ export default DitoTypeComponent.register('code', {
137
138
 
138
139
  blurElement() {
139
140
  this.$el.querySelector('textarea')?.blur()
140
- },
141
-
142
- onDragResize(event) {
143
- const getPoint = ({ clientX: x, clientY: y }) => ({ x, y })
144
-
145
- let prevY = getPoint(event).y
146
- let height = parseFloat(getComputedStyle(this.$el).height)
147
-
148
- const mousemove = event => {
149
- const { y } = getPoint(event)
150
- height += y - prevY
151
- prevY = y
152
- this.height = `${Math.max(height, 0)}px`
153
- }
154
-
155
- const handlers = this.domOn(document, {
156
- mousemove,
157
-
158
- mouseup(event) {
159
- mousemove(event)
160
- handlers.remove()
161
- }
162
- })
163
141
  }
164
142
  }
165
143
  })
@@ -14,22 +14,14 @@ DitoTrigger.dito-color(
14
14
  v-bind="attributes"
15
15
  )
16
16
  template(#prefix)
17
- DitoAffixes(
18
- :items="schema.prefix"
17
+ DitoInputAffixes(
19
18
  position="prefix"
20
- mode="input"
21
- :disabled="disabled"
22
- :parentContext="context"
19
+ v-bind="inputAffixesProps.prefix"
23
20
  )
24
21
  template(#suffix)
25
- DitoAffixes(
26
- :items="schema.suffix"
22
+ DitoInputAffixes(
27
23
  position="suffix"
28
- mode="input"
29
- :clearable="showClearButton"
30
- :disabled="disabled"
31
- :inlineInfo="inlineInfo"
32
- :parentContext="context"
24
+ v-bind="inputAffixesProps.suffix"
33
25
  @clear="clear"
34
26
  )
35
27
  template(#append)
@@ -52,12 +44,15 @@ DitoTrigger.dito-color(
52
44
  import { SketchPicker, tinycolor } from 'vue-color'
53
45
  import { DitoTrigger, DitoInput } from '@ditojs/ui/src'
54
46
  import DitoTypeComponent from '../DitoTypeComponent.js'
55
- import DitoAffixes from '../components/DitoAffixes.vue'
47
+ import TypeMixin from '../mixins/TypeMixin.js'
48
+ import DitoInputAffixes from '../components/DitoInputAffixes.vue'
49
+ import { getInputAffixesProps } from '../utils/affixes.js'
56
50
  import { getSchemaAccessor } from '../utils/accessor.js'
51
+ import { createChangeOnceEdited } from '../utils/changeOnceEdited.js'
57
52
 
58
53
  // @vue/component
59
54
  export default DitoTypeComponent.register('color', {
60
- components: { DitoTrigger, DitoInput, DitoAffixes, SketchPicker },
55
+ components: { DitoTrigger, DitoInput, DitoInputAffixes, SketchPicker },
61
56
 
62
57
  data() {
63
58
  return {
@@ -67,6 +62,10 @@ export default DitoTypeComponent.register('color', {
67
62
  },
68
63
 
69
64
  computed: {
65
+ inputAffixesProps() {
66
+ return getInputAffixesProps(this)
67
+ },
68
+
70
69
  canUpdateValue() {
71
70
  return !this.focused || this.readonly
72
71
  },
@@ -86,7 +85,9 @@ export default DitoTypeComponent.register('color', {
86
85
  !value ||
87
86
  color.toHex8String() !== tinycolor(value).toHex8String()
88
87
  ) {
89
- this.value = toTinyColorFormat(color, this.colorFormat)
88
+ this.setEditedValue(toTinyColorFormat(color, this.colorFormat))
89
+ // The input emits its own input events, the picker doesn't:
90
+ this.onInput()
90
91
  }
91
92
  }
92
93
  },
@@ -107,7 +108,7 @@ export default DitoTypeComponent.register('color', {
107
108
  if (color.isValid()) {
108
109
  const convertedValue = convertColor(value, this.colorFormat)
109
110
  if (this.canUpdateValue) {
110
- this.value = convertedValue
111
+ this.setEditedValue(convertedValue)
111
112
  } else {
112
113
  // Store to change later, once `canUpdateValue` is true again.
113
114
  // See `watch` below.
@@ -161,13 +162,60 @@ export default DitoTypeComponent.register('color', {
161
162
  },
162
163
 
163
164
  watch: {
164
- value: 'onChange',
165
-
166
165
  canUpdateValue(canUpdateValue) {
167
166
  if (canUpdateValue && this.convertedValue !== null) {
168
- this.value = this.convertedValue
167
+ this.setEditedValue(this.convertedValue)
169
168
  this.convertedValue = null
170
169
  }
170
+ },
171
+
172
+ showPopup(showPopup) {
173
+ if (!showPopup) {
174
+ this.changeOnceEdited.emitIfDone()
175
+ }
176
+ }
177
+ },
178
+
179
+ created() {
180
+ // Emits the change of the value once the editing is done, i.e. once
181
+ // neither the input is focused nor the picker is open.
182
+ this.changeOnceEdited = createChangeOnceEdited({
183
+ isEditing: () => this.focused || this.showPopup,
184
+ emitChange: () => this.onChange()
185
+ })
186
+ },
187
+
188
+ methods: {
189
+ // @override
190
+ getEvents() {
191
+ // The input's native change events don't tell when the editing is done,
192
+ // as the picker edits the value too, see `changeOnceEdited`. They
193
+ // are handled to keep them from emitting the schema's change events.
194
+ const { onFocus, onBlur, onInput } = this
195
+ return { onFocus, onBlur, onInput, onChange: () => {} }
196
+ },
197
+
198
+ // @override
199
+ onBlur() {
200
+ TypeMixin.methods.onBlur.call(this)
201
+ this.changeOnceEdited.emitIfDone()
202
+ },
203
+
204
+ // @override
205
+ clear() {
206
+ // Clearing replaces the edits that are pending, see `hexValue` and
207
+ // `changeOnceEdited`, and emits its own change event.
208
+ this.convertedValue = null
209
+ this.changeOnceEdited.reset()
210
+ TypeMixin.methods.clear.call(this)
211
+ },
212
+
213
+ // Sets a value that the user edited, through the input or the picker.
214
+ // Values changed by code don't emit change events.
215
+ setEditedValue(value) {
216
+ this.value = value
217
+ this.changeOnceEdited.markEdited()
218
+ this.changeOnceEdited.emitIfDone()
171
219
  }
172
220
  }
173
221
  })
@@ -10,24 +10,16 @@
10
10
  v-bind="attributes"
11
11
  )
12
12
  template(#prefix)
13
- DitoAffixes(
14
- :items="schema.prefix"
13
+ DitoInputAffixes(
15
14
  position="prefix"
16
- mode="input"
15
+ v-bind="inputAffixesProps.prefix"
17
16
  absolute
18
- :disabled="disabled"
19
- :parentContext="context"
20
17
  )
21
18
  template(#suffix)
22
- DitoAffixes(
23
- :items="schema.suffix"
19
+ DitoInputAffixes(
24
20
  position="suffix"
25
- mode="input"
21
+ v-bind="inputAffixesProps.suffix"
26
22
  absolute
27
- :clearable="showClearButton"
28
- :disabled="disabled"
29
- :inlineInfo="inlineInfo"
30
- :parentContext="context"
31
23
  @clear="clear"
32
24
  )
33
25
  </template>
@@ -35,7 +27,8 @@
35
27
  <script>
36
28
  import DitoTypeComponent from '../DitoTypeComponent.js'
37
29
  import { getSchemaAccessor } from '../utils/accessor.js'
38
- import DitoAffixes from '../components/DitoAffixes.vue'
30
+ import DitoInputAffixes from '../components/DitoInputAffixes.vue'
31
+ import { getInputAffixesProps } from '../utils/affixes.js'
39
32
  import {
40
33
  DitoDatePicker,
41
34
  DitoTimePicker,
@@ -52,7 +45,7 @@ export default DitoTypeComponent.register(
52
45
  ['date', 'datetime', 'time'],
53
46
  // @vue/component
54
47
  {
55
- components: { DitoAffixes },
48
+ components: { DitoInputAffixes },
56
49
  // TODO: This is only here so we get placeholder added. Come up with a
57
50
  // better way to support attributes per component (a list of actually
58
51
  // supported attributes)
@@ -60,6 +53,10 @@ export default DitoTypeComponent.register(
60
53
  textField: true,
61
54
 
62
55
  computed: {
56
+ inputAffixesProps() {
57
+ return getInputAffixesProps(this)
58
+ },
59
+
63
60
  dateValue: {
64
61
  get() {
65
62
  const { value } = this
@@ -73,14 +73,13 @@
73
73
  v-else
74
74
  )
75
75
  td
76
- DitoSchemaInlined(
77
- v-if="isInlined"
78
- :label="getItemLabel(schema, item, { index, asObject: true })"
79
- :schema="getItemFormSchema(schema, item, context)"
76
+ DitoSourceItem(
77
+ :schema="schema"
80
78
  :dataPath="getDataPath(index)"
81
79
  :data="item"
80
+ :index="index"
82
81
  :meta="nestedMeta"
83
- :store="getItemStore(schema, item, index)"
82
+ :store="getItemStore(item)"
84
83
  :disabled="disabled || isLoading"
85
84
  :collapsed="collapsed"
86
85
  :collapsible="collapsible"
@@ -91,75 +90,50 @@
91
90
  :createPath="createPath"
92
91
  :insertIndex="creatable && draggable ? index : null"
93
92
  @delete="deleteItem(item, index)"
94
- )
95
- component(
96
- v-else-if="schema.component"
97
- :is="schema.component"
98
- :dataPath="getDataPath(index)"
99
- :data="item"
100
- :nested="false"
101
- )
102
- span(
103
- v-else-if="render"
104
- v-html="render(getContext(item, index))"
105
- )
106
- span(
107
- v-else
108
- v-html="getItemLabel(schema, item, { index })"
93
+ @move="delta => moveItem(item, delta)"
109
94
  )
110
95
  td.dito-table__buttons(
111
96
  v-if="hasCellEditButtons"
112
97
  )
113
98
  DitoEditButtons(
114
99
  nested
115
- :schema="getItemFormSchema(schema, item, context)"
100
+ :schema="schema"
101
+ :formSchema="getItemFormSchema(schema, item, context)"
116
102
  :dataPath="getDataPath(index)"
117
103
  :data="item"
118
104
  :meta="nestedMeta"
119
- :store="getItemStore(schema, item, index)"
105
+ :store="getItemStore(item)"
120
106
  :disabled="disabled || isLoading"
121
107
  :deletable="deletable"
122
108
  :draggable="draggable"
123
109
  :editable="editable"
124
110
  :editPath="getEditPath(item, index)"
125
111
  @delete="deleteItem(item, index)"
112
+ @move="delta => moveItem(item, delta)"
126
113
  )
127
- //- Render create buttons inside table when not in a single component view:
114
+ //- Render the list buttons inside the table when not in a single
115
+ //- component view, with the item that holds the list as their data:
128
116
  tfoot(
129
117
  v-if="hasListButtons && !single"
130
118
  )
131
119
  tr
132
120
  td.dito-table__buttons(:colspan="numColumns")
133
121
  DitoEditButtons(
134
- :buttons="buttonSchemas"
135
- :schema="schema"
136
- :dataPath="dataPath"
122
+ v-bind="listButtonsAttributes"
137
123
  :data="data"
138
- :meta="meta"
139
- :store="store"
140
- :nested="nested"
141
- :disabled="disabled || isLoading"
142
- :creatable="creatable"
143
- :createPath="createPath"
144
124
  )
145
- //- Render create buttons outside table when in a single component view:
125
+ //- ...and outside, sticky to the bottom, when in a single component view,
126
+ //- with the list data as their data, e.g. for buttons that store its order:
146
127
  DitoEditButtons.dito-buttons--large.dito-buttons--main.dito-buttons--sticky(
147
128
  v-if="hasListButtons && single"
148
- :buttons="buttonSchemas"
149
- :schema="schema"
150
- :dataPath="dataPath"
129
+ v-bind="listButtonsAttributes"
151
130
  :data="listData"
152
- :meta="meta"
153
- :store="store"
154
- :disabled="disabled || isLoading"
155
- :creatable="creatable"
156
- :createPath="createPath"
157
131
  )
158
132
  </template>
159
133
 
160
134
  <script>
161
135
  import DitoTypeComponent from '../DitoTypeComponent.js'
162
- import DitoContext from '../DitoContext.js'
136
+ import DitoSourceItem from '../components/DitoSourceItem.vue'
163
137
  import SourceMixin from '../mixins/SourceMixin.js'
164
138
  import SortableMixin from '../mixins/SortableMixin.js'
165
139
  import {
@@ -167,14 +141,16 @@ import {
167
141
  resolveSchemaComponents
168
142
  } from '../utils/schema/setup.js'
169
143
  import { getViewEditPath } from '../utils/schema/lookup.js'
170
- import { createFiltersPanel } from '../utils/filter.js'
144
+ import { createFiltersPanel, getFiltersDataErrors } from '../utils/filter.js'
171
145
  import { appendDataPath } from '../utils/data.js'
172
- import { pickBy, equals, hyphenate } from '@ditojs/utils'
146
+ import { getListWithMovedItem } from '../utils/list.js'
147
+ import { hyphenate } from '@ditojs/utils'
173
148
  import { computed } from 'vue'
174
149
 
175
150
  // @vue/component
176
151
  export default DitoTypeComponent.register('list', {
177
152
  mixins: [SourceMixin, SortableMixin],
153
+ components: { DitoSourceItem },
178
154
 
179
155
  getSourceType(type) {
180
156
  // No need for transformation here. See TypeTreeList for details.
@@ -190,19 +166,17 @@ export default DitoTypeComponent.register('list', {
190
166
  api,
191
167
  filters,
192
168
  dataPath,
193
- // Pass a computed value to get / set the query, see getFiltersPanel()
169
+ // Pass a computed value to get / set the query, see
170
+ // `createFiltersPanel()`.
194
171
  computed({
195
172
  get() {
196
173
  return component.query
197
174
  },
198
175
 
199
- set(query) {
200
- // Filter out undefined values for comparing with equals()
201
- const filter = obj => pickBy(obj, value => value !== undefined)
202
- if (!equals(filter(query), filter(component.query))) {
203
- component.query = query
204
- component.loadData(false)
205
- }
176
+ // The panel clears the page, but it's only reset when the filters
177
+ // change the query, as the filtered list then has other pages.
178
+ set({ page, ...query }) {
179
+ component.listQuery.update(query, { resetPage: true })
206
180
  }
207
181
  })
208
182
  )
@@ -218,6 +192,20 @@ export default DitoTypeComponent.register('list', {
218
192
  return !!(this.buttonSchemas || this.creatable)
219
193
  },
220
194
 
195
+ listButtonsAttributes() {
196
+ return {
197
+ buttons: this.buttonSchemas,
198
+ schema: this.schema,
199
+ dataPath: this.dataPath,
200
+ meta: this.meta,
201
+ store: this.store,
202
+ nested: this.nested,
203
+ disabled: this.disabled || this.isLoading,
204
+ creatable: this.creatable,
205
+ createPath: this.createPath
206
+ }
207
+ },
208
+
221
209
  hasEditButtons() {
222
210
  const { listData } = this
223
211
  return (
@@ -265,24 +253,24 @@ export default DitoTypeComponent.register('list', {
265
253
  return null
266
254
  },
267
255
 
268
- getCellClass(column) {
269
- return `dito-cell--${hyphenate(column.name)}`
256
+ // Moves the item by `delta` positions, see `DitoDragHandle`.
257
+ moveItem(item, delta) {
258
+ const listData = getListWithMovedItem(this.listData, item, delta)
259
+ if (listData) {
260
+ this.listData = listData
261
+ this.onChange()
262
+ }
270
263
  },
271
264
 
272
- getContext(item, index) {
273
- return new DitoContext(this, {
274
- data: item,
275
- value: item,
276
- index,
277
- dataPath: this.getDataPath(index)
278
- })
265
+ getCellClass(column) {
266
+ return `dito-cell--${hyphenate(column.name)}`
279
267
  },
280
268
 
281
269
  onFilterErrors(errors) {
282
270
  const filtersDataPath = appendDataPath(this.dataPath, '$filters')
283
271
  const panel = this.schemaComponent.getPanelByDataPath(filtersDataPath)
284
272
  if (panel) {
285
- panel.showValidationErrors(errors, true)
273
+ panel.showValidationErrors(getFiltersDataErrors(errors), true)
286
274
  return true
287
275
  }
288
276
  }