UNPKG

ranui

Version:

A framework-agnostic Web Components UI library built on native custom elements, with TypeScript types, light/dark theming, SSR and PWA support.

367 lines (366 loc) 19.8 kB
import { a as T, n as O, o as F, r as D, s as B, t as L } from "./utils-D1iZnChu.js"; import { t as M } from "./events-C59wGdni.js"; import { _ as d, i as l, n as b, p as g } from "./factory-ZNhcQ4tf.js"; import { a as z, n as P } from "./ssr-DSGwPtEd.js"; var H = ".remove-wap-active-focus{outline:0;-webkit-tap-highlight-color:transparent}.remove-wap-active-focus:active,.remove-wap-active-focus:focus{outline:0;-webkit-tap-highlight-color:transparent}:host{position:var(--ran-modal-host-position, static)}.ran-modal-root{position:var(--ran-modal-root-position, fixed);inset:var(--ran-modal-root-inset, 0);z-index:var(--ran-modal-root-z-index, var(--ran-z-modal, 1000));display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity .3s,visibility .3s}.ran-modal-root[open]{opacity:1;visibility:visible}.ran-modal-mask{position:var(--ran-modal-mask-position, absolute);inset:var(--ran-modal-mask-inset, 0);background:var(--ran-modal-mask-background, rgba(0, 0, 0, .45))}.ran-modal-dialog{position:var(--ran-modal-dialog-position, relative);width:var(--ran-modal-dialog-width, min(560px, calc(100vw - 32px) ));margin:var(--ran-modal-dialog-margin, 0 auto);background:var(--ran-modal-dialog-background, var(--ran-color-bg-elevated, #fff));border-image-source:var(--ran-modal-dialog-border-image-source, none);border-image-slice:var(--ran-modal-dialog-border-image-slice, 8);border-image-width:var(--ran-modal-dialog-border-image-width, 4px);border-image-outset:0;border-image-repeat:stretch;border-radius:var(--ran-modal-dialog-border-radius, var(--ran-radius-md, 12px));box-shadow:var(--ran-modal-dialog-box-shadow, var(--ran-shadow-modal, var(--ran-shadow-elevated, 0 10px 32px rgba(0, 0, 0, .2))));outline:var(--ran-modal-dialog-outline, none);max-height:var(--ran-modal-dialog-max-height, 80vh );overflow:var(--ran-modal-dialog-overflow, hidden);transform:translateY(var(--ran-modal-dialog-offset-y, clamp(-56px, -6vh, -24px))) scale(.9);transition:transform .3s cubic-bezier(.075,.82,.165,1)}.ran-modal-root[open] .ran-modal-dialog{transform:translateY(var(--ran-modal-dialog-offset-y, clamp(-56px, -6vh, -24px))) scale(1)}.ran-modal-header{display:var(--ran-modal-header-display, flex);align-items:var(--ran-modal-header-align-items, center);justify-content:var(--ran-modal-header-justify-content, space-between);padding:var(--ran-modal-header-padding, 16px 18px);border-bottom:var(--ran-modal-header-border-bottom, 1px solid var(--ran-color-border-secondary, #f0f0f0))}.ran-modal-root.hide-header .ran-modal-header{position:absolute;top:0;right:0;z-index:1;padding:var(--ran-modal-hide-header-padding, 10px);border-bottom:none;background:transparent}.ran-modal-root.hide-header .ran-modal-title{display:none}.ran-modal-title{margin:var(--ran-modal-title-margin, 0);font-size:var(--ran-modal-title-font-size, 16px);line-height:var(--ran-modal-title-line-height, 1.5);color:var(--ran-modal-title-color, var(--ran-color-text, #111827))}.ran-modal-close{border:var(--ran-modal-close-border, none);background:var(--ran-modal-close-background, transparent);color:var(--ran-modal-close-color, var(--ran-color-text-secondary, #667085));font-size:var(--ran-modal-close-font-size, 18px);cursor:var(--ran-modal-close-cursor, pointer);padding:var(--ran-modal-close-padding, 0 4px)}.ran-modal-body{padding:var(--ran-modal-body-padding, 18px);max-height:var(--ran-modal-body-max-height, 68vh );overflow:var(--ran-modal-body-overflow, auto);color:var(--ran-modal-body-color, var(--ran-color-text-secondary, #344054))}.ran-modal-footer{display:none;align-items:var(--ran-modal-footer-align-items, center);justify-content:var(--ran-modal-footer-justify-content, flex-end);gap:var(--ran-modal-footer-gap, 10px);padding:var(--ran-modal-footer-padding, 12px 18px 18px);border-top:var(--ran-modal-footer-border-top, 1px solid var(--ran-color-border-secondary, #f0f0f0))}.ran-modal-root.has-footer .ran-modal-footer{display:flex}.ran-modal-footer::slotted(.ran-modal-actions){display:inline-flex;align-items:center;justify-content:flex-end;gap:10px}.ran-modal-footer::slotted(.ran-modal-action){border:1px solid var(--ran-color-border, #d0d5dd);background:var(--ran-color-bg-elevated, #fff);border-radius:var(--ran-radius-sm, 6px);height:34px;line-height:32px;padding:0 14px;cursor:pointer;font-size:14px;color:var(--ran-color-text-secondary, #344054)}.ran-modal-footer::slotted(.ran-modal-action-confirm){border-color:var(--ran-color-primary, #155eef);background:var(--ran-color-primary, #155eef);color:#fff}.ran-modal-footer::slotted(.ran-modal-action:disabled){opacity:.6;cursor:not-allowed}", v = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])', I = class o extends P { static _openStack = []; static _bodyScrollLockCount = 0; static _bodyOverflow = ""; static _bodyPaddingRight = ""; static _inertSnapshot = /* @__PURE__ */ new Map(); _events = new M(); _shadowDom; _root; _mask; _dialog; _title; _closeBtn; _slot; _footer; _footerSlot; _labelId; _previousActiveElement; _isDialogOpen; _scrollLocked; _afterOpenTimer; _afterCloseTimer; static get observedAttributes() { return [ "open", "title", "maskClosable", "closeOnEsc", "lockScroll", "autoFocus", "closable", "hide-header", "sheet" ]; } constructor() { super(), this._labelId = `ran-modal-title-${Math.random().toString(36).slice(2, 9)}`, this._previousActiveElement = null, this._isDialogOpen = !1, this._scrollLocked = !1, this._afterOpenTimer = null, this._afterCloseTimer = null, this._shadowDom = O(this, H); const t = L(this._shadowDom, ".ran-modal-root", () => l().class("ran-modal-root").part("root").children(l().class("ran-modal-mask").part("mask"), l().class("ran-modal-dialog").part("dialog").role("dialog").attr("aria-modal", "true").tabIndex(-1).labelledBy(this._labelId).children(d("header").class("ran-modal-header").part("header").children(d("h3").class("ran-modal-title").part("title").id(this._labelId), d("button").class("ran-modal-close").part("close").attr("type", "button").label("Close dialog").text("x")), l().class("ran-modal-body").part("body").children(g()), d("footer").class("ran-modal-footer").part("footer").children(g().attr("name", "footer")))).build()); this._root = t, this._mask = this._root.querySelector(".ran-modal-mask"), this._dialog = this._root.querySelector(".ran-modal-dialog"), this._title = this._root.querySelector(".ran-modal-title"), this._closeBtn = this._root.querySelector(".ran-modal-close"), this._slot = this._root.querySelector("slot"), this._footer = this._root.querySelector(".ran-modal-footer"), this._footerSlot = this._root.querySelector('slot[name="footer"]'), this.syncTitle(), this.syncClosable(), this.syncFooter(), this.syncHeader(); } get open() { return this.hasAttribute("open") && !B.includes(this.getAttribute("open")); } set open(t) { !t || t === "false" ? this.removeAttribute("open") : this.setAttribute("open", ""); } get title() { return this.getAttribute("title") || ""; } set title(t) { this.setAttribute("title", t || ""); } get maskClosable() { const t = this.getAttribute("maskClosable"); return !(t === "false" || t === "0"); } set maskClosable(t) { !t || t === "false" || t === "0" ? this.setAttribute("maskClosable", "false") : this.setAttribute("maskClosable", "true"); } get sheet() { return D(this, "sheet"); } set sheet(t) { T(this, "sheet", t); } get closeOnEsc() { const t = this.getAttribute("closeOnEsc"); return !(t === "false" || t === "0"); } set closeOnEsc(t) { !t || t === "false" || t === "0" ? this.setAttribute("closeOnEsc", "false") : this.setAttribute("closeOnEsc", "true"); } get lockScroll() { const t = this.getAttribute("lockScroll"); return !(t === "false" || t === "0"); } set lockScroll(t) { !t || t === "false" || t === "0" ? this.setAttribute("lockScroll", "false") : this.setAttribute("lockScroll", "true"); } get autoFocus() { const t = this.getAttribute("autoFocus"); return !(t === "false" || t === "0"); } set autoFocus(t) { !t || t === "false" || t === "0" ? this.setAttribute("autoFocus", "false") : this.setAttribute("autoFocus", "true"); } get closable() { const t = this.getAttribute("closable"); return !(t === "false" || t === "0"); } set closable(t) { !t || t === "false" || t === "0" ? this.setAttribute("closable", "false") : this.setAttribute("closable", "true"); } get hideHeader() { const t = this.getAttribute("hide-header"); return t !== null && t !== "false" && t !== "0"; } set hideHeader(t) { !t || t === "false" || t === "0" ? this.removeAttribute("hide-header") : this.setAttribute("hide-header", ""); } handlerExternalCss = () => { F(this, this._shadowDom, "sheet", null, this.sheet); }; syncTitle = () => { this._title.textContent = this.title || "Modal", this.hideHeader && this._dialog.setAttribute("aria-label", this.title || "Modal"); }; syncClosable = () => { const t = this.closable; this._closeBtn.hidden = !t, this._closeBtn.setAttribute("aria-hidden", t ? "false" : "true"), this._closeBtn.tabIndex = t ? 0 : -1; }; syncHeader = () => { const t = this.hideHeader; this._root.classList.toggle("hide-header", t), t ? (this._dialog.setAttribute("aria-label", this.title || "Modal"), this._dialog.removeAttribute("aria-labelledby")) : (this._dialog.setAttribute("aria-labelledby", this._labelId), this._dialog.removeAttribute("aria-label")); }; syncFooter = () => { const t = (this._footerSlot?.assignedNodes({ flatten: !0 }) || []).some((e) => e.nodeType === Node.ELEMENT_NODE ? !0 : e.nodeType === Node.TEXT_NODE ? (e.textContent || "").trim().length > 0 : !1); this._root.classList.toggle("has-footer", t); }; getFocusableElements = () => { const t = Array.from(this._dialog.querySelectorAll(v)), e = Array.from(this.querySelectorAll(v)); return [...t, ...e].filter((r) => { if (r.hasAttribute("disabled")) return !1; const i = r; return i.offsetParent !== null || i.getClientRects().length > 0; }); }; static getTopModal = () => o._openStack.length === 0 ? null : o._openStack[o._openStack.length - 1] || null; static pushOpenModal = (t) => { o._openStack.includes(t) || o._openStack.push(t); }; static removeOpenModal = (t) => { o._openStack = o._openStack.filter((e) => e !== t); }; static syncBackgroundInert = () => { if (typeof document > "u") return; const t = document.body; if (!t) return; for (const [r, i] of o._inertSnapshot.entries()) r.inert = i.inert, i.ariaHidden === null ? r.removeAttribute("aria-hidden") : r.setAttribute("aria-hidden", i.ariaHidden); if (o._inertSnapshot.clear(), o._openStack.length === 0) return; const e = o._openStack.map((r) => r); Array.from(t.children).forEach((r) => { e.some((i) => r === i || r.contains(i)) || (o._inertSnapshot.set(r, { inert: r.inert, ariaHidden: r.getAttribute("aria-hidden") }), r.inert = !0, r.setAttribute("aria-hidden", "true")); }); }; static lockBodyScroll = () => { if (typeof document > "u") return; const t = document.body; if (t) { if (o._bodyScrollLockCount === 0) { o._bodyOverflow = t.style.overflow, o._bodyPaddingRight = t.style.paddingRight; const e = Math.max(0, window.innerWidth - document.documentElement.clientWidth); if (e > 0) { const r = Number.parseFloat(window.getComputedStyle(t).paddingRight) || 0; t.style.paddingRight = `${r + e}px`; } t.style.overflow = "hidden"; } o._bodyScrollLockCount += 1; } }; static unlockBodyScroll = () => { if (typeof document > "u") return; const t = document.body; !t || o._bodyScrollLockCount === 0 || (o._bodyScrollLockCount -= 1, o._bodyScrollLockCount === 0 && (t.style.overflow = o._bodyOverflow, t.style.paddingRight = o._bodyPaddingRight)); }; clearAfterTimers = () => { this._afterOpenTimer !== null && (window.clearTimeout(this._afterOpenTimer), this._afterOpenTimer = null), this._afterCloseTimer !== null && (window.clearTimeout(this._afterCloseTimer), this._afterCloseTimer = null); }; getTransitionTimeout = () => { const t = window.getComputedStyle(this._dialog), e = (n) => { const s = n.split(",")[0]?.trim() || "0s"; return s.endsWith("ms") ? Number.parseFloat(s) : s.endsWith("s") ? Number.parseFloat(s) * 1e3 : Number.parseFloat(s) || 0; }, r = e(t.transitionDelay), i = e(t.transitionDuration); return Math.max(0, r + i); }; emitAfterOpen = () => { const t = this.getTransitionTimeout(); this._afterOpenTimer = window.setTimeout(() => { this._afterOpenTimer = null, this.dispatchEvent(new CustomEvent("afteropen")); }, t); }; emitAfterClose = (t) => { const e = this.getTransitionTimeout(); this._afterCloseTimer = window.setTimeout(() => { this._afterCloseTimer = null, this.dispatchEvent(new CustomEvent("afterclose", { detail: { trigger: t } })); }, e); }; trapFocus = (t) => { if (t.key !== "Tab") return; const e = this.getFocusableElements(); if (e.length === 0) { t.preventDefault(), this._dialog.focus(); return; } const r = e[0], i = e[e.length - 1], n = this._shadowDom.activeElement || document.activeElement; if (t.shiftKey && n === r) { t.preventDefault(), i.focus(); return; } !t.shiftKey && n === i && (t.preventDefault(), r.focus()); }; close = (t = "program") => { if (!this.open) return; const e = new CustomEvent("beforeclose", { cancelable: !0, detail: { trigger: t } }); this.dispatchEvent(e) && (this.removeAttribute("open"), this.dispatchEvent(new CustomEvent("close", { detail: { trigger: t } })), this.emitAfterClose(t)); }; openDialog = () => { if (this._isDialogOpen || this._root.hasAttribute("open")) return; const t = new CustomEvent("beforeopen", { cancelable: !0 }); if (!this.dispatchEvent(t)) { this.removeAttribute("open"); return; } this._previousActiveElement = document.activeElement, o.pushOpenModal(this), o.syncBackgroundInert(), this.lockScroll && (o.lockBodyScroll(), this._scrollLocked = !0), this._isDialogOpen = !0, this._root.setAttribute("open", ""), this._dialog.setAttribute("aria-hidden", "false"), this.dispatchEvent(new CustomEvent("open")), this.clearAfterTimers(), this.emitAfterOpen(), requestAnimationFrame(() => { this.autoFocus && (this.getFocusableElements()[0] || this._dialog).focus(); }); }; closeDialog = () => { if (!this._isDialogOpen) { this._root.removeAttribute("open"), this._dialog.setAttribute("aria-hidden", "true"); return; } this._isDialogOpen = !1, o.removeOpenModal(this), o.syncBackgroundInert(), this._scrollLocked && (o.unlockBodyScroll(), this._scrollLocked = !1), this._root.removeAttribute("open"), this._dialog.setAttribute("aria-hidden", "true"), this._previousActiveElement instanceof HTMLElement && this._previousActiveElement.focus(); }; onMaskClick = (t) => { this.maskClosable && (t.composedPath().includes(this._dialog) || this.close("mask")); }; onCloseButtonClick = () => { this.close("button"); }; onKeydown = (t) => { if (this.open && o.getTopModal() === this) { if (t.key === "Escape" && this.closeOnEsc) { t.preventDefault(), this.close("escape"); return; } this.trapFocus(t); } }; onDocumentFocusIn = (t) => { if (!this.open || o.getTopModal() !== this) return; const e = t.composedPath(); e.includes(this) || e.includes(this._dialog) || (this.getFocusableElements()[0] || this._dialog).focus(); }; connectedCallback() { this.handlerExternalCss(), this._events.on(this._mask, "click", this.onMaskClick).on(this._closeBtn, "click", this.onCloseButtonClick).on(document, "keydown", this.onKeydown).on(document, "focusin", this.onDocumentFocusIn).on(this._footerSlot, "slotchange", this.syncFooter), this.syncTitle(), this.syncClosable(), this.syncFooter(), this.syncHeader(), this.open ? this.openDialog() : this.closeDialog(); } disconnectedCallback() { this._root.hasAttribute("open") && this.closeDialog(), this._events.abort(), this.clearAfterTimers(); } attributeChangedCallback(t, e, r) { if (e !== r) { if (t === "title") { this.syncTitle(); return; } if (t === "closable") { this.syncClosable(); return; } if (t === "hide-header") { this.syncHeader(); return; } if (t === "open") { this.open ? this.openDialog() : this.closeDialog(); return; } t === "sheet" && this.handlerExternalCss(); } } static normalizeProgrammaticOptions = (t) => typeof t == "string" ? { content: t } : t || {}; static open = (t) => { if (typeof document > "u") return Promise.resolve({ action: "dismiss", trigger: "program" }); const { title: e = "Confirm", content: r = "", okText: i = "OK", cancelText: n = "Cancel", showCancel: s = !1, maskClosable: y = !1, closeOnEsc: _ = !0, lockScroll: k = !0, autoFocus: C = !0, closable: x = !0, onConfirm: w, onCancel: A } = o.normalizeProgrammaticOptions(t); return new Promise((S) => { const a = d("r-modal").build(); a.title = e, a.maskClosable = y, a.closeOnEsc = _, a.lockScroll = k, a.autoFocus = C, a.closable = x; const u = l().build(); typeof r == "string" ? u.textContent = r : r instanceof Node && u.appendChild(r), a.appendChild(u); let m = "dismiss"; const f = l().attr("slot", "footer").class("ran-modal-actions"); if (s) { const h = b().attr("type", "button").class("ran-modal-action ran-modal-action-cancel").text(n).build(); h.addEventListener("click", async () => { await A?.() !== !1 && (m = "cancel", a.close("program")); }), f.children(h); } const c = b().attr("type", "button").class("ran-modal-action ran-modal-action-confirm").text(i).build(); c.addEventListener("click", async () => { c.disabled = !0; try { if (await w?.() === !1) return; m = "confirm", a.close("program"); } finally { c.disabled = !1; } }), f.children(c), a.appendChild(f.build()); const p = (h) => { const E = h.detail?.trigger || "program"; a.removeEventListener("afterclose", p), a.parentElement && a.parentElement.removeChild(a), S({ action: m, trigger: E }); }; a.addEventListener("afterclose", p), document.body.appendChild(a), a.open = !0; }); }; static confirm = (t) => { const e = o.normalizeProgrammaticOptions(t); return o.open({ ...e, showCancel: !0 }); }; static info = (t) => { const e = o.normalizeProgrammaticOptions(t); return o.open({ ...e, showCancel: !1, title: e.title || "Info" }); }; static success = (t) => { const e = o.normalizeProgrammaticOptions(t); return o.open({ ...e, showCancel: !1, title: e.title || "Success" }); }; static warning = (t) => { const e = o.normalizeProgrammaticOptions(t); return o.open({ ...e, showCancel: !1, title: e.title || "Warning" }); }; static error = (t) => { const e = o.normalizeProgrammaticOptions(t); return o.open({ ...e, showCancel: !1, title: e.title || "Error" }); }; }; z("r-modal", I); export { I as t };