wave-ui 1.66.1 → 1.67.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.
@@ -6,11 +6,11 @@ ul.w-tree(:class="classes")
6
6
  :class="itemClasses(item)")
7
7
  //- The keys `route` & `disabled` are always present in any currentDepthItems.
8
8
  component.w-tree__item-label(
9
- :is="!disabled && !item.disabled && item.route ? (!$router || hasExternalLink(item) ? 'a' : 'router-link') : 'div'"
9
+ :is="getTreeItemComponent(item)"
10
10
  v-bind="item.route && { [!$router || hasExternalLink(item) ? 'href' : 'to']: item.route }"
11
11
  @click="!disabled && !item.disabled && onLabelClick(item, $event)"
12
12
  @keydown="!disabled && !item.disabled && onLabelKeydown(item, $event)"
13
- :tabindex="!disabled && !item.disabled && (item.children || item.branch || selectable) && !(unexpandableEmpty && !item.children) ? 0 : null")
13
+ :tabindex="getTreeItemTabindex(item)")
14
14
  //- @click.stop to not follow link if item is a link.
15
15
  w-button.w-tree__item-expand(
16
16
  v-if="(item.children || item.branch) && ((expandOpenIcon && item.open) || expandIcon) && !(unexpandableEmpty && !item.children)"
@@ -22,21 +22,31 @@ ul.w-tree(:class="classes")
22
22
  :disabled="disabled || item.disabled"
23
23
  text
24
24
  sm)
25
- slot(name="item" :item="item.originalItem" :depth="depth" :open="item.open")
26
- w-icon(v-if="itemIcon(item)" class="w-tree__item-icon" :color="item.originalItem[itemIconColorKey] || iconColor") {{ itemIcon(item) }}
25
+ slot(
26
+ name="item"
27
+ :item="item.originalItem"
28
+ :depth="depth"
29
+ :path="item.path"
30
+ :open="item.open")
31
+ w-icon(
32
+ v-if="itemIcon(item)"
33
+ class="w-tree__item-icon"
34
+ :color="item.originalItem[itemIconColorKey] || iconColor").
35
+ {{ itemIcon(item) }}
27
36
  span(v-html="item.label")
28
37
  span.ml1(v-if="counts && (item.children || item.branch)").
29
38
  ({{ item.originalItem.children ? item.originalItem.children.length : 0 }})
30
39
  component(
31
40
  :is="noTransition ? 'div' : 'w-transition-expand'"
32
41
  :y="!noTransition || null"
33
- @after-enter="$emit('open', { item: item.originalItem, open: item.open, depth })"
34
- @after-leave="$emit('close', { item: item.originalItem, open: item.open, depth })")
42
+ @after-enter="$emit('open', emitPayload(item))"
43
+ @after-leave="$emit('close', emitPayload(item))")
35
44
  w-tree(
36
45
  v-if="item.children && item.open"
37
46
  v-bind="$props"
38
47
  :depth="depth + 1"
39
48
  :data="item.originalItem.children"
49
+ :parent="item"
40
50
  @before-open="$emit('before-open', $event)"
41
51
  @open="$emit('open', $event)"
42
52
  @before-close="$emit('before-close', $event)"
@@ -44,8 +54,8 @@ ul.w-tree(:class="classes")
44
54
  @click="$emit('click', $event)"
45
55
  @select="$emit('select', $event)"
46
56
  @input="$emit('input', $event)")
47
- template(#item="{ item, depth, open }")
48
- slot(name="item" :item="item" :depth="depth" :open="open")
57
+ template(#item="{ item, depth, path, open }")
58
+ slot(name="item" :item="item" :depth="depth" :path="path" :open="open")
49
59
  </template>
50
60
 
51
61
  <script>
@@ -60,7 +70,8 @@ export default {
60
70
  props: {
61
71
  value: { type: [Object, Array] },
62
72
  data: { type: [Object, Array], required: true },
63
- depth: { type: Number, default: 0 },
73
+ depth: { type: Number, default: 0 }, // To get the context from nested items.
74
+ parent: { type: Object, default: null }, // To get the context from nested items.
64
75
  branchClass: { type: String },
65
76
  leafClass: { type: String },
66
77
  branchIcon: { type: String },
@@ -97,6 +108,7 @@ export default {
97
108
  return {
98
109
  [`w-tree--depth${this.depth}`]: true,
99
110
  'w-tree--expand-icon': this.expandIcon && !this.depth,
111
+ 'w-tree--selectable': this.selectable,
100
112
  'w-tree--disabled': this.disabled && !this.depth,
101
113
  'w-tree--no-expand-button': !this.expandIcon
102
114
  }
@@ -115,7 +127,7 @@ export default {
115
127
  if (!Array.isArray(items)) items = [items]
116
128
 
117
129
  items.forEach((item, i) => {
118
- this.currentDepthItems.push({
130
+ const itemWrapper = {
119
131
  originalItem: item, // Store the original item to return it on event emits.
120
132
  _uid: this.depth.toString() + (i + 1),
121
133
  label: item[this.itemLabelKey],
@@ -123,12 +135,50 @@ export default {
123
135
  branch: item.branch,
124
136
  route: item[this.itemRouteKey],
125
137
  disabled: item[this.itemDisabledKey],
138
+ selected: oldItems[i]?.selected || false,
126
139
  depth: this.depth,
127
- open: !!(oldItems[i]?.open || this.expandAll || item[this.itemOpenKey])
128
- })
140
+ open: !!(oldItems[i]?.open || this.expandAll || item[this.itemOpenKey]),
141
+ parent: this.parent || null,
142
+ path: [] // Ancestors path from root to leaf including self.
143
+ }
144
+ itemWrapper.path = this.getTreeItemPath(itemWrapper)
145
+ this.currentDepthItems.push(itemWrapper)
129
146
  })
130
147
  },
131
148
 
149
+ getTreeItemComponent (item) {
150
+ return !this.disabled && !item.disabled && item.route ? (!this.$router || this.hasExternalLink(item) ? 'a' : 'router-link') : 'div'
151
+ },
152
+
153
+ getTreeItemTabindex (item) {
154
+ return !this.disabled && !item.disabled && (item.children || item.branch || this.selectable) && !(this.unexpandableEmpty && !item.children) ? 0 : null
155
+ },
156
+
157
+ /**
158
+ * Get the tree path of the given item.
159
+ * The full ancestors items are stored in the array and not only their `originalItem`s in case
160
+ * it is mutated before we return it to the user through slots and emitted events.
161
+ * Before it is returned to the user, this array is mapped to only give the `originalItem`s.
162
+ *
163
+ * @param {Object} item the tree item to get the ancestors path for.
164
+ * @return an array of item objects from the root to the leaf (including the item itself).
165
+ */
166
+ getTreeItemPath (item) {
167
+ const ancestorsPath = [item]
168
+
169
+ let ancestor = item.parent
170
+ while (ancestor) {
171
+ ancestorsPath.push(ancestor)
172
+ ancestor = ancestor.parent
173
+ }
174
+ ancestorsPath.reverse()
175
+ return ancestorsPath
176
+ },
177
+
178
+ getTreeItemPathForOutput (item) {
179
+ return item.path.map(item => item.originalItem)
180
+ },
181
+
132
182
  /**
133
183
  * Expand/collapse the given tree item when possible (not disabled, has children).
134
184
  *
@@ -139,12 +189,12 @@ export default {
139
189
  if (typeof open === 'boolean') item.open = open
140
190
  else item.open = !item.open
141
191
 
142
- const emitParams = { item: item.originalItem, open: item.open, depth: this.depth }
192
+ const emitPayload = this.emitPayload(item)
143
193
 
144
- this.$emit(item.open ? 'before-open' : 'before-close', emitParams)
194
+ this.$emit(item.open ? 'before-open' : 'before-close', emitPayload)
145
195
 
146
196
  if (!this.unexpandableEmpty && !item.children) {
147
- this.$emit(item.open ? 'open' : 'close', emitParams)
197
+ this.$emit(item.open ? 'open' : 'close', emitPayload)
148
198
  }
149
199
 
150
200
  return true // Just to chain instructions.
@@ -154,19 +204,12 @@ export default {
154
204
  const route = item[this.itemRouteKey]
155
205
  if (route && this.$router && !this.hasExternalLink(item)) e.preventDefault()
156
206
 
157
- this.$emit('click', { item: item.originalItem, depth: this.depth, e })
158
207
  if (item.children || (item.branch && !this.unexpandableEmpty)) this.expandDepth(item)
208
+ if (this.selectable) item.selected = !item.selected
159
209
 
160
- if (this.selectable) this.emitItemSelection(item, e)
161
- },
162
-
163
- emitItemSelection (item, e) {
164
- const emitParams = { item: item.originalItem, depth: this.depth, e }
165
- if (item.children || (item.branch && !this.unexpandableEmpty)) {
166
- emitParams.open = item.open
167
- }
168
- this.$emit('input', emitParams)
169
- this.$emit('select', emitParams)
210
+ const emitPayload = this.emitPayload(item, e)
211
+ this.$emit('click', emitPayload)
212
+ this.emitItemSelection(item, e) // Always emitting on click, but different event for selection.
170
213
  },
171
214
 
172
215
  onLabelKeydown (item, e) {
@@ -195,7 +238,32 @@ export default {
195
238
  }
196
239
  }
197
240
 
198
- if (this.selectable) this.emitItemSelection(item, e)
241
+ if (e.which === 13) {
242
+ if (this.selectable) item.selected = !item.selected
243
+ // Always emitting on enter keydown, but different event for selection.
244
+ this.emitItemSelection(item, e)
245
+ }
246
+ },
247
+
248
+ emitItemSelection (item, e) {
249
+ const emitPayload = this.emitPayload(item, e)
250
+
251
+ this.$emit('update:model-value', emitPayload)
252
+ if (this.selectable) this.$emit('select', emitPayload)
253
+ },
254
+
255
+ emitPayload (item, e) {
256
+ const emitPayload = {
257
+ item: item.originalItem,
258
+ depth: this.depth,
259
+ path: this.getTreeItemPathForOutput(item)
260
+ }
261
+
262
+ if (e) emitPayload.e = e
263
+ if (item.children || (item.branch && !this.unexpandableEmpty)) emitPayload.open = item.open
264
+ if (this.selectable) emitPayload.selected = item.selected
265
+
266
+ return emitPayload
199
267
  },
200
268
 
201
269
  /**
@@ -246,6 +314,7 @@ export default {
246
314
  return {
247
315
  [item.children || item.branch ? 'w-tree__item--branch' : 'w-tree__item--leaf']: true,
248
316
  'w-tree__item--disabled': item[this.itemDisabledKey],
317
+ 'w-tree__item--selected': item.selected,
249
318
  'w-tree__item--empty': item.branch && !item.children,
250
319
  'w-tree__item--unexpandable': item.branch && !item.children && this.unexpandableEmpty
251
320
  }
@@ -299,13 +368,19 @@ $expand-icon-size: 20px;
299
368
  right: - $base-increment - 2px;
300
369
  border-radius: $border-radius;
301
370
  }
302
- &:hover:before {background-color: rgba($primary, 0.05);}
303
- &:focus:before {background-color: rgba($primary, 0.1);}
371
+ &:hover:before {background-color: $primary;opacity: 0.1;}
372
+ &:focus-visible:before {background-color: $primary;opacity: 0.15;}
304
373
  }
374
+ &.w-tree--selectable &__item-label {cursor: pointer;}
375
+ &.w-tree--selectable &__item--disabled &__item-label {cursor: auto;}
305
376
  &__item--leaf &__item-label:before {
306
377
  left: - $base-increment;
307
378
  right: - $base-increment;
308
379
  }
380
+ &__item--selected > &__item-label:before {
381
+ background-color: $primary;
382
+ opacity: 0.25;
383
+ }
309
384
  &__item--disabled &__item-label {opacity: 0.5;}
310
385
  &__item--disabled &__item-label:before {display: none;}
311
386
 
@@ -60,7 +60,7 @@ export default class WaveUI {
60
60
 
61
61
  // Register a-la-carte components from the given list.
62
62
  const { components = {} } = options || {}
63
- for (let id in components) {
63
+ for (const id in components) {
64
64
  const component = components[id]
65
65
  // If presets are defined for this component inject them into the props defaults.
66
66
  if (options.presets?.[component.name]) injectPresets(component, options.presets[component.name])