From 22f28808ee9452df49401719efdbaadab35359e2 Mon Sep 17 00:00:00 2001 From: Jeroen Zwartepoorte Date: Mon, 30 Dec 2024 20:16:33 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/components/tree/src/tree-node.scss | 4 ++- packages/components/tree/src/tree-node.ts | 28 +++++++++++++++++---- packages/components/tree/src/tree.ts | 21 +++++++++++++--- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/components/tree/src/tree-node.scss b/packages/components/tree/src/tree-node.scss index 50e4244e8..2d05ccfcd 100644 --- a/packages/components/tree/src/tree-node.scss +++ b/packages/components/tree/src/tree-node.scss @@ -2,6 +2,7 @@ align-items: center; background: var(--sl-elevation-surface-raised-default-idle); border-radius: var(--sl-size-borderRadius-default); + color: var(--sl-color-text-plain); cursor: pointer; display: flex; gap: var(--sl-size-075); @@ -21,8 +22,9 @@ --_guide-color: transparent; } -:host([aria-selected='true']) { +:host([selected]) { background: var(--sl-color-background-selected-subtle-idle); + color: var(--sl-color-text-selected); } :host(:focus-visible) { diff --git a/packages/components/tree/src/tree-node.ts b/packages/components/tree/src/tree-node.ts index eb060b46a..c6cbf0704 100644 --- a/packages/components/tree/src/tree-node.ts +++ b/packages/components/tree/src/tree-node.ts @@ -2,7 +2,7 @@ import { type ScopedElementsMap, ScopedElementsMixin } from '@open-wc/scoped-ele import { Checkbox } from '@sl-design-system/checkbox'; import { Icon } from '@sl-design-system/icon'; import { type EventEmitter, EventsController, event } from '@sl-design-system/shared'; -import { type SlChangeEvent, type SlToggleEvent } from '@sl-design-system/shared/events.js'; +import { type SlChangeEvent, type SlSelectEvent, type SlToggleEvent } from '@sl-design-system/shared/events.js'; import { type CSSResultGroup, LitElement, type PropertyValues, type TemplateResult, html, nothing } from 'lit'; import { property } from 'lit/decorators.js'; import { IndentGuides } from './indent-guides.js'; @@ -14,7 +14,8 @@ declare global { } } -export class TreeNode extends ScopedElementsMixin(LitElement) { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class TreeNode extends ScopedElementsMixin(LitElement) { /** @internal */ static override styles: CSSResultGroup = styles; @@ -36,6 +37,9 @@ export class TreeNode extends ScopedElementsMixin(LitElement) { /** Determines whether the checkbox is checked or not. */ @property({ type: Boolean }) checked?: boolean; + /** The node data. */ + @property({ attribute: false }) data?: T; + /** Whether the node is disabled. */ @property({ type: Boolean, reflect: true }) disabled?: boolean; @@ -57,6 +61,9 @@ export class TreeNode extends ScopedElementsMixin(LitElement) { /** The depth level of this node, 0 being the root of the tree. */ @property({ type: Number, reflect: true }) level = 0; + /** @internal Emits when the user clicks a the wrapper part of the tree node. */ + @event({ name: 'sl-select' }) selectEvent!: EventEmitter>; + /** Whether the node is currently selected. */ @property({ type: Boolean }) selected?: boolean; @@ -76,7 +83,7 @@ export class TreeNode extends ScopedElementsMixin(LitElement) { override updated(changes: PropertyValues): void { super.updated(changes); - if (changes.has('selects')) { + if (changes.has('checked') || changes.has('indeterminate') || changes.has('selected') || changes.has('selects')) { if (this.selects === 'multiple') { this.setAttribute('aria-checked', this.checked ? 'true' : this.indeterminate ? 'mixed' : 'false'); } else { @@ -136,8 +143,19 @@ export class TreeNode extends ScopedElementsMixin(LitElement) { this.indeterminate = false; } - #onClick(): void { - if (this.expandable) { + #onClick(event: Event): void { + const wrapper = this.renderRoot.querySelector('[part="wrapper"]'); + + const insideWrapper = !!event + .composedPath() + .filter((el): el is HTMLElement => el instanceof HTMLElement) + .find(el => el === wrapper); + + if (insideWrapper) { + event.preventDefault(); + + this.selectEvent.emit(this.data!); + } else if (this.expandable) { this.toggle(); } } diff --git a/packages/components/tree/src/tree.ts b/packages/components/tree/src/tree.ts index 85ad9b651..2e2f431a3 100644 --- a/packages/components/tree/src/tree.ts +++ b/packages/components/tree/src/tree.ts @@ -1,8 +1,8 @@ import { virtualize } from '@lit-labs/virtualizer/virtualize.js'; import { type ScopedElementsMap, ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js'; import { Icon } from '@sl-design-system/icon'; -import { RovingTabindexController, SelectionController } from '@sl-design-system/shared'; -import { type SlChangeEvent } from '@sl-design-system/shared/events.js'; +import { type EventEmitter, RovingTabindexController, SelectionController, event } from '@sl-design-system/shared'; +import { type SlChangeEvent, type SlSelectEvent } from '@sl-design-system/shared/events.js'; import { type CSSResultGroup, LitElement, type PropertyValues, type TemplateResult, html, nothing } from 'lit'; import { property } from 'lit/decorators.js'; import { TreeModel, type TreeModelArrayItem, type TreeModelId } from './tree-model.js'; @@ -72,6 +72,9 @@ export class Tree extends ScopedElementsMixin(LitElement) { /** Custom renderer function for tree items. */ @property({ attribute: false }) renderer?: TreeItemRenderer; + /** @internal Emits when the user selects a tree node. */ + @event({ name: 'sl-select' }) selectEvent!: EventEmitter>; + /** Contains the selection state for the tree when `selects` is defined. */ readonly selection = new SelectionController(this); @@ -119,7 +122,7 @@ export class Tree extends ScopedElementsMixin(LitElement) { setTimeout(() => this.#rovingTabindexController.clearElementCache(), 100); return html` -
+
${virtualize({ items, keyFunction: (item: TreeModelArrayItem) => this.model?.getId(item.dataNode), @@ -134,6 +137,8 @@ export class Tree extends ScopedElementsMixin(LitElement) { icon = this.model!.getIcon(dataNode, expanded), selected = this.selection.isSelected(this.model!.getId(dataNode)); + console.log('renderItem', dataNode, selected); + return html` ) => this.#onChange(event, dataNode)} @@ -144,6 +149,7 @@ export class Tree extends ScopedElementsMixin(LitElement) { ?hide-guides=${this.hideGuides} ?last-node-in-level=${lastNodeInLevel} ?selected=${selected && this.selects === 'single'} + .data=${dataNode} .level=${level} .selects=${this.selects} > @@ -161,6 +167,15 @@ export class Tree extends ScopedElementsMixin(LitElement) { console.log('event', event, event.detail, item); } + #onSelect(event: SlSelectEvent): void { + event.preventDefault(); + event.stopPropagation(); + + console.log('select', this.model!.getId(event.detail)); + + this.selection.select(this.model!.getId(event.detail)); + } + #onToggle(item: T): void { this.model?.toggle(this.model?.getId(item)); }