@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,26 @@
1
+ import { isString } from '@ditojs/utils'
2
+ import { getDefaultValue } from '../utils/schema.js'
3
+ import { getSchemaAccessor } from '../utils/accessor.js'
4
+
5
+ // @vue/component
6
+ export default {
7
+ computed: {
8
+ trim: getSchemaAccessor('trim', {
9
+ type: Boolean,
10
+ default: false
11
+ })
12
+ },
13
+
14
+ processValue(context) {
15
+ let { schema, value } = context
16
+ if (schema.trim && value != null && isString(value)) {
17
+ // Text fields don't necessarily have a `String` value when `format()`
18
+ // without `parse()` is used.
19
+ value = value.trim()
20
+ }
21
+ if (value === '') {
22
+ value = getDefaultValue(schema, context)
23
+ }
24
+ return value
25
+ }
26
+ }
@@ -0,0 +1,280 @@
1
+ import ValueMixin from './ValueMixin.js'
2
+ import ContextMixin from './ContextMixin.js'
3
+ import ValidationMixin from './ValidationMixin.js'
4
+ import { getSchemaAccessor } from '../utils/accessor.js'
5
+ import { asArray, camelize } from '@ditojs/utils'
6
+
7
+ // @vue/component
8
+ export default {
9
+ mixins: [ValueMixin, ContextMixin, ValidationMixin],
10
+ emits: ['update:component'],
11
+
12
+ props: {
13
+ schema: { type: Object, required: true },
14
+ // NOTE: While `dataPath` points to the actual `value`, `data` represents
15
+ // the `item` in which the `value` is contained, under the key `name`.
16
+ dataPath: { type: String, required: true },
17
+ data: { type: [Object, Array], required: true },
18
+ meta: { type: Object, required: true },
19
+ store: { type: Object, required: true },
20
+ width: { type: [Number, String], default: null },
21
+ label: { type: String, default: null },
22
+ single: { type: Boolean, default: false },
23
+ nested: { type: Boolean, default: true },
24
+ accumulatedBasis: { type: Number, default: null }
25
+ },
26
+
27
+ data() {
28
+ return {
29
+ parsedValue: undefined,
30
+ focused: false
31
+ }
32
+ },
33
+
34
+ computed: {
35
+ name() {
36
+ return this.schema.name
37
+ },
38
+
39
+ type() {
40
+ return this.schema.type
41
+ },
42
+
43
+ labelNode() {
44
+ const node = this.isMounted ? this.$el.previousElementSibling : null
45
+ return node?.matches('.dito-label') ? node : null
46
+ },
47
+
48
+ visible: getSchemaAccessor('visible', {
49
+ type: Boolean,
50
+ default() {
51
+ return this.$options.defaultVisible
52
+ }
53
+ }),
54
+
55
+ // TODO: Rename to `excluded` for consistent naming?
56
+ exclude: getSchemaAccessor('exclude', {
57
+ type: Boolean,
58
+ default: false
59
+ }),
60
+
61
+ required: getSchemaAccessor('required', {
62
+ type: Boolean,
63
+ default: false
64
+ }),
65
+
66
+ // TODO: Move these to a sub-class component used for all input components?
67
+ readonly: getSchemaAccessor('readonly', {
68
+ type: Boolean,
69
+ default: false
70
+ }),
71
+
72
+ autofocus: getSchemaAccessor('autofocus', {
73
+ type: Boolean,
74
+ default: false
75
+ }),
76
+
77
+ // To be used for selects and inputs only?
78
+ clearable: getSchemaAccessor('clearable', {
79
+ type: Boolean,
80
+ default: false,
81
+
82
+ get(clearable) {
83
+ return clearable && !this.readonly
84
+ }
85
+ }),
86
+
87
+ disabled: getSchemaAccessor('disabled', {
88
+ type: Boolean,
89
+ default: false
90
+ }),
91
+
92
+ maxLength: getSchemaAccessor('maxLength', {
93
+ type: Number
94
+ }),
95
+
96
+ placeholder: getSchemaAccessor('placeholder', {
97
+ type: String
98
+ }),
99
+
100
+ autocomplete: getSchemaAccessor('autocomplete', {
101
+ type: String
102
+ }),
103
+
104
+ info: getSchemaAccessor('info', {
105
+ type: String,
106
+ default: null
107
+ }),
108
+
109
+ inlineInfo() {
110
+ // When a label is present, info is shown in the label component.
111
+ // Otherwise, we have to show it inline.
112
+ return !this.label ? this.info : null
113
+ },
114
+
115
+ events() {
116
+ const events = this.getEvents()
117
+ // Register callbacks for all provides non-recognized events,
118
+ // assuming they are native events.
119
+ // TODO: Move to vue3-style `on[A-Z]` event handlers naming that aren't
120
+ // namespaced in `schema.events` once the transition is complete.
121
+ for (const event of Object.keys(this.schema.events || {})) {
122
+ events[`on${camelize(event, true)}`] ??= () => {
123
+ this.emitEvent(event)
124
+ }
125
+ }
126
+ return events
127
+ },
128
+
129
+ attributes() {
130
+ const { nativeField, textField } = this.$options
131
+
132
+ const attributes = {
133
+ ...this.events,
134
+ disabled: this.disabled
135
+ }
136
+
137
+ if (nativeField) {
138
+ attributes.name = this.dataPath
139
+ if (this.label) {
140
+ attributes.title = this.label
141
+ }
142
+ attributes.readonly = this.readonly
143
+ attributes.autofocus = this.autofocus
144
+ if (textField) {
145
+ attributes.maxlength = this.maxLength
146
+ attributes.placeholder = this.placeholder
147
+ attributes.autocomplete = this.autocomplete
148
+ }
149
+ }
150
+
151
+ return attributes
152
+ },
153
+
154
+ validations() {
155
+ const validations = { ...this.getValidations() }
156
+ if (this.required) {
157
+ validations.required = true
158
+ }
159
+ // Allow schema to override default rules and add any new ones:
160
+ for (const [key, value] of Object.entries(this.schema.rules || {})) {
161
+ if (value === undefined) {
162
+ delete validations[key]
163
+ } else {
164
+ validations[key] = value
165
+ }
166
+ }
167
+ return validations
168
+ },
169
+
170
+ showClearButton() {
171
+ return this.clearable && this.value != null
172
+ }
173
+ },
174
+
175
+ created() {
176
+ this._register(true)
177
+ this.setupSchemaFields()
178
+ },
179
+
180
+ unmounted() {
181
+ this._register(false)
182
+ },
183
+
184
+ methods: {
185
+ _register(add) {
186
+ // Provide component to container for schema accessor evaluation.
187
+ this.$emit('update:component', add ? this : null)
188
+ // Prevent unnested type components from overriding parent data paths
189
+ if (this.nested) {
190
+ this.schemaComponent._registerComponent(this, add)
191
+ }
192
+ },
193
+
194
+ // @overridable
195
+ getEvents() {
196
+ const { onFocus, onBlur, onInput, onChange } = this
197
+ return { onFocus, onBlur, onInput, onChange }
198
+ },
199
+
200
+ // @overridable
201
+ getValidations() {
202
+ return null
203
+ },
204
+
205
+ // @overridable
206
+ async scrollIntoView() {
207
+ await this.focusSchema()
208
+ this.getFocusElement()?.scrollIntoView?.({
209
+ behavior: 'smooth',
210
+ block: 'center'
211
+ })
212
+ },
213
+
214
+ // @overridable
215
+ focusElement() {
216
+ this.getFocusElement()?.focus?.()
217
+ },
218
+
219
+ // @overridable
220
+ blurElement() {
221
+ this.getFocusElement()?.blur?.()
222
+ },
223
+
224
+ getFocusElement() {
225
+ const element = asArray(this.$refs.element)[0] ?? this
226
+ return element.$el ?? element
227
+ },
228
+
229
+ async focusSchema() {
230
+ // Also focus this component's schema and panel in case it's a tab.
231
+ await this.schemaComponent.focus()
232
+ await this.tabComponent?.focus()
233
+ },
234
+
235
+ async focus() {
236
+ await this.focusSchema()
237
+ this.scrollIntoView()
238
+ this.focusElement()
239
+ },
240
+
241
+ blur() {
242
+ this.blurElement()
243
+ },
244
+
245
+ clear() {
246
+ this.value = null
247
+ this.blur()
248
+ this.onChange()
249
+ },
250
+
251
+ onFocus() {
252
+ this.focused = true
253
+ this.markTouched()
254
+ this.emitEvent('focus')
255
+ },
256
+
257
+ onBlur() {
258
+ this.focused = false
259
+ this.validate()
260
+ this.emitEvent('blur')
261
+ },
262
+
263
+ onInput() {
264
+ this.markDirty()
265
+ this.emitEvent('input')
266
+ },
267
+
268
+ onChange() {
269
+ this.markDirty()
270
+ this.emitEvent('change', {
271
+ context: {
272
+ // Prevent endless parse recursion:
273
+ value: this.parsedValue !== undefined ? this.parsedValue : this.value
274
+ },
275
+ // Pass `schemaComponent` as parent, so change events can propagate up.
276
+ parent: this.schemaComponent
277
+ })
278
+ }
279
+ }
280
+ }
@@ -0,0 +1,119 @@
1
+ import { isFunction } from '@ditojs/utils'
2
+ import * as validators from '../validators/index.js'
3
+
4
+ // @vue/component
5
+ export default {
6
+ emits: ['errors'],
7
+
8
+ data() {
9
+ return {
10
+ isTouched: false,
11
+ isDirty: false,
12
+ isValidated: false,
13
+ isValid: false,
14
+ errors: null
15
+ }
16
+ },
17
+
18
+ computed: {
19
+ hasErrors() {
20
+ return !!this.errors
21
+ }
22
+ },
23
+
24
+ methods: {
25
+ resetValidation() {
26
+ this.isTouched = false
27
+ this.isDirty = false
28
+ this.isValidated = false
29
+ this.isValid = false
30
+ this.clearErrors()
31
+ },
32
+
33
+ validate(notify = true) {
34
+ let isValid = true
35
+ if (notify) {
36
+ this.clearErrors()
37
+ }
38
+ const { value } = this
39
+ for (const [rule, setting] of Object.entries(this.validations)) {
40
+ // eslint-disable-next-line import/namespace
41
+ const validator = validators[rule]
42
+ if (
43
+ validator &&
44
+ // Only apply 'required' validator to empty values.
45
+ // Apply all other validators only to non-empty values.
46
+ (rule === 'required' || value != null && value !== '')
47
+ ) {
48
+ const { validate, message } = validator
49
+ if (!validate(value, setting, this.validations)) {
50
+ isValid = false
51
+ if (notify) {
52
+ const error = isFunction(message)
53
+ ? message(value, setting, this)
54
+ : message
55
+ this.addError(error, true)
56
+ }
57
+ }
58
+ }
59
+ }
60
+ if (notify) {
61
+ this.isValidated = true
62
+ this.isValid = isValid
63
+ }
64
+ return isValid
65
+ },
66
+
67
+ verify() {
68
+ return this.validate(false)
69
+ },
70
+
71
+ markTouched() {
72
+ this.isTouched = true
73
+ // Clear currently displayed errors when focusing input.
74
+ this.clearErrors()
75
+ },
76
+
77
+ markDirty() {
78
+ this.isDirty = true
79
+ this.isValidated = false
80
+ this.isValid = false
81
+ // Clear currently displayed errors on new input.
82
+ this.clearErrors()
83
+ },
84
+
85
+ addError(error, addLabel = false) {
86
+ this.errors ||= []
87
+ if (addLabel) {
88
+ const label = this.label || this.placeholder || this.name
89
+ error = `The ${label} field ${error}.`
90
+ }
91
+ this.errors.push(error)
92
+ this.$emit('errors', this.errors)
93
+ },
94
+
95
+ showValidationErrors(errors, focus) {
96
+ // Convert from AJV errors objects to an array of error messages
97
+ this.errors = []
98
+ if (errors.length === 0) {
99
+ return false
100
+ }
101
+ for (const { message } of errors) {
102
+ this.addError(message, true)
103
+ }
104
+ if (focus) {
105
+ this.scrollIntoView()
106
+ }
107
+ return true
108
+ },
109
+
110
+ getErrors() {
111
+ return this.errors ? [...this.errors] : null
112
+ },
113
+
114
+ clearErrors() {
115
+ this.errors = null
116
+ this.$emit('errors', this.errors)
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,57 @@
1
+ import SchemaParentMixin from './SchemaParentMixin.js'
2
+
3
+ // @vue/component
4
+ export default {
5
+ mixins: [SchemaParentMixin],
6
+
7
+ computed: {
8
+ errors() {
9
+ return this.schemaComponents.flatMap(({ errors }) => errors || [])
10
+ },
11
+
12
+ isTouched() {
13
+ return this.schemaComponents.some(it => it.isTouched)
14
+ },
15
+
16
+ isDirty() {
17
+ return this.schemaComponents.some(it => it.isDirty)
18
+ },
19
+
20
+ isValid() {
21
+ return this.schemaComponents.every(it => it.isValid)
22
+ },
23
+
24
+ isValidated() {
25
+ return this.schemaComponents.every(it => it.isValid)
26
+ }
27
+ },
28
+
29
+ methods: {
30
+ validateAll(match, notify = true) {
31
+ return this.schemaComponents.every(it => it.validateAll(match, notify))
32
+ },
33
+
34
+ verifyAll(match) {
35
+ return this.schemaComponents.every(it => it.verifyAll(match))
36
+ },
37
+
38
+ resetValidation() {
39
+ this.schemaComponents.forEach(it => it.resetValidation())
40
+ },
41
+
42
+ clearErrors() {
43
+ this.schemaComponents.forEach(it => it.clearErrors())
44
+ },
45
+
46
+ showValidationErrors(errors, focus, first = true) {
47
+ this.schemaComponents.forEach(
48
+ schemaComponent => {
49
+ if (schemaComponent.showValidationErrors(errors, focus, first)) {
50
+ first = false
51
+ }
52
+ }
53
+ )
54
+ return !first
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,31 @@
1
+ import DitoContext from '../DitoContext.js'
2
+ import { computeValue } from '../utils/schema.js'
3
+
4
+ export default {
5
+ computed: {
6
+ value: {
7
+ get() {
8
+ const value = computeValue(
9
+ this.schema,
10
+ this.data,
11
+ this.name,
12
+ this.dataPath,
13
+ { component: this }
14
+ )
15
+ const { format } = this.schema
16
+ return format
17
+ ? format(new DitoContext(this, { value }))
18
+ : value
19
+ },
20
+
21
+ set(value) {
22
+ const { parse } = this.schema
23
+ if (parse) {
24
+ value = parse(new DitoContext(this, { value }))
25
+ }
26
+ this.parsedValue = value
27
+ this.data[this.name] = value
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,17 @@
1
+ @include reset;
2
+
3
+ :root {
4
+ --font-size: #{$font-size};
5
+ --line-height: #{$line-height};
6
+ --color-text: #{$color-text};
7
+
8
+ font-size: var(--font-size);
9
+ line-height: var(--line-height);
10
+ color: var(--color-text);
11
+ background: $content-color-background;
12
+ font-family: $font-family-sans;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ -webkit-font-smoothing: antialiased;
15
+ overflow-x: hidden;
16
+ max-width: 100%;
17
+ }
@@ -0,0 +1,191 @@
1
+ @use 'sass:math';
2
+
3
+ .dito-button {
4
+ // .dito-button with order-arrows:
5
+ &__order-arrows {
6
+ display: inline-block;
7
+ width: math.round($button-order-arrow-size * $math-sqrt2);
8
+ padding-left: $form-spacing;
9
+
10
+ $arrow-offset: calc($button-order-arrow-size / 2 + $border-width);
11
+
12
+ &::before {
13
+ @include arrow($button-order-arrow-size, 'up');
14
+
15
+ bottom: $arrow-offset;
16
+ }
17
+
18
+ &::after {
19
+ @include arrow($button-order-arrow-size, 'down');
20
+
21
+ top: $arrow-offset;
22
+ }
23
+ }
24
+
25
+ &--order-asc &__order-arrows {
26
+ &::before {
27
+ bottom: 0;
28
+ }
29
+
30
+ &::after {
31
+ display: none;
32
+ }
33
+ }
34
+
35
+ &--order-desc &__order-arrows {
36
+ &::before {
37
+ display: none;
38
+ }
39
+
40
+ &::after {
41
+ top: 0;
42
+ }
43
+ }
44
+
45
+ &--create:empty::before {
46
+ content: 'Create';
47
+ }
48
+
49
+ &--add:empty::before {
50
+ content: 'Add';
51
+ }
52
+
53
+ &--delete:empty::before {
54
+ content: 'Delete';
55
+ }
56
+
57
+ &--remove:empty::before {
58
+ content: 'Remove';
59
+ }
60
+
61
+ &--edit:empty::before {
62
+ content: 'Edit';
63
+ }
64
+
65
+ &--clear:empty::before {
66
+ content: 'Clear';
67
+ }
68
+
69
+ &--save:empty::before {
70
+ content: 'Save';
71
+ }
72
+
73
+ &--apply:empty::before {
74
+ content: 'Apply';
75
+ }
76
+
77
+ &--login:empty::before {
78
+ content: 'Login';
79
+ }
80
+
81
+ &--ok:empty::before {
82
+ content: 'OK';
83
+ }
84
+
85
+ &--cancel:empty::before {
86
+ content: 'Cancel';
87
+ }
88
+
89
+ &--close:empty::before {
90
+ content: 'Close';
91
+ }
92
+
93
+ &--drag:empty::before {
94
+ content: 'Drag';
95
+ }
96
+
97
+ &--copy:empty::before {
98
+ content: 'Copy';
99
+ }
100
+
101
+ &--paste:empty::before {
102
+ content: 'Paste';
103
+ }
104
+
105
+ &--drag {
106
+ cursor: grab;
107
+
108
+ &:active {
109
+ cursor: grabbing;
110
+ }
111
+ }
112
+ }
113
+
114
+ .dito-buttons {
115
+ // TODO: BEM
116
+
117
+ &--large {
118
+ --button-margin: 3px;
119
+
120
+ font-size: $header-font-size;
121
+ flex-flow: row wrap;
122
+ justify-content: center;
123
+ padding-bottom: $content-padding;
124
+
125
+ .dito-container {
126
+ // Do not specify this on .dito-buttons directly as it would break borders
127
+ margin-top: $content-padding;
128
+ }
129
+
130
+ .dito-button {
131
+ border-radius: 2em;
132
+ padding: $button-round-padding;
133
+ }
134
+ }
135
+
136
+ &--main {
137
+ border-top: $border-style;
138
+ }
139
+
140
+ &--sticky {
141
+ align-self: flex-end;
142
+ position: sticky;
143
+ bottom: 0;
144
+ z-index: $z-index-main-buttons;
145
+ background: $content-color-background;
146
+ margin-top: 2 * $content-padding;
147
+ margin-bottom: calc(-1 * var(--pane-padding, 0px));
148
+ box-shadow: 0 (-$content-padding) $content-padding (-$content-padding)
149
+ $color-shadow;
150
+
151
+ &:empty {
152
+ display: none;
153
+ }
154
+ }
155
+
156
+ &--round,
157
+ &--small {
158
+ // For now, nothing for these:
159
+ // .dito-button--create:empty::before,
160
+ // .dito-button--add:empty::before
161
+
162
+ .dito-button--upload:empty::before {
163
+ @extend %icon-upload;
164
+ }
165
+
166
+ .dito-button--delete:empty::before,
167
+ .dito-button--remove:empty::before {
168
+ @extend %icon-remove;
169
+ }
170
+
171
+ .dito-button--edit:empty::before {
172
+ @extend %icon-edit;
173
+ }
174
+
175
+ .dito-button--clear:empty::before {
176
+ @extend %icon-clear;
177
+ }
178
+
179
+ .dito-button--drag:empty::before {
180
+ @extend %icon-drag;
181
+ }
182
+
183
+ .dito-button--copy:empty::before {
184
+ @extend %icon-copy;
185
+ }
186
+
187
+ .dito-button--paste:empty::before {
188
+ @extend %icon-paste;
189
+ }
190
+ }
191
+ }