WEBLEB
Startseite
Editor
Anmelden
Pro
Deutsch
English
Français
Español
Português
Deutsch
Italiano
हिंदी
HTML
CSS
JS
Andev Web
Windows
Mac
Linux
@charset "UTF-8"; * { border: 0; box-sizing: border-box; margin: 0; padding: 0; } :root { --hue: 223; --bg: hsl(0,0%,100%); --fg: hsl(var(--hue),10%,10%); --trans-dur: 0.3s; --trans-ease-in-out: cubic-bezier(0.65,0,0.35,1); --trans-ease-out: cubic-bezier(0.33,1,0.68,1); font-size: calc(20px + (40 - 20) * (100vw - 280px) / (3840 - 280)); } body, button { color: var(--fg); font: 1em/1.5 "DM Sans", sans-serif; transition: background-color var(--trans-dur), color var(--trans-dur); } body { background-color: var(--bg); display: flex; height: 100vh; } main { margin: auto; padding: 0.75em 0; width: min-content; height: 12em; } .drop { --drop-trans-dur: 0.5s; --drop-flare-dist: 0; border-radius: 0.5em; margin: auto; padding: 0.25em 0.25em 0 0.25em; position: relative; min-width: 9em; } .drop, .drop:after { background-color: hsl(var(--hue), 10%, 90%); transition: background-color var(--trans-dur); } .drop:before, .drop:after { content: ""; position: absolute; } .drop:before { background-image: radial-gradient(100% 100% at 100% 50%, hsla(var(--hue), 90%, 50%, 0.5), hsla(var(--hue), 90%, 50%, 0) 50%); display: none; right: 0; bottom: 100%; width: 9em; height: 9em; } .drop:after { border-radius: 0.4375em; display: block; top: 0.125em; left: 0.125em; width: calc(100% - 0.25em); height: calc(100% - 0.25em); } .drop__btn { background-color: white; border-radius: 0.375em; box-shadow: 0 0 0 0.25em hsla(var(--hue), 90%, 50%, 0); cursor: pointer; display: flex; align-items: center; line-height: 1; height: 2.5em; outline: transparent; margin-bottom: 0.25em; padding: 0.75em; position: relative; width: 100%; transition: background-color var(--trans-dur), box-shadow calc(var(--trans-dur) / 2) var(--trans-ease-in-out), color var(--drop-trans-dur); -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent; z-index: 1; } .drop__btn:hover, .drop__btn:focus-visible, .drop__btn[aria-expanded=true] { background-color: hsl(var(--hue), 10%, 95%); } .drop__btn:focus-visible { box-shadow: 0 0 0 0.25em hsla(var(--hue), 90%, 50%, 1); } .drop__btn:after { border-top: 0.375em solid currentColor; border-right: 0.375em solid transparent; border-left: 0.375em solid transparent; content: ""; display: block; margin-inline-start: auto; width: 0; height: 0; transform-origin: 50% 37.5%; transition: transform var(--drop-trans-dur) var(--trans-ease-out); } .drop__btn[aria-expanded=true]:after { transform: rotate(0.5turn); } .drop--collapsing, .drop--expanding, .drop__items { overflow: hidden; } .drop__items { height: 0; } .drop__items-inner { visibility: hidden; } .drop--collapsing .drop__btn[aria-expanded=true] { background-color: white; } .drop--collapsing .drop__btn[aria-expanded=true]:after { transform: rotate(0); } .drop--expanding:before { display: block; } .drop__items .drop__btn:hover, .drop__items .drop__btn:focus-visible { background-color: hsl(var(--hue), 10%, 97.5%); color: hsla(var(--hue), 90%, 50%); } .drop__items .drop__btn:focus-visible { box-shadow: 0 0 0 0.25em hsla(var(--hue), 90%, 50%, 0); } .drop__items .drop__btn:after { border: 0; content: "✓"; display: none; width: auto; height: auto; } .drop__items .drop__btn--selected:after { display: block; } .drop__btn[aria-expanded=true] ~ .drop__items { height: auto; } .drop__btn[aria-expanded=true] ~ .drop__items .drop__items-inner { visibility: visible; } .drop:has([aria-expanded=true]) { height: auto; } .drop:has([aria-expanded=true]):before { transform: translateY(calc(9em + var(--drop-flare-dist))); transition: transform var(--drop-trans-dur) linear; } /* Dark theme */ @media (prefers-color-scheme: dark) { :root { --bg: hsl(var(--hue),10%,10%); --fg: hsl(var(--hue),10%,90%); } .drop, .drop:after { background-color: black; } .drop__btn { background-color: hsl(var(--hue), 10%, 10%); } .drop__btn:hover, .drop__btn:focus-visible, .drop__btn[aria-expanded=true] { background-color: hsl(var(--hue), 10%, 20%); } .drop__items .drop__btn:hover, .drop__items .drop__btn:focus-visible { background-color: hsl(var(--hue), 10%, 15%); color: hsla(var(--hue), 90%, 70%); } .drop--collapsing .drop__btn[aria-expanded=true] { background-color: hsl(var(--hue), 10%, 10%); } }
"use strict"; window.addEventListener("DOMContentLoaded", () => { const drop = new DropdownMenu("#dummy"); }); class DropdownMenu { /** * @param el CSS selector of the menu */ constructor(el) { var _a, _b, _c; /** Animation objects */ this.animations = []; /** Options for this menu */ this.options = [ { name: "windows", friendlyName: "Windows" }, { name: "mac", friendlyName: "Mac" }, { name: "linux", friendlyName: "Linux" } ]; /** Selected option by name */ this.selected = "windows"; /** Menu is collapsing */ this.isCollapsing = false; /** Menu is expanding */ this.isExpanding = false; /** Actions to run after collapsing the item. */ this.animActionsCollapse = { onfinish: this.onAnimationFinish.bind(this, false), oncancel: () => { this.isCollapsing = false; } }; /** Actions to run after expanding the item. */ this.animActionsExpand = { onfinish: this.onAnimationFinish.bind(this, true), oncancel: () => { this.isExpanding = false; } }; this.el = document.querySelector(el); this.menuButton = (_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector("button"); this.itemList = (_b = this.el) === null || _b === void 0 ? void 0 : _b.querySelector("[data-items]"); this.defaultOption(); document.addEventListener("click", this.outsideToClose.bind(this)); window.addEventListener("keydown", this.escToClose.bind(this)); (_c = this.el) === null || _c === void 0 ? void 0 : _c.addEventListener("click", this.toggle.bind(this)); window.addEventListener("keydown", this.kbdAction.bind(this)); } /** Transition duration specific to the menu */ get transDuration() { if (this.el) { const style = getComputedStyle(this.el); const rawDur = style.getPropertyValue("--drop-trans-dur"); let dur = rawDur.substring(0, rawDur.indexOf("s")); const mIndex = dur.indexOf("m"); if (mIndex > -1) { dur = dur.substring(0, mIndex); return +dur; } // seconds to milliseconds return +dur * 1e3; } return 0; } /** Display the default selected option. */ defaultOption() { var _a; const buttonEl = (_a = this.itemList) === null || _a === void 0 ? void 0 : _a.querySelector(`[value="${this.selected}"]`); buttonEl === null || buttonEl === void 0 ? void 0 : buttonEl.classList.add("drop__btn--selected"); if (this.menuButton) { const optionFound = this.options.find(option => option.name === this.selected); this.menuButton.textContent = (optionFound === null || optionFound === void 0 ? void 0 : optionFound.friendlyName) || ""; } } /** * Navigate the menu options with arrow keys. * @param e Keydown event */ kbdAction(e) { var _a; const { key } = e; const tabOrArrow = key === "Tab" || key === "ArrowUp" || key === "ArrowDown"; const notAnimating = !this.isExpanding && !this.isCollapsing; if (notAnimating && ((_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.ariaExpanded) === "true" && tabOrArrow) { this.navigateOption(e); } } /** * Press Esc to close the menu. * @param e Keydown event */ escToClose(e) { var _a; if (e.key === "Escape" && (!this.isCollapsing && ((_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.ariaExpanded) === "true")) { this.toggle(e); } } /** * Click outside the menu to close. * @param e Click event */ outsideToClose(e) { var _a; let target = e.target; let elFound = false; if (!this.isCollapsing && ((_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.ariaExpanded) === "true") { do { target = target.parentElement; if (target === this.el) { elFound = true; } } while (target); if (!elFound) { this.toggle(e); } } } /** * Navigate the menu options with arrow keys. * @param e Keydown event */ navigateOption(e) { var _a; const itemList = (_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector("[data-items]"); const buttonEls = itemList === null || itemList === void 0 ? void 0 : itemList.querySelectorAll("button"); const buttons = Array.from(buttonEls || []); const buttonsTemp = [...buttons]; // for getting the first and last const first = buttonsTemp.shift(); const last = buttonsTemp.pop(); const currentItem = document.activeElement; const { key, shiftKey } = e; const downKey = key === "ArrowDown"; const upKey = key === "ArrowUp"; const currentIndex = buttons.indexOf(currentItem); if (!buttons.length) { // do nothing for no items e.preventDefault(); } else if (downKey) { // next item, go to the first if on the last e.preventDefault(); const nextIndex = currentIndex + 1; if (nextIndex >= buttons.length) { first === null || first === void 0 ? void 0 : first.focus(); return; } buttons[nextIndex].focus(); } else if (upKey) { // previous item, go to the last if on the first e.preventDefault(); const prevIndex = currentIndex - 1; if (prevIndex < 0) { last === null || last === void 0 ? void 0 : last.focus(); return; } buttons[prevIndex].focus(); } else if (buttons.length === 1 || ((!(itemList === null || itemList === void 0 ? void 0 : itemList.contains(currentItem)) || currentItem === last) && !shiftKey)) { // go to the first item if on the last e.preventDefault(); first === null || first === void 0 ? void 0 : first.focus(); } else if ((!(itemList === null || itemList === void 0 ? void 0 : itemList.contains(currentItem)) || currentItem === first) && shiftKey) { // go to the last item if on the first e.preventDefault(); last === null || last === void 0 ? void 0 : last.focus(); } } /** * Open or close the menu. * @param e Click event */ toggle(e) { var _a, _b; e.preventDefault(); (_a = this.el) === null || _a === void 0 ? void 0 : _a.classList.remove("drop--collapsing", "drop--expanding"); const shouldExpand = ((_b = this.menuButton) === null || _b === void 0 ? void 0 : _b.ariaExpanded) === "true"; if (this.isCollapsing || !shouldExpand) { this.expand(); } else if (this.isExpanding || shouldExpand) { this.collapse(e); } } /** Expand the menu. */ expand() { var _a, _b; if (!this.el || !this.itemList) return; this.itemList.style.height = `${this.itemList.offsetHeight}px`; (_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-expanded", "true"); this.el.classList.add("drop--expanding"); this.isExpanding = true; // reset the animations this.animations.forEach(anim => anim.cancel()); this.animations = []; const buttonEls = this.itemList.querySelectorAll("button"); const buttons = Array.from(buttonEls || []); // animate the menu height const startHeight = this.itemList.offsetHeight || 0; const endHeight = ((_b = this.itemList.firstElementChild) === null || _b === void 0 ? void 0 : _b.offsetHeight) || 0; const itemListAnim = this.itemList.animate({ height: [`${startHeight}px`, `${endHeight}px`] }, { duration: this.transDuration, easing: "cubic-bezier(0.33,1,0.68,1.33)" }); itemListAnim.onfinish = this.animActionsExpand.onfinish; itemListAnim.oncancel = this.animActionsExpand.oncancel; this.animations.push(itemListAnim); // animate the buttons buttons.forEach((button, i) => { // animate the buttons const buttomAnim = button.animate({ transform: ["translateY(100%)", "translateY(0)"] }, { duration: this.transDuration, delay: this.transDuration / 12 * i, easing: "cubic-bezier(0.33,1,0.68,1)" }); this.animations.push(buttomAnim); }); // animate the flare this.el.style.setProperty("--drop-flare-dist", `${endHeight}px`); } /** * Collapse the menu. * @param e Click event */ collapse(e) { var _a, _b, _c; if (!this.el || !this.itemList) return; this.el.classList.add("drop--collapsing"); this.isCollapsing = true; // reset the animations this.animations.forEach(anim => anim.cancel()); this.animations = []; const clickedButton = e.target; const buttonEls = (_a = this.itemList) === null || _a === void 0 ? void 0 : _a.querySelectorAll("button"); const buttons = Array.from(buttonEls || []); // animate the menu height const startHeight = ((_b = this.itemList) === null || _b === void 0 ? void 0 : _b.offsetHeight) || 0; const endHeight = 0; const easing = "cubic-bezier(0.33,1,0.68,1)"; const itemListAnim = this.itemList.animate({ height: [`${startHeight}px`, `${endHeight}px`] }, { duration: this.transDuration, easing }); itemListAnim.onfinish = this.animActionsCollapse.onfinish; itemListAnim.oncancel = this.animActionsCollapse.oncancel; this.animations.push(itemListAnim); // animate the buttons buttons.forEach((button, i) => { if (clickedButton.value) { // remove the previous checkmark after selecting an option button.classList.remove("drop__btn--selected"); } // animate the buttons const delayInc = this.transDuration / 12; const buttomAnim = button.animate({ transform: ["translateY(0)", "translateY(100%)"] }, { duration: this.transDuration, delay: delayInc * (buttons.length - 1) - (delayInc * i), easing }); this.animations.push(buttomAnim); }); if (clickedButton.value) { // set this menu’s option from a `value` clickedButton.classList.add("drop__btn--selected"); if (this.menuButton) { const optionFound = this.options.find(option => option.name === clickedButton.value); this.menuButton.textContent = (optionFound === null || optionFound === void 0 ? void 0 : optionFound.friendlyName) || ""; } } (_c = this.menuButton) === null || _c === void 0 ? void 0 : _c.focus(); // animate the flare this.el.style.setProperty("--drop-flare-dist", `${endHeight}px`); } /** * Actions to run when the animation is finished * @param open Menu should be expanded */ onAnimationFinish(open) { var _a, _b; if (!this.el || !this.itemList) return; (_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-expanded", `${open}`); this.animations = []; this.isCollapsing = false; this.isExpanding = false; this.itemList.style.height = ""; (_b = this.el) === null || _b === void 0 ? void 0 : _b.classList.remove("drop--collapsing", "drop--expanding"); } }
Validating your code, please wait...
HTML
CSS
JS
Andev Web
Windows
Mac
Linux
@charset "UTF-8"; * { border: 0; box-sizing: border-box; margin: 0; padding: 0; } :root { --hue: 223; --bg: hsl(0,0%,100%); --fg: hsl(var(--hue),10%,10%); --trans-dur: 0.3s; --trans-ease-in-out: cubic-bezier(0.65,0,0.35,1); --trans-ease-out: cubic-bezier(0.33,1,0.68,1); font-size: calc(20px + (40 - 20) * (100vw - 280px) / (3840 - 280)); } body, button { color: var(--fg); font: 1em/1.5 "DM Sans", sans-serif; transition: background-color var(--trans-dur), color var(--trans-dur); } body { background-color: var(--bg); display: flex; height: 100vh; } main { margin: auto; padding: 0.75em 0; width: min-content; height: 12em; } .drop { --drop-trans-dur: 0.5s; --drop-flare-dist: 0; border-radius: 0.5em; margin: auto; padding: 0.25em 0.25em 0 0.25em; position: relative; min-width: 9em; } .drop, .drop:after { background-color: hsl(var(--hue), 10%, 90%); transition: background-color var(--trans-dur); } .drop:before, .drop:after { content: ""; position: absolute; } .drop:before { background-image: radial-gradient(100% 100% at 100% 50%, hsla(var(--hue), 90%, 50%, 0.5), hsla(var(--hue), 90%, 50%, 0) 50%); display: none; right: 0; bottom: 100%; width: 9em; height: 9em; } .drop:after { border-radius: 0.4375em; display: block; top: 0.125em; left: 0.125em; width: calc(100% - 0.25em); height: calc(100% - 0.25em); } .drop__btn { background-color: white; border-radius: 0.375em; box-shadow: 0 0 0 0.25em hsla(var(--hue), 90%, 50%, 0); cursor: pointer; display: flex; align-items: center; line-height: 1; height: 2.5em; outline: transparent; margin-bottom: 0.25em; padding: 0.75em; position: relative; width: 100%; transition: background-color var(--trans-dur), box-shadow calc(var(--trans-dur) / 2) var(--trans-ease-in-out), color var(--drop-trans-dur); -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent; z-index: 1; } .drop__btn:hover, .drop__btn:focus-visible, .drop__btn[aria-expanded=true] { background-color: hsl(var(--hue), 10%, 95%); } .drop__btn:focus-visible { box-shadow: 0 0 0 0.25em hsla(var(--hue), 90%, 50%, 1); } .drop__btn:after { border-top: 0.375em solid currentColor; border-right: 0.375em solid transparent; border-left: 0.375em solid transparent; content: ""; display: block; margin-inline-start: auto; width: 0; height: 0; transform-origin: 50% 37.5%; transition: transform var(--drop-trans-dur) var(--trans-ease-out); } .drop__btn[aria-expanded=true]:after { transform: rotate(0.5turn); } .drop--collapsing, .drop--expanding, .drop__items { overflow: hidden; } .drop__items { height: 0; } .drop__items-inner { visibility: hidden; } .drop--collapsing .drop__btn[aria-expanded=true] { background-color: white; } .drop--collapsing .drop__btn[aria-expanded=true]:after { transform: rotate(0); } .drop--expanding:before { display: block; } .drop__items .drop__btn:hover, .drop__items .drop__btn:focus-visible { background-color: hsl(var(--hue), 10%, 97.5%); color: hsla(var(--hue), 90%, 50%); } .drop__items .drop__btn:focus-visible { box-shadow: 0 0 0 0.25em hsla(var(--hue), 90%, 50%, 0); } .drop__items .drop__btn:after { border: 0; content: "✓"; display: none; width: auto; height: auto; } .drop__items .drop__btn--selected:after { display: block; } .drop__btn[aria-expanded=true] ~ .drop__items { height: auto; } .drop__btn[aria-expanded=true] ~ .drop__items .drop__items-inner { visibility: visible; } .drop:has([aria-expanded=true]) { height: auto; } .drop:has([aria-expanded=true]):before { transform: translateY(calc(9em + var(--drop-flare-dist))); transition: transform var(--drop-trans-dur) linear; } /* Dark theme */ @media (prefers-color-scheme: dark) { :root { --bg: hsl(var(--hue),10%,10%); --fg: hsl(var(--hue),10%,90%); } .drop, .drop:after { background-color: black; } .drop__btn { background-color: hsl(var(--hue), 10%, 10%); } .drop__btn:hover, .drop__btn:focus-visible, .drop__btn[aria-expanded=true] { background-color: hsl(var(--hue), 10%, 20%); } .drop__items .drop__btn:hover, .drop__items .drop__btn:focus-visible { background-color: hsl(var(--hue), 10%, 15%); color: hsla(var(--hue), 90%, 70%); } .drop--collapsing .drop__btn[aria-expanded=true] { background-color: hsl(var(--hue), 10%, 10%); } }
"use strict"; window.addEventListener("DOMContentLoaded", () => { const drop = new DropdownMenu("#dummy"); }); class DropdownMenu { /** * @param el CSS selector of the menu */ constructor(el) { var _a, _b, _c; /** Animation objects */ this.animations = []; /** Options for this menu */ this.options = [ { name: "windows", friendlyName: "Windows" }, { name: "mac", friendlyName: "Mac" }, { name: "linux", friendlyName: "Linux" } ]; /** Selected option by name */ this.selected = "windows"; /** Menu is collapsing */ this.isCollapsing = false; /** Menu is expanding */ this.isExpanding = false; /** Actions to run after collapsing the item. */ this.animActionsCollapse = { onfinish: this.onAnimationFinish.bind(this, false), oncancel: () => { this.isCollapsing = false; } }; /** Actions to run after expanding the item. */ this.animActionsExpand = { onfinish: this.onAnimationFinish.bind(this, true), oncancel: () => { this.isExpanding = false; } }; this.el = document.querySelector(el); this.menuButton = (_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector("button"); this.itemList = (_b = this.el) === null || _b === void 0 ? void 0 : _b.querySelector("[data-items]"); this.defaultOption(); document.addEventListener("click", this.outsideToClose.bind(this)); window.addEventListener("keydown", this.escToClose.bind(this)); (_c = this.el) === null || _c === void 0 ? void 0 : _c.addEventListener("click", this.toggle.bind(this)); window.addEventListener("keydown", this.kbdAction.bind(this)); } /** Transition duration specific to the menu */ get transDuration() { if (this.el) { const style = getComputedStyle(this.el); const rawDur = style.getPropertyValue("--drop-trans-dur"); let dur = rawDur.substring(0, rawDur.indexOf("s")); const mIndex = dur.indexOf("m"); if (mIndex > -1) { dur = dur.substring(0, mIndex); return +dur; } // seconds to milliseconds return +dur * 1e3; } return 0; } /** Display the default selected option. */ defaultOption() { var _a; const buttonEl = (_a = this.itemList) === null || _a === void 0 ? void 0 : _a.querySelector(`[value="${this.selected}"]`); buttonEl === null || buttonEl === void 0 ? void 0 : buttonEl.classList.add("drop__btn--selected"); if (this.menuButton) { const optionFound = this.options.find(option => option.name === this.selected); this.menuButton.textContent = (optionFound === null || optionFound === void 0 ? void 0 : optionFound.friendlyName) || ""; } } /** * Navigate the menu options with arrow keys. * @param e Keydown event */ kbdAction(e) { var _a; const { key } = e; const tabOrArrow = key === "Tab" || key === "ArrowUp" || key === "ArrowDown"; const notAnimating = !this.isExpanding && !this.isCollapsing; if (notAnimating && ((_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.ariaExpanded) === "true" && tabOrArrow) { this.navigateOption(e); } } /** * Press Esc to close the menu. * @param e Keydown event */ escToClose(e) { var _a; if (e.key === "Escape" && (!this.isCollapsing && ((_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.ariaExpanded) === "true")) { this.toggle(e); } } /** * Click outside the menu to close. * @param e Click event */ outsideToClose(e) { var _a; let target = e.target; let elFound = false; if (!this.isCollapsing && ((_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.ariaExpanded) === "true") { do { target = target.parentElement; if (target === this.el) { elFound = true; } } while (target); if (!elFound) { this.toggle(e); } } } /** * Navigate the menu options with arrow keys. * @param e Keydown event */ navigateOption(e) { var _a; const itemList = (_a = this.el) === null || _a === void 0 ? void 0 : _a.querySelector("[data-items]"); const buttonEls = itemList === null || itemList === void 0 ? void 0 : itemList.querySelectorAll("button"); const buttons = Array.from(buttonEls || []); const buttonsTemp = [...buttons]; // for getting the first and last const first = buttonsTemp.shift(); const last = buttonsTemp.pop(); const currentItem = document.activeElement; const { key, shiftKey } = e; const downKey = key === "ArrowDown"; const upKey = key === "ArrowUp"; const currentIndex = buttons.indexOf(currentItem); if (!buttons.length) { // do nothing for no items e.preventDefault(); } else if (downKey) { // next item, go to the first if on the last e.preventDefault(); const nextIndex = currentIndex + 1; if (nextIndex >= buttons.length) { first === null || first === void 0 ? void 0 : first.focus(); return; } buttons[nextIndex].focus(); } else if (upKey) { // previous item, go to the last if on the first e.preventDefault(); const prevIndex = currentIndex - 1; if (prevIndex < 0) { last === null || last === void 0 ? void 0 : last.focus(); return; } buttons[prevIndex].focus(); } else if (buttons.length === 1 || ((!(itemList === null || itemList === void 0 ? void 0 : itemList.contains(currentItem)) || currentItem === last) && !shiftKey)) { // go to the first item if on the last e.preventDefault(); first === null || first === void 0 ? void 0 : first.focus(); } else if ((!(itemList === null || itemList === void 0 ? void 0 : itemList.contains(currentItem)) || currentItem === first) && shiftKey) { // go to the last item if on the first e.preventDefault(); last === null || last === void 0 ? void 0 : last.focus(); } } /** * Open or close the menu. * @param e Click event */ toggle(e) { var _a, _b; e.preventDefault(); (_a = this.el) === null || _a === void 0 ? void 0 : _a.classList.remove("drop--collapsing", "drop--expanding"); const shouldExpand = ((_b = this.menuButton) === null || _b === void 0 ? void 0 : _b.ariaExpanded) === "true"; if (this.isCollapsing || !shouldExpand) { this.expand(); } else if (this.isExpanding || shouldExpand) { this.collapse(e); } } /** Expand the menu. */ expand() { var _a, _b; if (!this.el || !this.itemList) return; this.itemList.style.height = `${this.itemList.offsetHeight}px`; (_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-expanded", "true"); this.el.classList.add("drop--expanding"); this.isExpanding = true; // reset the animations this.animations.forEach(anim => anim.cancel()); this.animations = []; const buttonEls = this.itemList.querySelectorAll("button"); const buttons = Array.from(buttonEls || []); // animate the menu height const startHeight = this.itemList.offsetHeight || 0; const endHeight = ((_b = this.itemList.firstElementChild) === null || _b === void 0 ? void 0 : _b.offsetHeight) || 0; const itemListAnim = this.itemList.animate({ height: [`${startHeight}px`, `${endHeight}px`] }, { duration: this.transDuration, easing: "cubic-bezier(0.33,1,0.68,1.33)" }); itemListAnim.onfinish = this.animActionsExpand.onfinish; itemListAnim.oncancel = this.animActionsExpand.oncancel; this.animations.push(itemListAnim); // animate the buttons buttons.forEach((button, i) => { // animate the buttons const buttomAnim = button.animate({ transform: ["translateY(100%)", "translateY(0)"] }, { duration: this.transDuration, delay: this.transDuration / 12 * i, easing: "cubic-bezier(0.33,1,0.68,1)" }); this.animations.push(buttomAnim); }); // animate the flare this.el.style.setProperty("--drop-flare-dist", `${endHeight}px`); } /** * Collapse the menu. * @param e Click event */ collapse(e) { var _a, _b, _c; if (!this.el || !this.itemList) return; this.el.classList.add("drop--collapsing"); this.isCollapsing = true; // reset the animations this.animations.forEach(anim => anim.cancel()); this.animations = []; const clickedButton = e.target; const buttonEls = (_a = this.itemList) === null || _a === void 0 ? void 0 : _a.querySelectorAll("button"); const buttons = Array.from(buttonEls || []); // animate the menu height const startHeight = ((_b = this.itemList) === null || _b === void 0 ? void 0 : _b.offsetHeight) || 0; const endHeight = 0; const easing = "cubic-bezier(0.33,1,0.68,1)"; const itemListAnim = this.itemList.animate({ height: [`${startHeight}px`, `${endHeight}px`] }, { duration: this.transDuration, easing }); itemListAnim.onfinish = this.animActionsCollapse.onfinish; itemListAnim.oncancel = this.animActionsCollapse.oncancel; this.animations.push(itemListAnim); // animate the buttons buttons.forEach((button, i) => { if (clickedButton.value) { // remove the previous checkmark after selecting an option button.classList.remove("drop__btn--selected"); } // animate the buttons const delayInc = this.transDuration / 12; const buttomAnim = button.animate({ transform: ["translateY(0)", "translateY(100%)"] }, { duration: this.transDuration, delay: delayInc * (buttons.length - 1) - (delayInc * i), easing }); this.animations.push(buttomAnim); }); if (clickedButton.value) { // set this menu’s option from a `value` clickedButton.classList.add("drop__btn--selected"); if (this.menuButton) { const optionFound = this.options.find(option => option.name === clickedButton.value); this.menuButton.textContent = (optionFound === null || optionFound === void 0 ? void 0 : optionFound.friendlyName) || ""; } } (_c = this.menuButton) === null || _c === void 0 ? void 0 : _c.focus(); // animate the flare this.el.style.setProperty("--drop-flare-dist", `${endHeight}px`); } /** * Actions to run when the animation is finished * @param open Menu should be expanded */ onAnimationFinish(open) { var _a, _b; if (!this.el || !this.itemList) return; (_a = this.menuButton) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-expanded", `${open}`); this.animations = []; this.isCollapsing = false; this.isExpanding = false; this.itemList.style.height = ""; (_b = this.el) === null || _b === void 0 ? void 0 : _b.classList.remove("drop--collapsing", "drop--expanding"); } }