@gnireeg/accordion 0.1.0 → 0.1.1
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.
- package/dist/index.d.ts +0 -142
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +269 -264
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,143 +1 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AccordionItem component - Expandable/collapsible content container with smooth animations
|
|
3
|
-
*
|
|
4
|
-
* @element accordion-item
|
|
5
|
-
*
|
|
6
|
-
* @attr {boolean} open - When present, the accordion starts in an expanded state
|
|
7
|
-
* @attr {string} animation-time - Animation duration in milliseconds (default: "300")
|
|
8
|
-
* @attr {string} animation-easing - CSS easing function (default: "ease")
|
|
9
|
-
*
|
|
10
|
-
* @slot trigger - The clickable element that toggles the accordion (typically a button)
|
|
11
|
-
* @slot - Default slot for the accordion content
|
|
12
|
-
*
|
|
13
|
-
* @fires accordion-opened - Dispatched when the accordion opens (detail: { open: true })
|
|
14
|
-
* @fires accordion-closed - Dispatched when the accordion closes (detail: { open: false })
|
|
15
|
-
*
|
|
16
|
-
* @cssprop [--animation-time] - Can be overridden via CSS custom properties
|
|
17
|
-
* @cssprop [--animation-easing] - Can be overridden via CSS custom properties
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```html
|
|
21
|
-
* <accordion-item>
|
|
22
|
-
* <button slot="trigger">Click to expand</button>
|
|
23
|
-
* <div>Your content here</div>
|
|
24
|
-
* </accordion-item>
|
|
25
|
-
*
|
|
26
|
-
* <!-- Start expanded with custom animation -->
|
|
27
|
-
* <accordion-item open animation-time="500" animation-easing="ease-in-out">
|
|
28
|
-
* <button slot="trigger">Already open</button>
|
|
29
|
-
* <div>This content is visible by default</div>
|
|
30
|
-
* </accordion-item>
|
|
31
|
-
* ```
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```javascript
|
|
35
|
-
* // Programmatic control
|
|
36
|
-
* const accordion = document.querySelector('accordion-item');
|
|
37
|
-
* accordion.show(); // Open
|
|
38
|
-
* accordion.close(); // Close
|
|
39
|
-
* accordion.toggle(); // Toggle state
|
|
40
|
-
*
|
|
41
|
-
* // Listen to events
|
|
42
|
-
* accordion.addEventListener('accordion-opened', (e) => {
|
|
43
|
-
* console.log('Opened!', e.detail);
|
|
44
|
-
* });
|
|
45
|
-
* ```
|
|
46
|
-
*
|
|
47
|
-
* @note Automatically adds ARIA attributes (aria-expanded) for accessibility
|
|
48
|
-
* @note Supports keyboard navigation (Enter/Space) for non-button triggers
|
|
49
|
-
*/
|
|
50
|
-
export declare class AccordionItem extends HTMLElement {
|
|
51
|
-
private shadow;
|
|
52
|
-
private trigger;
|
|
53
|
-
private triggerElement;
|
|
54
|
-
open: boolean;
|
|
55
|
-
private animationTime;
|
|
56
|
-
private easing;
|
|
57
|
-
constructor();
|
|
58
|
-
static get observedAttributes(): string[];
|
|
59
|
-
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
60
|
-
connectedCallback(): void;
|
|
61
|
-
disconnectedCallback(): void;
|
|
62
|
-
private setupTriggerAccessibility;
|
|
63
|
-
private handleTriggerClick;
|
|
64
|
-
private handleKeydown;
|
|
65
|
-
/**
|
|
66
|
-
* Toggles the accordion between open and closed states
|
|
67
|
-
* @public
|
|
68
|
-
*/
|
|
69
|
-
toggle: () => void;
|
|
70
|
-
/**
|
|
71
|
-
* Opens the accordion
|
|
72
|
-
* @public
|
|
73
|
-
* @fires accordion-opened
|
|
74
|
-
*/
|
|
75
|
-
show: () => void;
|
|
76
|
-
/**
|
|
77
|
-
* Closes the accordion
|
|
78
|
-
* @public
|
|
79
|
-
* @fires accordion-closed
|
|
80
|
-
*/
|
|
81
|
-
close: () => void;
|
|
82
|
-
private reflectState;
|
|
83
|
-
private dispatchStateEvent;
|
|
84
|
-
private updateTriggerAccessibility;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* AccordionGroup component - Container for managing multiple accordion items with mutual exclusion
|
|
88
|
-
*
|
|
89
|
-
* @element accordion-group
|
|
90
|
-
*
|
|
91
|
-
* @attr {boolean} allow-multiple-open - When present, allows multiple accordions to be open simultaneously. By default, opening one accordion closes all others in the group.
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* ```html
|
|
95
|
-
* <!-- Only one accordion can be open at a time -->
|
|
96
|
-
* <accordion-group>
|
|
97
|
-
* <accordion-item open>
|
|
98
|
-
* <button slot="trigger">First Item</button>
|
|
99
|
-
* <div>Opening another will close this</div>
|
|
100
|
-
* </accordion-item>
|
|
101
|
-
*
|
|
102
|
-
* <accordion-item>
|
|
103
|
-
* <button slot="trigger">Second Item</button>
|
|
104
|
-
* <div>Only one can be open at a time</div>
|
|
105
|
-
* </accordion-item>
|
|
106
|
-
*
|
|
107
|
-
* <accordion-item>
|
|
108
|
-
* <button slot="trigger">Third Item</button>
|
|
109
|
-
* <div>Same behavior here</div>
|
|
110
|
-
* </accordion-item>
|
|
111
|
-
* </accordion-group>
|
|
112
|
-
* ```
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* ```html
|
|
116
|
-
* <!-- Allow multiple accordions to be open -->
|
|
117
|
-
* <accordion-group allow-multiple-open>
|
|
118
|
-
* <accordion-item>
|
|
119
|
-
* <button slot="trigger">First Item</button>
|
|
120
|
-
* <div>Can be open with others</div>
|
|
121
|
-
* </accordion-item>
|
|
122
|
-
*
|
|
123
|
-
* <accordion-item>
|
|
124
|
-
* <button slot="trigger">Second Item</button>
|
|
125
|
-
* <div>Multiple can be open</div>
|
|
126
|
-
* </accordion-item>
|
|
127
|
-
* </accordion-group>
|
|
128
|
-
* ```
|
|
129
|
-
*
|
|
130
|
-
* @note Each accordion-group works independently. Multiple groups on the same page don't affect each other.
|
|
131
|
-
* @note The group listens to 'accordion-opened' events from child accordion-item elements
|
|
132
|
-
*/
|
|
133
|
-
export declare class AccordionGroup extends HTMLElement {
|
|
134
|
-
private allowMultiple;
|
|
135
|
-
private static stylesApplied;
|
|
136
|
-
constructor();
|
|
137
|
-
static get observedAttributes(): string[];
|
|
138
|
-
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
139
|
-
connectedCallback(): void;
|
|
140
|
-
disconnectedCallback(): void;
|
|
141
|
-
private handleAccordionOpened;
|
|
142
|
-
}
|
|
143
1
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -1,139 +1,142 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return;
|
|
59
|
-
this.triggerElement = this.trigger.assignedElements()[0];
|
|
60
|
-
if (!this.triggerElement)
|
|
61
|
-
return;
|
|
62
|
-
// If the slotted element is not a button, add keyboard support
|
|
63
|
-
if (this.triggerElement.tagName !== 'BUTTON' && !this.triggerElement.hasAttribute('role')) {
|
|
64
|
-
this.triggerElement.setAttribute('role', 'button');
|
|
65
|
-
this.triggerElement.setAttribute('tabindex', '0');
|
|
66
|
-
// Add keyboard listener
|
|
67
|
-
this.triggerElement.addEventListener('keydown', this.handleKeydown);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
this.handleTriggerClick = () => {
|
|
71
|
-
this.toggle();
|
|
72
|
-
};
|
|
73
|
-
this.handleKeydown = (e) => {
|
|
74
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
75
|
-
e.preventDefault();
|
|
76
|
-
this.toggle();
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
/**
|
|
80
|
-
* Toggles the accordion between open and closed states
|
|
81
|
-
* @public
|
|
82
|
-
*/
|
|
83
|
-
this.toggle = () => {
|
|
84
|
-
if (this.open) {
|
|
85
|
-
this.close();
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
this.show();
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* Opens the accordion
|
|
93
|
-
* @public
|
|
94
|
-
* @fires accordion-opened
|
|
95
|
-
*/
|
|
96
|
-
this.show = () => {
|
|
97
|
-
this.open = true;
|
|
98
|
-
this.reflectState();
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Closes the accordion
|
|
102
|
-
* @public
|
|
103
|
-
* @fires accordion-closed
|
|
104
|
-
*/
|
|
105
|
-
this.close = () => {
|
|
1
|
+
"use strict";
|
|
2
|
+
// SSR-safe: Only define and register components in browser environment
|
|
3
|
+
if (typeof window !== 'undefined' && typeof HTMLElement !== 'undefined') {
|
|
4
|
+
/**
|
|
5
|
+
* AccordionItem component - Expandable/collapsible content container with smooth animations
|
|
6
|
+
*
|
|
7
|
+
* @element accordion-item
|
|
8
|
+
*
|
|
9
|
+
* @attr {boolean} open - When present, the accordion starts in an expanded state
|
|
10
|
+
* @attr {string} animation-time - Animation duration in milliseconds (default: "300")
|
|
11
|
+
* @attr {string} animation-easing - CSS easing function (default: "ease")
|
|
12
|
+
*
|
|
13
|
+
* @slot trigger - The clickable element that toggles the accordion (typically a button)
|
|
14
|
+
* @slot - Default slot for the accordion content
|
|
15
|
+
*
|
|
16
|
+
* @fires accordion-opened - Dispatched when the accordion opens (detail: { open: true })
|
|
17
|
+
* @fires accordion-closed - Dispatched when the accordion closes (detail: { open: false })
|
|
18
|
+
*
|
|
19
|
+
* @cssprop [--animation-time] - Can be overridden via CSS custom properties
|
|
20
|
+
* @cssprop [--animation-easing] - Can be overridden via CSS custom properties
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```html
|
|
24
|
+
* <accordion-item>
|
|
25
|
+
* <button slot="trigger">Click to expand</button>
|
|
26
|
+
* <div>Your content here</div>
|
|
27
|
+
* </accordion-item>
|
|
28
|
+
*
|
|
29
|
+
* <!-- Start expanded with custom animation -->
|
|
30
|
+
* <accordion-item open animation-time="500" animation-easing="ease-in-out">
|
|
31
|
+
* <button slot="trigger">Already open</button>
|
|
32
|
+
* <div>This content is visible by default</div>
|
|
33
|
+
* </accordion-item>
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```javascript
|
|
38
|
+
* // Programmatic control
|
|
39
|
+
* const accordion = document.querySelector('accordion-item');
|
|
40
|
+
* accordion.show(); // Open
|
|
41
|
+
* accordion.close(); // Close
|
|
42
|
+
* accordion.toggle(); // Toggle state
|
|
43
|
+
*
|
|
44
|
+
* // Listen to events
|
|
45
|
+
* accordion.addEventListener('accordion-opened', (e) => {
|
|
46
|
+
* console.log('Opened!', e.detail);
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @note Automatically adds ARIA attributes (aria-expanded) for accessibility
|
|
51
|
+
* @note Supports keyboard navigation (Enter/Space) for non-button triggers
|
|
52
|
+
*/
|
|
53
|
+
class AccordionItem extends HTMLElement {
|
|
54
|
+
constructor() {
|
|
55
|
+
super();
|
|
56
|
+
this.trigger = null;
|
|
57
|
+
this.triggerElement = null;
|
|
106
58
|
this.open = false;
|
|
107
|
-
this.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
59
|
+
this.setupTriggerAccessibility = () => {
|
|
60
|
+
if (!this.trigger)
|
|
61
|
+
return;
|
|
62
|
+
this.triggerElement = this.trigger.assignedElements()[0];
|
|
63
|
+
if (!this.triggerElement)
|
|
64
|
+
return;
|
|
65
|
+
// If the slotted element is not a button, add keyboard support
|
|
66
|
+
if (this.triggerElement.tagName !== 'BUTTON' && !this.triggerElement.hasAttribute('role')) {
|
|
67
|
+
this.triggerElement.setAttribute('role', 'button');
|
|
68
|
+
this.triggerElement.setAttribute('tabindex', '0');
|
|
69
|
+
// Add keyboard listener
|
|
70
|
+
this.triggerElement.addEventListener('keydown', this.handleKeydown);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
this.handleTriggerClick = () => {
|
|
74
|
+
this.toggle();
|
|
75
|
+
};
|
|
76
|
+
this.handleKeydown = (e) => {
|
|
77
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
this.toggle();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Toggles the accordion between open and closed states
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
this.toggle = () => {
|
|
87
|
+
if (this.open) {
|
|
88
|
+
this.close();
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.show();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Opens the accordion
|
|
96
|
+
* @public
|
|
97
|
+
* @fires accordion-opened
|
|
98
|
+
*/
|
|
99
|
+
this.show = () => {
|
|
100
|
+
this.open = true;
|
|
101
|
+
this.reflectState();
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Closes the accordion
|
|
105
|
+
* @public
|
|
106
|
+
* @fires accordion-closed
|
|
107
|
+
*/
|
|
108
|
+
this.close = () => {
|
|
109
|
+
this.open = false;
|
|
110
|
+
this.reflectState();
|
|
111
|
+
};
|
|
112
|
+
this.reflectState = () => {
|
|
113
|
+
// Update attribute to match property
|
|
114
|
+
this.open ? this.setAttribute('open', '') : this.removeAttribute('open');
|
|
115
|
+
this.updateTriggerAccessibility();
|
|
116
|
+
this.dispatchStateEvent();
|
|
117
|
+
};
|
|
118
|
+
this.dispatchStateEvent = () => {
|
|
119
|
+
// Dispatch custom events
|
|
120
|
+
this.dispatchEvent(new CustomEvent(this.open ? 'accordion-opened' : 'accordion-closed', {
|
|
121
|
+
bubbles: true,
|
|
122
|
+
composed: true,
|
|
123
|
+
detail: { open: this.open }
|
|
124
|
+
}));
|
|
125
|
+
};
|
|
126
|
+
this.updateTriggerAccessibility = () => {
|
|
127
|
+
if (!this.trigger)
|
|
128
|
+
return;
|
|
129
|
+
const triggerElement = this.trigger.assignedElements()[0];
|
|
130
|
+
if (triggerElement) {
|
|
131
|
+
triggerElement.setAttribute('aria-expanded', String(this.open));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
// Initialize state from attribute
|
|
135
|
+
this.open = this.hasAttribute('open');
|
|
136
|
+
this.animationTime = this.getAttribute('animation-time') || '300';
|
|
137
|
+
this.easing = this.getAttribute('animation-easing') || 'ease';
|
|
138
|
+
this.shadow = this.attachShadow({ mode: 'open' });
|
|
139
|
+
this.shadow.innerHTML = /*HTML*/ `
|
|
137
140
|
<style>
|
|
138
141
|
:host{
|
|
139
142
|
display: block;
|
|
@@ -161,144 +164,146 @@ export class AccordionItem extends HTMLElement {
|
|
|
161
164
|
</div>
|
|
162
165
|
</div>
|
|
163
166
|
`;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
}
|
|
168
|
+
static get observedAttributes() {
|
|
169
|
+
return ['open'];
|
|
170
|
+
}
|
|
171
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
172
|
+
if (oldValue === newValue)
|
|
173
|
+
return;
|
|
174
|
+
switch (name) {
|
|
175
|
+
case 'open':
|
|
176
|
+
const shouldBeOpen = newValue !== null;
|
|
177
|
+
// Only update if state actually changed to prevent infinite loop
|
|
178
|
+
if (this.open !== shouldBeOpen) {
|
|
179
|
+
this.open = shouldBeOpen;
|
|
180
|
+
this.updateTriggerAccessibility();
|
|
181
|
+
this.dispatchStateEvent();
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
default:
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
connectedCallback() {
|
|
189
|
+
this.trigger = this.shadow.querySelector('slot[name="trigger"]');
|
|
190
|
+
if (this.trigger) {
|
|
191
|
+
this.trigger.addEventListener('click', this.handleTriggerClick);
|
|
192
|
+
// Setup accessibility for trigger element
|
|
193
|
+
requestAnimationFrame(() => {
|
|
194
|
+
this.setupTriggerAccessibility();
|
|
177
195
|
this.updateTriggerAccessibility();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
break;
|
|
181
|
-
default:
|
|
182
|
-
break;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
183
198
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.updateTriggerAccessibility();
|
|
193
|
-
});
|
|
199
|
+
disconnectedCallback() {
|
|
200
|
+
if (this.trigger) {
|
|
201
|
+
this.trigger.removeEventListener('click', this.handleTriggerClick);
|
|
202
|
+
}
|
|
203
|
+
// Cleanup keyboard listener
|
|
204
|
+
if (this.triggerElement) {
|
|
205
|
+
this.triggerElement.removeEventListener('keydown', this.handleKeydown);
|
|
206
|
+
}
|
|
194
207
|
}
|
|
195
208
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
209
|
+
/**
|
|
210
|
+
* AccordionGroup component - Container for managing multiple accordion items with mutual exclusion
|
|
211
|
+
*
|
|
212
|
+
* @element accordion-group
|
|
213
|
+
*
|
|
214
|
+
* @attr {boolean} allow-multiple-open - When present, allows multiple accordions to be open simultaneously. By default, opening one accordion closes all others in the group.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```html
|
|
218
|
+
* <!-- Only one accordion can be open at a time -->
|
|
219
|
+
* <accordion-group>
|
|
220
|
+
* <accordion-item open>
|
|
221
|
+
* <button slot="trigger">First Item</button>
|
|
222
|
+
* <div>Opening another will close this</div>
|
|
223
|
+
* </accordion-item>
|
|
224
|
+
*
|
|
225
|
+
* <accordion-item>
|
|
226
|
+
* <button slot="trigger">Second Item</button>
|
|
227
|
+
* <div>Only one can be open at a time</div>
|
|
228
|
+
* </accordion-item>
|
|
229
|
+
*
|
|
230
|
+
* <accordion-item>
|
|
231
|
+
* <button slot="trigger">Third Item</button>
|
|
232
|
+
* <div>Same behavior here</div>
|
|
233
|
+
* </accordion-item>
|
|
234
|
+
* </accordion-group>
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```html
|
|
239
|
+
* <!-- Allow multiple accordions to be open -->
|
|
240
|
+
* <accordion-group allow-multiple-open>
|
|
241
|
+
* <accordion-item>
|
|
242
|
+
* <button slot="trigger">First Item</button>
|
|
243
|
+
* <div>Can be open with others</div>
|
|
244
|
+
* </accordion-item>
|
|
245
|
+
*
|
|
246
|
+
* <accordion-item>
|
|
247
|
+
* <button slot="trigger">Second Item</button>
|
|
248
|
+
* <div>Multiple can be open</div>
|
|
249
|
+
* </accordion-item>
|
|
250
|
+
* </accordion-group>
|
|
251
|
+
* ```
|
|
252
|
+
*
|
|
253
|
+
* @note Each accordion-group works independently. Multiple groups on the same page don't affect each other.
|
|
254
|
+
* @note The group listens to 'accordion-opened' events from child accordion-item elements
|
|
255
|
+
*/
|
|
256
|
+
class AccordionGroup extends HTMLElement {
|
|
257
|
+
constructor() {
|
|
258
|
+
super();
|
|
259
|
+
this.handleAccordionOpened = (e) => {
|
|
260
|
+
if (this.allowMultiple)
|
|
261
|
+
return;
|
|
262
|
+
const childAccordions = this.querySelectorAll('accordion-item');
|
|
263
|
+
childAccordions.forEach(acc => {
|
|
264
|
+
const accordion = acc;
|
|
265
|
+
if (e.target !== accordion) {
|
|
266
|
+
if (accordion.open)
|
|
267
|
+
accordion.close();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
if (!AccordionGroup.stylesApplied) {
|
|
272
|
+
const sheet = new CSSStyleSheet();
|
|
273
|
+
sheet.replaceSync('accordion-group { display: block; }');
|
|
274
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
275
|
+
AccordionGroup.stylesApplied = true;
|
|
276
|
+
}
|
|
277
|
+
this.allowMultiple = this.hasAttribute('allow-multiple-open');
|
|
199
278
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
this.triggerElement.removeEventListener('keydown', this.handleKeydown);
|
|
279
|
+
static get observedAttributes() {
|
|
280
|
+
return ['allow-multiple-open'];
|
|
203
281
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* AccordionGroup component - Container for managing multiple accordion items with mutual exclusion
|
|
208
|
-
*
|
|
209
|
-
* @element accordion-group
|
|
210
|
-
*
|
|
211
|
-
* @attr {boolean} allow-multiple-open - When present, allows multiple accordions to be open simultaneously. By default, opening one accordion closes all others in the group.
|
|
212
|
-
*
|
|
213
|
-
* @example
|
|
214
|
-
* ```html
|
|
215
|
-
* <!-- Only one accordion can be open at a time -->
|
|
216
|
-
* <accordion-group>
|
|
217
|
-
* <accordion-item open>
|
|
218
|
-
* <button slot="trigger">First Item</button>
|
|
219
|
-
* <div>Opening another will close this</div>
|
|
220
|
-
* </accordion-item>
|
|
221
|
-
*
|
|
222
|
-
* <accordion-item>
|
|
223
|
-
* <button slot="trigger">Second Item</button>
|
|
224
|
-
* <div>Only one can be open at a time</div>
|
|
225
|
-
* </accordion-item>
|
|
226
|
-
*
|
|
227
|
-
* <accordion-item>
|
|
228
|
-
* <button slot="trigger">Third Item</button>
|
|
229
|
-
* <div>Same behavior here</div>
|
|
230
|
-
* </accordion-item>
|
|
231
|
-
* </accordion-group>
|
|
232
|
-
* ```
|
|
233
|
-
*
|
|
234
|
-
* @example
|
|
235
|
-
* ```html
|
|
236
|
-
* <!-- Allow multiple accordions to be open -->
|
|
237
|
-
* <accordion-group allow-multiple-open>
|
|
238
|
-
* <accordion-item>
|
|
239
|
-
* <button slot="trigger">First Item</button>
|
|
240
|
-
* <div>Can be open with others</div>
|
|
241
|
-
* </accordion-item>
|
|
242
|
-
*
|
|
243
|
-
* <accordion-item>
|
|
244
|
-
* <button slot="trigger">Second Item</button>
|
|
245
|
-
* <div>Multiple can be open</div>
|
|
246
|
-
* </accordion-item>
|
|
247
|
-
* </accordion-group>
|
|
248
|
-
* ```
|
|
249
|
-
*
|
|
250
|
-
* @note Each accordion-group works independently. Multiple groups on the same page don't affect each other.
|
|
251
|
-
* @note The group listens to 'accordion-opened' events from child accordion-item elements
|
|
252
|
-
*/
|
|
253
|
-
export class AccordionGroup extends HTMLElement {
|
|
254
|
-
constructor() {
|
|
255
|
-
super();
|
|
256
|
-
this.handleAccordionOpened = (e) => {
|
|
257
|
-
if (this.allowMultiple)
|
|
282
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
283
|
+
if (oldValue === newValue)
|
|
258
284
|
return;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
sheet.replaceSync('accordion-group { display: block; }');
|
|
271
|
-
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
272
|
-
AccordionGroup.stylesApplied = true;
|
|
285
|
+
switch (name) {
|
|
286
|
+
case 'allow-multiple-open':
|
|
287
|
+
const shouldBeAllowed = newValue !== null;
|
|
288
|
+
// Only update if state actually changed to prevent infinite loop
|
|
289
|
+
if (this.allowMultiple !== shouldBeAllowed) {
|
|
290
|
+
this.allowMultiple = shouldBeAllowed;
|
|
291
|
+
}
|
|
292
|
+
break;
|
|
293
|
+
default:
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
273
296
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
280
|
-
if (oldValue === newValue)
|
|
281
|
-
return;
|
|
282
|
-
switch (name) {
|
|
283
|
-
case 'allow-multiple-open':
|
|
284
|
-
const shouldBeAllowed = newValue !== null;
|
|
285
|
-
// Only update if state actually changed to prevent infinite loop
|
|
286
|
-
if (this.allowMultiple !== shouldBeAllowed) {
|
|
287
|
-
this.allowMultiple = shouldBeAllowed;
|
|
288
|
-
}
|
|
289
|
-
break;
|
|
290
|
-
default:
|
|
291
|
-
break;
|
|
297
|
+
connectedCallback() {
|
|
298
|
+
this.addEventListener('accordion-opened', this.handleAccordionOpened);
|
|
299
|
+
}
|
|
300
|
+
disconnectedCallback() {
|
|
301
|
+
this.removeEventListener('accordion-opened', this.handleAccordionOpened);
|
|
292
302
|
}
|
|
293
303
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
this.removeEventListener('accordion-opened', this.handleAccordionOpened);
|
|
299
|
-
}
|
|
304
|
+
AccordionGroup.stylesApplied = false;
|
|
305
|
+
// Register custom elements
|
|
306
|
+
customElements.define('accordion-item', AccordionItem);
|
|
307
|
+
customElements.define('accordion-group', AccordionGroup);
|
|
300
308
|
}
|
|
301
|
-
AccordionGroup.stylesApplied = false;
|
|
302
|
-
customElements.define('accordion-item', AccordionItem);
|
|
303
|
-
customElements.define('accordion-group', AccordionGroup);
|
|
304
309
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gnireeg/accordion",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Accessible accordion web component with smooth animations, keyboard support, and nested accordion groups",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"node": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
14
15
|
"files": [
|