11import { html , LitElement } from 'lit' ;
2- import { property , queryAssignedElements } from 'lit/decorators.js' ;
3-
2+ import { property } from 'lit/decorators.js' ;
43import {
54 addKeybindings ,
65 altKey ,
@@ -10,6 +9,7 @@ import {
109 homeKey ,
1110 shiftKey ,
1211} from '../common/controllers/key-bindings.js' ;
12+ import { addSlotController , setSlots } from '../common/controllers/slot.js' ;
1313import { registerComponent } from '../common/definitions/register.js' ;
1414import { addSafeEventListener , first , last } from '../common/util.js' ;
1515import IgcExpansionPanelComponent from '../expansion-panel/expansion-panel.js' ;
@@ -28,135 +28,189 @@ export default class IgcAccordionComponent extends LitElement {
2828 public static override styles = styles ;
2929
3030 /* blazorSuppress */
31- public static register ( ) {
31+ public static register ( ) : void {
3232 registerComponent ( IgcAccordionComponent , IgcExpansionPanelComponent ) ;
3333 }
3434
35- @queryAssignedElements ( {
36- selector : `${ IgcExpansionPanelComponent . tagName } :not([disabled])` ,
37- } )
38- private enabledPanels ! : Array < IgcExpansionPanelComponent > ;
35+ //#region Internal state and properties
36+
37+ private _panels : IgcExpansionPanelComponent [ ] = [ ] ;
38+
39+ private readonly _slots = addSlotController ( this , {
40+ slots : setSlots ( ) ,
41+ onChange : this . _handleSlotChange ,
42+ initial : true ,
43+ } ) ;
44+
45+ private get _interactivePanels ( ) : IgcExpansionPanelComponent [ ] {
46+ return this . _panels . filter ( ( panel ) => ! panel . disabled ) ;
47+ }
48+
49+ //#endregion
50+
51+ //#region Public attributes and properties
3952
4053 /**
4154 * Allows only one panel to be expanded at a time.
4255 * @attr single-expand
56+ * @default false
4357 */
44- @property ( { attribute : 'single-expand' , reflect : true , type : Boolean } )
58+ @property ( { type : Boolean , reflect : true , attribute : 'single-expand' } )
4559 public singleExpand = false ;
4660
4761 /* blazorSuppress */
4862 /** Returns all of the accordions's direct igc-expansion-panel children. */
49- @queryAssignedElements ( { selector : IgcExpansionPanelComponent . tagName } )
50- public panels ! : Array < IgcExpansionPanelComponent > ;
63+ public get panels ( ) : IgcExpansionPanelComponent [ ] {
64+ return Array . from ( this . _panels ) ;
65+ }
66+
67+ //#endregion
5168
5269 constructor ( ) {
5370 super ( ) ;
5471
55- addSafeEventListener ( this , 'igcOpening' as any , this . handlePanelOpening ) ;
72+ addSafeEventListener ( this , 'igcOpening' as any , this . _handlePanelOpening ) ;
73+
74+ addKeybindings ( this , { skip : this . _skipKeybinding } )
75+ . set ( homeKey , this . _navigateToFirst )
76+ . set ( endKey , this . _navigateToLast )
77+ . set ( arrowUp , this . _navigateToPrevious )
78+ . set ( arrowDown , this . _navigateToNext )
79+ . set ( [ shiftKey , altKey , arrowDown ] , this . _expandAll )
80+ . set ( [ shiftKey , altKey , arrowUp ] , this . _collapseAll ) ;
81+ }
5682
57- addKeybindings ( this , { skip : this . skipKeybinding } )
58- . set ( homeKey , ( ) =>
59- this . getPanelHeader ( first ( this . enabledPanels ) ) . focus ( )
60- )
61- . set ( endKey , ( ) => this . getPanelHeader ( last ( this . enabledPanels ) ) . focus ( ) )
62- . set ( arrowUp , this . navigatePrev )
63- . set ( arrowDown , this . navigateNext )
64- . set ( [ shiftKey , altKey , arrowDown ] , this . expandAll )
65- . set ( [ shiftKey , altKey , arrowUp ] , this . collapseAll ) ;
83+ //#region Event handlers
84+
85+ private _handleSlotChange ( ) : void {
86+ this . _panels = this . _slots . getAssignedElements ( '[default]' , {
87+ selector : IgcExpansionPanelComponent . tagName ,
88+ } ) ;
89+ }
90+
91+ private async _handlePanelOpening ( event : Event ) : Promise < void > {
92+ const current = event . target as IgcExpansionPanelComponent ;
93+
94+ if ( ! ( this . singleExpand && this . panels . includes ( current ) ) ) {
95+ return ;
96+ }
97+
98+ await Promise . all (
99+ this . _interactivePanels
100+ . filter ( ( panel ) => panel . open && panel !== current )
101+ . map ( ( panel ) => this . _closePanel ( panel ) )
102+ ) ;
66103 }
67104
68- private skipKeybinding ( target : Element ) {
105+ //#endregion
106+
107+ //#region Keyboard interaction handlers
108+
109+ private _skipKeybinding ( target : Element ) : boolean {
69110 return ! (
70- target . matches ( IgcExpansionPanelComponent . tagName ) &&
71- this . enabledPanels . includes ( target as IgcExpansionPanelComponent )
111+ target instanceof IgcExpansionPanelComponent &&
112+ this . _interactivePanels . includes ( target )
72113 ) ;
73114 }
74115
75- private navigatePrev ( event : KeyboardEvent ) {
116+ private _navigateToFirst ( ) : void {
117+ this . _getPanelHeader ( first ( this . _interactivePanels ) ) ?. focus ( ) ;
118+ }
119+
120+ private _navigateToLast ( ) : void {
121+ this . _getPanelHeader ( last ( this . _interactivePanels ) ) ?. focus ( ) ;
122+ }
123+
124+ private _navigateToPrevious ( event : KeyboardEvent ) : void {
76125 const current = event . target as IgcExpansionPanelComponent ;
77- const next = this . getNextPanel ( current , - 1 ) ;
126+ const next = this . _getNextPanel ( current , - 1 ) ;
78127
79128 if ( next !== current ) {
80- this . getPanelHeader ( next ) . focus ( ) ;
129+ this . _getPanelHeader ( next ) ? .focus ( ) ;
81130 }
82131 }
83132
84- private navigateNext ( event : KeyboardEvent ) {
133+ private _navigateToNext ( event : KeyboardEvent ) : void {
85134 const current = event . target as IgcExpansionPanelComponent ;
86- const next = this . getNextPanel ( current , 1 ) ;
135+ const next = this . _getNextPanel ( current , 1 ) ;
87136
88137 if ( next !== current ) {
89- this . getPanelHeader ( next ) . focus ( ) ;
138+ this . _getPanelHeader ( next ) ? .focus ( ) ;
90139 }
91140 }
92141
93- private collapseAll ( ) {
94- for ( const panel of this . enabledPanels ) {
95- this . closePanel ( panel ) ;
96- }
142+ private async _collapseAll ( ) : Promise < void > {
143+ await Promise . all (
144+ this . _interactivePanels . map ( ( panel ) => this . _closePanel ( panel ) )
145+ ) ;
97146 }
98147
99- private expandAll ( event : KeyboardEvent ) {
148+ private async _expandAll ( event : KeyboardEvent ) : Promise < void > {
100149 const current = event . target as IgcExpansionPanelComponent ;
101150
102- for ( const panel of this . enabledPanels ) {
103- if ( this . singleExpand ) {
104- current === panel ? this . openPanel ( panel ) : this . closePanel ( panel ) ;
105- } else {
106- this . openPanel ( panel ) ;
107- }
151+ if ( this . singleExpand ) {
152+ const closePromises = this . _interactivePanels
153+ . filter ( ( panel ) => panel . open && panel !== current )
154+ . map ( ( panel ) => this . _closePanel ( panel ) ) ;
155+
156+ await Promise . all ( closePromises ) ;
157+ await this . _openPanel ( current ) ;
158+ } else {
159+ await Promise . all (
160+ this . _interactivePanels . map ( ( panel ) => this . _openPanel ( panel ) )
161+ ) ;
108162 }
109163 }
110164
111- private handlePanelOpening ( event : Event ) {
112- const current = event . target as IgcExpansionPanelComponent ;
165+ //#endregion
113166
114- if ( ! ( this . singleExpand && this . panels . includes ( current ) ) ) {
115- return ;
116- }
167+ //#region Internal API
117168
118- for ( const panel of this . enabledPanels ) {
119- if ( panel . open && panel !== current ) {
120- this . closePanel ( panel ) ;
121- }
122- }
169+ private _getPanelHeader (
170+ panel : IgcExpansionPanelComponent
171+ ) : HTMLElement | undefined {
172+ // biome-ignore lint/complexity/useLiteralKeys: Direct property access instead of DOM query
173+ return panel [ '_headerRef' ] . value ;
123174 }
124175
125- private getNextPanel ( panel : IgcExpansionPanelComponent , dir : 1 | - 1 = 1 ) {
126- const idx = this . enabledPanels . indexOf ( panel ) ;
127- return this . enabledPanels [ idx + dir ] || panel ;
128- }
176+ private _getNextPanel (
177+ panel : IgcExpansionPanelComponent ,
178+ dir : 1 | - 1 = 1
179+ ) : IgcExpansionPanelComponent {
180+ const panels = this . _interactivePanels ;
181+ const idx = panels . indexOf ( panel ) ;
129182
130- private getPanelHeader ( panel : IgcExpansionPanelComponent ) {
131- return panel . renderRoot . querySelector ( 'div[part="header"]' ) as HTMLElement ;
183+ return panels [ idx + dir ] || panel ;
132184 }
133185
134- private async closePanel ( panel : IgcExpansionPanelComponent ) {
135- if (
136- ! (
137- panel . open &&
138- panel . emitEvent ( 'igcClosing' , { cancelable : true , detail : panel } )
139- )
140- ) {
186+ private async _closePanel ( p : IgcExpansionPanelComponent ) : Promise < void > {
187+ const args = { detail : p } ;
188+
189+ if ( ! ( p . open && p . emitEvent ( 'igcClosing' , { cancelable : true , ...args } ) ) ) {
141190 return ;
142191 }
143192
144- await panel . hide ( ) ;
145- panel . emitEvent ( 'igcClosed' , { detail : panel } ) ;
193+ if ( await p . hide ( ) ) {
194+ p . emitEvent ( 'igcClosed' , args ) ;
195+ }
146196 }
147197
148- private async openPanel ( panel : IgcExpansionPanelComponent ) {
149- if (
150- panel . open ||
151- ! panel . emitEvent ( 'igcOpening' , { cancelable : true , detail : panel } )
152- ) {
198+ private async _openPanel ( p : IgcExpansionPanelComponent ) : Promise < void > {
199+ const args = { detail : p } ;
200+
201+ if ( p . open || ! p . emitEvent ( 'igcOpening' , { cancelable : true , ...args } ) ) {
153202 return ;
154203 }
155204
156- await panel . show ( ) ;
157- panel . emitEvent ( 'igcOpened' , { detail : panel } ) ;
205+ if ( await p . show ( ) ) {
206+ p . emitEvent ( 'igcOpened' , args ) ;
207+ }
158208 }
159209
210+ //#endregion
211+
212+ //#region Public API
213+
160214 /** Hides all of the child expansion panels' contents. */
161215 public async hideAll ( ) : Promise < void > {
162216 await Promise . all ( this . panels . map ( ( panel ) => panel . hide ( ) ) ) ;
@@ -167,6 +221,8 @@ export default class IgcAccordionComponent extends LitElement {
167221 await Promise . all ( this . panels . map ( ( panel ) => panel . show ( ) ) ) ;
168222 }
169223
224+ //#endregion
225+
170226 protected override render ( ) {
171227 return html `< slot > </ slot > ` ;
172228 }
0 commit comments