UNPKG

gd-bs

Version:

Bootstrap JavaScript, TypeScript and Web Components library.

340 lines (290 loc) 11 kB
import { IOffcanvas, IOffcanvasOptions, IOffcanvasProps } from "./types"; import { ClassNames } from "../classNames"; import { Base } from "../base"; import { appendContent } from "../common"; import { HTML } from "./templates"; /** * Offcanvas Size */ export enum OffcanvasSize { Small = 1, Small1 = 2, Small2 = 3, Small3 = 4, Medium = 5, Medium1 = 6, Medium2 = 7, Medium3 = 8, Large = 9, Large1 = 10, Large2 = 11, Large3 = 12, XLarge = 13, XXLarge = 14, Full = 15 } /** * Offcanvas Size Classes */ export const OffcanvasSizeClassNames = new ClassNames([ "offcanvas-sm", "offcanvas-size-sm1", "offcanvas-size-sm2", "offcanvas-size-sm3", "offcanvas-md", "offcanvas-size-md1", "offcanvas-size-md2", "offcanvas-size-md3", "offcanvas-lg", "offcanvas-size-lg1", "offcanvas-size-lg2", "offcanvas-size-lg3", "offcanvas-xl", "offcanvas-xxl", "offcanvas-size-full" ]); /** * Offcanvas Types */ export enum OffcanvasTypes { Bottom = 1, End = 2, Start = 3, Top = 4 } /** * Offcanvas Types Classes */ export const OffcanvasTypesClassNames = new ClassNames([ "offcanvas-bottom", "offcanvas-end", "offcanvas-start", "offcanvas-top" ]); /** * Offcanvas */ class _Offcanvas extends Base<IOffcanvasProps> implements IOffcanvas { private _autoClose: boolean = null; private _eventCreated: boolean = false; private _tranisitioningFl: boolean = false; // Constructor constructor(props: IOffcanvasProps, template: string = HTML) { super(template, props); // Configure the offcanvas this.configure(); // Configure the events this.configureEvents(); // Configure the parent this.configureParent(); } // Configure the card group private configure() { // Set the attributes this.props.id ? this.el.id = this.props.id : null; // Set the size & type this.setSize(this.props.size); this.setType(this.props.type); // Get the options let options: IOffcanvasOptions = this.props.options || { backdrop: true, keyboard: true, scroll: false }; // Set the properties options.backdrop ? this.el.setAttribute("data-bs-body", "backdrop") : null; options.scroll ? this.el.setAttribute("data-bs-body", "scroll") : null; // Set the header appendContent(this.el.querySelector(".offcanvas-header > div"), this.props.title); // Set the body appendContent(this.el.querySelector(".offcanvas-body"), this.props.body); // Set the focus if (options.focus) { (this.el as HTMLElement).focus(); } // Set the visibility if (options.visible) { this.toggle(); } } // Configures the auto-close event private configureAutoCloseEvent() { // See if the event exists if (this._eventCreated) { return; } // Ensure the body exists if (document.body) { // Add a click event to the modal document.body.addEventListener("click", (ev: MouseEvent) => { // See if the auto close flag is set if (this._autoClose) { // Do nothing if we are tranisitionsing if (this._tranisitioningFl) { return; } // Do nothing if we clicked within the offcanvas if (ev.composedPath().includes(this.el)) { return; } else { // Get the mouse coordinates let x = ev.clientX; let y = ev.clientY; let elCoordinate = (this.el as HTMLElement).getBoundingClientRect(); // See if we clicked within the offcanvas if (x <= elCoordinate.right && x >= elCoordinate.left && y <= elCoordinate.bottom && y >= elCoordinate.top) { return; } // Else, see if something was selected else if (x == 0 && y == 0) { return; } } // Close the offcanvas if it's visible if (this.isVisible) { this.toggle(); } } }); // Set the flag this._eventCreated = true; } else { // Add the load event window.addEventListener("load", () => { // Configure the event this.configureAutoCloseEvent(); }); } } // Configure the events private configureEvents() { // Execute the events this.props.onRenderHeader ? this.props.onRenderHeader(this.el.querySelector(".offcanvas-header > div"), this.props) : null; this.props.onRenderBody ? this.props.onRenderBody(this.el.querySelector(".offcanvas-body"), this.props) : null; // Get the close button let elClose = this.el.querySelector(".btn-close"); if (elClose) { // Add a click event elClose.addEventListener("click", () => { // Hide the modal this.hide(); }); } // Set the flag to determine if the modal is sticky this.setAutoClose(this.props.options && typeof (this.props.options.autoClose) === "boolean" ? this.props.options.autoClose : true); // See if the keyboard option is set if (this.props.options && this.props.options.keyboard) { // Add a click event (this.el as HTMLElement).addEventListener("keydown", ev => { // See if the escape key was clicked and the modal is visible if ((ev.key === "Escape" || ev.keyCode == 27) && this.isVisible) { // Toggle the modal this.toggle(); } }); } } /** * Public Interface */ // Hides the modal hide() { // See if a transition is currently happening if (this._tranisitioningFl) { // Wait for the transition to complete let id = setInterval(() => { // See if the transition is complete if (!this._tranisitioningFl) { // Stop the loop clearInterval(id); // Toggle the modal this.isVisible ? this.toggle() : null; } }, 250); } else { // Toggle the modal this.isVisible ? this.toggle() : null; } } // Returns true if the modal is visible get isVisible() { return this.el.classList.contains("show"); } // Updates the auto close flag setAutoClose(value: boolean) { // Set the flag this._autoClose = value; // Configure the event if we are setting the value this._autoClose ? this.configureAutoCloseEvent() : null; } // Sets the offcanvas size setSize(offcanvasSize: number) { // Parse the class names OffcanvasSizeClassNames.parse(className => { // Remove the class names this.el.classList.remove(className); }); // Set the class name let className = OffcanvasSizeClassNames.getByType(offcanvasSize); if (className) { this.el.classList.add(className); } } // Sets the offcanvas type setType(offcanvasType: number) { // Parse the class names OffcanvasTypesClassNames.parse(className => { // Remove the class names this.el.classList.remove(className); }); // Set the class name let className = OffcanvasTypesClassNames.getByType(offcanvasType) || OffcanvasTypesClassNames.getByType(OffcanvasTypes.End); this.el.classList.add(className); } // Shows the modal show() { // See if a transition is currently happening if (this._tranisitioningFl) { // Wait for the transition to complete let id = setInterval(() => { // See if the transition is complete if (!this._tranisitioningFl) { // Stop the loop clearInterval(id); // Toggle the modal this.isVisible ? null : this.toggle(); } }, 250); } else { // Toggle the modal this.isVisible ? null : this.toggle(); } } // Toggles the modal toggle() { let backdrop = document.querySelector(".offcanvas-backdrop"); // Set the flag this._tranisitioningFl = true; // See if this modal is visible if (this.isVisible) { // Hide the modal this.el.classList.add("offcanvas-toggling"); this.el.classList.remove("show"); // Wait for the animation to complete setTimeout(() => { this.el.style.visibility = "hidden"; this.el.classList.remove("offcanvas-toggling"); // Remove the backdrop backdrop ? document.body.removeChild(backdrop) : null; backdrop = null; // Set the flag this._tranisitioningFl = false; // Call the event this.props.onClose ? this.props.onClose(this.el) : null; }, 250); } else { // Create the backdrop if we are showing it let showBackdrop = this.props.options && typeof (this.props.options.backdrop) === "boolean" ? this.props.options.backdrop : true; if (showBackdrop && backdrop == null) { backdrop = document.createElement("div"); backdrop.classList.add("offcanvas-backdrop"); backdrop.classList.add("fade"); backdrop.classList.add("show"); document.body.appendChild(backdrop); } // Show the modal this.el.style.visibility = "visible"; this.el.classList.add("offcanvas-toggling"); this.el.classList.add("show"); // Wait for the animation to complete setTimeout(() => { // Update the class this.el.classList.remove("offcanvas-toggling"); // Set the flag this._tranisitioningFl = false; }, 250); } } } export const Offcanvas = (props: IOffcanvasProps, template?: string): IOffcanvas => { return new _Offcanvas(props, template); }