{"version":3,"sources":["../src/components/Dialog/DialogSurface/useDialogSurface.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport {\n  getIntrinsicElementProps,\n  slot,\n  useMergedRefs,\n  useEventCallback,\n  useIsomorphicLayoutEffect,\n} from '@fluentui/react-utilities';\nimport { useDialogContext } from '../dialogContext';\nimport { stringifyDataAttribute } from '../../../utils';\nimport { lockDocumentScroll, unlockDocumentScroll } from '../utils/scroll';\nimport type { DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types';\n\nconst SUPPORTS_POPOVER_OPEN_SELECTOR =\n  typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('selector(:popover-open)');\n\ntype ToggleEvent = Event & { newState?: 'open' | 'closed' };\n\n/**\n * Create the state required to render DialogSurface.\n * Uses native `<dialog>` behavior:\n * - modal/alert => `showModal()`\n * - non-modal => `showPopover()` (fallback to `show()`)\n * - close => `close()`/`hidePopover()`\n *\n * @param props - props from this instance of DialogSurface\n * @param ref - reference to root HTMLDialogElement of DialogSurface\n */\nexport const useDialogSurface = (props: DialogSurfaceProps, ref: React.Ref<HTMLDialogElement>): DialogSurfaceState => {\n  const { open, modalType, unmountOnClose, requestOpenChange, dialogTitleId } = useDialogContext();\n  const { targetDocument } = useFluent();\n\n  const dialogRef = React.useRef<HTMLDialogElement>(null);\n  const previouslyFocusedElement = React.useRef<HTMLElement | null>(null);\n  const mergedRef = useMergedRefs(ref, dialogRef);\n\n  // Keep the element mounted one extra render so native close can run while connected.\n  const [shouldRender, setShouldRender] = React.useState(open || !unmountOnClose);\n\n  // Ensure the element exists before open side-effects run.\n  if (open && !shouldRender) {\n    setShouldRender(true);\n  }\n\n  const handleToggle = useEventCallback((event: Event) => {\n    const toggle = event as ToggleEvent;\n    const nextOpen = toggle.newState === 'open';\n\n    if (nextOpen !== open) {\n      requestOpenChange({ type: 'surfaceToggle', open: nextOpen, event });\n    }\n  });\n\n  // Open/close native dialog/popover and keep listeners in sync with mounted element.\n  useIsomorphicLayoutEffect(() => {\n    const dialog = dialogRef.current;\n    if (!dialog) {\n      return;\n    }\n\n    if (!open) {\n      if (modalType === 'non-modal') {\n        if (typeof dialog.hidePopover === 'function') {\n          const isPopoverOpen = SUPPORTS_POPOVER_OPEN_SELECTOR && dialog.matches(':popover-open');\n          if (isPopoverOpen) {\n            dialog.hidePopover();\n          }\n        } else if (dialog.open) {\n          dialog.close();\n        }\n\n        const elementToFocus = previouslyFocusedElement.current;\n        if (elementToFocus && targetDocument?.contains(elementToFocus)) {\n          elementToFocus.focus();\n        }\n        previouslyFocusedElement.current = null;\n      } else if (dialog.open) {\n        dialog.close();\n      }\n      if (unmountOnClose) {\n        setShouldRender(false);\n      }\n      return;\n    }\n\n    const shouldLockScroll = modalType !== 'non-modal' && !!targetDocument;\n    if (shouldLockScroll) {\n      lockDocumentScroll(targetDocument);\n    }\n\n    if (modalType === 'non-modal') {\n      previouslyFocusedElement.current = targetDocument?.activeElement as HTMLElement | null;\n    }\n\n    let handleCancel: ((event: Event) => void) | undefined;\n    if (modalType !== 'non-modal') {\n      // Let React own close state; prevent native Escape auto-close.\n      handleCancel = (event: Event) => event.preventDefault();\n      dialog.addEventListener('cancel', handleCancel);\n    }\n\n    if (modalType !== 'non-modal') {\n      if (!dialog.open) {\n        dialog.showModal();\n      }\n    } else if (typeof dialog.showPopover === 'function') {\n      const isPopoverOpen = SUPPORTS_POPOVER_OPEN_SELECTOR && dialog.matches(':popover-open');\n      if (!isPopoverOpen) {\n        dialog.showPopover();\n      }\n\n      dialog.addEventListener('toggle', handleToggle);\n    } else if (!dialog.open) {\n      dialog.show();\n    }\n\n    return () => {\n      if (handleCancel) {\n        dialog.removeEventListener('cancel', handleCancel);\n      }\n      dialog.removeEventListener('toggle', handleToggle);\n      if (shouldLockScroll) {\n        unlockDocumentScroll(targetDocument);\n      }\n    };\n  }, [open, modalType, targetDocument, unmountOnClose, handleToggle]);\n\n  const handleKeyDown = useEventCallback((event: React.KeyboardEvent<HTMLDialogElement>) => {\n    props.onKeyDown?.(event);\n    if (event.key === 'Escape' && !event.isDefaultPrevented()) {\n      requestOpenChange({ type: 'escapeKeyDown', open: false, event });\n      // Stop propagation to avoid closing parent dialogs simultaneously.\n      event.stopPropagation();\n      return;\n    }\n\n    // When the non-modal surface itself is focused, move Tab to the first\n    // focusable descendant explicitly.\n    if (\n      modalType === 'non-modal' &&\n      event.key === 'Tab' &&\n      !event.shiftKey &&\n      !event.isDefaultPrevented() &&\n      event.target === event.currentTarget\n    ) {\n      const firstFocusable = event.currentTarget.querySelector<HTMLElement>(\n        'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n      );\n      if (firstFocusable) {\n        event.preventDefault();\n        firstFocusable.focus();\n      }\n    }\n  });\n\n  // Backdrop click is detected by checking clicks outside the dialog rect.\n  const handleClick = useEventCallback((event: React.MouseEvent<HTMLDialogElement>) => {\n    props.onClick?.(event);\n    if (modalType !== 'modal' || event.isDefaultPrevented()) {\n      return;\n    }\n\n    if (event.target !== event.currentTarget) {\n      return;\n    }\n\n    const rect = event.currentTarget.getBoundingClientRect();\n    const isBackdropClick =\n      event.clientX < rect.left ||\n      event.clientX > rect.right ||\n      event.clientY < rect.top ||\n      event.clientY > rect.bottom;\n\n    if (isBackdropClick) {\n      requestOpenChange({ type: 'backdropClick', open: false, event });\n    }\n  });\n\n  const state: DialogSurfaceState = {\n    components: { root: 'dialog' },\n    open,\n    unmountOnClose,\n    modalType,\n    shouldRender,\n    root: slot.always(\n      getIntrinsicElementProps('dialog', {\n        role: modalType === 'alert' ? 'alertdialog' : undefined,\n        'aria-modal': modalType !== 'non-modal' ? true : undefined,\n        'aria-labelledby': props['aria-label'] ? undefined : dialogTitleId || undefined,\n        ...props,\n        tabIndex: -1,\n        ref: mergedRef,\n        onKeyDown: handleKeyDown,\n        onClick: handleClick,\n        popover: modalType === 'non-modal' ? ('manual' as const) : undefined,\n        'data-open': stringifyDataAttribute(open),\n        'data-modal-type': modalType,\n      }),\n      { elementType: 'dialog' },\n    ),\n  };\n\n  return state;\n};\n"],"names":["useDialogSurface","SUPPORTS_POPOVER_OPEN_SELECTOR","CSS","supports","props","ref","open","modalType","unmountOnClose","requestOpenChange","dialogTitleId","useDialogContext","targetDocument","useFluent","dialogRef","React","useRef","previouslyFocusedElement","mergedRef","useMergedRefs","shouldRender","setShouldRender","useState","handleToggle","useEventCallback","event","toggle","nextOpen","newState","type","useIsomorphicLayoutEffect","dialog","current","hidePopover","isPopoverOpen","matches","close","elementToFocus","contains","focus","shouldLockScroll","lockDocumentScroll","activeElement","handleCancel","preventDefault","addEventListener","showModal","showPopover","show","removeEventListener","unlockDocumentScroll","handleKeyDown","onKeyDown","key","isDefaultPrevented","stopPropagation","shiftKey","target","currentTarget","firstFocusable","querySelector","handleClick","onClick","rect","getBoundingClientRect","isBackdropClick","clientX","left","right","clientY","top","bottom","state","components","root","slot","always","getIntrinsicElementProps","role","undefined","tabIndex","popover","stringifyDataAttribute","elementType"],"mappings":"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"}