UNPKG

@sandlada/vue-mdc

Version:

![Vue MDC Logo](https://raw.githubusercontent.com/sandlada/vue-mdc/refs/heads/main/docs/vue-mdc-cover.png)

308 lines (307 loc) 10.6 kB
/** * @license * Copyright 2025 Sandlada & Kai Orion * SPDX-License-Identifier: MIT */ import { defineComponent, createVNode, createTextVNode } from "vue"; import { isServer } from "../../utils/is-server.js"; import { redispatchEvent } from "../../internals/events/redispatch-event.js"; import "../../internals/navigation/render-navigation-destination.js"; import { componentNamePrefix } from "../../internals/component-name-prefix/component-name-prefix.js"; import { Icon } from "../icon/icon.js"; import "../icon/icon.definition.js"; import { NavigationDrawerDefaultCloseAnimation, NavigationDrawerDefaultOpenAnimation } from "./animations.js"; import { NavigationDrawerController } from "./navigation-drawer-controller.js"; import { NavigationDrawerHeadline } from "./navigation-drawer-headline.js"; import { NavigationDrawerIndicator } from "./navigation-drawer-indicator.js"; import { props } from "./navigation-drawer.definition.js"; import { ENavigationDrawerState } from "./navigation-state.js"; import css from "./styles/navigation-drawer.module.scss.js"; const NavigationDrawer = /* @__PURE__ */ defineComponent({ name: `${componentNamePrefix}-navigation-drawer`, props, slots: {}, emits: ["open", "opend", "cancel", "close", "closed", "submit", "update:modelValue"], data: () => ({ animationController: null, open: false, opening: false, nextClickIsFromContent: false, escapePressedWithoutCancel: false, isAtScrollTop: false, isAtScrollBottom: false, intersectionObserver: null, controller: null }), computed: { isOpen: { get() { return this.open; }, set(value) { if (this.open === value) { return; } this.open = value; } } }, mounted() { if (isServer()) { return; } const root = this.$el; this.controller = new NavigationDrawerController(this.$el, this.show, this.close); this.isOpen = this.defaultOpen; if (this.isOpen) { this.setRootAttribute(ENavigationDrawerState.Opend); } else { this.setRootAttribute(ENavigationDrawerState.Closed); } this.intersectionObserver = new IntersectionObserver((entries) => { for (const entry of entries) { this.handleAnchorIntersection(entry); } }, { root: this.getScrollerElement() }); this.intersectionObserver.observe(this.getTopAnchorElement()); this.intersectionObserver.observe(this.getBottomAnchorElement()); this.getContainerElement().addEventListener("click", this.handleContentClick); this.getDialogElement().addEventListener("click", this.handleDialogClick); this.getDialogElement().addEventListener("cancel", this.handleCancel); this.getDialogElement().addEventListener("close", this.handleClose); this.getDialogElement().addEventListener("keydown", this.handleKeydown); this.getScrimElement().addEventListener("click", this.handleClose); root.addEventListener("request-route", this.handleRequestRoute); }, methods: { getDialogElement() { return this.$el.querySelector(`&>.${css.dialog}`); }, getScrimElement() { return this.$el.querySelector(`&>.${css.scrim}`); }, getContainerElement() { return this.$el.querySelector(`&>.${css.dialog}>.${css.container}`); }, getScrollerElement() { return this.$el.querySelector(`&>.${css.dialog}>.${css.scroller}`); }, getTopAnchorElement() { return this.$el.querySelector(`& .${css.top}.${css.anchor}`); }, getBottomAnchorElement() { return this.$el.querySelector(`& .${css.bottom}.${css.anchor}`); }, setRootAttribute(state) { const root = this.$el; root.setAttribute("state", state); }, handleRequestRoute(e) { this.controller?.setActiveIndicatorByElement(e.detail.element); }, handleClose(e) { if (!this.escapePressedWithoutCancel) { return; } this.escapePressedWithoutCancel = false; this.getDialogElement().dispatchEvent(new Event("cancel", { cancelable: true })); }, handleCancel(e) { if (e.target !== this.getDialogElement()) { return; } this.escapePressedWithoutCancel = false; this.$emit("cancel", e); const preventCancel = !redispatchEvent(this.$el, e); e.preventDefault(); if (preventCancel) { return; } this.close(); }, handleContentClick() { this.nextClickIsFromContent = true; }, handleDialogClick() { if (this.nextClickIsFromContent) { this.nextClickIsFromContent = false; return; } const cancelEvent = new Event("cancel"); this.$emit("cancel", cancelEvent); const preventCancel = !this.$el.dispatchEvent(cancelEvent); if (preventCancel) { return; } this.close(); }, handleKeydown(event) { if (event.key !== "Escape") { return; } this.escapePressedWithoutCancel = true; setTimeout(() => { this.escapePressedWithoutCancel = false; }); }, handleAnchorIntersection(entry) { const { target, isIntersecting } = entry; if (target === this.getTopAnchorElement()) { this.isAtScrollTop = isIntersecting; } if (target === this.getBottomAnchorElement()) { this.isAtScrollBottom = isIntersecting; } }, async show() { this.opening = true; if (this.getDialogElement().open || !this.opening) { this.opening = false; return; } const openEvent = new Event("open", { bubbles: false, cancelable: true, composed: false }); this.$emit("open", openEvent); const preventOpen = !this.$el.dispatchEvent(openEvent); this.setRootAttribute(ENavigationDrawerState.Opening); if (preventOpen) { this.isOpen = false; this.opening = false; return; } this.isOpen = true; if (this.modal) { this.getDialogElement().showModal(); } else { this.getDialogElement().show(); } await this.animateDialog(NavigationDrawerDefaultOpenAnimation); this.opening = false; const opendEvent = new Event("opend", { bubbles: false, cancelable: false, composed: false }); this.$emit("opend", opendEvent); this.$el.dispatchEvent(opendEvent); this.setRootAttribute(ENavigationDrawerState.Opend); }, async close() { this.opening = false; if (!this.getDialogElement().open || this.opening) { this.isOpen = false; return; } const closeEvent = new Event("close", { bubbles: false, cancelable: true, composed: false }); this.$emit("close", closeEvent); const preventClose = !this.$el.dispatchEvent(closeEvent); this.setRootAttribute(ENavigationDrawerState.Closing); if (preventClose) { return; } await this.animateDialog(NavigationDrawerDefaultCloseAnimation); this.getDialogElement().close(); this.isOpen = false; const closedEvent = new Event("closed"); this.$emit("closed", closedEvent); this.$el.dispatchEvent(closeEvent); this.setRootAttribute(ENavigationDrawerState.Closed); }, async animateDialog(animation) { this.animationController?.abort(); this.animationController = new AbortController(); const { container: containerAnimate, dialog: dialogAnimate, scrim: scrimAnimate, headline: headlineAnimate // content: contentAnimate, // actions: actionsAnimate, } = animation; const elementAndAnimation = [ [this.getDialogElement(), dialogAnimate ?? []], [this.getScrimElement(), scrimAnimate ?? []], [this.getContainerElement(), containerAnimate ?? []] // [this.getHeadlineElement(), headlineAnimate ?? []], // [this.getContentElement(), contentAnimate ?? []], // [this.getActionsElement(), actionsAnimate ?? []], ]; const animations = []; for (const [element, animation2] of elementAndAnimation) { for (const animateArgs of animation2) { const animation3 = element.animate(...animateArgs); this.animationController.signal.addEventListener("abort", () => { animation3.cancel(); }); animations.push(animation3); } } await Promise.all(animations.map((animation2) => animation2.finished.catch(() => { }))); } }, render() { const scrollable = this.isOpen && !(this.isAtScrollTop && this.isAtScrollBottom); const classes = [this.modal ? css.modal : css.standard, scrollable && css.scrollable, scrollable && !this.isAtScrollTop && css["show-top-divider"], scrollable && !this.isAtScrollBottom && css["show-bottom-divider"]]; return createVNode("div", { "data-component": "navigation-drawer", "role": "dialog", "class": [css["navigation-drawer-component-styles"], ...classes] }, [createVNode("span", { "aria-hidden": "true", "class": css.scrim }, null), createVNode("dialog", { "class": css.dialog, "role": "alertdialog" }, [createVNode("div", { "class": css.container }, [createVNode("div", { "class": css.scroller }, [createVNode("div", { "class": css["content-wrapper"] }, [createVNode("div", { "class": [css.top, css.anchor] }, null), createVNode("span", { "class": css.content }, [createVNode(NavigationDrawerHeadline, null, { default: () => [createTextVNode("Headline One")] }), createVNode("div", { "class": css["indicator-list"] }, [createVNode(NavigationDrawerIndicator, { "defaultActive": true }, { default: () => "Label", icon: () => createVNode(Icon, null, { default: () => [createTextVNode("Menu")] }) }), createVNode(NavigationDrawerIndicator, null, { default: () => [createTextVNode("Label")] }), createVNode(NavigationDrawerIndicator, null, { default: () => [createTextVNode("Label")] })]), this.$slots.default && this.$slots.default()]), createVNode("div", { "class": [css.bottom, css.anchor] }, null)])]), createVNode("span", { "aria-hidden": "true", "class": css.background }, null)])])]); }, inheritAttrs: true }); export { NavigationDrawer }; //# sourceMappingURL=navigation-drawer.js.map