UNPKG

@agnos-ui/core

Version:

Framework-agnostic headless component library.

249 lines (248 loc) 7.34 kB
import { computed, readable } from "@amadeus-it-group/tansu"; import { b as typeBoolean, e as typeFunction, j as typeString, f as typeHTMLElementOrNull } from "./writables-DYGjj5T3.js"; import { writablesForProps, stateStores } from "./utils/stores.js"; import { createTransition } from "./services/transitions/baseTransitions.js"; import { p as promiseFromStore } from "./promise-D-RZVPuv.js"; import { noop } from "./utils/func.js"; import { r as removeScrollbars, a as revertScrollbars } from "./scrollbars-CcxSrnCx.js"; import { u as registrationArray, b as bindDirective, g as createAttributesDirective, d as browserDirective, s as mergeDirectives, o as directiveSubscribe, c as bindDirectiveNoArg } from "./dom-Bb5ZCr_a.js"; import { portal } from "./services/portal.js"; import { siblingsInert } from "./services/siblingsInert.js"; import { createWidgetFactory } from "./utils/widget.js"; import { focusElement } from "./services/focusElement.js"; const modalOutsideClick = /* @__PURE__ */ Symbol(); const modalCloseButtonClick = /* @__PURE__ */ Symbol(); const modalCloseEscape = /* @__PURE__ */ Symbol(); const defaultConfig = { animated: true, ariaCloseButtonLabel: "Close", backdrop: true, backdropClass: "", backdropTransition: noop, closeButton: true, closeOnOutsideClick: true, container: typeof window !== "undefined" ? document.body : null, className: "", modalTransition: noop, onBeforeClose: noop, onVisibleChange: noop, onHidden: noop, onShown: noop, visible: false }; const configValidator = { animated: typeBoolean, ariaCloseButtonLabel: typeString, backdrop: typeBoolean, backdropClass: typeString, backdropTransition: typeFunction, closeButton: typeBoolean, closeOnOutsideClick: typeBoolean, container: typeHTMLElementOrNull, className: typeString, modalTransition: typeFunction, onBeforeClose: typeFunction, onVisibleChange: typeFunction, onHidden: typeFunction, onShown: typeFunction, visible: typeBoolean }; function getModalDefaultConfig() { return { ...defaultConfig }; } const modals$ = registrationArray(); const hasModals$ = computed(() => modals$().length > 0); const scrollbarsAction$ = computed(() => { if (hasModals$()) { removeScrollbars(); } else { revertScrollbars(); } }); const modalsAction$ = computed(() => { scrollbarsAction$(); }); const createModal = createWidgetFactory("modal", (config$) => { const [ { animated$, backdrop$, backdropTransition$, closeOnOutsideClick$, container$, modalTransition$, onBeforeClose$, onVisibleChange$, onHidden$, onShown$, ariaCloseButtonLabel$, className$, backdropClass$, visible$: requestedVisible$, ...stateProps }, patch ] = writablesForProps(defaultConfig, config$, configValidator); const modalTransition = createTransition({ props: { transition: modalTransition$, visible: requestedVisible$, animated: animated$, animatedOnInit: animated$, onVisibleChange: onVisibleChange$, // TODO: for onHidden and onShown, should we combine with information from the backdrop transition? // (especially in case one of the two transitions takes more time than the other) onHidden: onHidden$, onShown: onShown$ } }); const visible$ = modalTransition.stores.visible$; const backdropTransition = createTransition({ props: { transition: backdropTransition$, visible: requestedVisible$, animated: animated$, animatedOnInit: animated$ } }); const transitioning$ = computed(() => modalTransition.stores.transitioning$() || backdrop$() && backdropTransition.stores.transitioning$()); const hidden$ = computed(() => !transitioning$() && !visible$()); const backdropHidden$ = computed(() => !backdrop$() || hidden$()); let hideResult; const close = (result) => { hideResult = result; const beforeCloseEvent = { get result() { return hideResult; }, set result(value) { hideResult = value; }, cancel: false }; onBeforeClose$()(beforeCloseEvent); if (beforeCloseEvent.cancel) { return; } patch({ visible: false }); }; const modalPortalDirective = bindDirective( portal, computed(() => ({ container: container$() })) ); const backdropPortalDirective = bindDirective( portal, computed(() => { const container = container$(); const element = container ? modalTransition.stores.element$() : void 0; return { container, insertBefore: element?.parentElement === container ? element : void 0 }; }) ); const registerModalAction$ = readable(void 0, () => modals$.register(res)); const action$ = computed(() => { if (modalTransition.stores.elementPresent$() && !hidden$()) { registerModalAction$(); } modalsAction$(); }); const closeButtonDirective = createAttributesDirective(() => ({ attributes: { type: readable("button"), "aria-label": ariaCloseButtonLabel$ }, events: { click: () => close(modalCloseButtonClick) } })); const backdropAttributeDirective = createAttributesDirective(() => ({ attributes: { class: backdropClass$ } })); const modalAttributeDirective = createAttributesDirective(() => ({ attributes: { class: className$, tabIndex: readable("-1") }, events: { click: (event) => { if (event.currentTarget === event.target && closeOnOutsideClick$()) { close(modalOutsideClick); } }, keydown: (event) => { const { key } = event; switch (key) { case "Escape": close(modalCloseEscape); event.stopPropagation(); break; } } } })); const res = { ...stateStores({ backdropHidden$, container$, hidden$, transitioning$, visible$, modalElement$: modalTransition.stores.element$, ariaCloseButtonLabel$, className$, backdropClass$, ...stateProps }), directives: { modalPortalDirective, backdropPortalDirective, backdropDirective: mergeDirectives(backdropTransition.directives.directive, backdropAttributeDirective), modalDirective: mergeDirectives( modalTransition.directives.directive, bindDirectiveNoArg(siblingsInert), directiveSubscribe(action$), modalAttributeDirective, focusElement ), closeButtonDirective, dialogDirective: bindDirective( browserDirective((dialog, visible) => { const update = (visible2) => { if (visible2) { dialog.showModal(); } else { dialog.close(); } }; update(visible); return { update }; }), visible$ ) }, patch, api: { close, async open() { patch({ visible: true }); await promiseFromStore(hidden$).promise; return hideResult; }, patch } }; return res; }); export { modalCloseEscape as a, modalOutsideClick as b, createModal as c, getModalDefaultConfig as g, modalCloseButtonClick as m };