v1.3.0 #124
91
page/accordion.css
Normal file
91
page/accordion.css
Normal file
@ -0,0 +1,91 @@
|
||||
.accordion {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
gap: 2rem;
|
||||
}
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.accordion {
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.accordion > .item.box,
|
||||
.accordion > .wrapper > .item.box {
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
|
||||
padding: 0;
|
||||
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.accordion > .item.box > .head,
|
||||
.accordion > .wrapper > .item.box > .head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 1.5rem;
|
||||
|
||||
padding: 1rem 2rem;
|
||||
|
||||
/* HACK: This is equal to an `outline-bottom` */
|
||||
border-bottom: 0.125rem solid var(--color-gray-dark-dark);
|
||||
margin-bottom: -0.125rem;
|
||||
}
|
||||
.accordion > .item.box > .head:hover,
|
||||
.accordion > .wrapper > .item.box > .head:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.accordion > .item.box > .head > .icon,
|
||||
.accordion > .wrapper > .item.box > .head > .icon {
|
||||
color: var(--theme);
|
||||
}
|
||||
.accordion > .item.box > .head > .title,
|
||||
.accordion > .wrapper > .item.box > .head > .title {
|
||||
flex-grow: 1;
|
||||
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.accordion > .item.box > .head > .icon,
|
||||
.accordion > .wrapper > .item.box > .head > .icon,
|
||||
.accordion > .item.box > .head > .title,
|
||||
.accordion > .wrapper > .item.box > .head > .title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.accordion .head > .title .copylink {
|
||||
/* HACK: Fix copylink positioning */
|
||||
top: -0.35rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.accordion > .item.box > .body-container,
|
||||
.accordion > .wrapper > .item.box > .body-container {
|
||||
transition: max-height 0.5s ease-out;
|
||||
}
|
||||
|
||||
.accordion > .item.box:not(.open) > .body-container,
|
||||
.accordion > .wrapper > .item.box:not(.open) > .body-container {
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.accordion > .item.box > .body-container > .body,
|
||||
.accordion > .wrapper > .item.box > .body-container > .body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
gap: 1rem;
|
||||
|
||||
padding: 2rem;
|
||||
}
|
208
page/accordion.js
Normal file
208
page/accordion.js
Normal file
@ -0,0 +1,208 @@
|
||||
"use strict";
|
||||
|
||||
let accordion_view_list = [];
|
||||
const accordion_resize_observer = new ResizeObserver((changed_item_list) => {
|
||||
for(let one_changed_item of changed_item_list){
|
||||
accordion_body_resize(one_changed_item.target);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
window.addEventListener("load", function(event){
|
||||
// COLLECT ACCORDION VIEWS //
|
||||
let accordion_root_list = document.getElementsByClassName("accordion");
|
||||
for(let one_accordion_root of accordion_root_list){
|
||||
// collect this view
|
||||
let accordion_view = accordion_collect_view(one_accordion_root);
|
||||
accordion_view_list.push(accordion_view);
|
||||
|
||||
// initialized this view
|
||||
accordion_init(accordion_view);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* HELPER: Collect data for one accordion view.
|
||||
*
|
||||
* @param root Root element.
|
||||
*
|
||||
* @return Accordion view object.
|
||||
*/
|
||||
function accordion_collect_view(root){
|
||||
// COLLECT ITEMS //
|
||||
let item_list = [];
|
||||
for(let one_child of root.children){
|
||||
// maybe unwrap
|
||||
if(one_child.classList.contains("wrapper")) one_child = one_child.children[0] ?? null;
|
||||
|
||||
// validate child as item
|
||||
if(!one_child.classList.contains("item")) continue;
|
||||
|
||||
// find head element
|
||||
let head;
|
||||
for(let one_item_child of one_child.children){
|
||||
if(one_item_child.classList.contains("head")){
|
||||
head = one_item_child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(head === undefined) throw "Unable to find accordion item head";
|
||||
|
||||
// find icon
|
||||
let icon;
|
||||
for(let one_head_child of head.children){
|
||||
if(one_head_child.classList.contains("icon")){
|
||||
icon = one_head_child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(icon === undefined) throw "Unable to find accordion item icon";
|
||||
|
||||
// find title
|
||||
let title;
|
||||
for(let one_head_child of head.children){
|
||||
if(one_head_child.classList.contains("title")){
|
||||
title = one_head_child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(title === undefined) throw "Unable to find accordion item title";
|
||||
|
||||
// find body container
|
||||
let bodyContainer;
|
||||
for(let one_item_child of one_child.children){
|
||||
if(one_item_child.classList.contains("body-container")){
|
||||
bodyContainer = one_item_child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(bodyContainer === undefined) throw "Unable to find accordion body container";
|
||||
|
||||
// add to item list
|
||||
item_list.push({
|
||||
element: one_child,
|
||||
head: head,
|
||||
icon: icon,
|
||||
title: title,
|
||||
bodyContainer: bodyContainer,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// BUILD OBJECT //
|
||||
return {
|
||||
root: root,
|
||||
item_list: item_list,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* HELPER: Initialize accordion view.
|
||||
*
|
||||
* @param accordion Accordion view object.
|
||||
*/
|
||||
function accordion_init(accordion){
|
||||
// REGISTER ONCLICK HANDLERS //
|
||||
for(let one_item of accordion.item_list){
|
||||
// head
|
||||
one_item.head.onclick = function(event){
|
||||
event.stopPropagation();
|
||||
accordion_click(one_item, accordion);
|
||||
};
|
||||
|
||||
// title
|
||||
one_item.title.tabIndex = 0;
|
||||
one_item.title.onclick = function(event){
|
||||
event.stopPropagation();
|
||||
accordion_click(one_item, accordion);
|
||||
};
|
||||
one_item.title.onkeypress = function(event){
|
||||
if(event.key === "Enter"){
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
accordion_click(one_item, accordion);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// RESET OPENED STATE //
|
||||
for(let one_item of accordion.item_list){
|
||||
accordion_state_set(one_item, accordion_state_get(one_item));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* CALLBACK: Item head was clicked.
|
||||
*
|
||||
* @param item Accordion item.
|
||||
* @param accordion This item's accordion view object.
|
||||
*/
|
||||
function accordion_click(item, accordion){
|
||||
// get our old state
|
||||
let old_state = accordion_state_get(item);
|
||||
|
||||
// close all other items //
|
||||
for(let one_item of accordion.item_list){
|
||||
accordion_state_set(one_item, false);
|
||||
}
|
||||
|
||||
// set our new state
|
||||
accordion_state_set(item, !old_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* GETTER: Get state of one accordion item.
|
||||
*
|
||||
* @param item Accordion item.
|
||||
*/
|
||||
function accordion_state_get(item){
|
||||
return item.element.classList.contains("open");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* SETTER: Set state of one accordion item.
|
||||
*
|
||||
* @param item Accordion item.
|
||||
* @param state New state.
|
||||
*/
|
||||
function accordion_state_set(item, state){
|
||||
// set observe state
|
||||
if(state) accordion_resize_observer.observe(item.bodyContainer);
|
||||
else accordion_resize_observer.unobserve(item.bodyContainer);
|
||||
|
||||
// update class
|
||||
item.element.classList.remove("open");
|
||||
if(state) item.element.classList.add("open");
|
||||
|
||||
// update icon direction
|
||||
item.icon.classList.remove("ti-chevron-up", "ti-chevron-right", "ti-chevron-down", "ti-chevron-left");
|
||||
if(state) item.icon.classList.add("ti-chevron-down");
|
||||
else item.icon.classList.add("ti-chevron-right");
|
||||
|
||||
// set body height
|
||||
if(state) accordion_body_resize(item.bodyContainer);
|
||||
else item.bodyContainer.style.maxHeight = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* HELPER: Resize body height.
|
||||
*
|
||||
* @param bodyContainer Accordion body container.
|
||||
*/
|
||||
function accordion_body_resize(bodyContainer){
|
||||
bodyContainer.style.maxHeight = bodyContainer.scrollHeight + "px";
|
||||
}
|
@ -27,11 +27,15 @@
|
||||
|
||||
Page::css("./page/style.css");
|
||||
Page::css("./page/style.css.php", eval: true);
|
||||
|
||||
require("./page/copylink_strings.php");
|
||||
Page::js("./page/copylink_dict.js.php", eval: true);
|
||||
Page::js("./page/copylink.js");
|
||||
Page::css("./page/copylink.css");
|
||||
|
||||
Page::js("./page/accordion.js");
|
||||
Page::css("./page/accordion.css");
|
||||
|
||||
Page::font("ubuntu");
|
||||
Page::font("tabler");
|
||||
?>
|
||||
|
Loading…
Reference in New Issue
Block a user