11import { controller , target } from '@github/catalyst'
2- import { SelectStrategy , TreeViewSubTreeNodeElement } from './tree_view_sub_tree_node_element'
2+ import { SelectStrategy , SelectVariant , TreeViewSubTreeNodeElement } from './tree_view_sub_tree_node_element'
33import { useRovingTabIndex } from './tree_view_roving_tab_index'
4- import type { TreeViewNodeType , TreeViewCheckedValue , TreeViewNodeInfo } from '../../shared_events'
5-
6- export type SelectVariant = 'none' | 'single' | 'multiple'
4+ import type { TreeViewCheckedValue , TreeViewNodeInfo , TreeViewNodeType } from '../../shared_events'
75
86@controller
97export class TreeViewElement extends HTMLElement {
@@ -126,6 +124,8 @@ export class TreeViewElement extends HTMLElement {
126124 #handleNodeEvent( node : Element , event : Event ) {
127125 if ( this . #eventIsCheckboxToggle( event , node ) ) {
128126 this . #handleCheckboxToggle( event , node )
127+ } else if ( this . #eventIsSingleSelection( event , node ) ) {
128+ this . handleSingleSelection ( event , node )
129129 } else if ( this . #eventIsActivation( event ) ) {
130130 this . #handleNodeActivated( event , node )
131131 } else if ( event . type === 'focusin' ) {
@@ -179,6 +179,48 @@ export class TreeViewElement extends HTMLElement {
179179 )
180180 }
181181
182+ #eventIsSingleSelection( event : Event , node : Element ) {
183+ return event . type === 'click' && this . selectVariant ( node ) === 'single'
184+ }
185+
186+ handleSingleSelection ( event : Event , node : Element ) {
187+ if ( this . getNodeDisabledValue ( node ) ) {
188+ event . preventDefault ( )
189+ return
190+ }
191+
192+ // do not emit activation events for buttons and anchors, since it is assumed any activation
193+ // behavior for these element types is user- or browser-defined
194+ if ( ! ( node instanceof HTMLDivElement ) ) return
195+
196+ const path = this . getNodePath ( node )
197+ const nodeInfo = this . infoFromNode ( node , 'true' )
198+
199+ const checkSuccess = this . dispatchEvent (
200+ new CustomEvent ( 'treeViewBeforeNodeChecked' , {
201+ bubbles : true ,
202+ cancelable : true ,
203+ detail : [ nodeInfo ] ,
204+ } ) ,
205+ )
206+
207+ if ( ! checkSuccess ) return
208+
209+ const currentlyChecked = ! this . getNodeCheckedValue ( node )
210+
211+ // disallow unchecking checked item in single-select mode
212+ if ( ! currentlyChecked ) {
213+ this . checkOnlyAtPath ( path )
214+ }
215+
216+ this . dispatchEvent (
217+ new CustomEvent ( 'treeViewNodeChecked' , {
218+ bubbles : true ,
219+ detail : [ this . infoFromNode ( node , 'true' ) ] ,
220+ } ) ,
221+ )
222+ }
223+
182224 #handleNodeActivated( event : Event , node : Element ) {
183225 if ( this . getNodeDisabledValue ( node ) ) {
184226 event . preventDefault ( )
@@ -201,19 +243,6 @@ export class TreeViewElement extends HTMLElement {
201243
202244 if ( ! activationSuccess ) return
203245
204- if ( this . selectVariant ( node ) === 'single' ) {
205- const currentlyChecked = ! this . getNodeCheckedValue ( node )
206-
207- // disallow unchecking checked item in single-select mode
208- if ( ! currentlyChecked ) {
209- for ( const el of this . activeNodes ) {
210- this . uncheckAtPath ( this . getNodePath ( el ) )
211- }
212-
213- this . checkAtPath ( path )
214- }
215- }
216-
217246 // navigate or trigger button, don't toggle
218247 if ( ! this . nodeHasNativeAction ( node ) ) {
219248 this . toggleAtPath ( path )
@@ -246,14 +275,18 @@ export class TreeViewElement extends HTMLElement {
246275 break
247276 }
248277
249- if ( this . nodeHasCheckBox ( node ) ) {
278+ if ( this . selectVariant ( node ) == 'multiple' ) {
250279 event . preventDefault ( )
251280
252281 if ( this . getNodeCheckedValue ( node ) === 'true' ) {
253282 this . setNodeCheckedValue ( node , 'false' )
254283 } else {
255284 this . setNodeCheckedValue ( node , 'true' )
256285 }
286+ } else if ( this . selectVariant ( node ) == 'single' ) {
287+ event . preventDefault ( )
288+
289+ this . checkOnlyAtPath ( this . getNodePath ( node ) )
257290 } else if ( node instanceof HTMLAnchorElement ) {
258291 // simulate click on space
259292 node . click ( )
@@ -333,6 +366,14 @@ export class TreeViewElement extends HTMLElement {
333366 this . setNodeCheckedValue ( node , 'false' )
334367 }
335368
369+ checkOnlyAtPath ( path : string [ ] ) {
370+ for ( const el of this . activeNodes ) {
371+ this . uncheckAtPath ( this . getNodePath ( el ) )
372+ }
373+
374+ this . checkAtPath ( path )
375+ }
376+
336377 toggleCheckedAtPath ( path : string [ ] ) {
337378 const node = this . nodeAtPath ( path )
338379 if ( ! node ) return
@@ -440,7 +481,7 @@ export class TreeViewElement extends HTMLElement {
440481 }
441482 }
442483
443- selectVariant ( node : HTMLElement ) : SelectVariant {
484+ selectVariant ( node : Element ) : SelectVariant {
444485 return ( node . getAttribute ( 'data-select-variant' ) || 'none' ) as SelectVariant
445486 }
446487}
0 commit comments