Skip to content

Commit 8733f15

Browse files
committed
1 parent 8e34499 commit 8733f15

File tree

1 file changed

+122
-2
lines changed

1 file changed

+122
-2
lines changed

resources/views/studio/theme.blade.php

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
<br><br><br>
122122
<details>
123123
<summary><i class="bi bi-caret-down-fill"></i> Theme updater </summary>
124-
<div style="padding:10px;">
124+
<div class="content" style="padding:10px;">
125125
<table>
126126
<tr>
127127
<th style="width:85%;">Theme name:</th>
@@ -232,7 +232,127 @@
232232
</div>
233233
</form>
234234
</details>
235-
235+
236+
<script>
237+
class Accordion {
238+
constructor(el) {
239+
// Store the <details> element
240+
this.el = el;
241+
// Store the <summary> element
242+
this.summary = el.querySelector('summary');
243+
// Store the <div class="content"> element
244+
this.content = el.querySelector('.content');
245+
246+
// Store the animation object (so we can cancel it if needed)
247+
this.animation = null;
248+
// Store if the element is closing
249+
this.isClosing = false;
250+
// Store if the element is expanding
251+
this.isExpanding = false;
252+
// Detect user clicks on the summary element
253+
this.summary.addEventListener('click', (e) => this.onClick(e));
254+
}
255+
256+
onClick(e) {
257+
// Stop default behaviour from the browser
258+
e.preventDefault();
259+
// Add an overflow on the <details> to avoid content overflowing
260+
this.el.style.overflow = 'hidden';
261+
// Check if the element is being closed or is already closed
262+
if (this.isClosing || !this.el.open) {
263+
this.open();
264+
// Check if the element is being openned or is already open
265+
} else if (this.isExpanding || this.el.open) {
266+
this.shrink();
267+
}
268+
}
269+
270+
shrink() {
271+
// Set the element as "being closed"
272+
this.isClosing = true;
273+
274+
// Store the current height of the element
275+
const startHeight = `${this.el.offsetHeight}px`;
276+
// Calculate the height of the summary
277+
const endHeight = `${this.summary.offsetHeight}px`;
278+
279+
// If there is already an animation running
280+
if (this.animation) {
281+
// Cancel the current animation
282+
this.animation.cancel();
283+
}
284+
285+
// Start a WAAPI animation
286+
this.animation = this.el.animate({
287+
// Set the keyframes from the startHeight to endHeight
288+
height: [startHeight, endHeight]
289+
}, {
290+
duration: 400,
291+
easing: 'ease-out'
292+
});
293+
294+
// When the animation is complete, call onAnimationFinish()
295+
this.animation.onfinish = () => this.onAnimationFinish(false);
296+
// If the animation is cancelled, isClosing variable is set to false
297+
this.animation.oncancel = () => this.isClosing = false;
298+
}
299+
300+
open() {
301+
// Apply a fixed height on the element
302+
this.el.style.height = `${this.el.offsetHeight}px`;
303+
// Force the [open] attribute on the details element
304+
this.el.open = true;
305+
// Wait for the next frame to call the expand function
306+
window.requestAnimationFrame(() => this.expand());
307+
}
308+
309+
expand() {
310+
// Set the element as "being expanding"
311+
this.isExpanding = true;
312+
// Get the current fixed height of the element
313+
const startHeight = `${this.el.offsetHeight}px`;
314+
// Calculate the open height of the element (summary height + content height)
315+
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
316+
317+
// If there is already an animation running
318+
if (this.animation) {
319+
// Cancel the current animation
320+
this.animation.cancel();
321+
}
322+
323+
// Start a WAAPI animation
324+
this.animation = this.el.animate({
325+
// Set the keyframes from the startHeight to endHeight
326+
height: [startHeight, endHeight]
327+
}, {
328+
duration: 400,
329+
easing: 'ease-out'
330+
});
331+
// When the animation is complete, call onAnimationFinish()
332+
this.animation.onfinish = () => this.onAnimationFinish(true);
333+
// If the animation is cancelled, isExpanding variable is set to false
334+
this.animation.oncancel = () => this.isExpanding = false;
335+
}
336+
337+
onAnimationFinish(open) {
338+
// Set the open attribute based on the parameter
339+
this.el.open = open;
340+
// Clear the stored animation
341+
this.animation = null;
342+
// Reset isClosing & isExpanding
343+
this.isClosing = false;
344+
this.isExpanding = false;
345+
// Remove the overflow hidden and the fixed height
346+
this.el.style.height = this.el.style.overflow = '';
347+
}
348+
}
349+
350+
document.querySelectorAll('details').forEach((el) => {
351+
new Accordion(el);
352+
});
353+
354+
</script>
355+
236356
@endif
237357

238358
@endforeach

0 commit comments

Comments
 (0)