Skip to content

Commit

Permalink
feat(options): add transitionDuration option to allow for closing ani…
Browse files Browse the repository at this point in the history
…mations

See #319
  • Loading branch information
NickDJM authored Sep 25, 2024
1 parent f6f81ca commit 8729d85
Show file tree
Hide file tree
Showing 38 changed files with 3,491 additions and 1,506 deletions.
332 changes: 195 additions & 137 deletions demo/main.js
Original file line number Diff line number Diff line change
@@ -1,175 +1,235 @@
import AccessibleMenu from "../index.js";
import {
singleLevel,
twoLevel,
twoLevelDisclosure,
twoLevelDisclosureTopLink,
threeLevel,
threeLevelDisclosure,
threeLevelDisclosureTopLink,
} from "./menus.js";
import menus from "./menus.js";

const menuSettings = {
native: {
structure: {
one: menus.singleLevel,
two: menus.twoLevel,
three: menus.threeLevel,
},
class: "native",
},
DisclosureMenu: {
structure: {
one: menus.singleLevel,
two: menus.twoLevelDisclosure,
three: menus.threeLevelDisclosure,
},
class: "disclosure-menu",
},
Menubar: {
structure: {
one: menus.singleLevel,
two: menus.twoLevel,
three: menus.threeLevel,
},
class: "menubar",
},
TopLinkDisclosureMenu: {
structure: {
one: menus.singleLevel,
two: menus.twoLevelDisclosureTopLink,
three: menus.threeLevelDisclosureTopLink,
},
class: "top-link-disclosure-menu",
},
Treeview: {
structure: {
one: menus.singleLevel,
two: menus.twoLevel,
three: menus.threeLevel,
},
class: "treeview",
},
};
const options = {
hoverType: "off",
hoverDelay: 250,
enterDelay: -1,
leaveDelat: -1,
transitionClass: "transitioning",
transitionDuration: 250,
openDuration: -1,
closeDuration: -1,
optionalKeySupport: true,
};
const container = document.querySelector("header");

let type = "native";
let structure = "one";

/**
* Generates an accessible-menu.
*
* @param {string} type - The type of menu to use.
* @param {string} structure - The DOM structure to use.
* @param {string} hover - The hover type for the menu.
* @param {HTMLElement} container - The container to generate the menu in.
* @param {object} [options = {}] - Option overrides.
*/
function generateMenu(type, structure, hover, container, options = {}) {
menus.pop();
const MenuClass = AccessibleMenu[type] || null;
const menuDOM = menuStructures[structure];

if (type === "TopLinkDisclosureMenu") {
container.innerHTML = menuDOM.topLink;
} else if (type === "DisclosureMenu") {
container.innerHTML = menuDOM.disclosure;
} else {
container.innerHTML = menuDOM.default;
function generateMenu() {
// Remove the last menu from the stack.
if (window.AccessibleMenu) {
window.AccessibleMenu.menus = {};
}

const nav = container.querySelector("nav");

nav.classList.remove("disclosure-menu");
nav.classList.remove("menubar");
nav.classList.remove("top-link-disclosure-menu");
nav.classList.remove("treeview");
document.body.classList.remove("disclosure-menu");
document.body.classList.remove("menubar");
document.body.classList.remove("top-link-disclosure-menu");
document.body.classList.remove("treeview");

if (MenuClass !== null) {
switch (type) {
case "TopLinkDisclosureMenu":
nav.classList.add("top-link-disclosure-menu");
document.body.classList.add("top-link-disclosure-menu");
// Get the menu class and structure.
const MenuClass = AccessibleMenu[type] || null;
const menuStructure = menuSettings[type].structure[structure];

break;
// Set the menu structure.
container.innerHTML = menuStructure;

case "DisclosureMenu":
nav.classList.add("disclosure-menu");
document.body.classList.add("disclosure-menu");
// Get the nav element.
const nav = container.querySelector("nav");

break;
// Set the classes.
document.body.classList.remove(
...Object.values(menuSettings).map((setting) => setting.class)
);
document.body.classList.add(menuSettings[type].class);
nav.classList.add(menuSettings[type].class);

case "Menubar":
nav.classList.add("menubar");
document.body.classList.add("menubar");
if (MenuClass === null) {
return;
}

break;
new MenuClass({
menuElement: nav.querySelector("ul"),
containerElement: nav,
controllerElement: nav.querySelector("button"),
...options,
});
}

case "Treeview":
nav.classList.add("treeview");
document.body.classList.add("treeview");
// Set up menu structure buttons.
const domButtons = document.querySelectorAll("#domButtons button");

break;
}
domButtons.forEach((button) => {
button.addEventListener("click", () => {
structure = button.dataset.menuStructure;
generateMenu();

const menu = new MenuClass({
menuElement: nav.querySelector("ul"),
containerElement: nav,
controllerElement: nav.querySelector("button"),
hoverType: hover,
...options,
domButtons.forEach((button) => {
button.classList.remove("active");
});
button.classList.add("active");
});
});

menus.push(menu);
}
}

// Menu setup.
const menus = [];
const header = document.querySelector("header");
// Setup the menu type buttons.
const menuButtons = document.querySelectorAll("#menuButtons button");
const domButtons = document.querySelectorAll("#domButtons button");
const hoverButtons = document.querySelectorAll("#hoverButtons button");
const menuStructures = {
one: {
default: singleLevel,
disclosure: singleLevel,
topLink: singleLevel,
},
two: {
default: twoLevel,
disclosure: twoLevelDisclosure,
topLink: twoLevelDisclosureTopLink,
},
three: {
default: threeLevel,
disclosure: threeLevelDisclosure,
topLink: threeLevelDisclosureTopLink,
},
};
const menuOptions = {
DisclosureMenu: {
optionalKeySupport: true,
},
Menubar: {},
TopLinkDisclosureMenu: {
optionalKeySupport: true,
},
Treeview: {},
};

// Theme setup.
const preferredTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
const setTheme = window.localStorage.getItem("setTheme") || preferredTheme;
const themeToggle = document.querySelector("#themeToggle");

// Menu options.
let menuType = "";
let menuStructure = "one";
let hoverType = "off";

// Set up menu type switching.
menuButtons.forEach((button) => {
button.addEventListener("click", () => {
menuType = button.dataset.menuType;
const options = menuOptions[menuType] || {};
type = button.dataset.menuType;
generateMenu();

generateMenu(menuType, menuStructure, hoverType, header, options);
document
.querySelector("#menuButtons button.active")
.classList.remove("active");
menuButtons.forEach((button) => {
button.classList.remove("active");
});
button.classList.add("active");
});
});

// Set up menu structure switching.
domButtons.forEach((button) => {
// Set up the optional key support settings.
const keySupport = document.querySelectorAll("#keyButtons button");

keySupport.forEach((button) => {
button.addEventListener("click", () => {
menuStructure = button.dataset.menuStructure;
const options = menuOptions[menuType] || {};
options.optionalKeySupport = button.dataset.optionalKeySupport === "true";
generateMenu();

generateMenu(menuType, menuStructure, hoverType, header, options);
document
.querySelector("#domButtons button.active")
.classList.remove("active");
keySupport.forEach((button) => {
button.classList.remove("active");
});
button.classList.add("active");
});
});

// Set up hover type switching.
// Set up the hover type buttons.
const hoverButtons = document.querySelectorAll("#hoverButtons button");

hoverButtons.forEach((button) => {
button.addEventListener("click", () => {
hoverType = button.dataset.hoverType;
const options = menuOptions[menuType] || {};
options.hoverType = button.dataset.hoverType;
generateMenu();

generateMenu(menuType, menuStructure, hoverType, header, options);
document
.querySelector("#hoverButtons button.active")
.classList.remove("active");
hoverButtons.forEach((button) => {
button.classList.remove("active");
});
button.classList.add("active");
});
});

// Set up theme switching.
// Set up the hover delay input.
const hoverDelay = document.querySelector("#hoverDelay");

hoverDelay.addEventListener("input", () => {
options.hoverDelay = Number(hoverDelay.value);
generateMenu();
});

// Set up the enter delay input.
const enterDelay = document.querySelector("#enterDelay");

enterDelay.addEventListener("input", () => {
options.enterDelay = Number(enterDelay.value);
generateMenu();
});

// Set up the leave delay input.
const leaveDelay = document.querySelector("#leaveDelay");

leaveDelay.addEventListener("input", () => {
options.leaveDelay = Number(leaveDelay.value);
generateMenu();
});

// Set up transition buttons.
const transitionButtons = document.querySelectorAll(
"#transitionButtons button"
);

transitionButtons.forEach((button) => {
button.addEventListener("click", () => {
options.transitionClass = button.dataset.transitionClass;
generateMenu();

transitionButtons.forEach((button) => {
button.classList.remove("active");
});
button.classList.add("active");
});
});

// Set up transition duration input.
const transitionDuration = document.querySelector("#transitionDuration");

transitionDuration.addEventListener("change", () => {
options.transitionDuration = Number(transitionDuration.value);
generateMenu();
});

// Set up the open duration input.
const openDuration = document.querySelector("#openDuration");

openDuration.addEventListener("change", () => {
options.openDuration = Number(openDuration.value);
generateMenu();
});

// Set up the close duration input.
const closeDuration = document.querySelector("#closeDuration");

closeDuration.addEventListener("change", () => {
options.closeDuration = Number(closeDuration.value);
generateMenu();
});

// Set up the theme switcher.
// Theme setup.
const preferredTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
const setTheme = window.localStorage.getItem("setTheme") || preferredTheme;
const themeToggle = document.querySelector("#themeToggle");

themeToggle.addEventListener("click", () => {
if (document.body.classList.contains("dark-mode")) {
document.body.classList.remove("dark-mode");
Expand All @@ -182,11 +242,9 @@ themeToggle.addEventListener("click", () => {
}
});

// Generate an initial menu and set theme.
// Generate the menu and set the preferred theme.
document.addEventListener("DOMContentLoaded", () => {
const options = menuOptions[menuType] || {};

generateMenu(menuType, menuStructure, hoverType, header, options);
generateMenu();

if (setTheme === "dark") {
document.body.classList.add("dark-mode");
Expand Down
10 changes: 10 additions & 0 deletions demo/menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -837,3 +837,13 @@ export const threeLevelDisclosureTopLink =
</ul>
</nav>
`;

export default {
singleLevel,
twoLevel,
twoLevelDisclosure,
twoLevelDisclosureTopLink,
threeLevel,
threeLevelDisclosure,
threeLevelDisclosureTopLink,
};
Loading

0 comments on commit 8729d85

Please sign in to comment.