@magic-spells/collapsible-content 0.2.0 → 1.1.0
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/README.md +110 -62
- package/dist/collapsible-content.cjs.js +183 -64
- package/dist/collapsible-content.cjs.js.map +1 -1
- package/dist/collapsible-content.css +24 -27
- package/dist/collapsible-content.esm.js +183 -64
- package/dist/collapsible-content.esm.js.map +1 -1
- package/dist/collapsible-content.js +183 -64
- package/dist/collapsible-content.js.map +1 -1
- package/dist/collapsible-content.min.css +1 -1
- package/dist/collapsible-content.min.js +1 -1
- package/package.json +7 -11
- package/src/collapsible-content.css +31 -0
- package/src/collapsible-content.js +184 -65
- package/dist/collapsible-content.scss +0 -2
- package/dist/scss/collapsible-content.scss +0 -25
- package/dist/scss/variables.scss +0 -40
- package/src/index.scss +0 -2
- package/src/scss/collapsible-content.scss +0 -25
- package/src/scss/variables.scss +0 -40
|
@@ -4,11 +4,37 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.CollapsibleContent = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
+
const DEFAULT_SPEED = 900; // px per second
|
|
8
|
+
const MIN_DURATION = 0.25; // seconds
|
|
9
|
+
const MAX_DURATION = 1.0; // seconds
|
|
10
|
+
|
|
7
11
|
/**
|
|
8
12
|
* Custom element that creates a collapsible/expandable component with proper accessibility
|
|
9
13
|
*/
|
|
10
14
|
class CollapsibleComponent extends HTMLElement {
|
|
15
|
+
static #groups = new Map();
|
|
16
|
+
|
|
17
|
+
static #addToGroup(name, instance) {
|
|
18
|
+
if (!CollapsibleComponent.#groups.has(name)) {
|
|
19
|
+
CollapsibleComponent.#groups.set(name, new Set());
|
|
20
|
+
}
|
|
21
|
+
CollapsibleComponent.#groups.get(name).add(instance);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static #removeFromGroup(name, instance) {
|
|
25
|
+
const group = CollapsibleComponent.#groups.get(name);
|
|
26
|
+
if (group) {
|
|
27
|
+
group.delete(instance);
|
|
28
|
+
if (group.size === 0) CollapsibleComponent.#groups.delete(name);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static get observedAttributes() {
|
|
33
|
+
return ['group'];
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
#handleClick;
|
|
37
|
+
#abortController;
|
|
12
38
|
|
|
13
39
|
/**
|
|
14
40
|
* Initializes the component and sets up references to child elements
|
|
@@ -21,13 +47,12 @@
|
|
|
21
47
|
_.button = null;
|
|
22
48
|
_.content = null;
|
|
23
49
|
|
|
24
|
-
// define click handler
|
|
25
|
-
_.#handleClick = (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
_
|
|
30
|
-
_.content.hidden = !expanded;
|
|
50
|
+
// define click handler — content is source of truth
|
|
51
|
+
_.#handleClick = () => {
|
|
52
|
+
const wasCollapsed = _.content.collapsed;
|
|
53
|
+
_.content.collapsed = !wasCollapsed;
|
|
54
|
+
_.button.setAttribute('aria-expanded', !_.content.collapsed);
|
|
55
|
+
if (wasCollapsed) _.#closeSiblings();
|
|
31
56
|
};
|
|
32
57
|
}
|
|
33
58
|
|
|
@@ -43,7 +68,16 @@
|
|
|
43
68
|
_.content = _.querySelector('collapsible-content');
|
|
44
69
|
|
|
45
70
|
if (!_.button || !_.content) {
|
|
46
|
-
|
|
71
|
+
const error = new Error(
|
|
72
|
+
'CollapsibleComponent requires a <button> and a <collapsible-content>.'
|
|
73
|
+
);
|
|
74
|
+
console.error(error.message);
|
|
75
|
+
_.dispatchEvent(
|
|
76
|
+
new CustomEvent('collapsible-error', {
|
|
77
|
+
bubbles: true,
|
|
78
|
+
detail: { error },
|
|
79
|
+
})
|
|
80
|
+
);
|
|
47
81
|
return;
|
|
48
82
|
}
|
|
49
83
|
|
|
@@ -52,18 +86,38 @@
|
|
|
52
86
|
_.content.id ||= `collapsible-content-${crypto.randomUUID().slice(0, 8)}`;
|
|
53
87
|
|
|
54
88
|
// set accessibility attributes
|
|
89
|
+
if (!_.button.hasAttribute('type')) {
|
|
90
|
+
_.button.type = 'button';
|
|
91
|
+
}
|
|
55
92
|
_.button.setAttribute('aria-controls', _.content.id);
|
|
56
|
-
_.content.setAttribute('role', 'region');
|
|
57
93
|
_.content.setAttribute('aria-labelledby', _.button.id);
|
|
94
|
+
if (!_.content.hasAttribute('role')) {
|
|
95
|
+
_.content.setAttribute('role', 'region');
|
|
96
|
+
}
|
|
58
97
|
|
|
59
|
-
// set initial state
|
|
60
|
-
const
|
|
98
|
+
// set initial state without triggering an opening/closing animation
|
|
99
|
+
const open = _.content.hasAttribute('open');
|
|
100
|
+
_.button.setAttribute('aria-expanded', open);
|
|
101
|
+
_.content.style.height = open ? 'auto' : '0px';
|
|
102
|
+
if (open) {
|
|
103
|
+
_.content.removeAttribute('aria-hidden');
|
|
104
|
+
_.content.removeAttribute('inert');
|
|
105
|
+
} else {
|
|
106
|
+
_.content.setAttribute('aria-hidden', 'true');
|
|
107
|
+
_.content.setAttribute('inert', '');
|
|
108
|
+
}
|
|
61
109
|
|
|
62
|
-
//
|
|
63
|
-
_
|
|
110
|
+
// use AbortController to prevent duplicate listeners on reconnection
|
|
111
|
+
_.#abortController = new AbortController();
|
|
112
|
+
_.button.addEventListener('click', _.#handleClick, {
|
|
113
|
+
signal: _.#abortController.signal,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
64
116
|
|
|
65
|
-
|
|
66
|
-
|
|
117
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
118
|
+
if (name !== 'group') return;
|
|
119
|
+
if (oldValue) CollapsibleComponent.#removeFromGroup(oldValue, this);
|
|
120
|
+
if (newValue) CollapsibleComponent.#addToGroup(newValue, this);
|
|
67
121
|
}
|
|
68
122
|
|
|
69
123
|
/**
|
|
@@ -72,8 +126,26 @@
|
|
|
72
126
|
*/
|
|
73
127
|
disconnectedCallback() {
|
|
74
128
|
const _ = this;
|
|
75
|
-
|
|
76
|
-
|
|
129
|
+
const group = _.getAttribute('group');
|
|
130
|
+
if (group) CollapsibleComponent.#removeFromGroup(group, _);
|
|
131
|
+
if (_.#abortController) {
|
|
132
|
+
_.#abortController.abort();
|
|
133
|
+
_.#abortController = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
#closeSiblings() {
|
|
138
|
+
const _ = this;
|
|
139
|
+
const groupName = _.getAttribute('group');
|
|
140
|
+
if (!groupName) return;
|
|
141
|
+
const group = CollapsibleComponent.#groups.get(groupName);
|
|
142
|
+
if (!group) return;
|
|
143
|
+
for (const sibling of group) {
|
|
144
|
+
if (sibling === _) continue;
|
|
145
|
+
if (!sibling.content.collapsed) {
|
|
146
|
+
sibling.content.collapsed = true;
|
|
147
|
+
sibling.button.setAttribute('aria-expanded', 'false');
|
|
148
|
+
}
|
|
77
149
|
}
|
|
78
150
|
}
|
|
79
151
|
}
|
|
@@ -83,6 +155,8 @@
|
|
|
83
155
|
*/
|
|
84
156
|
class CollapsibleContent extends HTMLElement {
|
|
85
157
|
#handleTransitionEnd;
|
|
158
|
+
#abortController;
|
|
159
|
+
#animating = false;
|
|
86
160
|
|
|
87
161
|
/**
|
|
88
162
|
* Initializes the content element and binds event handlers
|
|
@@ -93,36 +167,32 @@
|
|
|
93
167
|
|
|
94
168
|
// define event handler using arrow function for proper binding
|
|
95
169
|
_.#handleTransitionEnd = (event) => {
|
|
96
|
-
|
|
97
|
-
if (event.propertyName
|
|
170
|
+
if (event.target !== _) return;
|
|
171
|
+
if (event.propertyName !== 'height') return;
|
|
172
|
+
|
|
173
|
+
_.#animating = false;
|
|
174
|
+
_.style.removeProperty('--collapsible-duration');
|
|
98
175
|
|
|
99
176
|
// remove the inline height to allow dynamic content changes
|
|
100
|
-
if (!_.
|
|
177
|
+
if (!_.collapsed) {
|
|
101
178
|
_.style.height = 'auto';
|
|
102
179
|
}
|
|
103
180
|
};
|
|
104
|
-
|
|
105
|
-
// add event listener to remove height after transition
|
|
106
|
-
_.addEventListener('transitionend', _.#handleTransitionEnd);
|
|
107
181
|
}
|
|
108
182
|
|
|
109
183
|
/**
|
|
110
184
|
* Called when element is added to the DOM
|
|
111
|
-
* Sets initial height based on
|
|
185
|
+
* Sets initial height based on open attribute
|
|
112
186
|
*/
|
|
113
187
|
connectedCallback() {
|
|
114
188
|
const _ = this;
|
|
189
|
+
_.style.height = _.hasAttribute('open') ? 'auto' : '0';
|
|
115
190
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (expanded) {
|
|
122
|
-
_.style.height = 'auto';
|
|
123
|
-
} else {
|
|
124
|
-
_.style.height = '0';
|
|
125
|
-
}
|
|
191
|
+
// use AbortController to prevent duplicate listeners on reconnection
|
|
192
|
+
_.#abortController = new AbortController();
|
|
193
|
+
_.addEventListener('transitionend', _.#handleTransitionEnd, {
|
|
194
|
+
signal: _.#abortController.signal,
|
|
195
|
+
});
|
|
126
196
|
}
|
|
127
197
|
|
|
128
198
|
/**
|
|
@@ -131,57 +201,106 @@
|
|
|
131
201
|
*/
|
|
132
202
|
disconnectedCallback() {
|
|
133
203
|
const _ = this;
|
|
134
|
-
_
|
|
204
|
+
_.#animating = false;
|
|
205
|
+
if (_.#abortController) {
|
|
206
|
+
_.#abortController.abort();
|
|
207
|
+
_.#abortController = null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get #speed() {
|
|
212
|
+
const attr = this.getAttribute('speed');
|
|
213
|
+
if (attr === null) return DEFAULT_SPEED;
|
|
214
|
+
const value = Number(attr);
|
|
215
|
+
return value > 0 ? value : DEFAULT_SPEED;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
get #minDuration() {
|
|
219
|
+
const attr = this.getAttribute('min-duration');
|
|
220
|
+
if (attr === null) return MIN_DURATION;
|
|
221
|
+
const value = Number(attr);
|
|
222
|
+
return value > 0 ? value : MIN_DURATION;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
get #maxDuration() {
|
|
226
|
+
const attr = this.getAttribute('max-duration');
|
|
227
|
+
if (attr === null) return MAX_DURATION;
|
|
228
|
+
const value = Number(attr);
|
|
229
|
+
return value > 0 ? value : MAX_DURATION;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#setDynamicDuration(currentHeight, targetHeight) {
|
|
233
|
+
const _ = this;
|
|
234
|
+
const delta = Math.abs(targetHeight - currentHeight);
|
|
235
|
+
const duration = Math.min(_.#maxDuration, Math.max(_.#minDuration, delta / _.#speed));
|
|
236
|
+
_.style.setProperty('--collapsible-duration', `${duration.toFixed(3)}s`);
|
|
135
237
|
}
|
|
136
238
|
|
|
137
239
|
/**
|
|
138
|
-
* Handles setting the
|
|
139
|
-
* @param {boolean} value - Whether element should be
|
|
240
|
+
* Handles setting the collapsed state with animation
|
|
241
|
+
* @param {boolean} value - Whether element should be collapsed
|
|
140
242
|
*/
|
|
141
|
-
set
|
|
243
|
+
set collapsed(value) {
|
|
142
244
|
const _ = this;
|
|
245
|
+
const collapsed = Boolean(value);
|
|
246
|
+
if (_.collapsed === collapsed) return;
|
|
143
247
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
// reset height to animate from
|
|
147
|
-
_.style.height = `${_.scrollHeight}px`;
|
|
148
|
-
|
|
149
|
-
// wait one frame and animate to 0
|
|
150
|
-
setTimeout(() => {
|
|
151
|
-
_.style.height = '0px';
|
|
152
|
-
}, 1);
|
|
248
|
+
// check if transitions are enabled (respects prefers-reduced-motion)
|
|
249
|
+
const hasTransition = getComputedStyle(_).transitionDuration !== '0s';
|
|
153
250
|
|
|
154
|
-
|
|
251
|
+
if (collapsed) {
|
|
252
|
+
_.removeAttribute('open');
|
|
155
253
|
_.setAttribute('aria-hidden', 'true');
|
|
156
254
|
_.setAttribute('inert', '');
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// animate to open
|
|
162
|
-
_.style.height = `${_.scrollHeight}px`;
|
|
255
|
+
|
|
256
|
+
if (!hasTransition) {
|
|
257
|
+
_.style.height = '0px';
|
|
258
|
+
return;
|
|
163
259
|
}
|
|
164
260
|
|
|
165
|
-
//
|
|
261
|
+
// capture current height in px (converts auto or mid-animation value)
|
|
262
|
+
const currentHeight = _.getBoundingClientRect().height;
|
|
263
|
+
_.style.height = `${currentHeight}px`;
|
|
264
|
+
_.#setDynamicDuration(currentHeight, 0);
|
|
265
|
+
_.#animating = true;
|
|
266
|
+
_.offsetHeight; // force reflow — browser commits the px start value
|
|
267
|
+
_.style.height = '0px';
|
|
268
|
+
} else {
|
|
269
|
+
_.setAttribute('open', '');
|
|
166
270
|
_.removeAttribute('aria-hidden');
|
|
167
271
|
_.removeAttribute('inert');
|
|
168
|
-
|
|
272
|
+
|
|
273
|
+
if (!hasTransition) {
|
|
274
|
+
_.style.height = 'auto';
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// capture current height in px (0px or mid-animation value)
|
|
279
|
+
const currentHeight = _.getBoundingClientRect().height;
|
|
280
|
+
_.style.height = `${currentHeight}px`;
|
|
281
|
+
_.#setDynamicDuration(currentHeight, _.scrollHeight);
|
|
282
|
+
_.#animating = true;
|
|
283
|
+
_.offsetHeight; // force reflow — browser commits the px start value
|
|
284
|
+
_.style.height = `${_.scrollHeight}px`;
|
|
169
285
|
}
|
|
170
286
|
}
|
|
171
287
|
|
|
172
288
|
/**
|
|
173
|
-
* Gets the
|
|
174
|
-
* @returns {boolean} Whether element is
|
|
289
|
+
* Gets the collapsed state
|
|
290
|
+
* @returns {boolean} Whether element is collapsed
|
|
175
291
|
*/
|
|
176
|
-
get
|
|
177
|
-
|
|
178
|
-
return _.hasAttribute('hidden');
|
|
292
|
+
get collapsed() {
|
|
293
|
+
return !this.hasAttribute('open');
|
|
179
294
|
}
|
|
180
295
|
}
|
|
181
296
|
|
|
182
|
-
// register custom elements
|
|
183
|
-
customElements.
|
|
184
|
-
|
|
297
|
+
// register custom elements if not already defined
|
|
298
|
+
if (!customElements.get('collapsible-content')) {
|
|
299
|
+
customElements.define('collapsible-content', CollapsibleContent);
|
|
300
|
+
}
|
|
301
|
+
if (!customElements.get('collapsible-component')) {
|
|
302
|
+
customElements.define('collapsible-component', CollapsibleComponent);
|
|
303
|
+
}
|
|
185
304
|
|
|
186
305
|
exports.CollapsibleComponent = CollapsibleComponent;
|
|
187
306
|
exports.CollapsibleContent = CollapsibleContent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapsible-content.js","sources":["../src/collapsible-content.js"],"sourcesContent":["import './index.scss';\n\n/**\n * Custom element that creates a collapsible/expandable component with proper accessibility\n */\nclass CollapsibleComponent extends HTMLElement {\n\t#handleClick;\n\n\t/**\n\t * Initializes the component and sets up references to child elements\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// store references to elements once to avoid re-querying\n\t\t_.button = null;\n\t\t_.content = null;\n\n\t\t// define click handler with proper this binding\n\t\t_.#handleClick = (e) => {\n\t\t\te.preventDefault();\n\t\t\t// toggle expanded value\n\t\t\tconst expanded = _.button.getAttribute('aria-expanded') !== 'true';\n\t\t\t_.button.setAttribute('aria-expanded', expanded);\n\t\t\t_.content.hidden = !expanded;\n\t\t};\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets up accessibility attributes and event listeners\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// initialize element references once\n\t\t_.button = _.querySelector('button');\n\t\t_.content = _.querySelector('collapsible-content');\n\n\t\tif (!_.button || !_.content) {\n\t\t\tconsole.error('CollapsibleComponent requires a <button> and a <collapsible-content>.');\n\t\t\treturn;\n\t\t}\n\n\t\t// generate ids if not provided\n\t\t_.button.id ||= `collapsible-button-${crypto.randomUUID().slice(0, 8)}`;\n\t\t_.content.id ||= `collapsible-content-${crypto.randomUUID().slice(0, 8)}`;\n\n\t\t// set accessibility attributes\n\t\t_.button.setAttribute('aria-controls', _.content.id);\n\t\t_.content.setAttribute('role', 'region');\n\t\t_.content.setAttribute('aria-labelledby', _.button.id);\n\n\t\t// set initial state based on aria-expanded attribute\n\t\tconst expanded = _.button.getAttribute('aria-expanded') === 'true';\n\n\t\t// make sure content state matches button state\n\t\t_.content.hidden = !expanded;\n\n\t\t// add event listener\n\t\t_.button.addEventListener('click', _.#handleClick);\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tif (_.button) {\n\t\t\t_.button.removeEventListener('click', _.#handleClick);\n\t\t}\n\t}\n}\n\n/**\n * Custom element that provides animated collapsible content\n */\nclass CollapsibleContent extends HTMLElement {\n\t#handleTransitionEnd;\n\n\t/**\n\t * Initializes the content element and binds event handlers\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// define event handler using arrow function for proper binding\n\t\t_.#handleTransitionEnd = (event) => {\n\t\t\t// exit if isn't height property\n\t\t\tif (event.propertyName != 'height') return;\n\n\t\t\t// remove the inline height to allow dynamic content changes\n\t\t\tif (!_.hidden) {\n\t\t\t\t_.style.height = 'auto';\n\t\t\t}\n\t\t};\n\n\t\t// add event listener to remove height after transition\n\t\t_.addEventListener('transitionend', _.#handleTransitionEnd);\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets initial height based on hidden state\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// get aria-expanded state from button\n\t\tconst component = _.closest('collapsible-component');\n\t\tconst button = component.querySelector('button');\n\t\tconst expanded = button.getAttribute('aria-expanded') === 'true';\n\n\t\tif (expanded) {\n\t\t\t_.style.height = 'auto';\n\t\t} else {\n\t\t\t_.style.height = '0';\n\t\t}\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\t_.removeEventListener('transitionend', _.#handleTransitionEnd);\n\t}\n\n\t/**\n\t * Handles setting the hidden attribute with animation\n\t * @param {boolean} value - Whether element should be hidden\n\t */\n\tset hidden(value) {\n\t\tconst _ = this;\n\n\t\t// animate to closed\n\t\tif (value) {\n\t\t\t// reset height to animate from\n\t\t\t_.style.height = `${_.scrollHeight}px`;\n\n\t\t\t// wait one frame and animate to 0\n\t\t\tsetTimeout(() => {\n\t\t\t\t_.style.height = '0px';\n\t\t\t}, 1);\n\n\t\t\t// set accessibility attributes\n\t\t\t_.setAttribute('aria-hidden', 'true');\n\t\t\t_.setAttribute('inert', '');\n\t\t\t_.setAttribute('hidden', '');\n\t\t} else {\n\t\t\t// only animate if not set to auto\n\t\t\tif (_.style.height !== 'auto') {\n\t\t\t\t// animate to open\n\t\t\t\t_.style.height = `${_.scrollHeight}px`;\n\t\t\t}\n\n\t\t\t// remove accessibility attributes\n\t\t\t_.removeAttribute('aria-hidden');\n\t\t\t_.removeAttribute('inert');\n\t\t\t_.removeAttribute('hidden');\n\t\t}\n\t}\n\n\t/**\n\t * Gets the hidden state from the attribute\n\t * @returns {boolean} Whether element is hidden\n\t */\n\tget hidden() {\n\t\tconst _ = this;\n\t\treturn _.hasAttribute('hidden');\n\t}\n}\n\n// register custom elements\ncustomElements.define('collapsible-content', CollapsibleContent);\ncustomElements.define('collapsible-component', CollapsibleComponent);\n\nexport { CollapsibleContent, CollapsibleComponent };\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collapsible-content.js","sources":["../src/collapsible-content.js"],"sourcesContent":["import './collapsible-content.css';\n\nconst DEFAULT_SPEED = 900; // px per second\nconst MIN_DURATION = 0.25; // seconds\nconst MAX_DURATION = 1.0; // seconds\n\n/**\n * Custom element that creates a collapsible/expandable component with proper accessibility\n */\nclass CollapsibleComponent extends HTMLElement {\n\tstatic #groups = new Map();\n\n\tstatic #addToGroup(name, instance) {\n\t\tif (!CollapsibleComponent.#groups.has(name)) {\n\t\t\tCollapsibleComponent.#groups.set(name, new Set());\n\t\t}\n\t\tCollapsibleComponent.#groups.get(name).add(instance);\n\t}\n\n\tstatic #removeFromGroup(name, instance) {\n\t\tconst group = CollapsibleComponent.#groups.get(name);\n\t\tif (group) {\n\t\t\tgroup.delete(instance);\n\t\t\tif (group.size === 0) CollapsibleComponent.#groups.delete(name);\n\t\t}\n\t}\n\n\tstatic get observedAttributes() {\n\t\treturn ['group'];\n\t}\n\n\t#handleClick;\n\t#abortController;\n\n\t/**\n\t * Initializes the component and sets up references to child elements\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// store references to elements once to avoid re-querying\n\t\t_.button = null;\n\t\t_.content = null;\n\n\t\t// define click handler — content is source of truth\n\t\t_.#handleClick = () => {\n\t\t\tconst wasCollapsed = _.content.collapsed;\n\t\t\t_.content.collapsed = !wasCollapsed;\n\t\t\t_.button.setAttribute('aria-expanded', !_.content.collapsed);\n\t\t\tif (wasCollapsed) _.#closeSiblings();\n\t\t};\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets up accessibility attributes and event listeners\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\n\t\t// initialize element references once\n\t\t_.button = _.querySelector('button');\n\t\t_.content = _.querySelector('collapsible-content');\n\n\t\tif (!_.button || !_.content) {\n\t\t\tconst error = new Error(\n\t\t\t\t'CollapsibleComponent requires a <button> and a <collapsible-content>.'\n\t\t\t);\n\t\t\tconsole.error(error.message);\n\t\t\t_.dispatchEvent(\n\t\t\t\tnew CustomEvent('collapsible-error', {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tdetail: { error },\n\t\t\t\t})\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// generate ids if not provided\n\t\t_.button.id ||= `collapsible-button-${crypto.randomUUID().slice(0, 8)}`;\n\t\t_.content.id ||= `collapsible-content-${crypto.randomUUID().slice(0, 8)}`;\n\n\t\t// set accessibility attributes\n\t\tif (!_.button.hasAttribute('type')) {\n\t\t\t_.button.type = 'button';\n\t\t}\n\t\t_.button.setAttribute('aria-controls', _.content.id);\n\t\t_.content.setAttribute('aria-labelledby', _.button.id);\n\t\tif (!_.content.hasAttribute('role')) {\n\t\t\t_.content.setAttribute('role', 'region');\n\t\t}\n\n\t\t// set initial state without triggering an opening/closing animation\n\t\tconst open = _.content.hasAttribute('open');\n\t\t_.button.setAttribute('aria-expanded', open);\n\t\t_.content.style.height = open ? 'auto' : '0px';\n\t\tif (open) {\n\t\t\t_.content.removeAttribute('aria-hidden');\n\t\t\t_.content.removeAttribute('inert');\n\t\t} else {\n\t\t\t_.content.setAttribute('aria-hidden', 'true');\n\t\t\t_.content.setAttribute('inert', '');\n\t\t}\n\n\t\t// use AbortController to prevent duplicate listeners on reconnection\n\t\t_.#abortController = new AbortController();\n\t\t_.button.addEventListener('click', _.#handleClick, {\n\t\t\tsignal: _.#abortController.signal,\n\t\t});\n\t}\n\n\tattributeChangedCallback(name, oldValue, newValue) {\n\t\tif (name !== 'group') return;\n\t\tif (oldValue) CollapsibleComponent.#removeFromGroup(oldValue, this);\n\t\tif (newValue) CollapsibleComponent.#addToGroup(newValue, this);\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\tconst group = _.getAttribute('group');\n\t\tif (group) CollapsibleComponent.#removeFromGroup(group, _);\n\t\tif (_.#abortController) {\n\t\t\t_.#abortController.abort();\n\t\t\t_.#abortController = null;\n\t\t}\n\t}\n\n\t#closeSiblings() {\n\t\tconst _ = this;\n\t\tconst groupName = _.getAttribute('group');\n\t\tif (!groupName) return;\n\t\tconst group = CollapsibleComponent.#groups.get(groupName);\n\t\tif (!group) return;\n\t\tfor (const sibling of group) {\n\t\t\tif (sibling === _) continue;\n\t\t\tif (!sibling.content.collapsed) {\n\t\t\t\tsibling.content.collapsed = true;\n\t\t\t\tsibling.button.setAttribute('aria-expanded', 'false');\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Custom element that provides animated collapsible content\n */\nclass CollapsibleContent extends HTMLElement {\n\t#handleTransitionEnd;\n\t#abortController;\n\t#animating = false;\n\n\t/**\n\t * Initializes the content element and binds event handlers\n\t */\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\n\t\t// define event handler using arrow function for proper binding\n\t\t_.#handleTransitionEnd = (event) => {\n\t\t\tif (event.target !== _) return;\n\t\t\tif (event.propertyName !== 'height') return;\n\n\t\t\t_.#animating = false;\n\t\t\t_.style.removeProperty('--collapsible-duration');\n\n\t\t\t// remove the inline height to allow dynamic content changes\n\t\t\tif (!_.collapsed) {\n\t\t\t\t_.style.height = 'auto';\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Called when element is added to the DOM\n\t * Sets initial height based on open attribute\n\t */\n\tconnectedCallback() {\n\t\tconst _ = this;\n\t\t_.style.height = _.hasAttribute('open') ? 'auto' : '0';\n\n\t\t// use AbortController to prevent duplicate listeners on reconnection\n\t\t_.#abortController = new AbortController();\n\t\t_.addEventListener('transitionend', _.#handleTransitionEnd, {\n\t\t\tsignal: _.#abortController.signal,\n\t\t});\n\t}\n\n\t/**\n\t * Called when element is removed from the DOM\n\t * Cleans up event listeners\n\t */\n\tdisconnectedCallback() {\n\t\tconst _ = this;\n\t\t_.#animating = false;\n\t\tif (_.#abortController) {\n\t\t\t_.#abortController.abort();\n\t\t\t_.#abortController = null;\n\t\t}\n\t}\n\n\tget #speed() {\n\t\tconst attr = this.getAttribute('speed');\n\t\tif (attr === null) return DEFAULT_SPEED;\n\t\tconst value = Number(attr);\n\t\treturn value > 0 ? value : DEFAULT_SPEED;\n\t}\n\n\tget #minDuration() {\n\t\tconst attr = this.getAttribute('min-duration');\n\t\tif (attr === null) return MIN_DURATION;\n\t\tconst value = Number(attr);\n\t\treturn value > 0 ? value : MIN_DURATION;\n\t}\n\n\tget #maxDuration() {\n\t\tconst attr = this.getAttribute('max-duration');\n\t\tif (attr === null) return MAX_DURATION;\n\t\tconst value = Number(attr);\n\t\treturn value > 0 ? value : MAX_DURATION;\n\t}\n\n\t#setDynamicDuration(currentHeight, targetHeight) {\n\t\tconst _ = this;\n\t\tconst delta = Math.abs(targetHeight - currentHeight);\n\t\tconst duration = Math.min(_.#maxDuration, Math.max(_.#minDuration, delta / _.#speed));\n\t\t_.style.setProperty('--collapsible-duration', `${duration.toFixed(3)}s`);\n\t}\n\n\t/**\n\t * Handles setting the collapsed state with animation\n\t * @param {boolean} value - Whether element should be collapsed\n\t */\n\tset collapsed(value) {\n\t\tconst _ = this;\n\t\tconst collapsed = Boolean(value);\n\t\tif (_.collapsed === collapsed) return;\n\n\t\t// check if transitions are enabled (respects prefers-reduced-motion)\n\t\tconst hasTransition = getComputedStyle(_).transitionDuration !== '0s';\n\n\t\tif (collapsed) {\n\t\t\t_.removeAttribute('open');\n\t\t\t_.setAttribute('aria-hidden', 'true');\n\t\t\t_.setAttribute('inert', '');\n\n\t\t\tif (!hasTransition) {\n\t\t\t\t_.style.height = '0px';\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// capture current height in px (converts auto or mid-animation value)\n\t\t\tconst currentHeight = _.getBoundingClientRect().height;\n\t\t\t_.style.height = `${currentHeight}px`;\n\t\t\t_.#setDynamicDuration(currentHeight, 0);\n\t\t\t_.#animating = true;\n\t\t\t_.offsetHeight; // force reflow — browser commits the px start value\n\t\t\t_.style.height = '0px';\n\t\t} else {\n\t\t\t_.setAttribute('open', '');\n\t\t\t_.removeAttribute('aria-hidden');\n\t\t\t_.removeAttribute('inert');\n\n\t\t\tif (!hasTransition) {\n\t\t\t\t_.style.height = 'auto';\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// capture current height in px (0px or mid-animation value)\n\t\t\tconst currentHeight = _.getBoundingClientRect().height;\n\t\t\t_.style.height = `${currentHeight}px`;\n\t\t\t_.#setDynamicDuration(currentHeight, _.scrollHeight);\n\t\t\t_.#animating = true;\n\t\t\t_.offsetHeight; // force reflow — browser commits the px start value\n\t\t\t_.style.height = `${_.scrollHeight}px`;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the collapsed state\n\t * @returns {boolean} Whether element is collapsed\n\t */\n\tget collapsed() {\n\t\treturn !this.hasAttribute('open');\n\t}\n}\n\n// register custom elements if not already defined\nif (!customElements.get('collapsible-content')) {\n\tcustomElements.define('collapsible-content', CollapsibleContent);\n}\nif (!customElements.get('collapsible-component')) {\n\tcustomElements.define('collapsible-component', CollapsibleComponent);\n}\n\nexport { CollapsibleContent, CollapsibleComponent };\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
collapsible-component{display:block}collapsible-component button{background:none;border:none;cursor:pointer;text-align:left;width:100%}collapsible-component button:focus-visible{outline:2px solid currentColor;outline-offset:2px}collapsible-content{--collapsible-duration:0.35s;--collapsible-easing:ease;display:block;overflow:hidden;transition:height var(--collapsible-duration) var(--collapsible-easing)}@media (prefers-reduced-motion:reduce){collapsible-content{transition:none}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CollapsibleContent={})}(this,(function(t){"use strict";class CollapsibleComponent extends HTMLElement{#t;constructor(){super();const t=this;t.button=null,t.content=null,t.#
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CollapsibleContent={})}(this,(function(t){"use strict";class CollapsibleComponent extends HTMLElement{static#t=new Map;static#e(t,e){CollapsibleComponent.#t.has(t)||CollapsibleComponent.#t.set(t,new Set),CollapsibleComponent.#t.get(t).add(e)}static#o(t,e){const o=CollapsibleComponent.#t.get(t);o&&(o.delete(e),0===o.size&&CollapsibleComponent.#t.delete(t))}static get observedAttributes(){return["group"]}#n;#l;constructor(){super();const t=this;t.button=null,t.content=null,t.#n=()=>{const e=t.content.collapsed;t.content.collapsed=!e,t.button.setAttribute("aria-expanded",!t.content.collapsed),e&&t.#r()}}connectedCallback(){const t=this;if(t.button=t.querySelector("button"),t.content=t.querySelector("collapsible-content"),!t.button||!t.content){const e=new Error("CollapsibleComponent requires a <button> and a <collapsible-content>.");return console.error(e.message),void t.dispatchEvent(new CustomEvent("collapsible-error",{bubbles:!0,detail:{error:e}}))}t.button.id||=`collapsible-button-${crypto.randomUUID().slice(0,8)}`,t.content.id||=`collapsible-content-${crypto.randomUUID().slice(0,8)}`,t.button.hasAttribute("type")||(t.button.type="button"),t.button.setAttribute("aria-controls",t.content.id),t.content.setAttribute("aria-labelledby",t.button.id),t.content.hasAttribute("role")||t.content.setAttribute("role","region");const e=t.content.hasAttribute("open");t.button.setAttribute("aria-expanded",e),t.content.style.height=e?"auto":"0px",e?(t.content.removeAttribute("aria-hidden"),t.content.removeAttribute("inert")):(t.content.setAttribute("aria-hidden","true"),t.content.setAttribute("inert","")),t.#l=new AbortController,t.button.addEventListener("click",t.#n,{signal:t.#l.signal})}attributeChangedCallback(t,e,o){"group"===t&&(e&&CollapsibleComponent.#o(e,this),o&&CollapsibleComponent.#e(o,this))}disconnectedCallback(){const t=this,e=t.getAttribute("group");e&&CollapsibleComponent.#o(e,t),t.#l&&(t.#l.abort(),t.#l=null)}#r(){const t=this,e=t.getAttribute("group");if(!e)return;const o=CollapsibleComponent.#t.get(e);if(o)for(const e of o)e!==t&&(e.content.collapsed||(e.content.collapsed=!0,e.button.setAttribute("aria-expanded","false")))}}class CollapsibleContent extends HTMLElement{#i;#l;#s=!1;constructor(){super();const t=this;t.#i=e=>{e.target===t&&"height"===e.propertyName&&(t.#s=!1,t.style.removeProperty("--collapsible-duration"),t.collapsed||(t.style.height="auto"))}}connectedCallback(){const t=this;t.style.height=t.hasAttribute("open")?"auto":"0",t.#l=new AbortController,t.addEventListener("transitionend",t.#i,{signal:t.#l.signal})}disconnectedCallback(){const t=this;t.#s=!1,t.#l&&(t.#l.abort(),t.#l=null)}get#a(){const t=this.getAttribute("speed");if(null===t)return 900;const e=Number(t);return e>0?e:900}get#c(){const t=this.getAttribute("min-duration");if(null===t)return.25;const e=Number(t);return e>0?e:.25}get#u(){const t=this.getAttribute("max-duration");if(null===t)return 1;const e=Number(t);return e>0?e:1}#b(t,e){const o=this,n=Math.abs(e-t),l=Math.min(o.#u,Math.max(o.#c,n/o.#a));o.style.setProperty("--collapsible-duration",`${l.toFixed(3)}s`)}set collapsed(t){const e=this,o=Boolean(t);if(e.collapsed===o)return;const n="0s"!==getComputedStyle(e).transitionDuration;if(o){if(e.removeAttribute("open"),e.setAttribute("aria-hidden","true"),e.setAttribute("inert",""),!n)return void(e.style.height="0px");const t=e.getBoundingClientRect().height;e.style.height=`${t}px`,e.#b(t,0),e.#s=!0,e.offsetHeight,e.style.height="0px"}else{if(e.setAttribute("open",""),e.removeAttribute("aria-hidden"),e.removeAttribute("inert"),!n)return void(e.style.height="auto");const t=e.getBoundingClientRect().height;e.style.height=`${t}px`,e.#b(t,e.scrollHeight),e.#s=!0,e.offsetHeight,e.style.height=`${e.scrollHeight}px`}}get collapsed(){return!this.hasAttribute("open")}}customElements.get("collapsible-content")||customElements.define("collapsible-content",CollapsibleContent),customElements.get("collapsible-component")||customElements.define("collapsible-component",CollapsibleComponent),t.CollapsibleComponent=CollapsibleComponent,t.CollapsibleContent=CollapsibleContent}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magic-spells/collapsible-content",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Collapsible content web component",
|
|
5
5
|
"author": "Cory Schulz",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"module": "dist/collapsible-content.esm.js",
|
|
10
10
|
"unpkg": "dist/collapsible-content.min.js",
|
|
11
11
|
"style": "dist/collapsible-content.min.css",
|
|
12
|
-
"sass": "dist/collapsible-content.scss",
|
|
13
12
|
"exports": {
|
|
14
13
|
".": {
|
|
15
14
|
"import": "./dist/collapsible-content.esm.js",
|
|
@@ -17,9 +16,7 @@
|
|
|
17
16
|
"default": "./dist/collapsible-content.esm.js"
|
|
18
17
|
},
|
|
19
18
|
"./css": "./dist/collapsible-content.css",
|
|
20
|
-
"./css/min": "./dist/collapsible-content.min.css"
|
|
21
|
-
"./scss": "./dist/collapsible-content.scss",
|
|
22
|
-
"./scss/*": "./dist/scss/*"
|
|
19
|
+
"./css/min": "./dist/collapsible-content.min.css"
|
|
23
20
|
},
|
|
24
21
|
"sideEffects": true,
|
|
25
22
|
"repository": {
|
|
@@ -60,17 +57,16 @@
|
|
|
60
57
|
"not ie <= 11"
|
|
61
58
|
],
|
|
62
59
|
"devDependencies": {
|
|
63
|
-
"@eslint/js": "^
|
|
60
|
+
"@eslint/js": "^9.38.0",
|
|
64
61
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
65
|
-
"@rollup/plugin-terser": "^0.
|
|
66
|
-
"eslint": "^
|
|
67
|
-
"globals": "^
|
|
62
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
63
|
+
"eslint": "^9.38.0",
|
|
64
|
+
"globals": "^15.15.0",
|
|
68
65
|
"postcss-lightningcss": "^1.0.1",
|
|
69
66
|
"prettier": "^3.3.3",
|
|
70
67
|
"rollup": "^3.0.0",
|
|
71
68
|
"rollup-plugin-copy": "^3.5.0",
|
|
72
69
|
"rollup-plugin-postcss": "^4.0.2",
|
|
73
|
-
"rollup-plugin-serve": "^1.1.1"
|
|
74
|
-
"sass": "^1.86.3"
|
|
70
|
+
"rollup-plugin-serve": "^1.1.1"
|
|
75
71
|
}
|
|
76
72
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
collapsible-component {
|
|
2
|
+
display: block;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
collapsible-component button {
|
|
6
|
+
width: 100%;
|
|
7
|
+
text-align: left;
|
|
8
|
+
background: none;
|
|
9
|
+
border: none;
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
collapsible-component button:focus-visible {
|
|
14
|
+
outline: 2px solid currentColor;
|
|
15
|
+
outline-offset: 2px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
collapsible-content {
|
|
19
|
+
--collapsible-duration: 0.35s;
|
|
20
|
+
--collapsible-easing: ease;
|
|
21
|
+
|
|
22
|
+
display: block;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
transition: height var(--collapsible-duration) var(--collapsible-easing);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (prefers-reduced-motion: reduce) {
|
|
28
|
+
collapsible-content {
|
|
29
|
+
transition: none;
|
|
30
|
+
}
|
|
31
|
+
}
|