@oruga-ui/oruga-next
Version:
UI components for Vue.js and CSS framework agnostic
1 lines • 14.3 kB
Source Map (JSON)
{"version":3,"file":"modal.mjs","sources":["../../src/components/modal/Modal.vue","../../src/components/modal/useModalProgrammatic.ts","../../src/components/modal/index.ts"],"sourcesContent":["<script setup lang=\"ts\" generic=\"C extends Component\">\nimport {\n ref,\n computed,\n watch,\n nextTick,\n onMounted,\n useTemplateRef,\n type Component,\n} from \"vue\";\n\nimport OIcon from \"../icon/Icon.vue\";\n\nimport { getDefault } from \"@/utils/config\";\nimport { toCssDimension } from \"@/utils/helpers\";\nimport { isClient } from \"@/utils/ssr\";\nimport {\n defineClasses,\n useClickOutside,\n useMatchMedia,\n usePreventScrolling,\n useTeleportDefault,\n useTrapFocus,\n} from \"@/composables\";\nimport type { CloseEventArgs } from \"../programmatic\";\n\nimport type { ModalProps } from \"./props\";\n\n/**\n * Classic modal overlay to include any content you may need.\n * @displayName Modal\n * @style _modal.scss\n */\ndefineOptions({\n isOruga: true,\n name: \"OModal\",\n configField: \"modal\",\n inheritAttrs: false,\n});\n\nconst props = withDefaults(defineProps<ModalProps<C>>(), {\n override: undefined,\n active: false,\n fullScreen: false,\n content: undefined,\n width: () => getDefault(\"modal.width\", 960),\n animation: () => getDefault(\"modal.animation\", \"zoom-out\"),\n overlay: () => getDefault(\"modal.overlay\", true),\n cancelable: () =>\n getDefault(\"modal.cancelable\", [\"escape\", \"x\", \"outside\"]),\n trapFocus: () => getDefault(\"modal.trapFocus\", true),\n alert: () => getDefault(\"modal.alert\", false),\n ariaLabel: () => getDefault(\"modal.ariaLabel\"),\n autoFocus: () => getDefault(\"modal.autoFocus\", true),\n closeIcon: () => getDefault(\"modal.closeIcon\", \"close\"),\n closeIconSize: () => getDefault(\"modal.closeIconSize\", \"medium\"),\n ariaCloseLabel: () => getDefault(\"modal.ariaCloseLabel\", \"Close\"),\n mobileBreakpoint: () => getDefault(\"modal.mobileBreakpoint\"),\n teleport: () => getDefault(\"modal.teleport\", false),\n clipScroll: () => getDefault(\"modal.clipScroll\", false),\n component: undefined,\n props: undefined,\n events: undefined,\n});\n\nconst emits = defineEmits<{\n /**\n * active prop two-way binding\n * @param value {boolean} - updated active prop\n */\n \"update:active\": [value: boolean];\n /**\n * on component close event\n * @param value {string} - close event method\n */\n close: [...args: [] | [string] | CloseEventArgs<C>];\n}>();\n\nconst { vTrapFocus } = useTrapFocus();\n\nconst rootRef = useTemplateRef(\"rootElement\");\nconst contentRef = useTemplateRef(\"contentElement\");\n\nconst isActive = defineModel<boolean>(\"active\", { default: false });\n\nconst { isMobile } = useMatchMedia(props.mobileBreakpoint);\n\nconst _teleport = computed(() =>\n typeof props.teleport === \"boolean\"\n ? { to: useTeleportDefault(), disabled: !props.teleport }\n : { to: props.teleport, disabled: false },\n);\n\nconst showX = computed(() =>\n Array.isArray(props.cancelable)\n ? props.cancelable.indexOf(\"x\") >= 0\n : props.cancelable,\n);\n\nconst customStyle = computed(() =>\n !props.fullScreen ? { maxWidth: toCssDimension(props.width) } : null,\n);\n\nconst toggleScroll = usePreventScrolling(props.clipScroll);\n\nwatch(isActive, (value) => {\n if (props.overlay) toggleScroll(value);\n // if autoFocus focus the element\n if (value && props.autoFocus)\n nextTick(() => {\n if (rootRef.value) rootRef.value.focus();\n });\n});\n\nonMounted(() => {\n if (isActive.value && props.overlay) toggleScroll(isActive.value);\n});\n\n// --- Events Feature ---\n\nif (isClient)\n if (!props.overlay)\n // register outside click event listener when is active\n useClickOutside(contentRef, onClickedOutside, {\n trigger: isActive,\n });\n\n/** Close fixed sidebar if clicked outside. */\nfunction onClickedOutside(event: Event): void {\n if (!isActive.value || isAnimating.value) return;\n if (\n props.overlay ||\n (contentRef.value && !event.composedPath().includes(contentRef.value))\n )\n event.preventDefault();\n cancel(\"outside\");\n}\n\n/** Escape key press event bound to the component root. */\nfunction onEscapePress(): void {\n if (!isActive.value) return;\n cancel(\"escape\");\n}\n\n/**\n * Check if method is cancelable.\n * Call close() with action `cancel`.\n * @param method Cancel method\n */\nfunction cancel(method: string): void {\n // check if method is cancelable\n if (\n (typeof props.cancelable === \"boolean\" && !props.cancelable) ||\n !props.cancelable ||\n (Array.isArray(props.cancelable) && !props.cancelable.includes(method))\n )\n return;\n close(method);\n}\n\n/** set active to false and emit close event */\nfunction close(...args: [] | [string] | CloseEventArgs<C>): void {\n isActive.value = false;\n emits(\"close\", ...args);\n}\n\n// --- Animation Feature ---\n\nconst isAnimating = ref(!props.active);\n\n/** Transition after-enter hook */\nfunction afterEnter(): void {\n isAnimating.value = false;\n}\n\n/** Transition before-leave hook */\nfunction beforeLeave(): void {\n isAnimating.value = true;\n}\n\n// --- Computed Component Classes ---\n\nconst rootClasses = defineClasses(\n [\"rootClass\", \"o-modal\"],\n [\"mobileClass\", \"o-modal--mobile\", null, isMobile],\n [\"activeClass\", \"o-modal--active\", null, isActive],\n);\n\nconst overlayClasses = defineClasses([\"overlayClass\", \"o-modal__overlay\"]);\n\nconst contentClasses = defineClasses(\n [\"contentClass\", \"o-modal__content\"],\n [\n \"fullScreenClass\",\n \"o-modal__content--full-screen\",\n null,\n computed(() => props.fullScreen),\n ],\n);\n\nconst closeClasses = defineClasses([\"closeClass\", \"o-modal__close\"]);\n\n// --- Expose Public Functionalities ---\n\n/** expose functionalities for programmatic usage */\ndefineExpose({ close });\n</script>\n\n<template>\n <Teleport :to=\"_teleport.to\" :disabled=\"_teleport.disabled\">\n <transition\n :name=\"animation\"\n @after-enter=\"afterEnter\"\n @before-leave=\"beforeLeave\">\n <div\n v-show=\"isActive\"\n ref=\"rootElement\"\n v-bind=\"$attrs\"\n v-trap-focus=\"trapFocus && isActive\"\n data-oruga=\"modal\"\n :class=\"rootClasses\"\n :tabindex=\"-1\"\n :role=\"alert ? 'alertdialog' : 'dialog'\"\n :aria-label=\"ariaLabel\"\n :aria-modal=\"isActive\"\n @keyup.escape=\"onEscapePress\">\n <div\n v-if=\"overlay\"\n :class=\"overlayClasses\"\n tabindex=\"-1\"\n @click=\"onClickedOutside\" />\n\n <div\n ref=\"contentElement\"\n :class=\"contentClasses\"\n :style=\"customStyle\">\n <!-- injected component for programmatic usage -->\n <component\n :is=\"component\"\n v-if=\"component\"\n v-bind=\"$props.props\"\n v-on=\"$props.events || {}\"\n @close=\"close\" />\n <!--\n @slot Modal default content, default is content prop\n @binding {(...args): void} close - function to close the component\n -->\n <slot v-else :close=\"close\">\n <div v-if=\"content\">{{ content }}</div>\n </slot>\n\n <o-icon\n v-if=\"showX\"\n v-show=\"!isAnimating\"\n :class=\"closeClasses\"\n :icon=\"closeIcon\"\n :size=\"closeIconSize\"\n clickable\n :aria-label=\"ariaCloseLabel\"\n @click=\"cancel('x')\" />\n </div>\n </div>\n </transition>\n </Teleport>\n</template>\n","import {\n type Component,\n type ComponentInternalInstance,\n type MaybeRefOrGetter,\n} from \"vue\";\nimport {\n InstanceRegistry,\n ComponentProgrammatic,\n type ProgrammaticComponentOptions,\n type ProgrammaticExpose,\n} from \"../programmatic\";\n\nimport Modal from \"./Modal.vue\";\n\nimport type { ModalProps } from \"./props\";\n\ndeclare module \"../../index\" {\n interface OrugaProgrammatic {\n modal: typeof ModalProgrammatic;\n }\n}\n\n/** modal component programmatic instance registry **/\nconst registry = new InstanceRegistry<ComponentInternalInstance>();\n\n/** useModalProgrammatic composable options */\nexport type ModalProgrammaticOptions<C extends Component> = Readonly<\n ModalProps<C>\n> &\n ProgrammaticComponentOptions<typeof Modal<C>>;\n\nconst ModalProgrammatic = {\n /** Returns the number of registered active instances. */\n count: registry.count,\n /**\n * Create a new programmatic modal component instance.\n * @param options modal content string or modal component props object\n * @param target specify a target the component get rendered into - default is `document.body`\n * @returns ProgrammaticExpose\n */\n open<C extends Component>(\n options: string | ModalProgrammaticOptions<C>,\n target?: MaybeRefOrGetter<string | HTMLElement | null>,\n ): ProgrammaticExpose<typeof Modal<C>> {\n const _options: ModalProgrammaticOptions<C> =\n typeof options === \"string\" ? { content: options } : options;\n\n const componentProps: ModalProps<C> = {\n active: true, // set the active default state to true\n ...(_options as ModalProps<C>),\n };\n\n // create programmatic component\n return ComponentProgrammatic.open(Modal, {\n registry, // custom programmatic instance registry\n target, // target the component get rendered into\n props: componentProps, // component specific props\n onClose: _options.onClose, // on close event handler\n });\n },\n /** Close the last registred instance in the modal programmatic instance registry. */\n close(...args: unknown[]): void {\n registry.last()?.exposed?.close(...args);\n },\n /** Close all instances in the programmatic modal instance registry. */\n closeAll(...args: unknown[]): void {\n registry.walk((entry) => entry.exposed?.close(...args));\n },\n};\n\nexport default ModalProgrammatic;\n","import type { App, Plugin } from \"vue\";\n\nimport Modal from \"./Modal.vue\";\nimport ModalProgrammatic from \"./useModalProgrammatic\";\n\nimport {\n registerComponent,\n registerComponentProgrammatic,\n} from \"@/utils/plugins\";\n\n/** export modal specific types */\nexport type { ModalProgrammaticOptions } from \"./useModalProgrammatic\";\n\n/** export modal plugin */\nexport default {\n install(app: App) {\n registerComponent(app, Modal);\n registerComponentProgrammatic(app, \"modal\", ModalProgrammatic);\n },\n} as Plugin;\n\n/** export modal components & composables */\nexport { Modal as OModal, ModalProgrammatic };\n"],"names":["_useModel","Modal"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,UAAM,QAAQ;AAyBd,UAAM,QAAQ;AAaR,UAAA,EAAE,WAAW,IAAI,aAAa;AAE9B,UAAA,UAAU,eAAe,aAAa;AACtC,UAAA,aAAa,eAAe,gBAAgB;AAE5C,UAAA,WAAWA,SAAoB,SAAC,QAA4B;AAElE,UAAM,EAAE,SAAa,IAAA,cAAc,MAAM,gBAAgB;AAEzD,UAAM,YAAY;AAAA,MAAS,MACvB,OAAO,MAAM,aAAa,YACpB,EAAE,IAAI,sBAAsB,UAAU,CAAC,MAAM,aAC7C,EAAE,IAAI,MAAM,UAAU,UAAU,MAAM;AAAA,IAChD;AAEA,UAAM,QAAQ;AAAA,MAAS,MACnB,MAAM,QAAQ,MAAM,UAAU,IACxB,MAAM,WAAW,QAAQ,GAAG,KAAK,IACjC,MAAM;AAAA,IAChB;AAEA,UAAM,cAAc;AAAA,MAAS,MACzB,CAAC,MAAM,aAAa,EAAE,UAAU,eAAe,MAAM,KAAK,MAAM;AAAA,IACpE;AAEM,UAAA,eAAe,oBAAoB,MAAM,UAAU;AAEnD,UAAA,UAAU,CAAC,UAAU;AACnB,UAAA,MAAM,QAAS,cAAa,KAAK;AAErC,UAAI,SAAS,MAAM;AACf,iBAAS,MAAM;AACX,cAAI,QAAQ,MAAe,SAAA,MAAM,MAAM;AAAA,QAAA,CAC1C;AAAA,IAAA,CACR;AAED,cAAU,MAAM;AACZ,UAAI,SAAS,SAAS,MAAM,QAAS,cAAa,SAAS,KAAK;AAAA,IAAA,CACnE;AAIG,QAAA,UAAA;AACA,UAAI,CAAC,MAAM;AAEP,wBAAgB,YAAY,kBAAkB;AAAA,UAC1C,SAAS;AAAA,QAAA,CACZ;AAAA,IAAA;AAGT,aAAS,iBAAiB,OAAoB;AAC1C,UAAI,CAAC,SAAS,SAAS,YAAY,MAAO;AAEtC,UAAA,MAAM,WACL,WAAW,SAAS,CAAC,MAAM,eAAe,SAAS,WAAW,KAAK;AAEpE,cAAM,eAAe;AACzB,aAAO,SAAS;AAAA,IAAA;AAIpB,aAAS,gBAAsB;AACvB,UAAA,CAAC,SAAS,MAAO;AACrB,aAAO,QAAQ;AAAA,IAAA;AAQnB,aAAS,OAAO,QAAsB;AAElC,UACK,OAAO,MAAM,eAAe,aAAa,CAAC,MAAM,cACjD,CAAC,MAAM,cACN,MAAM,QAAQ,MAAM,UAAU,KAAK,CAAC,MAAM,WAAW,SAAS,MAAM;AAErE;AACJ,YAAM,MAAM;AAAA,IAAA;AAIhB,aAAS,SAAS,MAA+C;AAC7D,eAAS,QAAQ;AACX,YAAA,SAAS,GAAG,IAAI;AAAA,IAAA;AAK1B,UAAM,cAAc,IAAI,CAAC,MAAM,MAAM;AAGrC,aAAS,aAAmB;AACxB,kBAAY,QAAQ;AAAA,IAAA;AAIxB,aAAS,cAAoB;AACzB,kBAAY,QAAQ;AAAA,IAAA;AAKxB,UAAM,cAAc;AAAA,MAChB,CAAC,aAAa,SAAS;AAAA,MACvB,CAAC,eAAe,mBAAmB,MAAM,QAAQ;AAAA,MACjD,CAAC,eAAe,mBAAmB,MAAM,QAAQ;AAAA,IACrD;AAEA,UAAM,iBAAiB,cAAc,CAAC,gBAAgB,kBAAkB,CAAC;AAEzE,UAAM,iBAAiB;AAAA,MACnB,CAAC,gBAAgB,kBAAkB;AAAA,MACnC;AAAA,QACI;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,MAAM,MAAM,UAAU;AAAA,MAAA;AAAA,IAEvC;AAEA,UAAM,eAAe,cAAc,CAAC,cAAc,gBAAgB,CAAC;AAKtD,aAAA,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtLtB,MAAM,WAAW,IAAI,iBAA4C;AAQjE,MAAM,oBAAoB;AAAA;AAAA,EAEtB,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhB,KACI,SACA,QACmC;AACnC,UAAM,WACF,OAAO,YAAY,WAAW,EAAE,SAAS,YAAY;AAEzD,UAAM,iBAAgC;AAAA,MAClC,QAAQ;AAAA;AAAA,MACR,GAAI;AAAA,IACR;AAGO,WAAA,sBAAsB,KAAKC,WAAO;AAAA,MACrC;AAAA;AAAA,MACA;AAAA;AAAA,MACA,OAAO;AAAA;AAAA,MACP,SAAS,SAAS;AAAA;AAAA,IAAA,CACrB;AAAA,EACL;AAAA;AAAA,EAEA,SAAS,MAAuB;;AAC5B,yBAAS,KAAK,MAAd,mBAAiB,YAAjB,mBAA0B,MAAM,GAAG;AAAA,EACvC;AAAA;AAAA,EAEA,YAAY,MAAuB;AACtB,aAAA,KAAK,CAAC,UAAU;;AAAA,yBAAM,YAAN,mBAAe,MAAM,GAAG;AAAA,KAAK;AAAA,EAAA;AAE9D;ACtDA,MAAe,QAAA;AAAA,EACX,QAAQ,KAAU;AACd,sBAAkB,KAAKA,SAAK;AACE,kCAAA,KAAK,SAAS,iBAAiB;AAAA,EAAA;AAErE;"}