wave-ui 2.46.0 → 2.48.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.
@@ -0,0 +1,300 @@
1
+ <template lang="pug">
2
+ ul.w-tree(:class="classes")
3
+ li.w-tree__item(
4
+ v-for="(item, i) in currentDepthItems"
5
+ :key="i"
6
+ :class="itemClasses(item)")
7
+ .w-tree__item-label(
8
+ @click="!disabled && onLabelClick(item, $event)"
9
+ @keydown="!disabled && onLabelKeydown(item, $event)"
10
+ :tabindex="!disabled && (item.children || item.branch || selectable) && !(unexpandableEmpty && !item.children) ? 0 : null")
11
+ w-button.w-tree__item-expand(
12
+ v-if="(item.children || item.branch) && ((expandOpenIcon && item.open) || expandIcon) && !(unexpandableEmpty && !item.children)"
13
+ color="inherit"
14
+ :icon="(item.open && expandOpenIcon) || expandIcon"
15
+ :icon-props="{ rotate90a: !item.open }"
16
+ :tabindex="-1"
17
+ :disabled="disabled"
18
+ text
19
+ sm)
20
+ slot(name="item-label" :item="item.originalItem" :depth="depth" :open="item.open")
21
+ w-icon(v-if="itemIcon(item)" class="w-tree__item-icon" :color="item.originalItem[itemIconColorKey] || iconColor") {{ itemIcon(item) }}
22
+ span {{ item.label }}
23
+ span.ml1(v-if="counts && (item.children || item.branch)").
24
+ ({{ item.originalItem.children?.length || 0 }})
25
+ component(
26
+ :is="noTransition ? 'div' : 'w-transition-expand'"
27
+ :y="!noTransition || null"
28
+ @after-enter="$emit('open', { item: item.originalItem, open: item.open, depth })"
29
+ @after-leave="$emit('close', { item: item.originalItem, open: item.open, depth })")
30
+ w-tree(
31
+ v-if="item.children && item.open"
32
+ v-bind="$props"
33
+ :depth="depth + 1"
34
+ :data="item.originalItem.children"
35
+ @before-open="$emit('before-open', $event)"
36
+ @open="$emit('open', $event)"
37
+ @before-close="$emit('before-close', $event)"
38
+ @close="$emit('close', $event)"
39
+ @click="$emit('click', $event)"
40
+ @select="$emit('select', $event)"
41
+ @update:model-value="$emit('update:model-value', $event)")
42
+ template(#item-label="{ item, depth, open }")
43
+ slot(name="item-label" :item="item" :depth="depth" :open="open")
44
+ </template>
45
+
46
+ <script>
47
+ /**
48
+ * @todo things to support:
49
+ * - items routes
50
+ * - icon per item
51
+ * - left border?
52
+ **/
53
+
54
+ export default {
55
+ name: 'w-tree',
56
+ props: {
57
+ modelValue: { type: [Object, Array] },
58
+ data: { type: [Object, Array], required: true },
59
+ depth: { type: Number, default: 0 },
60
+ branchClass: { type: String },
61
+ leafClass: { type: String },
62
+ branchIcon: { type: String },
63
+ branchOpenIcon: { type: String },
64
+ leafIcon: { type: String },
65
+ expandIcon: { type: [Boolean, String], default: 'wi-triangle-down' },
66
+ expandOpenIcon: { type: [Boolean, String] },
67
+ expandAll: { type: Boolean },
68
+ unexpandableEmpty: { type: Boolean },
69
+ disabled: { type: Boolean },
70
+ noTransition: { type: Boolean },
71
+ selectable: { type: Boolean },
72
+ // By default it only reacts to items count change (added or deleted items) not property of items change.
73
+ deepReactivity: { type: Boolean },
74
+ counts: { type: Boolean },
75
+ itemIconKey: { type: String, default: 'icon' }, // Support a different icon per item.
76
+ iconColor: { type: String }, // Applies a color on all the label item icons.
77
+ itemIconColorKey: { type: String, default: 'iconColor' } // Applies a specific color on each label item icons.
78
+ },
79
+
80
+ emits: ['update:model-value', 'before-open', 'open', 'before-close', 'close', 'click', 'select'],
81
+
82
+ data: () => ({
83
+ currentDepthItems: [], // A clone of the data prop with additional info per item.
84
+ dataPropUnwatch: null // Holds the unwatch handler of the data prop.
85
+ }),
86
+
87
+ computed: {
88
+ classes () {
89
+ return {
90
+ [`w-tree--depth${this.depth}`]: true,
91
+ 'w-tree--expand-icon': this.expandIcon && !this.depth,
92
+ 'w-tree--disabled': this.disabled && !this.depth,
93
+ 'w-tree--no-expand-button': !this.expandIcon
94
+ }
95
+ }
96
+ },
97
+
98
+ methods: {
99
+ // From data watcher, retain the oldItems open state.
100
+ updateCurrentDepthTree (items, oldItems = []) {
101
+ this.currentDepthItems = []
102
+
103
+ items.forEach((item, i) => {
104
+ this.currentDepthItems.push({
105
+ originalItem: item, // Store the original item to return it on event emits.
106
+ _uid: this.depth.toString() + (i + 1),
107
+ label: item.label,
108
+ children: !!item.children, // The children tree remains available in originalItem.
109
+ branch: item.branch,
110
+ depth: this.depth,
111
+ open: !!(oldItems[i]?.open || this.expandAll)
112
+ })
113
+ })
114
+ },
115
+
116
+ /**
117
+ * Expand/collapse the given tree item when possible (not disabled, has children).
118
+ *
119
+ * @param {Object} item the item to expand.
120
+ * @param {Boolean|Undefined} open when a boolean is received, force a state (open or close).
121
+ */
122
+ expandDepth (item, open) {
123
+ if (typeof open === 'boolean') item.open = open
124
+ else item.open = !item.open
125
+
126
+ const emitParams = { item: item.originalItem, open: item.open, depth: this.depth }
127
+
128
+ this.$emit(item.open ? 'before-open' : 'before-close', emitParams)
129
+
130
+ if (!this.unexpandableEmpty && !item.children) {
131
+ this.$emit(item.open ? 'open' : 'close', emitParams)
132
+ }
133
+
134
+ return true // Just to chain instructions.
135
+ },
136
+
137
+ onLabelClick (item, e) {
138
+ this.$emit('click', { item: item.originalItem, depth: this.depth, e })
139
+ if (item.children || (item.branch && !this.unexpandableEmpty)) this.expandDepth(item)
140
+
141
+ if (this.selectable) this.emitItemSelection(item, e)
142
+ },
143
+
144
+ emitItemSelection (item, e) {
145
+ const emitParams = { item: item.originalItem, depth: this.depth, e }
146
+ if (item.children || (item.branch && !this.unexpandableEmpty)) {
147
+ emitParams.open = item.open
148
+ }
149
+ this.$emit('update:model-value', emitParams)
150
+ this.$emit('select', emitParams)
151
+ },
152
+
153
+ onLabelKeydown (item, e) {
154
+ // Keys: 13 enter, 32 space, 37 arrow left, 38 arrow up, 39 arrow right, 40 arrow down.
155
+ if (!(e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) && [13, 32, 37, 38, 39, 40].includes(e.which)) {
156
+ if (item.children || item.branch) {
157
+ if ([13, 32].includes(e.which)) this.expandDepth(item) && e.preventDefault()
158
+ else if (e.which === 37) this.expandDepth(item, false) && e.preventDefault()
159
+ else if (e.which === 39) this.expandDepth(item, true) && e.preventDefault()
160
+ }
161
+
162
+ // On arrow up or down, focus the prev or next item.
163
+ if ([38, 40].includes(e.which)) {
164
+ const treeRoot = this.$el.closest('.w-tree--depth0')
165
+ const treeTabbableItems = treeRoot.querySelectorAll('.w-tree__item-label[tabindex="0"]')
166
+ const currLabel = e.target.closest('.w-tree__item-label')
167
+ const indexModifier = e.which === 38 ? -1 : 1;
168
+
169
+ ([...treeTabbableItems]).some((item, i) => {
170
+ if (item.isSameNode(currLabel)) {
171
+ treeTabbableItems[i + indexModifier] && treeTabbableItems[i + indexModifier].focus()
172
+ return true // Break the loop.
173
+ }
174
+ return false
175
+ })
176
+ }
177
+ }
178
+
179
+ if (this.selectable) this.emitItemSelection(item, e)
180
+ },
181
+
182
+ /**
183
+ * Returns the previous sibling matching the given selector, or false if not found.
184
+ *
185
+ * @param {Object} node the DOM node to find sibling for.
186
+ * @param {String} selector any valid DOM selector to match the siblings.
187
+ */
188
+ getPreviousSibling (node, selector) {
189
+ // eslint-disable-next-line no-unmodified-loop-condition
190
+ while (selector && (node = node.previousElementSibling)) {
191
+ if (node.matches(selector)) return node
192
+ }
193
+ return false
194
+ },
195
+
196
+ /**
197
+ * Returns the next sibling matching the given selector, or false if not found.
198
+ *
199
+ * @param {Object} node the DOM node to find sibling for.
200
+ * @param {String} selector any valid DOM selector to match the siblings.
201
+ */
202
+ getNextSibling (node, selector) {
203
+ // eslint-disable-next-line no-unmodified-loop-condition
204
+ while (selector && (node = node.nextElementSibling)) {
205
+ if (node.matches(selector)) return node
206
+ }
207
+ return false
208
+ },
209
+
210
+ focusTreeItem (liNode) {
211
+ liNode && liNode.querySelector('.w-tree__item-label').focus()
212
+ },
213
+
214
+ itemIcon (item) {
215
+ return (
216
+ item.originalItem[this.itemIconKey] ||
217
+ (!item.children && !item.branch && this.leafIcon) ||
218
+ ((item.children || item.branch) && ((item.open && this.branchOpenIcon) || this.branchIcon))
219
+ )
220
+ },
221
+
222
+ itemClasses (item) {
223
+ return {
224
+ [item.children || item.branch ? 'w-tree__item--branch' : 'w-tree__item--leaf']: true,
225
+ 'w-tree__item--empty': item.branch && !item.children,
226
+ 'w-tree__item--unexpandable': item.branch && !item.children && this.unexpandableEmpty
227
+ }
228
+ }
229
+ },
230
+
231
+ created () {
232
+ this.updateCurrentDepthTree(this.data)
233
+ this.dataPropUnwatch = this.$watch(
234
+ 'data',
235
+ // The open property of each item has to be retained from this.currentDepthItems in order to stay
236
+ // in the same state after DOM repaint.
237
+ items => this.updateCurrentDepthTree(items, this.currentDepthItems),
238
+ { deep: !!this.deepReactivity } // Deep watching is more resource consuming. Only enable on user demand.
239
+ )
240
+ },
241
+
242
+ unmounted () {
243
+ this.dataPropUnwatch()
244
+ }
245
+ }
246
+ </script>
247
+
248
+ <style lang="scss">
249
+ $expand-icon-size: 20px;
250
+
251
+ .w-tree {
252
+ margin: 0;
253
+
254
+ // Tree items.
255
+ // ------------------------------------------------------
256
+ &__item {list-style-type: none;}
257
+ &__item--branch {}
258
+ &__item--leaf {margin-left: $base-increment * 5 + 2px;}
259
+ &--no-expand-button &__item--leaf {margin-left: 0;}
260
+
261
+ // Tree item label.
262
+ // ------------------------------------------------------
263
+ &__item-label {
264
+ position: relative;
265
+ display: inline-flex;
266
+ align-items: center;
267
+
268
+ &:before {
269
+ content: '';
270
+ position: absolute;
271
+ top: -1px;
272
+ bottom: -1px;
273
+ left: - $base-increment + 2px;
274
+ right: - $base-increment - 2px;
275
+ border-radius: $border-radius;
276
+ }
277
+ &:focus:before {background-color: rgba($primary, 0.1);}
278
+ }
279
+ &__item--leaf &__item-label:before {
280
+ left: - $base-increment;
281
+ right: - $base-increment;
282
+ }
283
+
284
+ &__item-expand {margin-right: 2px;}
285
+
286
+ &__item--branch > &__item-label {cursor: pointer;}
287
+ &__item--unexpandable > &__item-label {
288
+ margin-left: $expand-icon-size + 2px;
289
+ cursor: auto;
290
+ }
291
+ &--disabled &__item-label {cursor: auto;}
292
+ &--disabled &__item--branch > &__item-label {opacity: 0.5;}
293
+
294
+ &__item-icon {margin-right: $base-increment;}
295
+
296
+ // Recursive children.
297
+ // ------------------------------------------------------
298
+ .w-tree {margin-left: $base-increment * 5;}
299
+ }
300
+ </style>
@@ -4,9 +4,9 @@ import NotificationManager from './utils/notification-manager'
4
4
  import colors from './utils/colors'
5
5
  // import * as directives from './directives'
6
6
 
7
- const shadeColor = (col, amt) => {
8
- return '#' + col.slice(1).match(/../g)
9
- .map(x => (x =+ `0x${x}` + amt, x < 0 ? 0 : ( x > 255 ? 255 : x)).toString(16).padStart(2, 0))
7
+ const shadeColor = (color, amount) => {
8
+ return '#' + color.slice(1).match(/../g)
9
+ .map(x => (x =+ `0x${x}` + amount, x < 0 ? 0 : ( x > 255 ? 255 : x)).toString(16).padStart(2, 0))
10
10
  .join('')
11
11
  }
12
12
 
@@ -41,28 +41,79 @@
41
41
  .#{$label}--bg {background-color: $color;}
42
42
  .#{$label} {color: $color;}
43
43
 
44
- @for $i from 1 through 5 {
45
- $light-increment: 8;
44
+ @for $i from 1 through 6 {
45
+ $light-increment: 7.5;
46
+ $light-offset: 0;
47
+ $dark-increment: 6.2;
46
48
  // Some color shades need bigger or smaller increments to end up with the same scale.
47
49
  @if $label == 'deep-orange' {
48
- $light-increment: 7;
50
+ $light-increment: 6.4;
49
51
  }
50
- @if $label == 'green' or $label == 'amber' {
51
- $light-increment: 8.3;
52
+ @if $label == 'orange' {
52
53
  }
53
- @if $label == 'pink' or $label == 'orange' {
54
- $light-increment: 8.5;
54
+ @else if $label == 'green' {
55
+ $light-increment: 7.6;
56
+ $dark-increment: 5.7;
55
57
  }
56
- @if $label == 'cyan' or $label == 'purple' or $label == 'deep-purple' or $label == 'indigo' or $label == 'light-blue' {
57
- $light-increment: 9;
58
+ @else if $label == 'amber' {
58
59
  }
59
- @if $label == 'teal' or $label == 'brown' {
60
- $light-increment: 10;
60
+ @else if $label == 'pink' {
61
+ $light-increment: 6.7;
62
+ $light-offset: -4;
61
63
  }
62
- .#{$label}-light#{$i}--bg {background-color: lighten($color, $light-increment * $i);}
63
- .#{$label}-light#{$i} {color: lighten($color, $light-increment * $i);}
64
- .#{$label}-dark#{$i}--bg {background-color: darken($color, 6 * $i);}
65
- .#{$label}-dark#{$i} {color: darken($color, 6 * $i);}
64
+ @else if $label == 'red' {
65
+ $light-increment: 6.5;
66
+ $light-offset: -1;
67
+ }
68
+ @else if $label == 'indigo' {
69
+ $light-increment: 8;
70
+ $dark-increment: 5.7;
71
+ }
72
+ @else if $label == 'deep-purple' {
73
+ $light-increment: 8;
74
+ $dark-increment: 5.7;
75
+ }
76
+ @else if $label == 'light-blue' {
77
+ $light-increment: 7.8;
78
+ }
79
+ @else if $label == 'light-green' {
80
+ $light-increment: 6;
81
+ $light-offset: -5;
82
+ }
83
+ @else if $label == 'lime' {
84
+ $light-increment: 6.2;
85
+ $light-offset: -6;
86
+ }
87
+ @else if $label == 'yellow' {
88
+ $light-increment: 5.5;
89
+ $light-offset: -8;
90
+ }
91
+ @else if $label == 'purple' {
92
+ $light-increment: 6.5;
93
+ $light-offset: -8.5;
94
+ }
95
+ @else if $label == 'cyan' {
96
+ $light-increment: 9.4;
97
+ $light-offset: 6.5;
98
+ $dark-increment: 5.7;
99
+ }
100
+ @else if $label == 'teal' {
101
+ $light-increment: 9.6;
102
+ $light-offset: 5;
103
+ $dark-increment: 5.4;
104
+ }
105
+ @else if $label == 'blue' {
106
+ $light-increment: 6.8;
107
+ $dark-increment: 6.8;
108
+ }
109
+ @else if $label == 'brown' {
110
+ $light-increment: 8.8;
111
+ $dark-increment: 5;
112
+ }
113
+ .#{$label}-light#{$i}--bg {background-color: lighten($color, $light-increment * $i - $light-offset);}
114
+ .#{$label}-light#{$i} {color: lighten($color, $light-increment * $i - $light-offset);}
115
+ .#{$label}-dark#{$i}--bg {background-color: darken($color, $dark-increment * $i);}
116
+ .#{$label}-dark#{$i} {color: darken($color, $dark-increment * $i);}
66
117
  }
67
118
  }
68
119
  }