{ "name": "dialog", "type": "registry:ui", "registryDependencies": [], "dependencies": [ "motion-v" ], "devDependencies": [], "tailwind": {}, "cssVars": { "light": {}, "dark": {} }, "files": [ { "path": "Dialog.vue", "content": "\n\n\n", "type": "registry:ui" }, { "path": "DialogClose.vue", "content": "\n\n\n", "type": "registry:ui" }, { "path": "DialogContent.vue", "content": "\n\n\n", "type": "registry:ui" }, { "path": "DialogDescription.vue", "content": "\n\n\n", "type": "registry:ui" }, { "path": "DialogHeader.vue", "content": "\n\n\n", "type": "registry:ui" }, { "path": "DialogTitle.vue", "content": "\n\n\n", "type": "registry:ui" }, { "path": "DialogTrigger.vue", "content": "\n\n\n", "type": "registry:ui" }, { "path": "composables/usePreventScroll.ts", "content": "/* eslint-disable @typescript-eslint/no-explicit-any */\n// Adapted from https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/overlays/src/usePreventScroll.ts\n\nfunction isMac(): boolean | undefined {\n return testPlatform(/^Mac/)\n}\n\nfunction isIPhone(): boolean | undefined {\n return testPlatform(/^iPhone/)\n}\n\nfunction isIPad(): boolean | undefined {\n return (\n testPlatform(/^iPad/) ||\n (isMac() && navigator.maxTouchPoints > 1)\n )\n}\n\nfunction isIOS(): boolean | undefined {\n return isIPhone() || isIPad()\n}\n\nfunction testPlatform(re: RegExp): boolean | undefined {\n return typeof window !== 'undefined' && window.navigator != null\n ? re.test(window.navigator.platform)\n : undefined\n}\n\nfunction chain(...callbacks: any[]): (...args: any[]) => void {\n return (...args: any[]) => {\n for (const callback of callbacks) {\n if (typeof callback === 'function') {\n callback(...args)\n }\n }\n }\n}\n\nconst visualViewport =\n typeof document !== 'undefined' && window.visualViewport\n\nfunction isScrollable(node: Element): boolean {\n const style = window.getComputedStyle(node)\n return /(auto|scroll)/.test(\n style.overflow + style.overflowX + style.overflowY,\n )\n}\n\nfunction getScrollParent(node: Element): Element {\n if (isScrollable(node)) {\n node = node.parentElement as HTMLElement\n }\n while (node && !isScrollable(node)) {\n node = node.parentElement as HTMLElement\n }\n return node || document.scrollingElement || document.documentElement\n}\n\nconst nonTextInputTypes = new Set([\n 'checkbox',\n 'radio',\n 'range',\n 'color',\n 'file',\n 'image',\n 'button',\n 'submit',\n 'reset',\n])\n\nfunction isInput(target: Element) {\n return (\n (target instanceof HTMLInputElement &&\n !nonTextInputTypes.has(target.type)) ||\n target instanceof HTMLTextAreaElement ||\n (target instanceof HTMLElement && target.isContentEditable)\n )\n}\n\nconst KEYBOARD_BUFFER = 24\n\nlet preventScrollCount = 0\nlet restore: () => void\n\ninterface PreventScrollOptions {\n isDisabled?: boolean\n}\n\nexport function usePreventScroll(options: PreventScrollOptions = {}) {\n const { isDisabled } = options\n\n watch(\n () => isDisabled,\n (disabled) => {\n if (disabled) return\n\n preventScrollCount++\n if (preventScrollCount === 1) {\n if (isIOS()) {\n restore = preventScrollMobileSafari()\n }\n }\n },\n { immediate: true },\n )\n\n onUnmounted(() => {\n if (!isDisabled) {\n preventScrollCount--\n if (preventScrollCount === 0) {\n restore?.()\n }\n }\n })\n}\n\nfunction setStyle(\n element: HTMLElement,\n style: string,\n value: string,\n) {\n const cur = (element.style as any)[style]\n ;(element.style as any)[style] = value\n return () => {\n ;(element.style as any)[style] = cur\n }\n}\n\nfunction addEvent(\n target: EventTarget,\n event: K,\n handler: (ev: GlobalEventHandlersEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n) {\n target.addEventListener(event as string, handler as any, options)\n return () => {\n target.removeEventListener(event as string, handler as any, options)\n }\n}\n\nfunction scrollIntoView(target: Element) {\n const root = document.scrollingElement || document.documentElement\n while (target && target !== root) {\n const scrollable = getScrollParent(target)\n if (\n scrollable !== document.documentElement &&\n scrollable !== document.body &&\n scrollable !== target\n ) {\n const targetTop = target.getBoundingClientRect().top\n const scrollableTop = scrollable.getBoundingClientRect().top\n const targetBottom = target.getBoundingClientRect().bottom\n const keyboardHeight =\n scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER\n if (targetBottom > keyboardHeight) {\n scrollable.scrollTop += targetTop - scrollableTop\n }\n }\n target = scrollable.parentElement as Element\n }\n}\n\nfunction preventScrollMobileSafari() {\n let scrollable: Element\n let lastY = 0\n\n const onTouchStart = (e: TouchEvent) => {\n scrollable = getScrollParent(e.target as Element)\n if (\n scrollable === document.documentElement &&\n scrollable === document.body\n ) {\n return\n }\n lastY = e.changedTouches[0]!.pageY\n }\n\n const onTouchMove = (e: TouchEvent) => {\n if (\n !scrollable ||\n scrollable === document.documentElement ||\n scrollable === document.body\n ) {\n e.preventDefault()\n return\n }\n const y = e.changedTouches[0]!.pageY\n const scrollTop = scrollable.scrollTop\n const bottom = scrollable.scrollHeight - scrollable.clientHeight\n if (bottom === 0) return\n if ((scrollTop <= 0 && y > lastY) || (scrollTop >= bottom && y < lastY)) {\n e.preventDefault()\n }\n lastY = y\n }\n\n const onTouchEnd = (e: TouchEvent) => {\n const target = e.target as HTMLElement\n if (isInput(target) && target !== document.activeElement) {\n e.preventDefault()\n target.style.transform = 'translateY(-2000px)'\n target.focus()\n requestAnimationFrame(() => {\n target.style.transform = ''\n })\n }\n }\n\n const onFocus = (e: FocusEvent) => {\n const target = e.target as HTMLElement\n if (isInput(target)) {\n target.style.transform = 'translateY(-2000px)'\n requestAnimationFrame(() => {\n target.style.transform = ''\n if (visualViewport) {\n if (visualViewport.height < window.innerHeight) {\n requestAnimationFrame(() => {\n scrollIntoView(target)\n })\n } else {\n visualViewport.addEventListener(\n 'resize',\n () => scrollIntoView(target),\n { once: true },\n )\n }\n }\n })\n }\n }\n\n const onWindowScroll = () => {\n window.scrollTo(0, 0)\n }\n\n const scrollX = window.pageXOffset\n const scrollY = window.pageYOffset\n\n const restoreStyles = chain(\n setStyle(\n document.documentElement,\n 'paddingRight',\n `${window.innerWidth - document.documentElement.clientWidth}px`,\n ),\n )\n\n window.scrollTo(0, 0)\n\n const removeEvents = chain(\n addEvent(document, 'touchstart', onTouchStart, {\n passive: false,\n capture: true,\n }),\n addEvent(document, 'touchmove', onTouchMove, {\n passive: false,\n capture: true,\n }),\n addEvent(document, 'touchend', onTouchEnd, {\n passive: false,\n capture: true,\n }),\n addEvent(document, 'focus', onFocus, true),\n addEvent(window, 'scroll', onWindowScroll),\n )\n\n return () => {\n restoreStyles()\n removeEvents()\n window.scrollTo(scrollX, scrollY)\n }\n}\n", "type": "registry:hook" } ] }