|
61 | 61 | :class="customClasses.treeViewNodeSelfText"> |
62 | 62 | {{ model.label }} |
63 | 63 | </span> |
| 64 | + |
| 65 | + <!-- Delete button --> |
| 66 | + <button :id="nodeId + '-delete'" |
| 67 | + type="button" |
| 68 | + v-if="model.deletable" |
| 69 | + class="tree-view-node-self-delete" |
| 70 | + :class="customClasses.treeViewNodeSelfDelete" |
| 71 | + @click="$_treeViewNode_onDelete"> |
| 72 | + <i class="tree-view-node-self-delete-icon" |
| 73 | + :class="customClasses.treeViewNodeSelfDeleteIcon"></i> |
| 74 | + </button> |
64 | 75 | </div> |
65 | 76 |
|
66 | 77 | <!-- Children --> |
|
81 | 92 | @treeViewNodeDblclick="(t, e)=>$emit('treeViewNodeDblclick', t, e)" |
82 | 93 | @treeViewNodeCheckboxChange="(t, e)=>$emit('treeViewNodeCheckboxChange', t, e)" |
83 | 94 | @treeViewNodeRadioChange="(t, e)=>$emit('treeViewNodeRadioChange', t, e)" |
84 | | - @treeViewNodeExpandedChange="(t, e)=>$emit('treeViewNodeExpandedChange', t, e)"> |
| 95 | + @treeViewNodeExpandedChange="(t, e)=>$emit('treeViewNodeExpandedChange', t, e)" |
| 96 | + @treeViewNodeDelete="(t, e)=>$_treeViewNode_handleChildDeletion(t, e)"> |
85 | 97 | </TreeViewNode> |
86 | 98 | </ul> |
87 | 99 | </li> |
|
171 | 183 | if (!Array.isArray(this.model.children)) { |
172 | 184 | this.$set(this.model, 'children', []); |
173 | 185 | } |
174 | | -
|
175 | | - // Set basic node options |
176 | 186 | if (typeof this.model.expandable !== 'boolean') { |
177 | 187 | this.$set(this.model, 'expandable', true); |
178 | 188 | } |
179 | 189 | if (typeof this.model.selectable !== 'boolean') { |
180 | 190 | this.$set(this.model, 'selectable', false); |
181 | 191 | } |
| 192 | + if (typeof this.model.deletable !== 'boolean') { |
| 193 | + this.$set(this.model, 'deletable', false); |
| 194 | + } |
182 | 195 |
|
183 | 196 | this.$_treeViewNode_normalizeNodeInputData(); |
184 | 197 | this.$_treeViewNode_normalizeNodeStateData(); |
|
258 | 271 | this.$emit('treeViewNodeExpandedChange', this.model, event); |
259 | 272 | }, |
260 | 273 | $_treeViewNode_onClick(event) { |
261 | | - // Don't fire this if the target is the input or expander, which have their own events |
262 | | - if (!event.target.matches("input, .tree-view-node-self-expander")) { |
| 274 | + // Don't fire this if the target is an element which has its own events |
| 275 | + if (!event.target.matches("input, .tree-view-node-self-expander, .tree-view-node-self-delete")) { |
263 | 276 | this.$emit('treeViewNodeClick', this.model, event); |
264 | 277 | } |
265 | 278 | }, |
266 | 279 | $_treeViewNode_onDblclick(event) { |
267 | | - // Don't fire this if the target is the input or expander, which have their own events |
268 | | - if (!event.target.matches("input, .tree-view-node-self-expander")) { |
| 280 | + // Don't fire this if the target is an element which has its own events |
| 281 | + if (!event.target.matches("input, .tree-view-node-self-expander, .tree-view-node-self-delete")) { |
269 | 282 | this.$emit('treeViewNodeDblclick', this.model, event); |
270 | 283 | } |
| 284 | + }, |
| 285 | + $_treeViewNode_onDelete(event) { |
| 286 | + this.$emit('treeViewNodeDelete', this.model, event); |
| 287 | + }, |
| 288 | + $_treeViewNode_handleChildDeletion(node, event) { |
| 289 | + // Remove the node from the array of children if this is an immediate child. |
| 290 | + // Note that only the node that was deleted fires these, not any subnode. |
| 291 | + let targetIndex = this.model.children.indexOf(node); |
| 292 | + if (targetIndex > -1) { |
| 293 | + this.model.children.splice(targetIndex, 1); |
| 294 | + } |
| 295 | +
|
| 296 | + this.$emit('treeViewNodeDelete', node, event); |
271 | 297 | } |
272 | 298 | }, |
273 | 299 | }; |
274 | 300 | </script> |
275 | 301 |
|
276 | 302 | <style lang="scss"> |
| 303 | + $baseHeight: 1.2rem; |
| 304 | + $itemSpacing: 1.2rem; |
| 305 | +
|
277 | 306 | .tree-view { |
278 | 307 |
|
279 | 308 | .tree-view-node { |
|
286 | 315 | .tree-view-node-self { |
287 | 316 | display: flex; |
288 | 317 | align-items: flex-start; |
289 | | - line-height: 1.2rem; |
| 318 | + line-height: $baseHeight; |
290 | 319 |
|
291 | 320 | .tree-view-node-self-expander { |
292 | 321 | padding: 0; |
|
315 | 344 | .tree-view-node-self-expander, |
316 | 345 | .tree-view-node-self-checkbox, |
317 | 346 | .tree-view-node-self-radio, |
318 | | - .tree-view-node-self-spacer { |
| 347 | + .tree-view-node-self-spacer, |
| 348 | + .tree-view-node-self-delete { |
319 | 349 | min-width: 1rem; |
320 | 350 | } |
321 | 351 |
|
|
326 | 356 |
|
327 | 357 | .tree-view-node-self-checkbox, |
328 | 358 | .tree-view-node-self-radio { |
329 | | - margin: 0 0 0 -1.2rem; |
| 359 | + margin: 0 0 0 (-$itemSpacing); |
330 | 360 | } |
331 | 361 |
|
332 | 362 | .tree-view-node-self-text, |
333 | 363 | .tree-view-node-self-label { |
334 | | - margin-left: 1.2rem; |
| 364 | + margin-left: $itemSpacing; |
| 365 | + } |
| 366 | + |
| 367 | + .tree-view-node-self-delete { |
| 368 | + padding: 0; |
| 369 | + background: none; |
| 370 | + border: none; |
| 371 | + height: $baseHeight; |
| 372 | +
|
| 373 | + i.tree-view-node-self-delete-icon { |
| 374 | + font-style: normal; |
| 375 | +
|
| 376 | + &::before { |
| 377 | + content: 'x'; |
| 378 | + } |
| 379 | + } |
335 | 380 | } |
336 | 381 | } |
337 | 382 |
|
338 | 383 | .tree-view-node-children { |
339 | | - margin: 0 0 0 2.2rem; |
| 384 | + margin: 0 0 0 (1rem + $itemSpacing); |
340 | 385 | padding: 0; |
341 | 386 | list-style: none; |
342 | 387 | } |
|
0 commit comments