✨ accordion view (fix #117)
This commit is contained in:
parent
d55ff7c47c
commit
5c0a348369
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");
|
||||||
Page::css("./page/style.css.php", eval: true);
|
Page::css("./page/style.css.php", eval: true);
|
||||||
|
|
||||||
require("./page/copylink_strings.php");
|
require("./page/copylink_strings.php");
|
||||||
Page::js("./page/copylink_dict.js.php", eval: true);
|
Page::js("./page/copylink_dict.js.php", eval: true);
|
||||||
Page::js("./page/copylink.js");
|
Page::js("./page/copylink.js");
|
||||||
Page::css("./page/copylink.css");
|
Page::css("./page/copylink.css");
|
||||||
|
|
||||||
|
Page::js("./page/accordion.js");
|
||||||
|
Page::css("./page/accordion.css");
|
||||||
|
|
||||||
Page::font("ubuntu");
|
Page::font("ubuntu");
|
||||||
Page::font("tabler");
|
Page::font("tabler");
|
||||||
?>
|
?>
|
||||||
|
Loading…
Reference in New Issue
Block a user