zenkit-css 1.0.3 → 1.0.5
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 +7 -4
- package/docs/zenkit-logo.svg +14 -0
- package/js/components/accordion.js +119 -0
- package/js/components/carousel.js +281 -0
- package/js/components/collapse.js +145 -0
- package/js/components/dropdown.js +226 -0
- package/js/components/modal.js +238 -0
- package/js/components/offcanvas.js +181 -0
- package/js/components/popover.js +281 -0
- package/js/components/scrollspy.js +224 -0
- package/js/components/tabs.js +103 -0
- package/js/components/toast.js +184 -0
- package/js/components/tooltip.js +294 -0
- package/js/zenkit.js +69 -0
- package/js/zenkit.umd.js +958 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://sayedabdulkarim.github.io/-zenkit-css/
|
|
3
|
-
<
|
|
2
|
+
<a href="https://sayedabdulkarim.github.io/-zenkit-css/">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/npm/zenkit-css@latest/docs/zenkit-logo.svg">
|
|
5
|
+
<img src="https://cdn.jsdelivr.net/npm/zenkit-css@latest/docs/zenkit-logo.svg" alt="ZenKit" width="120">
|
|
6
|
+
</picture>
|
|
4
7
|
</a>
|
|
5
8
|
</p>
|
|
6
9
|
|
|
7
10
|
<h3 align="center">ZenKit</h3>
|
|
8
11
|
|
|
9
12
|
<p align="center">
|
|
10
|
-
|
|
13
|
+
Sleek, intuitive, and lightweight CSS framework for faster web development.
|
|
11
14
|
<br>
|
|
12
|
-
<a href="https://sayedabdulkarim.github.io/-zenkit-css/
|
|
15
|
+
<a href="https://sayedabdulkarim.github.io/-zenkit-css/"><strong>Explore ZenKit docs »</strong></a>
|
|
13
16
|
<br>
|
|
14
17
|
<br>
|
|
15
18
|
<a href="https://github.com/sayedabdulkarim/-zenkit-css/issues/new?labels=bug">Report bug</a>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="zenGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" style="stop-color:#818CF8;stop-opacity:1" />
|
|
5
|
+
<stop offset="100%" style="stop-color:#6366F1;stop-opacity:1" />
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
|
|
9
|
+
<!-- Background rounded square -->
|
|
10
|
+
<rect x="20" y="20" width="160" height="160" rx="32" fill="url(#zenGradient)"/>
|
|
11
|
+
|
|
12
|
+
<!-- Z letter -->
|
|
13
|
+
<path d="M60 65 L140 65 L140 80 L85 135 L140 135 L140 150 L60 150 L60 135 L115 80 L60 80 Z" fill="white"/>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// ========================================
|
|
2
|
+
// ZenKit - Accordion Component
|
|
3
|
+
// ========================================
|
|
4
|
+
|
|
5
|
+
class Accordion {
|
|
6
|
+
constructor(element, options = {}) {
|
|
7
|
+
this.element = typeof element === 'string' ? document.querySelector(element) : element;
|
|
8
|
+
if (!this.element) return;
|
|
9
|
+
|
|
10
|
+
this.options = {
|
|
11
|
+
alwaysOpen: false,
|
|
12
|
+
...options
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
this.items = this.element.querySelectorAll('.accordion-item');
|
|
16
|
+
this.init();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
init() {
|
|
20
|
+
this.items.forEach(item => {
|
|
21
|
+
const button = item.querySelector('.accordion-button');
|
|
22
|
+
const collapse = item.querySelector('.accordion-collapse');
|
|
23
|
+
|
|
24
|
+
if (button && collapse) {
|
|
25
|
+
button.addEventListener('click', (e) => this.toggle(e, item));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
toggle(event, item) {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
const button = item.querySelector('.accordion-button');
|
|
33
|
+
const collapse = item.querySelector('.accordion-collapse');
|
|
34
|
+
const isOpen = collapse.classList.contains('show');
|
|
35
|
+
|
|
36
|
+
if (!this.options.alwaysOpen) {
|
|
37
|
+
// Close all other items
|
|
38
|
+
this.items.forEach(otherItem => {
|
|
39
|
+
if (otherItem !== item) {
|
|
40
|
+
this.close(otherItem);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (isOpen) {
|
|
46
|
+
this.close(item);
|
|
47
|
+
} else {
|
|
48
|
+
this.open(item);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
open(item) {
|
|
53
|
+
const button = item.querySelector('.accordion-button');
|
|
54
|
+
const collapse = item.querySelector('.accordion-collapse');
|
|
55
|
+
|
|
56
|
+
button.classList.remove('collapsed');
|
|
57
|
+
button.setAttribute('aria-expanded', 'true');
|
|
58
|
+
|
|
59
|
+
collapse.classList.add('collapsing');
|
|
60
|
+
collapse.style.height = '0px';
|
|
61
|
+
|
|
62
|
+
requestAnimationFrame(() => {
|
|
63
|
+
collapse.style.height = collapse.scrollHeight + 'px';
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const transitionEnd = () => {
|
|
67
|
+
collapse.classList.remove('collapsing');
|
|
68
|
+
collapse.classList.add('show', 'collapse');
|
|
69
|
+
collapse.style.height = '';
|
|
70
|
+
collapse.removeEventListener('transitionend', transitionEnd);
|
|
71
|
+
|
|
72
|
+
// Dispatch event
|
|
73
|
+
this.element.dispatchEvent(new CustomEvent('shown.zk.accordion', {
|
|
74
|
+
detail: { item }
|
|
75
|
+
}));
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
collapse.addEventListener('transitionend', transitionEnd);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
close(item) {
|
|
82
|
+
const button = item.querySelector('.accordion-button');
|
|
83
|
+
const collapse = item.querySelector('.accordion-collapse');
|
|
84
|
+
|
|
85
|
+
if (!collapse.classList.contains('show')) return;
|
|
86
|
+
|
|
87
|
+
button.classList.add('collapsed');
|
|
88
|
+
button.setAttribute('aria-expanded', 'false');
|
|
89
|
+
|
|
90
|
+
collapse.style.height = collapse.scrollHeight + 'px';
|
|
91
|
+
collapse.classList.remove('show');
|
|
92
|
+
|
|
93
|
+
requestAnimationFrame(() => {
|
|
94
|
+
collapse.classList.add('collapsing');
|
|
95
|
+
collapse.style.height = '0px';
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const transitionEnd = () => {
|
|
99
|
+
collapse.classList.remove('collapsing');
|
|
100
|
+
collapse.classList.add('collapse');
|
|
101
|
+
collapse.style.height = '';
|
|
102
|
+
collapse.removeEventListener('transitionend', transitionEnd);
|
|
103
|
+
|
|
104
|
+
// Dispatch event
|
|
105
|
+
this.element.dispatchEvent(new CustomEvent('hidden.zk.accordion', {
|
|
106
|
+
detail: { item }
|
|
107
|
+
}));
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
collapse.addEventListener('transitionend', transitionEnd);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Static method to initialize all accordions
|
|
114
|
+
static init(selector = '.accordion') {
|
|
115
|
+
document.querySelectorAll(selector).forEach(el => new Accordion(el));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default Accordion;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// ========================================
|
|
2
|
+
// ZenKit - Carousel Component
|
|
3
|
+
// ========================================
|
|
4
|
+
|
|
5
|
+
class Carousel {
|
|
6
|
+
constructor(element, options = {}) {
|
|
7
|
+
this.element = typeof element === 'string' ? document.querySelector(element) : element;
|
|
8
|
+
if (!this.element) return;
|
|
9
|
+
|
|
10
|
+
this.options = {
|
|
11
|
+
interval: 5000,
|
|
12
|
+
keyboard: true,
|
|
13
|
+
pause: 'hover',
|
|
14
|
+
ride: false,
|
|
15
|
+
wrap: true,
|
|
16
|
+
touch: true,
|
|
17
|
+
...options
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
this.inner = this.element.querySelector('.carousel-inner');
|
|
21
|
+
this.items = this.element.querySelectorAll('.carousel-item');
|
|
22
|
+
this.indicators = this.element.querySelectorAll('.carousel-indicators [data-slide-to]');
|
|
23
|
+
|
|
24
|
+
this.activeIndex = 0;
|
|
25
|
+
this.isSliding = false;
|
|
26
|
+
this.interval = null;
|
|
27
|
+
this.touchStartX = 0;
|
|
28
|
+
this.touchStartY = 0;
|
|
29
|
+
|
|
30
|
+
this.init();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
init() {
|
|
34
|
+
// Find initial active index
|
|
35
|
+
this.items.forEach((item, index) => {
|
|
36
|
+
if (item.classList.contains('active')) {
|
|
37
|
+
this.activeIndex = index;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Controls
|
|
42
|
+
const prevBtn = this.element.querySelector('.carousel-control-prev');
|
|
43
|
+
const nextBtn = this.element.querySelector('.carousel-control-next');
|
|
44
|
+
|
|
45
|
+
if (prevBtn) {
|
|
46
|
+
prevBtn.addEventListener('click', (e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
this.prev();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (nextBtn) {
|
|
53
|
+
nextBtn.addEventListener('click', (e) => {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
this.next();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Indicators
|
|
60
|
+
this.indicators.forEach((indicator, index) => {
|
|
61
|
+
indicator.addEventListener('click', (e) => {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
this.goTo(index);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Keyboard
|
|
68
|
+
if (this.options.keyboard) {
|
|
69
|
+
this.element.addEventListener('keydown', (e) => this.handleKeydown(e));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Pause on hover
|
|
73
|
+
if (this.options.pause === 'hover') {
|
|
74
|
+
this.element.addEventListener('mouseenter', () => this.pause());
|
|
75
|
+
this.element.addEventListener('mouseleave', () => this.cycle());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Touch support
|
|
79
|
+
if (this.options.touch) {
|
|
80
|
+
this.element.addEventListener('touchstart', (e) => this.handleTouchStart(e), { passive: true });
|
|
81
|
+
this.element.addEventListener('touchend', (e) => this.handleTouchEnd(e), { passive: true });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Auto-play
|
|
85
|
+
if (this.options.ride === 'carousel' || this.options.ride === true) {
|
|
86
|
+
this.cycle();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
next() {
|
|
91
|
+
if (!this.isSliding) {
|
|
92
|
+
this.slide('next');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
prev() {
|
|
97
|
+
if (!this.isSliding) {
|
|
98
|
+
this.slide('prev');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
goTo(index) {
|
|
103
|
+
if (this.isSliding || index === this.activeIndex) return;
|
|
104
|
+
|
|
105
|
+
const direction = index > this.activeIndex ? 'next' : 'prev';
|
|
106
|
+
this.slide(direction, index);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
slide(direction, targetIndex = null) {
|
|
110
|
+
const activeItem = this.items[this.activeIndex];
|
|
111
|
+
let nextIndex;
|
|
112
|
+
|
|
113
|
+
if (targetIndex !== null) {
|
|
114
|
+
nextIndex = targetIndex;
|
|
115
|
+
} else if (direction === 'next') {
|
|
116
|
+
nextIndex = this.activeIndex + 1;
|
|
117
|
+
if (nextIndex >= this.items.length) {
|
|
118
|
+
if (!this.options.wrap) return;
|
|
119
|
+
nextIndex = 0;
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
nextIndex = this.activeIndex - 1;
|
|
123
|
+
if (nextIndex < 0) {
|
|
124
|
+
if (!this.options.wrap) return;
|
|
125
|
+
nextIndex = this.items.length - 1;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const nextItem = this.items[nextIndex];
|
|
130
|
+
if (!nextItem) return;
|
|
131
|
+
|
|
132
|
+
const slideEvent = new CustomEvent('slide.zk.carousel', {
|
|
133
|
+
detail: { from: this.activeIndex, to: nextIndex, direction }
|
|
134
|
+
});
|
|
135
|
+
this.element.dispatchEvent(slideEvent);
|
|
136
|
+
if (slideEvent.defaultPrevented) return;
|
|
137
|
+
|
|
138
|
+
this.isSliding = true;
|
|
139
|
+
|
|
140
|
+
// Add positioning classes
|
|
141
|
+
const directionalClass = direction === 'next' ? 'carousel-item-start' : 'carousel-item-end';
|
|
142
|
+
const orderClass = direction === 'next' ? 'carousel-item-next' : 'carousel-item-prev';
|
|
143
|
+
|
|
144
|
+
nextItem.classList.add(orderClass);
|
|
145
|
+
|
|
146
|
+
// Force reflow
|
|
147
|
+
nextItem.offsetHeight;
|
|
148
|
+
|
|
149
|
+
activeItem.classList.add(directionalClass);
|
|
150
|
+
nextItem.classList.add(directionalClass);
|
|
151
|
+
|
|
152
|
+
const complete = () => {
|
|
153
|
+
nextItem.classList.remove(directionalClass, orderClass);
|
|
154
|
+
nextItem.classList.add('active');
|
|
155
|
+
|
|
156
|
+
activeItem.classList.remove('active', directionalClass, orderClass);
|
|
157
|
+
|
|
158
|
+
this.isSliding = false;
|
|
159
|
+
this.activeIndex = nextIndex;
|
|
160
|
+
this.updateIndicators();
|
|
161
|
+
|
|
162
|
+
this.element.dispatchEvent(new CustomEvent('slid.zk.carousel', {
|
|
163
|
+
detail: { from: this.activeIndex, to: nextIndex, direction }
|
|
164
|
+
}));
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
activeItem.addEventListener('transitionend', complete, { once: true });
|
|
168
|
+
|
|
169
|
+
// Fallback timeout
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
if (this.isSliding) {
|
|
172
|
+
complete();
|
|
173
|
+
}
|
|
174
|
+
}, 600);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
updateIndicators() {
|
|
178
|
+
this.indicators.forEach((indicator, index) => {
|
|
179
|
+
indicator.classList.toggle('active', index === this.activeIndex);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
cycle() {
|
|
184
|
+
if (this.interval) {
|
|
185
|
+
clearInterval(this.interval);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (this.options.interval) {
|
|
189
|
+
this.interval = setInterval(() => this.next(), this.options.interval);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
pause() {
|
|
194
|
+
if (this.interval) {
|
|
195
|
+
clearInterval(this.interval);
|
|
196
|
+
this.interval = null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
handleKeydown(event) {
|
|
201
|
+
if (/input|textarea/i.test(event.target.tagName)) return;
|
|
202
|
+
|
|
203
|
+
switch (event.key) {
|
|
204
|
+
case 'ArrowLeft':
|
|
205
|
+
event.preventDefault();
|
|
206
|
+
this.prev();
|
|
207
|
+
break;
|
|
208
|
+
case 'ArrowRight':
|
|
209
|
+
event.preventDefault();
|
|
210
|
+
this.next();
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
handleTouchStart(event) {
|
|
216
|
+
this.touchStartX = event.touches[0].clientX;
|
|
217
|
+
this.touchStartY = event.touches[0].clientY;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
handleTouchEnd(event) {
|
|
221
|
+
const touchEndX = event.changedTouches[0].clientX;
|
|
222
|
+
const touchEndY = event.changedTouches[0].clientY;
|
|
223
|
+
|
|
224
|
+
const deltaX = touchEndX - this.touchStartX;
|
|
225
|
+
const deltaY = touchEndY - this.touchStartY;
|
|
226
|
+
|
|
227
|
+
// Only handle horizontal swipes
|
|
228
|
+
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) {
|
|
229
|
+
if (deltaX > 0) {
|
|
230
|
+
this.prev();
|
|
231
|
+
} else {
|
|
232
|
+
this.next();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
dispose() {
|
|
238
|
+
this.pause();
|
|
239
|
+
Carousel.instances.delete(this.element);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
nextWhenVisible() {
|
|
243
|
+
// Only cycle to next when the page is visible
|
|
244
|
+
if (!document.hidden && this.element.offsetWidth > 0 && this.element.offsetHeight > 0) {
|
|
245
|
+
this.next();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
to(index) {
|
|
250
|
+
// Alias for goTo for Bootstrap compatibility
|
|
251
|
+
this.goTo(index);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Static methods
|
|
255
|
+
static instances = new WeakMap();
|
|
256
|
+
|
|
257
|
+
static getInstance(element) {
|
|
258
|
+
return Carousel.instances.get(element);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
static getOrCreateInstance(element, options) {
|
|
262
|
+
return Carousel.getInstance(element) || new Carousel(element, options);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static init(selector = '.carousel') {
|
|
266
|
+
document.querySelectorAll(selector).forEach(el => {
|
|
267
|
+
const options = {
|
|
268
|
+
interval: el.dataset.interval ? parseInt(el.dataset.interval) : 5000,
|
|
269
|
+
keyboard: el.dataset.keyboard !== 'false',
|
|
270
|
+
pause: el.dataset.pause || 'hover',
|
|
271
|
+
ride: el.dataset.ride || false,
|
|
272
|
+
wrap: el.dataset.wrap !== 'false',
|
|
273
|
+
touch: el.dataset.touch !== 'false'
|
|
274
|
+
};
|
|
275
|
+
const instance = new Carousel(el, options);
|
|
276
|
+
Carousel.instances.set(el, instance);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export default Carousel;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// ========================================
|
|
2
|
+
// ZenKit - Collapse Component
|
|
3
|
+
// ========================================
|
|
4
|
+
|
|
5
|
+
class Collapse {
|
|
6
|
+
constructor(element, options = {}) {
|
|
7
|
+
this.element = typeof element === 'string' ? document.querySelector(element) : element;
|
|
8
|
+
if (!this.element) return;
|
|
9
|
+
|
|
10
|
+
this.options = {
|
|
11
|
+
toggle: true,
|
|
12
|
+
parent: null,
|
|
13
|
+
...options
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
this.isTransitioning = false;
|
|
17
|
+
this.triggerElements = document.querySelectorAll(
|
|
18
|
+
`[data-toggle="collapse"][data-target="#${this.element.id}"],` +
|
|
19
|
+
`[data-toggle="collapse"][href="#${this.element.id}"]`
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
this.init();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
init() {
|
|
26
|
+
this.triggerElements.forEach(trigger => {
|
|
27
|
+
trigger.addEventListener('click', (e) => {
|
|
28
|
+
e.preventDefault();
|
|
29
|
+
this.toggle();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (this.options.toggle) {
|
|
34
|
+
// Already handled by trigger elements
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
toggle() {
|
|
39
|
+
if (this.element.classList.contains('show')) {
|
|
40
|
+
this.hide();
|
|
41
|
+
} else {
|
|
42
|
+
this.show();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
show() {
|
|
47
|
+
if (this.isTransitioning || this.element.classList.contains('show')) return;
|
|
48
|
+
|
|
49
|
+
// Close siblings if parent is set
|
|
50
|
+
if (this.options.parent) {
|
|
51
|
+
const parent = document.querySelector(this.options.parent);
|
|
52
|
+
if (parent) {
|
|
53
|
+
const siblings = parent.querySelectorAll('.collapse.show');
|
|
54
|
+
siblings.forEach(sibling => {
|
|
55
|
+
if (sibling !== this.element) {
|
|
56
|
+
const collapseInstance = Collapse.getInstance(sibling);
|
|
57
|
+
if (collapseInstance) collapseInstance.hide();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.isTransitioning = true;
|
|
64
|
+
this.element.classList.remove('collapse');
|
|
65
|
+
this.element.classList.add('collapsing');
|
|
66
|
+
this.element.style.height = '0px';
|
|
67
|
+
|
|
68
|
+
this.updateTriggers(true);
|
|
69
|
+
|
|
70
|
+
requestAnimationFrame(() => {
|
|
71
|
+
this.element.style.height = this.element.scrollHeight + 'px';
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const complete = () => {
|
|
75
|
+
this.isTransitioning = false;
|
|
76
|
+
this.element.classList.remove('collapsing');
|
|
77
|
+
this.element.classList.add('collapse', 'show');
|
|
78
|
+
this.element.style.height = '';
|
|
79
|
+
this.element.removeEventListener('transitionend', complete);
|
|
80
|
+
this.element.dispatchEvent(new CustomEvent('shown.zk.collapse'));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
this.element.addEventListener('transitionend', complete);
|
|
84
|
+
this.element.dispatchEvent(new CustomEvent('show.zk.collapse'));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
hide() {
|
|
88
|
+
if (this.isTransitioning || !this.element.classList.contains('show')) return;
|
|
89
|
+
|
|
90
|
+
this.isTransitioning = true;
|
|
91
|
+
this.element.style.height = this.element.scrollHeight + 'px';
|
|
92
|
+
this.element.classList.remove('show');
|
|
93
|
+
|
|
94
|
+
this.updateTriggers(false);
|
|
95
|
+
|
|
96
|
+
requestAnimationFrame(() => {
|
|
97
|
+
this.element.classList.add('collapsing');
|
|
98
|
+
this.element.style.height = '0px';
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const complete = () => {
|
|
102
|
+
this.isTransitioning = false;
|
|
103
|
+
this.element.classList.remove('collapsing');
|
|
104
|
+
this.element.classList.add('collapse');
|
|
105
|
+
this.element.style.height = '';
|
|
106
|
+
this.element.removeEventListener('transitionend', complete);
|
|
107
|
+
this.element.dispatchEvent(new CustomEvent('hidden.zk.collapse'));
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.element.addEventListener('transitionend', complete);
|
|
111
|
+
this.element.dispatchEvent(new CustomEvent('hide.zk.collapse'));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
updateTriggers(isOpen) {
|
|
115
|
+
this.triggerElements.forEach(trigger => {
|
|
116
|
+
trigger.classList.toggle('collapsed', !isOpen);
|
|
117
|
+
trigger.setAttribute('aria-expanded', isOpen);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Store instance on element
|
|
122
|
+
static instances = new WeakMap();
|
|
123
|
+
|
|
124
|
+
static getInstance(element) {
|
|
125
|
+
return Collapse.instances.get(element);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static getOrCreateInstance(element, options) {
|
|
129
|
+
return Collapse.getInstance(element) || new Collapse(element, options);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Static method to initialize all collapses
|
|
133
|
+
static init(selector = '[data-toggle="collapse"]') {
|
|
134
|
+
document.querySelectorAll(selector).forEach(trigger => {
|
|
135
|
+
const target = trigger.dataset.target || trigger.getAttribute('href');
|
|
136
|
+
const element = document.querySelector(target);
|
|
137
|
+
if (element && !Collapse.getInstance(element)) {
|
|
138
|
+
const instance = new Collapse(element, { toggle: false });
|
|
139
|
+
Collapse.instances.set(element, instance);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export default Collapse;
|