@ahrowe/ui 0.40.0 → 0.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +13 -13
  2. package/dist/cjs-types/common/ahroweProvider/ahroweProvider.d.ts +4 -0
  3. package/dist/cjs-types/common/ahroweProvider/ahroweProvider.types.d.ts +12 -0
  4. package/dist/cjs-types/common/ahroweProvider/index.d.ts +2 -0
  5. package/dist/cjs-types/common/bodyEnd/portalHostContext.d.ts +6 -0
  6. package/dist/cjs-types/common/errorBoundary/errorBoundary.types.d.ts +2 -2
  7. package/dist/cjs-types/common/hooks/useOverlay.d.ts +4 -1
  8. package/dist/cjs-types/common/popover/popover.types.d.ts +1 -1
  9. package/dist/cjs-types/common/themeProvider/index.d.ts +1 -1
  10. package/dist/cjs-types/common/themeProvider/theme.types.d.ts +8 -0
  11. package/dist/cjs-types/common/themeProvider/themeProvider.d.ts +7 -9
  12. package/dist/cjs-types/common/themeProvider/themeVariablesContext.d.ts +2 -2
  13. package/dist/cjs-types/common/toast/index.d.ts +1 -2
  14. package/dist/cjs-types/common/toast/toast.types.d.ts +4 -4
  15. package/dist/cjs-types/common/toast/toastHost.d.ts +4 -0
  16. package/dist/cjs-types/common/toast/toastService.d.ts +1 -1
  17. package/dist/cjs-types/index.d.ts +2 -1
  18. package/dist/esm/common/ahroweProvider/ahroweProvider.mjs +2 -0
  19. package/dist/esm/common/ahroweProvider/ahroweProvider.mjs.map +1 -0
  20. package/dist/esm/common/bodyEnd/bodyEnd.mjs +1 -1
  21. package/dist/esm/common/bodyEnd/bodyEnd.mjs.map +1 -1
  22. package/dist/esm/common/bodyEnd/portalHostContext.mjs +2 -0
  23. package/dist/esm/common/bodyEnd/portalHostContext.mjs.map +1 -0
  24. package/dist/esm/common/calendar/calendar.mjs +1 -1
  25. package/dist/esm/common/calendar/calendar.mjs.map +1 -1
  26. package/dist/esm/common/carousel/carousel.module.mjs.map +1 -1
  27. package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
  28. package/dist/esm/common/hooks/useAnchorTracking.mjs.map +1 -1
  29. package/dist/esm/common/hooks/useOverlay.mjs +1 -1
  30. package/dist/esm/common/hooks/useOverlay.mjs.map +1 -1
  31. package/dist/esm/common/modal/modal.mjs +1 -1
  32. package/dist/esm/common/modal/modal.mjs.map +1 -1
  33. package/dist/esm/common/otpInput/otpInput.mjs +1 -1
  34. package/dist/esm/common/otpInput/otpInput.mjs.map +1 -1
  35. package/dist/esm/common/popover/popover.mjs +1 -1
  36. package/dist/esm/common/popover/popover.mjs.map +1 -1
  37. package/dist/esm/common/popover/popover.types.mjs.map +1 -1
  38. package/dist/esm/common/scrollbarProvider/scrollbarProvider.module.mjs.map +1 -1
  39. package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
  40. package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
  41. package/dist/esm/common/themeProvider/themeVariablesContext.mjs.map +1 -1
  42. package/dist/esm/common/timer/timer.mjs +1 -1
  43. package/dist/esm/common/timer/timer.mjs.map +1 -1
  44. package/dist/esm/common/toast/toastHost.mjs +2 -0
  45. package/dist/esm/common/toast/toastHost.mjs.map +1 -0
  46. package/dist/esm/common/toast/toastService.mjs +1 -1
  47. package/dist/esm/common/toast/toastService.mjs.map +1 -1
  48. package/dist/esm/common/tooltip/tooltip.module.mjs.map +1 -1
  49. package/dist/esm/common/virtualList/virtualList.mjs +1 -1
  50. package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
  51. package/dist/esm/index.mjs +1 -1
  52. package/dist/index.cjs +3 -3
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/mcp.js +2 -2
  55. package/dist/style.css +1 -1
  56. package/dist/types/common/ahroweProvider/ahroweProvider.d.ts +4 -0
  57. package/dist/types/common/ahroweProvider/ahroweProvider.types.d.ts +12 -0
  58. package/dist/types/common/ahroweProvider/index.d.ts +2 -0
  59. package/dist/types/common/bodyEnd/portalHostContext.d.ts +6 -0
  60. package/dist/types/common/errorBoundary/errorBoundary.types.d.ts +2 -2
  61. package/dist/types/common/hooks/useOverlay.d.ts +4 -1
  62. package/dist/types/common/popover/popover.types.d.ts +1 -1
  63. package/dist/types/common/themeProvider/index.d.ts +1 -1
  64. package/dist/types/common/themeProvider/theme.types.d.ts +8 -0
  65. package/dist/types/common/themeProvider/themeProvider.d.ts +7 -9
  66. package/dist/types/common/themeProvider/themeVariablesContext.d.ts +2 -2
  67. package/dist/types/common/toast/index.d.ts +1 -2
  68. package/dist/types/common/toast/toast.types.d.ts +4 -4
  69. package/dist/types/common/toast/toastHost.d.ts +4 -0
  70. package/dist/types/common/toast/toastService.d.ts +1 -1
  71. package/dist/types/index.d.ts +2 -1
  72. package/docs/AhroweProvider.md +90 -0
  73. package/docs/BodyEnd.md +5 -12
  74. package/docs/Breadcrumb.md +0 -2
  75. package/docs/CLAUDE.md +14 -11
  76. package/docs/CommandPalette.md +0 -2
  77. package/docs/ConfigProvider.md +11 -8
  78. package/docs/ConfirmModal.md +0 -2
  79. package/docs/ErrorBoundary.md +5 -3
  80. package/docs/FormValidatorGroup.md +1 -1
  81. package/docs/KlipyPicker.md +1 -1
  82. package/docs/Menu.md +0 -2
  83. package/docs/Modal.md +1 -3
  84. package/docs/MultiSelect.md +0 -2
  85. package/docs/OtpInput.md +0 -2
  86. package/docs/Popover.md +2 -4
  87. package/docs/RadioGroup.md +1 -1
  88. package/docs/ScrollbarProvider.md +4 -0
  89. package/docs/SplitButton.md +0 -2
  90. package/docs/ThemeProvider.md +13 -10
  91. package/docs/Toast.md +18 -17
  92. package/docs/Tooltip.md +1 -1
  93. package/docs/VirtualList.md +0 -2
  94. package/package.json +1 -1
  95. package/dist/cjs-types/common/toast/toastProvider.d.ts +0 -8
  96. package/dist/esm/common/toast/toastProvider.mjs +0 -2
  97. package/dist/esm/common/toast/toastProvider.mjs.map +0 -1
  98. package/dist/types/common/toast/toastProvider.d.ts +0 -8
@@ -1 +1 @@
1
- {"version":3,"file":"modal.mjs","names":[],"sources":["../../../../package/common/modal/modal.tsx"],"sourcesContent":["import cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './modal.module.pcss';\nimport sheetStyles from 'components/common/styles/sheet.module.pcss';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faXmark as closeIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ModalProps } from './modal.types';\nimport type { ModalLabels } from './modal.types';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useVisualViewportSize } from 'components/common/hooks/useVisualViewportSize';\nimport { useOverlay } from 'components/common/hooks/useOverlay';\nimport { useSheet } from 'components/common/hooks/useSheet';\nimport { useScrollEdges } from 'components/common/hooks/useScrollEdges';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\nimport { useComponentDefaults, useLabels, usePresentationDefault } from 'components/common/configProvider';\n\nconst DEFAULT_LABELS: Required<ModalLabels> = {\n close: 'Close',\n};\n\nfunction Modal(props: ModalProps) {\n const {\n children = null,\n title,\n isOpen = false,\n onClose = () => null,\n footer = null,\n className = '',\n style,\n hideHeader = false,\n closeOnEscape = true,\n presentation: presentationProp,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('Modal', props);\n const text = useLabels('Modal', labels, DEFAULT_LABELS);\n const [isRendered, setIsRendered] = useState(isOpen);\n const [animateIn, setAnimateIn] = useState(isOpen);\n const modalRef = useRef<HTMLDivElement>(null);\n const bodyRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // Centering/sizing against the actually-visible viewport (not the full layout viewport) keeps\n // the modal — and whatever's focused inside it — from ending up under a mobile keyboard.\n const { height: viewportHeight, offsetTop: viewportOffsetTop } = useVisualViewportSize();\n // Explicit prop → this component's global default → the app-wide one. There's no built-in\n // fallback to add: `Default` and no presentation at all both mean the ordinary centred dialog.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n useEffect(() => {\n if (isOpen) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsRendered(isOpen);\n setTimeout(() => setAnimateIn(true), 1);\n } else {\n setAnimateIn(false);\n setTimeout(() => setIsRendered(isOpen), 300);\n }\n }, [isOpen]);\n\n // Keyed on `animateIn` rather than `isOpen`, because the panel this needs to focus and trap\n // focus inside of only mounts once that flips.\n useOverlay({\n isActive: isRendered && animateIn,\n containerRef: modalRef,\n onEscape: closeOnEscape ? onClose : undefined,\n });\n\n const { hasStart, hasEnd } = useScrollEdges(bodyRef, { isEnabled: isRendered && animateIn });\n\n if (!isRendered) return null;\n\n const hasHeaderTitle = !hideHeader && !!title;\n\n return (\n <BodyEnd>\n <div\n className={cx(styles.modalContainer, classNames?.container)}\n style={{ height: viewportHeight, top: viewportOffsetTop, ...slotStyles?.container }}\n >\n <div\n className={cx(styles.modalWrapper, animateIn && styles.modalWrapperShown, classNames?.backdrop)}\n style={slotStyles?.backdrop}\n onClick={onClose}\n />\n {animateIn && (\n <div\n {...rest}\n ref={modalRef}\n role='dialog'\n aria-modal='true'\n // aria-labelledby would outrank aria-label, which is the only way to name a title with no text.\n aria-labelledby={hasHeaderTitle && !rest['aria-label'] ? titleId : undefined}\n // Focusable so the trap has somewhere to put focus when the content has no tabbables.\n tabIndex={-1}\n className={cx(\n styles.modal,\n animateIn && styles.modalShown,\n // The panel is its own sheet surface here: there's no inner panel to fill, since\n // Modal already splits itself into a fixed header and a scrolling body.\n isSheet && [sheetStyles.sheet, sheetStyles.sheetSurface, styles.modalSheet],\n className,\n classNames?.modal,\n )}\n style={{ ...(isSheet ? sheetStyle : undefined), ...style, ...slotStyles?.modal }}\n onClick={(e) => e.stopPropagation()}\n >\n {!hideHeader && (\n <div className={cx(styles.modalHeader, classNames?.header)} style={slotStyles?.header}>\n <div id={titleId} className={classNames?.title} style={slotStyles?.title}>\n {title}\n </div>\n <button\n type='button'\n onClick={onClose}\n aria-label={text.close}\n className={cx(styles.modalCloseButton, classNames?.closeButton)}\n style={slotStyles?.closeButton}\n >\n <FontAwesomeIcon icon={closeIcon} />\n </button>\n </div>\n )}\n <div\n ref={bodyRef}\n className={cx(\n styles.modalBody,\n fadeStyles.scrollFade,\n fadeStyles.scrollFadeY,\n hasStart && fadeStyles.scrollFadeFadedStart,\n hasEnd && fadeStyles.scrollFadeFadedEnd,\n classNames?.body,\n )}\n style={slotStyles?.body}\n >\n {children}\n </div>\n {footer && (\n <div className={cx(styles.modalFooter, classNames?.footer)} style={slotStyles?.footer}>\n {footer}\n </div>\n )}\n </div>\n )}\n </div>\n </BodyEnd>\n );\n}\n\nexport default Modal;\n"],"mappings":"o4BAiBA,IAAM,EAAwC,CAC5C,MAAO,OACT,EAEA,SAAS,EAAM,EAAmB,CAChC,GAAM,CACJ,WAAW,KACX,QACA,SAAS,GACT,cAAgB,KAChB,SAAS,KACT,YAAY,GACZ,QACA,aAAa,GACb,gBAAgB,GAChB,aAAc,EACd,aACA,OAAQ,EACR,SACA,GAAG,GACD,EAAqB,QAAS,CAAK,EACjC,EAAO,EAAU,QAAS,EAAQ,CAAc,EAChD,CAAC,EAAY,GAAiB,EAAS,CAAM,EAC7C,CAAC,EAAW,GAAgB,EAAS,CAAM,EAC3C,EAAW,EAAuB,IAAI,EACtC,EAAU,EAAuB,IAAI,EACrC,EAAU,EAAM,EAGhB,CAAE,OAAQ,EAAgB,UAAW,GAAsB,EAAsB,EAGjF,EAAkB,EAAuB,EAEzC,CAAE,UAAS,cAAe,EADX,GAAoB,CACY,EAErD,MAAgB,CACV,GAEF,EAAc,CAAM,EACpB,eAAiB,EAAa,EAAI,EAAG,CAAC,IAEtC,EAAa,EAAK,EAClB,eAAiB,EAAc,CAAM,EAAG,GAAG,EAE/C,EAAG,CAAC,CAAM,CAAC,EAIX,EAAW,CACT,SAAU,GAAc,EACxB,aAAc,EACd,SAAU,EAAgB,EAAU,IAAA,EACtC,CAAC,EAED,GAAM,CAAE,WAAU,UAAW,EAAe,EAAS,CAAE,UAAW,GAAc,CAAU,CAAC,EAE3F,GAAI,CAAC,EAAY,OAAO,KAExB,IAAM,EAAiB,CAAC,GAAc,CAAC,CAAC,EAExC,OACE,EAAC,EAAD,CAAA,SACE,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,eAAgB,GAAY,SAAS,EAC1D,MAAO,CAAE,OAAQ,EAAgB,IAAK,EAAmB,GAAG,GAAY,SAAU,EAFpF,SAAA,CAIE,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,aAAc,GAAa,EAAO,kBAAmB,GAAY,QAAQ,EAC9F,MAAO,GAAY,SACnB,QAAS,CACV,CAAA,EACA,GACC,EAAC,MAAD,CACE,GAAI,EACJ,IAAK,EACL,KAAK,SACL,aAAW,OAEX,kBAAiB,GAAkB,CAAC,EAAK,cAAgB,EAAU,IAAA,GAEnE,SAAU,GACV,UAAW,EACT,EAAO,MACP,GAAa,EAAO,WAGpB,GAAW,CAAC,EAAY,MAAO,EAAY,aAAc,EAAO,UAAU,EAC1E,EACA,GAAY,KACd,EACA,MAAO,CAAE,GAAI,EAAU,EAAa,IAAA,GAAY,GAAG,EAAO,GAAG,GAAY,KAAM,EAC/E,QAAU,GAAM,EAAE,gBAAgB,EAnBpC,SAAA,CAqBG,CAAC,GACA,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,YAAa,GAAY,MAAM,EAAG,MAAO,GAAY,OAA/E,SAAA,CACE,EAAC,MAAD,CAAK,GAAI,EAAS,UAAW,GAAY,MAAO,MAAO,GAAY,MAChE,SAAA,CACE,CAAA,EACL,EAAC,SAAD,CACE,KAAK,SACL,QAAS,EACT,aAAY,EAAK,MACjB,UAAW,EAAG,EAAO,iBAAkB,GAAY,WAAW,EAC9D,MAAO,GAAY,YAEnB,SAAA,EAAC,EAAD,CAAiB,KAAM,CAAY,CAAA,CAC7B,CAAA,CACL,IAEP,EAAC,MAAD,CACE,IAAK,EACL,UAAW,EACT,EAAO,UACP,EAAW,WACX,EAAW,YACX,GAAY,EAAW,qBACvB,GAAU,EAAW,mBACrB,GAAY,IACd,EACA,MAAO,GAAY,KAElB,UACE,CAAA,EACJ,GACC,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,YAAa,GAAY,MAAM,EAAG,MAAO,GAAY,OAC5E,SAAA,CACE,CAAA,CAEJ,CAEJ,CAAA,CAAA,CACE,CAAA,CAAA,CAAA,CAEb"}
1
+ {"version":3,"file":"modal.mjs","names":[],"sources":["../../../../package/common/modal/modal.tsx"],"sourcesContent":["import cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './modal.module.pcss';\nimport sheetStyles from 'components/common/styles/sheet.module.pcss';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faXmark as closeIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ModalProps } from './modal.types';\nimport type { ModalLabels } from './modal.types';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useVisualViewportSize } from 'components/common/hooks/useVisualViewportSize';\nimport { useOverlay } from 'components/common/hooks/useOverlay';\nimport { useSheet } from 'components/common/hooks/useSheet';\nimport { useScrollEdges } from 'components/common/hooks/useScrollEdges';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\nimport { useComponentDefaults, useLabels, usePresentationDefault } from 'components/common/configProvider';\n\nconst DEFAULT_LABELS: Required<ModalLabels> = {\n close: 'Close',\n};\n\nfunction Modal(props: ModalProps) {\n const {\n children = null,\n title,\n isOpen = false,\n onClose = () => null,\n footer = null,\n className = '',\n style,\n hideHeader = false,\n closeOnEscape = true,\n presentation: presentationProp,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('Modal', props);\n const text = useLabels('Modal', labels, DEFAULT_LABELS);\n const [isRendered, setIsRendered] = useState(isOpen);\n const [animateIn, setAnimateIn] = useState(isOpen);\n const modalRef = useRef<HTMLDivElement>(null);\n const bodyRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // Centering/sizing against the actually-visible viewport (not the full layout viewport) keeps\n // the modal — and whatever's focused inside it — from ending up under a mobile keyboard.\n const { height: viewportHeight, offsetTop: viewportOffsetTop } = useVisualViewportSize();\n // Explicit prop → this component's global default → the app-wide one. There's no built-in\n // fallback to add: `Default` and no presentation at all both mean the ordinary centred dialog.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n // Cancelled on a flip back, or a reopen during the close animation is hidden by its late unmount.\n useEffect(() => {\n if (isOpen) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsRendered(true);\n const id = setTimeout(() => setAnimateIn(true), 1);\n return () => clearTimeout(id);\n }\n setAnimateIn(false);\n const id = setTimeout(() => setIsRendered(false), 300);\n return () => clearTimeout(id);\n }, [isOpen]);\n\n // Keyed on `animateIn` rather than `isOpen`, because the panel this needs to focus and trap\n // focus inside of only mounts once that flips.\n useOverlay({\n isActive: isRendered && animateIn,\n containerRef: modalRef,\n onEscape: closeOnEscape ? onClose : undefined,\n });\n\n const { hasStart, hasEnd } = useScrollEdges(bodyRef, { isEnabled: isRendered && animateIn });\n\n if (!isRendered) return null;\n\n const hasHeaderTitle = !hideHeader && !!title;\n\n return (\n <BodyEnd>\n <div\n className={cx(styles.modalContainer, classNames?.container)}\n style={{ height: viewportHeight, top: viewportOffsetTop, ...slotStyles?.container }}\n >\n <div\n className={cx(styles.modalWrapper, animateIn && styles.modalWrapperShown, classNames?.backdrop)}\n style={slotStyles?.backdrop}\n onClick={onClose}\n />\n {animateIn && (\n <div\n {...rest}\n ref={modalRef}\n role='dialog'\n aria-modal='true'\n // aria-labelledby would outrank aria-label, which is the only way to name a title with no text.\n aria-labelledby={hasHeaderTitle && !rest['aria-label'] ? titleId : undefined}\n // Focusable so the trap has somewhere to put focus when the content has no tabbables.\n tabIndex={-1}\n className={cx(\n styles.modal,\n animateIn && styles.modalShown,\n // The panel is its own sheet surface here: there's no inner panel to fill, since\n // Modal already splits itself into a fixed header and a scrolling body.\n isSheet && [sheetStyles.sheet, sheetStyles.sheetSurface, styles.modalSheet],\n className,\n classNames?.modal,\n )}\n style={{ ...(isSheet ? sheetStyle : undefined), ...style, ...slotStyles?.modal }}\n onClick={(e) => e.stopPropagation()}\n >\n {!hideHeader && (\n <div className={cx(styles.modalHeader, classNames?.header)} style={slotStyles?.header}>\n <div id={titleId} className={classNames?.title} style={slotStyles?.title}>\n {title}\n </div>\n <button\n type='button'\n onClick={onClose}\n aria-label={text.close}\n className={cx(styles.modalCloseButton, classNames?.closeButton)}\n style={slotStyles?.closeButton}\n >\n <FontAwesomeIcon icon={closeIcon} />\n </button>\n </div>\n )}\n <div\n ref={bodyRef}\n className={cx(\n styles.modalBody,\n fadeStyles.scrollFade,\n fadeStyles.scrollFadeY,\n hasStart && fadeStyles.scrollFadeFadedStart,\n hasEnd && fadeStyles.scrollFadeFadedEnd,\n classNames?.body,\n )}\n style={slotStyles?.body}\n >\n {children}\n </div>\n {footer && (\n <div className={cx(styles.modalFooter, classNames?.footer)} style={slotStyles?.footer}>\n {footer}\n </div>\n )}\n </div>\n )}\n </div>\n </BodyEnd>\n );\n}\n\nexport default Modal;\n"],"mappings":"o4BAiBA,IAAM,EAAwC,CAC5C,MAAO,OACT,EAEA,SAAS,EAAM,EAAmB,CAChC,GAAM,CACJ,WAAW,KACX,QACA,SAAS,GACT,cAAgB,KAChB,SAAS,KACT,YAAY,GACZ,QACA,aAAa,GACb,gBAAgB,GAChB,aAAc,EACd,aACA,OAAQ,EACR,SACA,GAAG,GACD,EAAqB,QAAS,CAAK,EACjC,EAAO,EAAU,QAAS,EAAQ,CAAc,EAChD,CAAC,EAAY,GAAiB,EAAS,CAAM,EAC7C,CAAC,EAAW,GAAgB,EAAS,CAAM,EAC3C,EAAW,EAAuB,IAAI,EACtC,EAAU,EAAuB,IAAI,EACrC,EAAU,EAAM,EAGhB,CAAE,OAAQ,EAAgB,UAAW,GAAsB,EAAsB,EAGjF,EAAkB,EAAuB,EAEzC,CAAE,UAAS,cAAe,EADX,GAAoB,CACY,EAGrD,MAAgB,CACd,GAAI,EAAQ,CAEV,EAAc,EAAI,EAClB,IAAM,EAAK,eAAiB,EAAa,EAAI,EAAG,CAAC,EACjD,UAAa,aAAa,CAAE,CAC9B,CACA,EAAa,EAAK,EAClB,IAAM,EAAK,eAAiB,EAAc,EAAK,EAAG,GAAG,EACrD,UAAa,aAAa,CAAE,CAC9B,EAAG,CAAC,CAAM,CAAC,EAIX,EAAW,CACT,SAAU,GAAc,EACxB,aAAc,EACd,SAAU,EAAgB,EAAU,IAAA,EACtC,CAAC,EAED,GAAM,CAAE,WAAU,UAAW,EAAe,EAAS,CAAE,UAAW,GAAc,CAAU,CAAC,EAE3F,GAAI,CAAC,EAAY,OAAO,KAExB,IAAM,EAAiB,CAAC,GAAc,CAAC,CAAC,EAExC,OACE,EAAC,EAAD,CAAA,SACE,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,eAAgB,GAAY,SAAS,EAC1D,MAAO,CAAE,OAAQ,EAAgB,IAAK,EAAmB,GAAG,GAAY,SAAU,EAFpF,SAAA,CAIE,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,aAAc,GAAa,EAAO,kBAAmB,GAAY,QAAQ,EAC9F,MAAO,GAAY,SACnB,QAAS,CACV,CAAA,EACA,GACC,EAAC,MAAD,CACE,GAAI,EACJ,IAAK,EACL,KAAK,SACL,aAAW,OAEX,kBAAiB,GAAkB,CAAC,EAAK,cAAgB,EAAU,IAAA,GAEnE,SAAU,GACV,UAAW,EACT,EAAO,MACP,GAAa,EAAO,WAGpB,GAAW,CAAC,EAAY,MAAO,EAAY,aAAc,EAAO,UAAU,EAC1E,EACA,GAAY,KACd,EACA,MAAO,CAAE,GAAI,EAAU,EAAa,IAAA,GAAY,GAAG,EAAO,GAAG,GAAY,KAAM,EAC/E,QAAU,GAAM,EAAE,gBAAgB,EAnBpC,SAAA,CAqBG,CAAC,GACA,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,YAAa,GAAY,MAAM,EAAG,MAAO,GAAY,OAA/E,SAAA,CACE,EAAC,MAAD,CAAK,GAAI,EAAS,UAAW,GAAY,MAAO,MAAO,GAAY,MAChE,SAAA,CACE,CAAA,EACL,EAAC,SAAD,CACE,KAAK,SACL,QAAS,EACT,aAAY,EAAK,MACjB,UAAW,EAAG,EAAO,iBAAkB,GAAY,WAAW,EAC9D,MAAO,GAAY,YAEnB,SAAA,EAAC,EAAD,CAAiB,KAAM,CAAY,CAAA,CAC7B,CAAA,CACL,IAEP,EAAC,MAAD,CACE,IAAK,EACL,UAAW,EACT,EAAO,UACP,EAAW,WACX,EAAW,YACX,GAAY,EAAW,qBACvB,GAAU,EAAW,mBACrB,GAAY,IACd,EACA,MAAO,GAAY,KAElB,UACE,CAAA,EACJ,GACC,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,YAAa,GAAY,MAAM,EAAG,MAAO,GAAY,OAC5E,SAAA,CACE,CAAA,CAEJ,CAEJ,CAAA,CAAA,CACE,CAAA,CAAA,CAAA,CAEb"}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import n from"../tooltip/tooltip.mjs";import{OtpInputCharset as r}from"./otpInput.types.mjs";import i from"./otpInput.module.mjs";import{useEffect as a,useRef as o,useState as s}from"react";import c from"classnames";import{jsx as l,jsxs as u}from"react/jsx-runtime";var d=/[0-9]/g,f=/[a-zA-Z0-9]/g,p={field:`One-time code`,digit:(e,t)=>`Digit ${e} of ${t}`};function m(e,t){let n=t===r.Alphanumeric?f:d;return(e.match(n)||[]).join(``)}function h(d){let{length:f=6,value:h,onChange:g=()=>{},onComplete:_=()=>{},formValidator:v=null,charset:y=r.Numeric,masked:b=!1,groupAfter:x=[],label:S=``,isValid:C=!0,errorMessage:w=``,isRequired:T=!1,clearOnError:E=!1,clearErrorDelay:D=900,disabled:O=!1,autoFocus:k=!1,className:A=``,style:j,classNames:M,styles:N,labels:P,...F}=e(`OtpInput`,d),I=t(`OtpInput`,P,p),L=(v?v.value:h)??``,[,R]=s(0),[z,B]=s(!1),[V,H]=s(!1),[U,W]=s(!1),G=o([]),K=o(!1);a(()=>{if(!v)return;v.validate();let e=()=>R(e=>e+1);return v.registerOnUpdateListener(e),()=>v.removeOnUpdateListener(e)},[v]),a(()=>{if(!k)return;let e=Math.min(L.length,f-1);G.current[e]?.focus()},[]);let q=v?v.touched&&v.hasError():!C||!!w,J=v?v.getCurrentErrorMessage():w||``;a(()=>{let e=q&&!K.current;if(K.current=q,!e)return;B(!0);let t=window.setTimeout(()=>B(!1),400),n=E?window.setTimeout(()=>{Y(``),G.current[0]?.focus()},D):null;return()=>{window.clearTimeout(t),n!=null&&window.clearTimeout(n)}},[q,E,D]);function Y(e){let t=L.length===f,n=m(e,y).slice(0,f);v&&v.set(n),g?.(n),n.length===f&&!t&&_?.(n)}function X(e,t){let n=L.split(``);n[e]=t,Y(n.join(``))}function Z(e,t){if(O)return;if(t===``){X(e,``);return}let n=m(t,y);if(!n)return;let r=Math.min(e,L.length);if(t.length>1){Q(r,n);return}X(r,n),r<f-1?G.current[r+1]?.focus():G.current[r]?.blur()}function Q(e,t){let n=m(t,y);if(!n)return;let r=L.padEnd(f,` `).split(``),i=e;for(let e of n){if(i>=f)break;r[i]=e,i+=1}Y(r.join(``).trimEnd());let a=Math.min(i,f-1);G.current[a]?.focus(),i>=f&&G.current[f-1]?.blur()}function $(e,t){if(O)return;let n=t.clipboardData.getData(`text`);n&&(t.preventDefault(),Q(Math.min(e,L.length),n))}function ee(e,t){if(!O){if(t.key.length===1&&!t.ctrlKey&&!t.metaKey&&!t.altKey){let n=m(t.key,y);if(n&&L[e]===n){t.preventDefault(),X(e,n),e<f-1?G.current[e+1]?.focus():G.current[e]?.blur();return}}if(t.key===`Backspace`){if(L[e]){X(e,``);return}e>0&&(t.preventDefault(),X(e-1,``),G.current[e-1]?.focus());return}if(t.key===`Delete`){L[e]&&X(e,``);return}if(t.key===`ArrowLeft`&&e>0){t.preventDefault(),G.current[e-1]?.focus();return}t.key===`ArrowRight`&&e<f-1&&(t.preventDefault(),G.current[e+1]?.focus())}}function te(e){e.target.select()}function ne(){H(!0)}function re(e){e.currentTarget.contains(e.relatedTarget)||(H(!1),v&&(v.touched=!0))}return u(`div`,{...F,className:c(i.otpInput,O&&i.otpInputDisabled,A),style:j,children:[S&&u(`div`,{className:c(i.otpInputLabel,M?.label),style:N?.label,children:[S,T&&l(`span`,{className:i.otpInputRequiredMark,children:` *`})]}),l(`div`,{role:`group`,"aria-label":S||I.field,className:c(i.otpInputBoxes,z&&i.otpInputBoxesShake,M?.inputs),style:N?.inputs,onFocus:ne,onBlur:re,onMouseEnter:()=>W(!0),onMouseLeave:()=>W(!1),children:Array.from({length:f},(e,t)=>{let n=L[t]||``;return u(`div`,{className:i.otpInputBoxWrapper,children:[l(`input`,{ref:e=>{G.current[t]=e},type:b?`password`:`text`,inputMode:y===r.Numeric?`numeric`:`text`,pattern:y===r.Numeric?`[0-9]*`:void 0,autoComplete:t===0?`one-time-code`:`off`,maxLength:1,disabled:O,value:n,"aria-label":I.digit(t+1,f),"aria-invalid":q||void 0,className:c(i.otpInputBox,q&&i.otpInputBoxError,n&&i.otpInputBoxFilled,M?.input),style:N?.input,onChange:e=>Z(t,e.target.value),onKeyDown:e=>ee(t,e),onPaste:e=>$(t,e),onFocus:te}),x.includes(t+1)&&t<f-1&&l(`span`,{className:c(i.otpInputSeparator,M?.separator),style:N?.separator})]},t)})}),l(n,{variant:`error`,message:J,isVisible:q&&(V||U)})]})}export{h as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import n from"../tooltip/tooltip.mjs";import{OtpInputCharset as r}from"./otpInput.types.mjs";import i from"./otpInput.module.mjs";import{useEffect as a,useRef as o,useState as s}from"react";import c from"classnames";import{jsx as l,jsxs as u}from"react/jsx-runtime";var d=/[0-9]/g,f=/[a-zA-Z0-9]/g,ee={field:`One-time code`,digit:(e,t)=>`Digit ${e} of ${t}`};function p(e,t){let n=t===r.Alphanumeric?f:d;return(e.match(n)||[]).join(``)}function m(d){let{length:f=6,value:m,onChange:h=()=>{},onComplete:g=()=>{},formValidator:_=null,charset:v=r.Numeric,masked:y=!1,groupAfter:b=[],label:x=``,isValid:S=!0,errorMessage:C=``,isRequired:w=!1,clearOnError:T=!1,clearErrorDelay:E=900,disabled:D=!1,autoFocus:O=!1,className:k=``,style:A,classNames:j,styles:M,labels:N,...P}=e(`OtpInput`,d),F=t(`OtpInput`,N,ee),I=(_?_.value:m)??``,[,L]=s(0),[R,z]=s(!1),[B,V]=s(!1),[H,U]=s(!1),[W,G]=s(!1),[K,q]=s(!1),J=o([]);a(()=>{if(!_)return;_.validate();let e=()=>L(e=>e+1);return _.registerOnUpdateListener(e),()=>_.removeOnUpdateListener(e)},[_]),a(()=>{if(!O)return;let e=Math.min(I.length,f-1);J.current[e]?.focus()},[]);let Y=_?_.touched&&_.hasError():!S||!!C,X=_?_.getCurrentErrorMessage():C||``;Y!==K&&(q(Y),Y&&z(!0),G(Y&&T)),a(()=>{if(!R)return;let e=window.setTimeout(()=>z(!1),400);return()=>window.clearTimeout(e)},[R]),a(()=>{if(!W||!T)return;let e=window.setTimeout(()=>{G(!1),Z(``),J.current[0]?.focus()},E);return()=>window.clearTimeout(e)},[W,T,E]);function Z(e){let t=I.length===f,n=p(e,v).slice(0,f);_&&_.set(n),h?.(n),n.length===f&&!t&&g?.(n)}function Q(e,t){let n=I.split(``);n[e]=t,Z(n.join(``))}function te(e,t){if(D)return;if(t===``){Q(e,``);return}let n=p(t,v);if(!n)return;let r=Math.min(e,I.length);if(t.length>1){$(r,n);return}Q(r,n),r<f-1?J.current[r+1]?.focus():J.current[r]?.blur()}function $(e,t){let n=p(t,v);if(!n)return;let r=I.padEnd(f,` `).split(``),i=e;for(let e of n){if(i>=f)break;r[i]=e,i+=1}Z(r.join(``).trimEnd());let a=Math.min(i,f-1);J.current[a]?.focus(),i>=f&&J.current[f-1]?.blur()}function ne(e,t){if(D)return;let n=t.clipboardData.getData(`text`);n&&(t.preventDefault(),$(Math.min(e,I.length),n))}function re(e,t){if(!D){if(t.key.length===1&&!t.ctrlKey&&!t.metaKey&&!t.altKey){let n=p(t.key,v);if(n&&I[e]===n){t.preventDefault(),Q(e,n),e<f-1?J.current[e+1]?.focus():J.current[e]?.blur();return}}if(t.key===`Backspace`){if(I[e]){Q(e,``);return}e>0&&(t.preventDefault(),Q(e-1,``),J.current[e-1]?.focus());return}if(t.key===`Delete`){I[e]&&Q(e,``);return}if(t.key===`ArrowLeft`&&e>0){t.preventDefault(),J.current[e-1]?.focus();return}t.key===`ArrowRight`&&e<f-1&&(t.preventDefault(),J.current[e+1]?.focus())}}function ie(e){e.target.select()}function ae(){V(!0)}function oe(e){e.currentTarget.contains(e.relatedTarget)||(V(!1),_&&(_.touched=!0))}return u(`div`,{...P,className:c(i.otpInput,D&&i.otpInputDisabled,k),style:A,children:[x&&u(`div`,{className:c(i.otpInputLabel,j?.label),style:M?.label,children:[x,w&&l(`span`,{className:i.otpInputRequiredMark,children:` *`})]}),l(`div`,{role:`group`,"aria-label":x||F.field,className:c(i.otpInputBoxes,R&&i.otpInputBoxesShake,j?.inputs),style:M?.inputs,onFocus:ae,onBlur:oe,onMouseEnter:()=>U(!0),onMouseLeave:()=>U(!1),children:Array.from({length:f},(e,t)=>{let n=I[t]||``;return u(`div`,{className:i.otpInputBoxWrapper,children:[l(`input`,{ref:e=>{J.current[t]=e},type:y?`password`:`text`,inputMode:v===r.Numeric?`numeric`:`text`,pattern:v===r.Numeric?`[0-9]*`:void 0,autoComplete:t===0?`one-time-code`:`off`,maxLength:1,disabled:D,value:n,"aria-label":F.digit(t+1,f),"aria-invalid":Y||void 0,className:c(i.otpInputBox,Y&&i.otpInputBoxError,n&&i.otpInputBoxFilled,j?.input),style:M?.input,onChange:e=>te(t,e.target.value),onKeyDown:e=>re(t,e),onPaste:e=>ne(t,e),onFocus:ie}),b.includes(t+1)&&t<f-1&&l(`span`,{className:c(i.otpInputSeparator,j?.separator),style:M?.separator})]},t)})}),l(n,{variant:`error`,message:X,isVisible:Y&&(B||H)})]})}export{m as default};
2
2
  //# sourceMappingURL=otpInput.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"otpInput.mjs","names":[],"sources":["../../../../package/common/otpInput/otpInput.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport Tooltip from 'components/common/tooltip';\nimport { useComponentDefaults, useLabels } from '../configProvider';\n\nimport { OtpInputCharset, type OtpInputLabels, type OtpInputProps } from './otpInput.types';\nimport styles from './otpInput.module.pcss';\n\nconst NUMERIC_RE = /[0-9]/g;\nconst ALPHANUMERIC_RE = /[a-zA-Z0-9]/g;\n\nconst DEFAULT_LABELS: Required<OtpInputLabels> = {\n field: 'One-time code',\n digit: (position, length) => `Digit ${position} of ${length}`,\n};\n\nfunction filterChars(raw: string, charset: OtpInputCharset): string {\n const re = charset === OtpInputCharset.Alphanumeric ? ALPHANUMERIC_RE : NUMERIC_RE;\n return (raw.match(re) || []).join('');\n}\n\nfunction OtpInput(props: OtpInputProps): ReactElement {\n const {\n length = 6,\n value,\n onChange = () => {},\n onComplete = () => {},\n formValidator = null,\n charset = OtpInputCharset.Numeric,\n masked = false,\n groupAfter = [],\n label = '',\n isValid = true,\n errorMessage = '',\n isRequired = false,\n clearOnError = false,\n clearErrorDelay = 900,\n disabled = false,\n autoFocus = false,\n className = '',\n style,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('OtpInput', props);\n const text = useLabels('OtpInput', labels, DEFAULT_LABELS);\n\n const currentValue = (formValidator ? (formValidator.value as string) : value) ?? '';\n const [, forceUpdate] = useState(0);\n const [isShaking, setIsShaking] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const inputRefs = useRef<Array<HTMLInputElement | null>>([]);\n const wasErrorRef = useRef(false);\n\n useEffect(() => {\n if (!formValidator) return;\n formValidator.validate();\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => formValidator.removeOnUpdateListener(listener);\n }, [formValidator]);\n\n useEffect(() => {\n if (!autoFocus) return;\n const firstEmpty = Math.min(currentValue.length, length - 1);\n inputRefs.current[firstEmpty]?.focus();\n // Only run once, on mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const showError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const displayedErrorMessage = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n useEffect(() => {\n const justErrored = showError && !wasErrorRef.current;\n wasErrorRef.current = showError;\n if (!justErrored) return;\n\n setIsShaking(true);\n const shakeTimeoutId = window.setTimeout(() => setIsShaking(false), 400);\n const clearTimeoutId = clearOnError\n ? window.setTimeout(() => {\n commit('');\n inputRefs.current[0]?.focus();\n }, clearErrorDelay)\n : null;\n\n return () => {\n window.clearTimeout(shakeTimeoutId);\n if (clearTimeoutId != null) window.clearTimeout(clearTimeoutId);\n };\n // Only re-evaluate on an actual error-state or config change, not on every value/render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [showError, clearOnError, clearErrorDelay]);\n\n function commit(next: string) {\n const wasComplete = currentValue.length === length;\n const clipped = filterChars(next, charset).slice(0, length);\n if (formValidator) {\n formValidator.set(clipped);\n }\n onChange?.(clipped);\n // Only fire once per genuinely new completion (going from partial to full), not on every\n // keystroke made while correcting an already-full code.\n if (clipped.length === length && !wasComplete) {\n onComplete?.(clipped);\n }\n }\n\n function setCharAt(index: number, char: string) {\n const chars = currentValue.split('');\n chars[index] = char;\n commit(chars.join(''));\n }\n\n function handleChange(index: number, rawInput: string) {\n if (disabled) return;\n if (rawInput === '') {\n // Box was cleared (e.g. select + delete) — Backspace itself is handled in onKeyDown.\n setCharAt(index, '');\n return;\n }\n\n const filtered = filterChars(rawInput, charset);\n if (!filtered) {\n // Typed an invalid/disallowed character — the controlled `value` re-render discards it.\n return;\n }\n\n // Clicking straight into a box past the contiguous filled prefix (skipping ahead of\n // empty boxes) fills the next expected slot instead, rather than leaving a gap before it\n // that setCharAt/distributeFrom can't represent in a plain string and would silently collapse.\n const targetIndex = Math.min(index, currentValue.length);\n\n if (rawInput.length > 1) {\n // Mobile autofill / SMS suggestions can drop the whole code into one box, bypassing maxLength.\n distributeFrom(targetIndex, filtered);\n return;\n }\n\n setCharAt(targetIndex, filtered);\n if (targetIndex < length - 1) {\n inputRefs.current[targetIndex + 1]?.focus();\n } else {\n inputRefs.current[targetIndex]?.blur();\n }\n }\n\n function distributeFrom(startIndex: number, chars: string) {\n const clean = filterChars(chars, charset);\n if (!clean) return;\n const existing = currentValue.padEnd(length, ' ').split('');\n let cursor = startIndex;\n for (const char of clean) {\n if (cursor >= length) break;\n existing[cursor] = char;\n cursor += 1;\n }\n commit(existing.join('').trimEnd());\n const nextFocus = Math.min(cursor, length - 1);\n inputRefs.current[nextFocus]?.focus();\n if (cursor >= length) {\n inputRefs.current[length - 1]?.blur();\n }\n }\n\n function handlePaste(index: number, event: React.ClipboardEvent<HTMLInputElement>) {\n if (disabled) return;\n const pasted = event.clipboardData.getData('text');\n if (!pasted) return;\n event.preventDefault();\n // Same clamp as handleChange — pasting into a box past the filled prefix starts at the\n // next expected slot instead of leaving an unrepresentable gap before it.\n distributeFrom(Math.min(index, currentValue.length), pasted);\n }\n\n function handleKeyDown(index: number, event: React.KeyboardEvent<HTMLInputElement>) {\n if (disabled) return;\n\n // React's controlled-input onChange never fires when the resulting value is identical to\n // what's already rendered (its value tracker sees no change) — so retyping the exact digit\n // already in a box would otherwise silently do nothing. keydown always fires, so handle that\n // one case here and skip the native edit entirely.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n const typed = filterChars(event.key, charset);\n if (typed && currentValue[index] === typed) {\n event.preventDefault();\n setCharAt(index, typed);\n if (index < length - 1) {\n inputRefs.current[index + 1]?.focus();\n } else {\n inputRefs.current[index]?.blur();\n }\n return;\n }\n }\n\n if (event.key === 'Backspace') {\n if (currentValue[index]) {\n setCharAt(index, '');\n return;\n }\n if (index > 0) {\n event.preventDefault();\n setCharAt(index - 1, '');\n inputRefs.current[index - 1]?.focus();\n }\n return;\n }\n if (event.key === 'Delete') {\n if (currentValue[index]) setCharAt(index, '');\n return;\n }\n if (event.key === 'ArrowLeft' && index > 0) {\n event.preventDefault();\n inputRefs.current[index - 1]?.focus();\n return;\n }\n if (event.key === 'ArrowRight' && index < length - 1) {\n event.preventDefault();\n inputRefs.current[index + 1]?.focus();\n }\n }\n\n function handleFocus(event: React.FocusEvent<HTMLInputElement>) {\n event.target.select();\n }\n\n function handleGroupFocus() {\n setIsFocused(true);\n }\n\n function handleGroupBlur(event: React.FocusEvent<HTMLDivElement>) {\n // Auto-advance moves focus between boxes constantly while typing — only treat this as\n // leaving the group once focus actually lands outside it, not on the next box.\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n if (formValidator) formValidator.touched = true;\n }\n\n return (\n <div {...rest} className={cx(styles.otpInput, disabled && styles.otpInputDisabled, className)} style={style}>\n {label && (\n <div className={cx(styles.otpInputLabel, classNames?.label)} style={slotStyles?.label}>\n {label}\n {isRequired && <span className={styles.otpInputRequiredMark}> *</span>}\n </div>\n )}\n <div\n role='group'\n aria-label={label || text.field}\n className={cx(styles.otpInputBoxes, isShaking && styles.otpInputBoxesShake, classNames?.inputs)}\n style={slotStyles?.inputs}\n onFocus={handleGroupFocus}\n onBlur={handleGroupBlur}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {Array.from({ length }, (_, index) => {\n const char = currentValue[index] || '';\n return (\n <div key={index} className={styles.otpInputBoxWrapper}>\n <input\n ref={(el) => {\n inputRefs.current[index] = el;\n }}\n type={masked ? 'password' : 'text'}\n inputMode={charset === OtpInputCharset.Numeric ? 'numeric' : 'text'}\n pattern={charset === OtpInputCharset.Numeric ? '[0-9]*' : undefined}\n autoComplete={index === 0 ? 'one-time-code' : 'off'}\n maxLength={1}\n disabled={disabled}\n value={char}\n aria-label={text.digit(index + 1, length)}\n aria-invalid={showError || undefined}\n className={cx(\n styles.otpInputBox,\n showError && styles.otpInputBoxError,\n char && styles.otpInputBoxFilled,\n classNames?.input,\n )}\n style={slotStyles?.input}\n onChange={(e) => handleChange(index, e.target.value)}\n onKeyDown={(e) => handleKeyDown(index, e)}\n onPaste={(e) => handlePaste(index, e)}\n onFocus={handleFocus}\n />\n {groupAfter.includes(index + 1) && index < length - 1 && (\n <span className={cx(styles.otpInputSeparator, classNames?.separator)} style={slotStyles?.separator} />\n )}\n </div>\n );\n })}\n </div>\n <Tooltip variant='error' message={displayedErrorMessage} isVisible={showError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default OtpInput;\n"],"mappings":"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"}
1
+ {"version":3,"file":"otpInput.mjs","names":[],"sources":["../../../../package/common/otpInput/otpInput.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport Tooltip from 'components/common/tooltip';\nimport { useComponentDefaults, useLabels } from '../configProvider';\n\nimport { OtpInputCharset, type OtpInputLabels, type OtpInputProps } from './otpInput.types';\nimport styles from './otpInput.module.pcss';\n\nconst NUMERIC_RE = /[0-9]/g;\nconst ALPHANUMERIC_RE = /[a-zA-Z0-9]/g;\n\nconst DEFAULT_LABELS: Required<OtpInputLabels> = {\n field: 'One-time code',\n digit: (position, length) => `Digit ${position} of ${length}`,\n};\n\nfunction filterChars(raw: string, charset: OtpInputCharset): string {\n const re = charset === OtpInputCharset.Alphanumeric ? ALPHANUMERIC_RE : NUMERIC_RE;\n return (raw.match(re) || []).join('');\n}\n\nfunction OtpInput(props: OtpInputProps): ReactElement {\n const {\n length = 6,\n value,\n onChange = () => {},\n onComplete = () => {},\n formValidator = null,\n charset = OtpInputCharset.Numeric,\n masked = false,\n groupAfter = [],\n label = '',\n isValid = true,\n errorMessage = '',\n isRequired = false,\n clearOnError = false,\n clearErrorDelay = 900,\n disabled = false,\n autoFocus = false,\n className = '',\n style,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('OtpInput', props);\n const text = useLabels('OtpInput', labels, DEFAULT_LABELS);\n\n const currentValue = (formValidator ? (formValidator.value as string) : value) ?? '';\n const [, forceUpdate] = useState(0);\n const [isShaking, setIsShaking] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [pendingClear, setPendingClear] = useState(false);\n const [wasError, setWasError] = useState(false);\n const inputRefs = useRef<Array<HTMLInputElement | null>>([]);\n\n useEffect(() => {\n if (!formValidator) return;\n formValidator.validate();\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => formValidator.removeOnUpdateListener(listener);\n }, [formValidator]);\n\n useEffect(() => {\n if (!autoFocus) return;\n const firstEmpty = Math.min(currentValue.length, length - 1);\n inputRefs.current[firstEmpty]?.focus();\n // Only run once, on mount.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const showError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const displayedErrorMessage = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n // State set during render, not a ref read in an effect: the ref survives a remount (StrictMode,\n // <Activity>), so the re-run saw no new error and dropped the pending clear.\n if (showError !== wasError) {\n setWasError(showError);\n if (showError) setIsShaking(true);\n setPendingClear(showError && clearOnError);\n }\n\n useEffect(() => {\n if (!isShaking) return;\n const id = window.setTimeout(() => setIsShaking(false), 400);\n return () => window.clearTimeout(id);\n }, [isShaking]);\n\n useEffect(() => {\n if (!pendingClear || !clearOnError) return;\n const id = window.setTimeout(() => {\n setPendingClear(false);\n commit('');\n inputRefs.current[0]?.focus();\n }, clearErrorDelay);\n return () => window.clearTimeout(id);\n // commit is recreated every render; listing it would restart the delay on each keystroke.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [pendingClear, clearOnError, clearErrorDelay]);\n\n function commit(next: string) {\n const wasComplete = currentValue.length === length;\n const clipped = filterChars(next, charset).slice(0, length);\n if (formValidator) {\n formValidator.set(clipped);\n }\n onChange?.(clipped);\n // Only fire once per genuinely new completion (going from partial to full), not on every\n // keystroke made while correcting an already-full code.\n if (clipped.length === length && !wasComplete) {\n onComplete?.(clipped);\n }\n }\n\n function setCharAt(index: number, char: string) {\n const chars = currentValue.split('');\n chars[index] = char;\n commit(chars.join(''));\n }\n\n function handleChange(index: number, rawInput: string) {\n if (disabled) return;\n if (rawInput === '') {\n // Box was cleared (e.g. select + delete) — Backspace itself is handled in onKeyDown.\n setCharAt(index, '');\n return;\n }\n\n const filtered = filterChars(rawInput, charset);\n if (!filtered) {\n // Typed an invalid/disallowed character — the controlled `value` re-render discards it.\n return;\n }\n\n // Clicking straight into a box past the contiguous filled prefix (skipping ahead of\n // empty boxes) fills the next expected slot instead, rather than leaving a gap before it\n // that setCharAt/distributeFrom can't represent in a plain string and would silently collapse.\n const targetIndex = Math.min(index, currentValue.length);\n\n if (rawInput.length > 1) {\n // Mobile autofill / SMS suggestions can drop the whole code into one box, bypassing maxLength.\n distributeFrom(targetIndex, filtered);\n return;\n }\n\n setCharAt(targetIndex, filtered);\n if (targetIndex < length - 1) {\n inputRefs.current[targetIndex + 1]?.focus();\n } else {\n inputRefs.current[targetIndex]?.blur();\n }\n }\n\n function distributeFrom(startIndex: number, chars: string) {\n const clean = filterChars(chars, charset);\n if (!clean) return;\n const existing = currentValue.padEnd(length, ' ').split('');\n let cursor = startIndex;\n for (const char of clean) {\n if (cursor >= length) break;\n existing[cursor] = char;\n cursor += 1;\n }\n commit(existing.join('').trimEnd());\n const nextFocus = Math.min(cursor, length - 1);\n inputRefs.current[nextFocus]?.focus();\n if (cursor >= length) {\n inputRefs.current[length - 1]?.blur();\n }\n }\n\n function handlePaste(index: number, event: React.ClipboardEvent<HTMLInputElement>) {\n if (disabled) return;\n const pasted = event.clipboardData.getData('text');\n if (!pasted) return;\n event.preventDefault();\n // Same clamp as handleChange — pasting into a box past the filled prefix starts at the\n // next expected slot instead of leaving an unrepresentable gap before it.\n distributeFrom(Math.min(index, currentValue.length), pasted);\n }\n\n function handleKeyDown(index: number, event: React.KeyboardEvent<HTMLInputElement>) {\n if (disabled) return;\n\n // React's controlled-input onChange never fires when the resulting value is identical to\n // what's already rendered (its value tracker sees no change) — so retyping the exact digit\n // already in a box would otherwise silently do nothing. keydown always fires, so handle that\n // one case here and skip the native edit entirely.\n if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {\n const typed = filterChars(event.key, charset);\n if (typed && currentValue[index] === typed) {\n event.preventDefault();\n setCharAt(index, typed);\n if (index < length - 1) {\n inputRefs.current[index + 1]?.focus();\n } else {\n inputRefs.current[index]?.blur();\n }\n return;\n }\n }\n\n if (event.key === 'Backspace') {\n if (currentValue[index]) {\n setCharAt(index, '');\n return;\n }\n if (index > 0) {\n event.preventDefault();\n setCharAt(index - 1, '');\n inputRefs.current[index - 1]?.focus();\n }\n return;\n }\n if (event.key === 'Delete') {\n if (currentValue[index]) setCharAt(index, '');\n return;\n }\n if (event.key === 'ArrowLeft' && index > 0) {\n event.preventDefault();\n inputRefs.current[index - 1]?.focus();\n return;\n }\n if (event.key === 'ArrowRight' && index < length - 1) {\n event.preventDefault();\n inputRefs.current[index + 1]?.focus();\n }\n }\n\n function handleFocus(event: React.FocusEvent<HTMLInputElement>) {\n event.target.select();\n }\n\n function handleGroupFocus() {\n setIsFocused(true);\n }\n\n function handleGroupBlur(event: React.FocusEvent<HTMLDivElement>) {\n // Auto-advance moves focus between boxes constantly while typing — only treat this as\n // leaving the group once focus actually lands outside it, not on the next box.\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n if (formValidator) formValidator.touched = true;\n }\n\n return (\n <div {...rest} className={cx(styles.otpInput, disabled && styles.otpInputDisabled, className)} style={style}>\n {label && (\n <div className={cx(styles.otpInputLabel, classNames?.label)} style={slotStyles?.label}>\n {label}\n {isRequired && <span className={styles.otpInputRequiredMark}> *</span>}\n </div>\n )}\n <div\n role='group'\n aria-label={label || text.field}\n className={cx(styles.otpInputBoxes, isShaking && styles.otpInputBoxesShake, classNames?.inputs)}\n style={slotStyles?.inputs}\n onFocus={handleGroupFocus}\n onBlur={handleGroupBlur}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {Array.from({ length }, (_, index) => {\n const char = currentValue[index] || '';\n return (\n <div key={index} className={styles.otpInputBoxWrapper}>\n <input\n ref={(el) => {\n inputRefs.current[index] = el;\n }}\n type={masked ? 'password' : 'text'}\n inputMode={charset === OtpInputCharset.Numeric ? 'numeric' : 'text'}\n pattern={charset === OtpInputCharset.Numeric ? '[0-9]*' : undefined}\n autoComplete={index === 0 ? 'one-time-code' : 'off'}\n maxLength={1}\n disabled={disabled}\n value={char}\n aria-label={text.digit(index + 1, length)}\n aria-invalid={showError || undefined}\n className={cx(\n styles.otpInputBox,\n showError && styles.otpInputBoxError,\n char && styles.otpInputBoxFilled,\n classNames?.input,\n )}\n style={slotStyles?.input}\n onChange={(e) => handleChange(index, e.target.value)}\n onKeyDown={(e) => handleKeyDown(index, e)}\n onPaste={(e) => handlePaste(index, e)}\n onFocus={handleFocus}\n />\n {groupAfter.includes(index + 1) && index < length - 1 && (\n <span className={cx(styles.otpInputSeparator, classNames?.separator)} style={slotStyles?.separator} />\n )}\n </div>\n );\n })}\n </div>\n <Tooltip variant='error' message={displayedErrorMessage} isVisible={showError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default OtpInput;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import r from"../bodyEnd/bodyEnd.mjs";import i from"./popover.module.mjs";import a from"../styles/sheet.module.mjs";import o from"../sheetBackdrop/sheetBackdrop.mjs";import{PopoverAlign as ee,PopoverPlacement as s,PopoverTrigger as c}from"./popover.types.mjs";import{usePopoverPosition as l}from"./usePopoverPosition.mjs";import{tabbablesIn as u,useFocusBoundary as te}from"../hooks/useFocusBoundary.mjs";import ne from"../hooks/useSheet.mjs";import{useOverlay as re}from"../hooks/useOverlay.mjs";import{useCallback as ie,useEffect as d,useRef as f,useState as p}from"react";import m from"classnames";import{jsx as h,jsxs as g}from"react/jsx-runtime";var ae=1e3;function _(e,t){return e?{current:e}:t}function v(v){let{children:oe=null,anchor:y,content:b=null,placement:x=s.Bottom,align:S=ee.Center,trigger:C=c.Click,isOpen:w,defaultOpen:se=!1,onOpenChange:T=()=>{},offset:ce=10,withArrow:E=!0,closeOnOutsideClick:D=!0,closeOnEscape:le=!0,openDelay:ue=100,closeDelay:de=120,disabled:O=!1,focusOnOpen:k=`keyboard`,closeOnTriggerClick:fe=!0,width:pe=`auto`,constrainWidth:me=!0,presentation:he,className:ge,style:_e,classNames:A,styles:j,...M}=e(`Popover`,v),N=f(null),P=f(null),F=f(null),I=f(null),L=f(void 0),R=f(!1),z=f(!1),B=f(!1);function V(){R.current=!0}let[H,ve]=p(()=>({anchor:y,ref:_(y,N)}));H.anchor!==y&&ve({anchor:y,ref:_(y,N)});let U=H.ref,W=f(null),G=y!==void 0;d(()=>{if(!G)return;function e(e){W.current={target:e.target,keyboard:e.detail===0,time:performance.now()}}return document.addEventListener(`click`,e,!0),()=>document.removeEventListener(`click`,e,!0)},[G]);let K=w!==void 0,[ye,be]=p(se),q=K?w:ye,J=f(T);d(()=>{J.current=T});let Y=ie((e,t=!1)=>{O&&e||(e&&(B.current=t),K||be(e),J.current(e,{fromKeyboard:t}))},[O,K]),xe=t(),Se=he??xe??n.Default,{isSheet:Ce,sheetStyle:we}=ne(Se),X=Ce&&C===c.Click;l({isOpen:!!q&&!X,placement:x,align:S,offset:ce,triggerRef:U,floatingRef:P,panelRef:F,arrowRef:E?I:{current:null},matchTriggerWidth:pe===`trigger`});let{restoreFocusToTrigger:Z,focusAfterTrigger:Q}=te({triggerRef:U,contentRef:P});re({isActive:!!q,containerRef:F,onEscape:le?()=>{P.current?.contains(document.activeElement)&&Z(),Y(!1,!0)}:void 0,trapFocus:!1,lockScroll:X}),d(()=>{if(!q){B.current=!1;return}let e=W.current;W.current=null;let t=!!y&&!!e?.keyboard&&y.contains(e.target)&&performance.now()-e.time<ae;if(!(k===!0||k===`keyboard`&&(B.current||t))||C!==c.Click)return;let n=u(F.current);n.length!==0&&(P.current?.setAttribute(`role`,`dialog`),n[0].focus())},[q,C,k,y]);function Te(e){if(e.key!==`Tab`||C!==c.Click)return;let t=u(F.current);if(t.length===0)return;let n=document.activeElement;!e.shiftKey&&n===t[t.length-1]?(e.preventDefault(),Q(),Y(!1,!0)):e.shiftKey&&n===t[0]&&(e.preventDefault(),Z(),Y(!1,!0))}d(()=>{if(!q)return;R.current=!1,z.current=!1;function e(e){let t=R.current;R.current=!1;let n=e.target,r=P.current?.contains(n),i=N.current?.contains(n)||U.current?.contains(n);return!!(t||r||i)}function t(t){z.current=!e(t)}function n(t){let n=z.current;z.current=!1,!e(t)&&n&&Y(!1)}function r(t){e(t)||Y(!1,!0)}return D&&C!==c.Hover&&(window.addEventListener(`mousedown`,t),window.addEventListener(`mouseup`,n),document.addEventListener(`focusin`,r)),()=>{window.removeEventListener(`mousedown`,t),window.removeEventListener(`mouseup`,n),document.removeEventListener(`focusin`,r)}},[q,D,C,Y,U]),d(()=>()=>clearTimeout(L.current),[]);function $(e){clearTimeout(L.current),L.current=setTimeout(()=>Y(e),e?ue:de)}function Ee(e){q||e.key!==`ArrowDown`&&e.key!==`ArrowUp`||(e.preventDefault(),Y(!0,!0))}let De=C===c.Click?{onClick:e=>{(!q||fe)&&Y(!q,e.detail===0)},onKeyDown:Ee}:C===c.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{onFocus:()=>Y(!0),onBlur:()=>Y(!1)},Oe=C===c.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{};return g(`div`,{...M,className:m(i.popover,ge,A?.root),style:{..._e,...j?.root},children:[h(`div`,{ref:N,className:m(i.popoverTrigger,A?.trigger),style:j?.trigger,...De,children:oe}),q&&g(r,{children:[X&&h(o,{onClose:()=>{D&&Y(!1)},className:A?.backdrop,style:j?.backdrop}),g(`div`,{ref:P,className:m(X?a.sheet:i.popoverFloating,A?.floating),style:{...X?we:void 0,...j?.floating},"data-placement":x,onKeyDown:Te,onMouseDownCapture:V,onMouseUpCapture:V,onFocusCapture:V,...Oe,children:[h(`div`,{ref:F,className:m(i.popoverPanel,me&&i.popoverPanelConstrained,X&&a.sheetPanel,A?.panel),style:j?.panel,children:b}),E&&!X&&h(`div`,{ref:I,className:m(i.popoverArrow,A?.arrow),style:j?.arrow})]})]})]})}export{v as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import r from"../bodyEnd/bodyEnd.mjs";import i from"./popover.module.mjs";import a from"../styles/sheet.module.mjs";import ee from"../sheetBackdrop/sheetBackdrop.mjs";import{PopoverAlign as te,PopoverPlacement as ne,PopoverTrigger as o}from"./popover.types.mjs";import{usePopoverPosition as re}from"./usePopoverPosition.mjs";import{tabbablesIn as s,useFocusBoundary as ie}from"../hooks/useFocusBoundary.mjs";import ae from"../hooks/useSheet.mjs";import{useOverlay as c}from"../hooks/useOverlay.mjs";import{useCallback as l,useEffect as u,useRef as d,useState as f}from"react";import p from"classnames";import{jsx as m,jsxs as h}from"react/jsx-runtime";var oe=1e3;function g(e,t){return e?{current:e}:t}function _(_){let{children:se=null,anchor:v,content:ce=null,placement:y=ne.Bottom,align:le=te.Center,trigger:b=o.Click,isOpen:x,defaultOpen:ue=!1,onOpenChange:S=()=>{},offset:C=10,withArrow:w=!0,closeOnOutsideClick:T=!0,closeOnEscape:de=!0,openDelay:E=100,closeDelay:fe=120,disabled:D=!1,focusOnOpen:O=`keyboard`,closeOnTriggerClick:pe=!0,width:me=`auto`,constrainWidth:he=!0,presentation:ge,className:_e,style:ve,classNames:k,styles:A,...ye}=e(`Popover`,_),j=d(null),M=d(null),N=d(null),P=d(null),F=d(void 0),I=d(!1),L=d(!1),R=d(!1),z=d(null);function B(){I.current=!0}function be(e){B(),z.current=e.target}let[V,xe]=f(()=>({anchor:v,ref:g(v,j)}));V.anchor!==v&&xe({anchor:v,ref:g(v,j)});let H=V.ref,U=d(null),W=v!==void 0;u(()=>{if(!W)return;function e(e){U.current={target:e.target,keyboard:e.detail===0,time:performance.now()}}return document.addEventListener(`click`,e,!0),()=>document.removeEventListener(`click`,e,!0)},[W]);let G=x!==void 0,[Se,Ce]=f(ue),K=G?x:Se,q=d(S);u(()=>{q.current=S});let J=l((e,t=!1)=>{D&&e||(e&&(R.current=t),G||Ce(e),q.current(e,{fromKeyboard:t}))},[D,G]),we=t(),Te=ge??we??n.Default,{isSheet:Ee,sheetStyle:De}=ae(Te),Y=Ee&&b===o.Click;re({isOpen:!!K&&!Y,placement:y,align:le,offset:C,triggerRef:H,floatingRef:M,panelRef:N,arrowRef:w?P:{current:null},matchTriggerWidth:me===`trigger`});let{restoreFocusToTrigger:X,focusAfterTrigger:Oe}=ie({triggerRef:H,contentRef:M}),Z=d(X);u(()=>{Z.current=X});let Q=d(!1),ke=l(e=>{if(M.current=e,Q.current=!1,e)return()=>{M.current=null;let t=document.activeElement;Q.current=!!t&&(e.contains(t)||t===z.current)}},[]);u(()=>{if(K||!Q.current)return;Q.current=!1;let e=document.activeElement;(!e||e===document.body)&&Z.current()},[K]),c({isActive:!!K,containerRef:N,onEscape:de?()=>{M.current?.contains(document.activeElement)&&X(),J(!1,!0)}:void 0,trapFocus:!1,lockScroll:Y}),u(()=>{if(!K){R.current=!1;return}let e=U.current;U.current=null;let t=!!v&&!!e?.keyboard&&v.contains(e.target)&&performance.now()-e.time<oe;if(!(O===!0||O===`keyboard`&&(R.current||t))||b!==o.Click)return;let n=s(N.current);n.length!==0&&(M.current?.setAttribute(`role`,`dialog`),n[0].focus())},[K,b,O,v]);function Ae(e){if(e.key!==`Tab`||b!==o.Click)return;let t=s(N.current);if(t.length===0)return;let n=document.activeElement;!e.shiftKey&&n===t[t.length-1]?(e.preventDefault(),Oe(),J(!1,!0)):e.shiftKey&&n===t[0]&&(e.preventDefault(),X(),J(!1,!0))}u(()=>{if(!K)return;I.current=!1,L.current=!1;function e(e){return!!(M.current?.contains(e)||j.current?.contains(e)||H.current?.contains(e))}function t(t){let n=I.current;return I.current=!1,n||e(t.target)}function n(e){L.current=!t(e)}function r(e){let n=L.current;L.current=!1,!t(e)&&n&&J(!1)}function i(n){if(t(n))return;let r=n.relatedTarget;r&&(r===z.current||e(r))&&J(!1,!L.current)}return T&&b!==o.Hover&&(window.addEventListener(`mousedown`,n),window.addEventListener(`mouseup`,r),document.addEventListener(`focusin`,i)),()=>{window.removeEventListener(`mousedown`,n),window.removeEventListener(`mouseup`,r),document.removeEventListener(`focusin`,i)}},[K,T,b,J,H]),u(()=>()=>clearTimeout(F.current),[]);function $(e){clearTimeout(F.current),F.current=setTimeout(()=>J(e),e?E:fe)}function je(e){K||e.key!==`ArrowDown`&&e.key!==`ArrowUp`||(e.preventDefault(),J(!0,!0))}let Me=b===o.Click?{onClick:e=>{(!K||pe)&&J(!K,e.detail===0)},onKeyDown:je}:b===o.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{onFocus:()=>J(!0),onBlur:()=>J(!1)},Ne=b===o.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{};return h(`div`,{...ye,className:p(i.popover,_e,k?.root),style:{...ve,...A?.root},children:[m(`div`,{ref:j,className:p(i.popoverTrigger,k?.trigger),style:A?.trigger,...Me,children:se}),K&&h(r,{children:[Y&&m(ee,{onClose:()=>{T&&J(!1)},className:k?.backdrop,style:A?.backdrop}),h(`div`,{ref:ke,className:p(Y?a.sheet:i.popoverFloating,k?.floating),style:{...Y?De:void 0,...A?.floating},"data-placement":y,onKeyDown:Ae,onMouseDownCapture:B,onMouseUpCapture:B,onFocusCapture:be,...Ne,children:[m(`div`,{ref:N,className:p(i.popoverPanel,he&&i.popoverPanelConstrained,Y&&a.sheetPanel,k?.panel),style:A?.panel,children:ce}),w&&!Y&&m(`div`,{ref:P,className:p(i.popoverArrow,k?.arrow),style:A?.arrow})]})]})]})}export{_ as default};
2
2
  //# sourceMappingURL=popover.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"popover.mjs","names":[],"sources":["../../../../package/common/popover/popover.tsx"],"sourcesContent":["import { useRef, useEffect, useState, useCallback, type RefObject } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './popover.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport SheetBackdrop from '../sheetBackdrop';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { useFocusBoundary, tabbablesIn } from '../hooks/useFocusBoundary';\nimport { useSheet } from '../hooks/useSheet';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { Presentation } from '../configProvider/presentation.types';\nimport { PopoverPlacement, PopoverAlign, PopoverTrigger, type PopoverProps } from './popover.types';\n\n/**\n * How long after its click an anchored popover still counts as opened by it: long enough for the\n * render the click causes, short enough that an old keypress does not decide a later open.\n */\nconst ACTIVATION_WINDOW_MS = 1000;\n\nfunction toReference(\n anchor: HTMLElement | null | undefined,\n triggerRef: RefObject<HTMLElement | null>,\n): RefObject<HTMLElement | null> {\n return anchor ? { current: anchor } : triggerRef;\n}\n\nfunction Popover(props: PopoverProps) {\n const {\n children = null,\n anchor,\n content = null,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Center,\n trigger = PopoverTrigger.Click,\n isOpen,\n defaultOpen = false,\n onOpenChange = () => {},\n offset = 10,\n withArrow = true,\n closeOnOutsideClick = true,\n closeOnEscape = true,\n openDelay = 100,\n closeDelay = 120,\n disabled = false,\n focusOnOpen = 'keyboard',\n closeOnTriggerClick = true,\n width = 'auto',\n constrainWidth = true,\n presentation: presentationProp,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Popover', props);\n const triggerRef = useRef<HTMLDivElement>(null);\n const floatingRef = useRef<HTMLDivElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const arrowRef = useRef<HTMLDivElement>(null);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // Set from a capture handler on the panel, which React fires for its whole\n // React subtree — including children that portal elsewhere in the DOM, such as\n // a Dropdown's list inside a DatePicker's calendar. A DOM `contains` check\n // cannot see those, and without this the panel closes when one is used.\n const panelInteractedRef = useRef(false);\n // An outside click is a press that starts and ends outside while the panel is open. One that\n // starts inside is a drag, such as selecting a field's text, and Chromium and WebKit send its\n // `mouseup` to wherever it ends. One that started before the panel opened is the gesture that\n // opened it, such as Calendar's drag to create, which opens an editor on `pointerup`.\n const pressStartedOutsideRef = useRef(false);\n // Whether the open that is running came from the keyboard, which is what the\n // default `focusOnOpen` follows and what `onOpenChange` reports. A click raised\n // by Enter or Space reports `detail === 0`, where a pointer press counts the\n // clicks; an open with no trigger event at all, such as a panel that appears\n // while the user types, leaves this false.\n const openedByKeyboardRef = useRef(false);\n function markPanelInteraction() {\n panelInteractedRef.current = true;\n }\n\n // What the panel is placed against, and where the focus goes back to. A new object exactly when\n // `anchor` changes, which is what restarts the position tracking for the new element: state, not\n // a memo, because a memo React chose to drop would restart it for no reason.\n const [reference, setReference] = useState(() => ({ anchor, ref: toReference(anchor, triggerRef) }));\n if (reference.anchor !== anchor) setReference({ anchor, ref: toReference(anchor, triggerRef) });\n const referenceRef = reference.ref;\n\n // An anchored popover is opened by its consumer through `isOpen`, so none of the key handling\n // below sees how. The click that caused it does: Enter and Space raise one with `detail === 0`,\n // the same test the trigger uses. Not `:focus-visible`, which a text field matches after a\n // mouse click too, and which stays on a keyboard-focused button that is then clicked.\n const activationRef = useRef<{ target: Node; keyboard: boolean; time: number } | null>(null);\n const anchored = anchor !== undefined;\n useEffect(() => {\n if (!anchored) return;\n function onClick(event: MouseEvent) {\n activationRef.current = { target: event.target as Node, keyboard: event.detail === 0, time: performance.now() };\n }\n // Capturing, so it is recorded before the consumer's own handler opens the panel.\n document.addEventListener('click', onClick, true);\n return () => document.removeEventListener('click', onClick, true);\n }, [anchored]);\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = isControlled ? isOpen : internalOpen;\n\n // Keep the outside-click/Escape listeners pointed at the latest setter without re-subscribing.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => {\n onOpenChangeRef.current = onOpenChange;\n });\n\n const setOpen = useCallback(\n (next: boolean, fromKeyboard = false) => {\n if (disabled && next) return;\n if (next) openedByKeyboardRef.current = fromKeyboard;\n if (!isControlled) setInternalOpen(next);\n onOpenChangeRef.current(next, { fromKeyboard });\n },\n [disabled, isControlled],\n );\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet: sheetPresentation, sheetStyle } = useSheet(presentation);\n // Hover and focus are pointer affordances that open something small next to what caused them;\n // a sheet is a deliberate, screen-owning open. Only a click asks for one.\n const isSheet = sheetPresentation && trigger === PopoverTrigger.Click;\n\n // A sheet isn't anchored to anything, so there's nothing to track, flip or clamp.\n usePopoverPosition({\n isOpen: !!open && !isSheet,\n placement,\n align,\n offset,\n triggerRef: referenceRef,\n floatingRef,\n panelRef,\n arrowRef: withArrow ? arrowRef : { current: null },\n matchTriggerWidth: width === 'trigger',\n });\n\n // Registered for as long as it's open, not just as a sheet: this is what puts the popover on\n // the overlay stack, so an enclosing Modal knows something sits above it and neither drags\n // focus out of this (portaled, so outside the modal's panel) panel nor closes itself on the\n // Escape meant for this. Focus isn't trapped: a Popover is deliberately non-modal, and it\n // already moves focus into the panel on open by itself.\n const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({\n triggerRef: referenceRef,\n contentRef: floatingRef,\n });\n\n useOverlay({\n isActive: !!open,\n containerRef: panelRef,\n onEscape: closeOnEscape\n ? () => {\n // Hand focus back to the trigger when it currently sits inside the panel.\n if (floatingRef.current?.contains(document.activeElement)) restoreFocusToTrigger();\n setOpen(false, true);\n }\n : undefined,\n trapFocus: false,\n lockScroll: isSheet,\n });\n\n // The panel is portaled to the end of the DOM, so Tab never flows into it naturally. When we\n // manage focus (click trigger + focusable content), move focus in on open and, on Tab out of\n // either end, close and continue focus as if the panel sat right after the trigger.\n useEffect(() => {\n if (!open) {\n openedByKeyboardRef.current = false;\n return;\n }\n const activation = activationRef.current;\n activationRef.current = null;\n const anchorKeyed =\n !!anchor &&\n !!activation?.keyboard &&\n anchor.contains(activation.target) &&\n performance.now() - activation.time < ACTIVATION_WINDOW_MS;\n const takeFocus =\n focusOnOpen === true || (focusOnOpen === 'keyboard' && (openedByKeyboardRef.current || anchorKeyed));\n if (!takeFocus || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n // Only claim the dialog role when we're actually managing focus for real content.\n floatingRef.current?.setAttribute('role', 'dialog');\n focusables[0].focus();\n }, [open, trigger, focusOnOpen, anchor]);\n\n function handleFloatingKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'Tab' || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n const active = document.activeElement;\n if (!event.shiftKey && active === focusables[focusables.length - 1]) {\n event.preventDefault();\n focusAfterTrigger();\n setOpen(false, true);\n } else if (event.shiftKey && active === focusables[0]) {\n event.preventDefault();\n restoreFocusToTrigger();\n setOpen(false, true);\n }\n }\n\n // Outside click. Registered only while open and only for the relevant trigger mode; Escape is\n // handled by `useOverlay` above, which scopes it to the innermost open layer.\n useEffect(() => {\n if (!open) return;\n // Focus that landed in the panel before these listeners existed (ours on open, or content\n // that focuses itself on mount) set the flag with nothing to clear it, and the first\n // click outside was swallowed.\n panelInteractedRef.current = false;\n pressStartedOutsideRef.current = false;\n\n function isInside(event: Event) {\n // Read and clear: the flag describes this one event, and the panel's React\n // handler has already run by the time this does.\n const inPanelSubtree = panelInteractedRef.current;\n panelInteractedRef.current = false;\n const target = event.target as Node;\n const inPanel = floatingRef.current?.contains(target);\n // Both: with an anchor, `children` still toggle the panel, and a press on them is theirs.\n const inTrigger = triggerRef.current?.contains(target) || referenceRef.current?.contains(target);\n return Boolean(inPanelSubtree || inPanel || inTrigger);\n }\n function onMouseDown(event: MouseEvent) {\n pressStartedOutsideRef.current = !isInside(event);\n }\n function onMouseUp(event: MouseEvent) {\n const pressStartedOutside = pressStartedOutsideRef.current;\n pressStartedOutsideRef.current = false;\n if (!isInside(event) && pressStartedOutside) setOpen(false);\n }\n // Keyboard counterpart to the outside click: once focus leaves both the trigger\n // and the (portaled) panel — e.g. tabbing on to the next element — close.\n function onFocusIn(event: FocusEvent) {\n if (!isInside(event)) setOpen(false, true);\n }\n\n if (closeOnOutsideClick && trigger !== PopoverTrigger.Hover) {\n window.addEventListener('mousedown', onMouseDown);\n window.addEventListener('mouseup', onMouseUp);\n document.addEventListener('focusin', onFocusIn);\n }\n return () => {\n window.removeEventListener('mousedown', onMouseDown);\n window.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('focusin', onFocusIn);\n };\n }, [open, closeOnOutsideClick, trigger, setOpen, referenceRef]);\n\n useEffect(() => () => clearTimeout(hoverTimer.current), []);\n\n function scheduleHover(next: boolean) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);\n }\n\n // Opening from the keyboard without the trigger having to be a button that\n // happens to toggle on Enter: the arrow keys are what the menu-button pattern\n // asks for, and what a consumer reaches for first.\n function handleTriggerKeyDown(event: React.KeyboardEvent) {\n if (open || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return;\n event.preventDefault();\n setOpen(true, true);\n }\n\n const triggerHandlers =\n trigger === PopoverTrigger.Click\n ? {\n onClick: (event: React.MouseEvent) => {\n // A field that opens a picker keeps it open when clicked again, so\n // that placing the caret in the field does not close the panel.\n if (open && !closeOnTriggerClick) return;\n setOpen(!open, event.detail === 0);\n },\n onKeyDown: handleTriggerKeyDown,\n }\n : trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : { onFocus: () => setOpen(true), onBlur: () => setOpen(false) };\n\n const floatingHoverHandlers =\n trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : {};\n\n return (\n <div\n {...rest}\n className={cx(styles.popover, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div\n ref={triggerRef}\n className={cx(styles.popoverTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n {...triggerHandlers}\n >\n {children}\n </div>\n {open && (\n <BodyEnd>\n {isSheet && (\n <SheetBackdrop\n onClose={() => {\n if (closeOnOutsideClick) setOpen(false);\n }}\n className={classNames?.backdrop}\n style={slotStyles?.backdrop}\n />\n )}\n <div\n ref={floatingRef}\n className={cx(isSheet ? sheetStyles.sheet : styles.popoverFloating, classNames?.floating)}\n style={{ ...(isSheet ? sheetStyle : undefined), ...slotStyles?.floating }}\n data-placement={placement}\n onKeyDown={handleFloatingKeyDown}\n onMouseDownCapture={markPanelInteraction}\n onMouseUpCapture={markPanelInteraction}\n onFocusCapture={markPanelInteraction}\n {...floatingHoverHandlers}\n >\n <div\n ref={panelRef}\n className={cx(\n styles.popoverPanel,\n constrainWidth && styles.popoverPanelConstrained,\n isSheet && sheetStyles.sheetPanel,\n classNames?.panel,\n )}\n style={slotStyles?.panel}\n >\n {content}\n </div>\n {withArrow && !isSheet && (\n <div ref={arrowRef} className={cx(styles.popoverArrow, classNames?.arrow)} style={slotStyles?.arrow} />\n )}\n </div>\n </BodyEnd>\n )}\n </div>\n );\n}\n\nexport default Popover;\n"],"mappings":"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"}
1
+ {"version":3,"file":"popover.mjs","names":[],"sources":["../../../../package/common/popover/popover.tsx"],"sourcesContent":["import { useRef, useEffect, useState, useCallback, type RefObject } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './popover.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport SheetBackdrop from '../sheetBackdrop';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { useFocusBoundary, tabbablesIn } from '../hooks/useFocusBoundary';\nimport { useSheet } from '../hooks/useSheet';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { Presentation } from '../configProvider/presentation.types';\nimport { PopoverPlacement, PopoverAlign, PopoverTrigger, type PopoverProps } from './popover.types';\n\n/**\n * How long after its click an anchored popover still counts as opened by it: long enough for the\n * render the click causes, short enough that an old keypress does not decide a later open.\n */\nconst ACTIVATION_WINDOW_MS = 1000;\n\nfunction toReference(\n anchor: HTMLElement | null | undefined,\n triggerRef: RefObject<HTMLElement | null>,\n): RefObject<HTMLElement | null> {\n return anchor ? { current: anchor } : triggerRef;\n}\n\nfunction Popover(props: PopoverProps) {\n const {\n children = null,\n anchor,\n content = null,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Center,\n trigger = PopoverTrigger.Click,\n isOpen,\n defaultOpen = false,\n onOpenChange = () => {},\n offset = 10,\n withArrow = true,\n closeOnOutsideClick = true,\n closeOnEscape = true,\n openDelay = 100,\n closeDelay = 120,\n disabled = false,\n focusOnOpen = 'keyboard',\n closeOnTriggerClick = true,\n width = 'auto',\n constrainWidth = true,\n presentation: presentationProp,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Popover', props);\n const triggerRef = useRef<HTMLDivElement>(null);\n const floatingRef = useRef<HTMLDivElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const arrowRef = useRef<HTMLDivElement>(null);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // Set from a capture handler on the panel, which React fires for its whole\n // React subtree — including children that portal elsewhere in the DOM, such as\n // a Dropdown's list inside a DatePicker's calendar. A DOM `contains` check\n // cannot see those, and without this the panel closes when one is used.\n const panelInteractedRef = useRef(false);\n // An outside click is a press that starts and ends outside while the panel is open. One that\n // starts inside is a drag, such as selecting a field's text, and Chromium and WebKit send its\n // `mouseup` to wherever it ends. One that started before the panel opened is the gesture that\n // opened it, such as Calendar's drag to create, which opens an editor on `pointerup`.\n const pressStartedOutsideRef = useRef(false);\n // Whether the open that is running came from the keyboard, which is what the\n // default `focusOnOpen` follows and what `onOpenChange` reports. A click raised\n // by Enter or Space reports `detail === 0`, where a pointer press counts the\n // clicks; an open with no trigger event at all, such as a panel that appears\n // while the user types, leaves this false.\n const openedByKeyboardRef = useRef(false);\n // A nested portal sits outside the panel's DOM; this is how focus leaving one counts as leaving.\n const lastPanelFocusRef = useRef<EventTarget | null>(null);\n function markPanelInteraction() {\n panelInteractedRef.current = true;\n }\n function markPanelFocus(event: React.FocusEvent) {\n markPanelInteraction();\n lastPanelFocusRef.current = event.target;\n }\n\n // What the panel is placed against, and where the focus goes back to. A new object exactly when\n // `anchor` changes, which is what restarts the position tracking for the new element: state, not\n // a memo, because a memo React chose to drop would restart it for no reason.\n const [reference, setReference] = useState(() => ({ anchor, ref: toReference(anchor, triggerRef) }));\n if (reference.anchor !== anchor) setReference({ anchor, ref: toReference(anchor, triggerRef) });\n const referenceRef = reference.ref;\n\n // An anchored popover is opened by its consumer through `isOpen`, so none of the key handling\n // below sees how. The click that caused it does: Enter and Space raise one with `detail === 0`,\n // the same test the trigger uses. Not `:focus-visible`, which a text field matches after a\n // mouse click too, and which stays on a keyboard-focused button that is then clicked.\n const activationRef = useRef<{ target: Node; keyboard: boolean; time: number } | null>(null);\n const anchored = anchor !== undefined;\n useEffect(() => {\n if (!anchored) return;\n function onClick(event: MouseEvent) {\n activationRef.current = { target: event.target as Node, keyboard: event.detail === 0, time: performance.now() };\n }\n // Capturing, so it is recorded before the consumer's own handler opens the panel.\n document.addEventListener('click', onClick, true);\n return () => document.removeEventListener('click', onClick, true);\n }, [anchored]);\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = isControlled ? isOpen : internalOpen;\n\n // Keep the outside-click/Escape listeners pointed at the latest setter without re-subscribing.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => {\n onOpenChangeRef.current = onOpenChange;\n });\n\n const setOpen = useCallback(\n (next: boolean, fromKeyboard = false) => {\n if (disabled && next) return;\n if (next) openedByKeyboardRef.current = fromKeyboard;\n if (!isControlled) setInternalOpen(next);\n onOpenChangeRef.current(next, { fromKeyboard });\n },\n [disabled, isControlled],\n );\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet: sheetPresentation, sheetStyle } = useSheet(presentation);\n // Hover and focus are pointer affordances that open something small next to what caused them;\n // a sheet is a deliberate, screen-owning open. Only a click asks for one.\n const isSheet = sheetPresentation && trigger === PopoverTrigger.Click;\n\n // A sheet isn't anchored to anything, so there's nothing to track, flip or clamp.\n usePopoverPosition({\n isOpen: !!open && !isSheet,\n placement,\n align,\n offset,\n triggerRef: referenceRef,\n floatingRef,\n panelRef,\n arrowRef: withArrow ? arrowRef : { current: null },\n matchTriggerWidth: width === 'trigger',\n });\n\n // Registered for as long as it's open, not just as a sheet: this is what puts the popover on\n // the overlay stack, so an enclosing Modal knows something sits above it and neither drags\n // focus out of this (portaled, so outside the modal's panel) panel nor closes itself on the\n // Escape meant for this. Focus isn't trapped: a Popover is deliberately non-modal, and it\n // already moves focus into the panel on open by itself.\n const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({\n triggerRef: referenceRef,\n contentRef: floatingRef,\n });\n const restoreFocusRef = useRef(restoreFocusToTrigger);\n useEffect(() => {\n restoreFocusRef.current = restoreFocusToTrigger;\n });\n\n // Focus inside the panel returns to the trigger however it closes, as with `hidePopover()`. Read\n // in a ref cleanup, while the panel is still in the DOM; moved in an effect, because StrictMode\n // detaches a new panel's refs once more right after it took the focus.\n const focusLeftWithPanelRef = useRef(false);\n const floatingCallbackRef = useCallback((node: HTMLDivElement | null) => {\n floatingRef.current = node;\n focusLeftWithPanelRef.current = false;\n if (!node) return;\n return () => {\n floatingRef.current = null;\n const active = document.activeElement;\n focusLeftWithPanelRef.current = !!active && (node.contains(active) || active === lastPanelFocusRef.current);\n };\n }, []);\n useEffect(() => {\n if (open || !focusLeftWithPanelRef.current) return;\n focusLeftWithPanelRef.current = false;\n const active = document.activeElement;\n if (!active || active === document.body) restoreFocusRef.current();\n }, [open]);\n\n useOverlay({\n isActive: !!open,\n containerRef: panelRef,\n onEscape: closeOnEscape\n ? () => {\n // Hand focus back to the trigger when it currently sits inside the panel.\n if (floatingRef.current?.contains(document.activeElement)) restoreFocusToTrigger();\n setOpen(false, true);\n }\n : undefined,\n trapFocus: false,\n lockScroll: isSheet,\n });\n\n // The panel is portaled to the end of the DOM, so Tab never flows into it naturally. When we\n // manage focus (click trigger + focusable content), move focus in on open and, on Tab out of\n // either end, close and continue focus as if the panel sat right after the trigger.\n useEffect(() => {\n if (!open) {\n openedByKeyboardRef.current = false;\n return;\n }\n const activation = activationRef.current;\n activationRef.current = null;\n const anchorKeyed =\n !!anchor &&\n !!activation?.keyboard &&\n anchor.contains(activation.target) &&\n performance.now() - activation.time < ACTIVATION_WINDOW_MS;\n const takeFocus =\n focusOnOpen === true || (focusOnOpen === 'keyboard' && (openedByKeyboardRef.current || anchorKeyed));\n if (!takeFocus || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n // Only claim the dialog role when we're actually managing focus for real content.\n floatingRef.current?.setAttribute('role', 'dialog');\n focusables[0].focus();\n }, [open, trigger, focusOnOpen, anchor]);\n\n function handleFloatingKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'Tab' || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n const active = document.activeElement;\n if (!event.shiftKey && active === focusables[focusables.length - 1]) {\n event.preventDefault();\n focusAfterTrigger();\n setOpen(false, true);\n } else if (event.shiftKey && active === focusables[0]) {\n event.preventDefault();\n restoreFocusToTrigger();\n setOpen(false, true);\n }\n }\n\n // Outside click. Registered only while open and only for the relevant trigger mode; Escape is\n // handled by `useOverlay` above, which scopes it to the innermost open layer.\n useEffect(() => {\n if (!open) return;\n // Focus that landed in the panel before these listeners existed (ours on open, or content\n // that focuses itself on mount) set the flag with nothing to clear it, and the first\n // click outside was swallowed.\n panelInteractedRef.current = false;\n pressStartedOutsideRef.current = false;\n\n function containsNode(node: Node) {\n // Both trigger refs: with an anchor, `children` still toggle the panel, and a press on them is theirs.\n return Boolean(\n floatingRef.current?.contains(node) ||\n triggerRef.current?.contains(node) ||\n referenceRef.current?.contains(node),\n );\n }\n function isInside(event: Event) {\n // Read and clear: the flag describes this one event, and the panel's React\n // handler has already run by the time this does.\n const inPanelSubtree = panelInteractedRef.current;\n panelInteractedRef.current = false;\n return inPanelSubtree || containsNode(event.target as Node);\n }\n function onMouseDown(event: MouseEvent) {\n pressStartedOutsideRef.current = !isInside(event);\n }\n function onMouseUp(event: MouseEvent) {\n const pressStartedOutside = pressStartedOutsideRef.current;\n pressStartedOutsideRef.current = false;\n if (!isInside(event) && pressStartedOutside) setOpen(false);\n }\n // Keyboard counterpart to the outside click. Only focus that comes from inside closes it: code\n // moving focus elsewhere (a Modal restoring it) must not, nor `null`, focus back from another tab.\n function onFocusIn(event: FocusEvent) {\n if (isInside(event)) return;\n const from = event.relatedTarget as Node | null;\n if (!from || (from !== lastPanelFocusRef.current && !containsNode(from))) return;\n // A press on an outside field focuses it before its `mouseup`, and that is not the keyboard.\n setOpen(false, !pressStartedOutsideRef.current);\n }\n\n if (closeOnOutsideClick && trigger !== PopoverTrigger.Hover) {\n window.addEventListener('mousedown', onMouseDown);\n window.addEventListener('mouseup', onMouseUp);\n document.addEventListener('focusin', onFocusIn);\n }\n return () => {\n window.removeEventListener('mousedown', onMouseDown);\n window.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('focusin', onFocusIn);\n };\n }, [open, closeOnOutsideClick, trigger, setOpen, referenceRef]);\n\n useEffect(() => () => clearTimeout(hoverTimer.current), []);\n\n function scheduleHover(next: boolean) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);\n }\n\n // Opening from the keyboard without the trigger having to be a button that\n // happens to toggle on Enter: the arrow keys are what the menu-button pattern\n // asks for, and what a consumer reaches for first.\n function handleTriggerKeyDown(event: React.KeyboardEvent) {\n if (open || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return;\n event.preventDefault();\n setOpen(true, true);\n }\n\n const triggerHandlers =\n trigger === PopoverTrigger.Click\n ? {\n onClick: (event: React.MouseEvent) => {\n // A field that opens a picker keeps it open when clicked again, so\n // that placing the caret in the field does not close the panel.\n if (open && !closeOnTriggerClick) return;\n setOpen(!open, event.detail === 0);\n },\n onKeyDown: handleTriggerKeyDown,\n }\n : trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : { onFocus: () => setOpen(true), onBlur: () => setOpen(false) };\n\n const floatingHoverHandlers =\n trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : {};\n\n return (\n <div\n {...rest}\n className={cx(styles.popover, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div\n ref={triggerRef}\n className={cx(styles.popoverTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n {...triggerHandlers}\n >\n {children}\n </div>\n {open && (\n <BodyEnd>\n {isSheet && (\n <SheetBackdrop\n onClose={() => {\n if (closeOnOutsideClick) setOpen(false);\n }}\n className={classNames?.backdrop}\n style={slotStyles?.backdrop}\n />\n )}\n <div\n ref={floatingCallbackRef}\n className={cx(isSheet ? sheetStyles.sheet : styles.popoverFloating, classNames?.floating)}\n style={{ ...(isSheet ? sheetStyle : undefined), ...slotStyles?.floating }}\n data-placement={placement}\n onKeyDown={handleFloatingKeyDown}\n onMouseDownCapture={markPanelInteraction}\n onMouseUpCapture={markPanelInteraction}\n onFocusCapture={markPanelFocus}\n {...floatingHoverHandlers}\n >\n <div\n ref={panelRef}\n className={cx(\n styles.popoverPanel,\n constrainWidth && styles.popoverPanelConstrained,\n isSheet && sheetStyles.sheetPanel,\n classNames?.panel,\n )}\n style={slotStyles?.panel}\n >\n {content}\n </div>\n {withArrow && !isSheet && (\n <div ref={arrowRef} className={cx(styles.popoverArrow, classNames?.arrow)} style={slotStyles?.arrow} />\n )}\n </div>\n </BodyEnd>\n )}\n </div>\n );\n}\n\nexport default Popover;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"popover.types.mjs","names":[],"sources":["../../../../package/common/popover/popover.types.ts"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\n/** Preferred side of the trigger the panel opens on. Flips to the opposite side when there's no room. */\nexport enum PopoverPlacement {\n Top = 'top',\n Bottom = 'bottom',\n Left = 'left',\n Right = 'right',\n}\n\n/** Alignment along the trigger's cross axis (horizontal for top/bottom, vertical for left/right). */\nexport enum PopoverAlign {\n Start = 'start',\n Center = 'center',\n End = 'end',\n}\n\n/** What opens the popover. */\nexport enum PopoverTrigger {\n Click = 'click',\n Hover = 'hover',\n Focus = 'focus',\n}\n\nexport type PopoverSlots = 'root' | 'trigger' | 'floating' | 'panel' | 'arrow' | 'backdrop';\n\nexport interface PopoverProps extends Omit<HtmlProps, 'content'> {\n /** The trigger element. */\n children?: ReactNode;\n /**\n * Place the panel against this element instead of `children`, for one Popover does not wrap.\n * Open it with `isOpen`; `null` falls back to `children`.\n */\n anchor?: HTMLElement | null;\n /** The popover body. */\n content?: ReactNode;\n /** Preferred side (default `Bottom`). */\n placement?: PopoverPlacement;\n /** Cross-axis alignment (default `Center`). */\n align?: PopoverAlign;\n /** What opens the popover (default `Click`). */\n trigger?: PopoverTrigger;\n /** Controlled open state. Omit for uncontrolled. */\n isOpen?: boolean;\n /** Initial open state when uncontrolled (default `false`). */\n defaultOpen?: boolean;\n /**\n * Fires whenever the popover wants to open or close. `fromKeyboard` says\n * whether a key caused it, which is what a menu needs in order to put the\n * focus on an entry only when the pointer was not what opened it.\n */\n onOpenChange?: (isOpen: boolean, meta: { fromKeyboard: boolean }) => void;\n /** Gap in px between trigger and panel (default `10`). */\n offset?: number;\n /** Render the arrow pointing at the trigger (default `true`). */\n withArrow?: boolean;\n /** Close when clicking outside the panel (default `true`). */\n closeOnOutsideClick?: boolean;\n /** Close when pressing Escape (default `true`). */\n closeOnEscape?: boolean;\n /** Delay in ms before a hover-triggered popover opens (default `100`). */\n openDelay?: number;\n /** Delay in ms before a hover-triggered popover closes (default `120`). */\n closeDelay?: number;\n /** Prevent the popover from opening. */\n disabled?: boolean;\n /**\n * Move focus into the panel when it opens, and claim `role=\"dialog\"` with it.\n * `'keyboard'` (the default) does it only when the open came from the\n * keyboard: the panel is portaled to the end of the document, so Tab never\n * reaches it on its own, while a click leaves the focus where the pointer\n * already put it. `true` always, `false` never.\n */\n focusOnOpen?: boolean | 'keyboard';\n /**\n * Whether clicking the trigger while the panel is open closes it (default\n * `true`). A menu button should toggle; a text field that opens a picker\n * should not, or clicking into it to place the caret would close the panel.\n */\n closeOnTriggerClick?: boolean;\n /**\n * `'trigger'` gives the panel at least the trigger's width, for a list that\n * should line up with the field it belongs to. `'auto'` (the default) lets it\n * size to its own content.\n */\n width?: 'auto' | 'trigger';\n /**\n * Cap the panel at a readable measure (default `true`). Off for a panel with\n * its own width, a calendar or a media grid.\n */\n constrainWidth?: boolean;\n /** Where the panel opens: anchored to the trigger (`Default`), pinned to the bottom of the\n * screen, or the latter only on a small touch screen. Only a `Click` trigger presents as a\n * sheet. */\n presentation?: Presentation;\n\n className?: string;\n style?: CSSProperties;\n classNames?: SlotClassNames<PopoverSlots>;\n styles?: SlotStyles<PopoverSlots>;\n}\n"],"mappings":"aAKA,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,IAAA,MACA,EAAA,OAAA,SACA,EAAA,KAAA,OACA,EAAA,MAAA,SACF,EAAA,CAAA,CAAA,EAGY,EAAL,SAAA,EAAA,OACL,GAAA,MAAA,QACA,EAAA,OAAA,SACA,EAAA,IAAA,OACF,EAAA,CAAA,CAAA,EAGY,EAAL,SAAA,EAAA,OACL,GAAA,MAAA,QACA,EAAA,MAAA,QACA,EAAA,MAAA,SACF,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"popover.types.mjs","names":[],"sources":["../../../../package/common/popover/popover.types.ts"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\n/** Preferred side of the trigger the panel opens on. Flips to the opposite side when there's no room. */\nexport enum PopoverPlacement {\n Top = 'top',\n Bottom = 'bottom',\n Left = 'left',\n Right = 'right',\n}\n\n/** Alignment along the trigger's cross axis (horizontal for top/bottom, vertical for left/right). */\nexport enum PopoverAlign {\n Start = 'start',\n Center = 'center',\n End = 'end',\n}\n\n/** What opens the popover. */\nexport enum PopoverTrigger {\n Click = 'click',\n Hover = 'hover',\n Focus = 'focus',\n}\n\nexport type PopoverSlots = 'root' | 'trigger' | 'floating' | 'panel' | 'arrow' | 'backdrop';\n\nexport interface PopoverProps extends Omit<HtmlProps, 'content'> {\n /** The trigger element. */\n children?: ReactNode;\n /**\n * Place the panel against this element instead of `children`, for one Popover does not wrap.\n * Open it with `isOpen`; `null` falls back to `children`.\n */\n anchor?: HTMLElement | null;\n /** The popover body. */\n content?: ReactNode;\n /** Preferred side (default `Bottom`). */\n placement?: PopoverPlacement;\n /** Cross-axis alignment (default `Center`). */\n align?: PopoverAlign;\n /** What opens the popover (default `Click`). */\n trigger?: PopoverTrigger;\n /** Controlled open state. Omit for uncontrolled. */\n isOpen?: boolean;\n /** Initial open state when uncontrolled (default `false`). */\n defaultOpen?: boolean;\n /**\n * Fires whenever the popover wants to open or close. `fromKeyboard` says\n * whether a key caused it, which is what a menu needs in order to put the\n * focus on an entry only when the pointer was not what opened it.\n */\n onOpenChange?: (isOpen: boolean, meta: { fromKeyboard: boolean }) => void;\n /** Gap in px between trigger and panel (default `10`). */\n offset?: number;\n /** Render the arrow pointing at the trigger (default `true`). */\n withArrow?: boolean;\n /** Close on a click outside, or when focus leaves the trigger and panel (default `true`). */\n closeOnOutsideClick?: boolean;\n /** Close when pressing Escape (default `true`). */\n closeOnEscape?: boolean;\n /** Delay in ms before a hover-triggered popover opens (default `100`). */\n openDelay?: number;\n /** Delay in ms before a hover-triggered popover closes (default `120`). */\n closeDelay?: number;\n /** Prevent the popover from opening. */\n disabled?: boolean;\n /**\n * Move focus into the panel when it opens, and claim `role=\"dialog\"` with it.\n * `'keyboard'` (the default) does it only when the open came from the\n * keyboard: the panel is portaled to the end of the document, so Tab never\n * reaches it on its own, while a click leaves the focus where the pointer\n * already put it. `true` always, `false` never.\n */\n focusOnOpen?: boolean | 'keyboard';\n /**\n * Whether clicking the trigger while the panel is open closes it (default\n * `true`). A menu button should toggle; a text field that opens a picker\n * should not, or clicking into it to place the caret would close the panel.\n */\n closeOnTriggerClick?: boolean;\n /**\n * `'trigger'` gives the panel at least the trigger's width, for a list that\n * should line up with the field it belongs to. `'auto'` (the default) lets it\n * size to its own content.\n */\n width?: 'auto' | 'trigger';\n /**\n * Cap the panel at a readable measure (default `true`). Off for a panel with\n * its own width, a calendar or a media grid.\n */\n constrainWidth?: boolean;\n /** Where the panel opens: anchored to the trigger (`Default`), pinned to the bottom of the\n * screen, or the latter only on a small touch screen. Only a `Click` trigger presents as a\n * sheet. */\n presentation?: Presentation;\n\n className?: string;\n style?: CSSProperties;\n classNames?: SlotClassNames<PopoverSlots>;\n styles?: SlotStyles<PopoverSlots>;\n}\n"],"mappings":"aAKA,IAAY,EAAL,SAAA,EAAA,OACL,GAAA,IAAA,MACA,EAAA,OAAA,SACA,EAAA,KAAA,OACA,EAAA,MAAA,SACF,EAAA,CAAA,CAAA,EAGY,EAAL,SAAA,EAAA,OACL,GAAA,MAAA,QACA,EAAA,OAAA,SACA,EAAA,IAAA,OACF,EAAA,CAAA,CAAA,EAGY,EAAL,SAAA,EAAA,OACL,GAAA,MAAA,QACA,EAAA,MAAA,QACA,EAAA,MAAA,SACF,EAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"scrollbarProvider.module.mjs","names":[],"sources":["../../../../package/common/scrollbarProvider/scrollbarProvider.module.pcss"],"sourcesContent":[".scrollbarProvider {\n display: contents;\n\n /* Firefox */\n * {\n scrollbar-width: thin;\n scrollbar-color: var(--background-accent-light) transparent;\n }\n\n /* Chrome, Edge, and Safari */\n *::-webkit-scrollbar {\n width: 12px;\n }\n\n *::-webkit-scrollbar-track {\n background: transparent;\n }\n\n *::-webkit-scrollbar-thumb {\n background-color: var(--background-accent-light);\n border-radius: 10px;\n border: 3px solid transparent;\n background-clip: padding-box;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"scrollbarProvider.module.mjs","names":[],"sources":["../../../../package/common/scrollbarProvider/scrollbarProvider.module.pcss"],"sourcesContent":[".scrollbarProvider {\n display: contents;\n}\n\n/* Zero specificity, so any scrollbar rule of a component's or the app's own wins. As\n `.scrollbarProvider *` these tied with a single class and won or lost on stylesheet order. */\n\n/* Firefox */\n:where(.scrollbarProvider *) {\n scrollbar-width: thin;\n scrollbar-color: var(--background-accent-light) transparent;\n}\n\n/* Chrome, Edge, and Safari */\n:where(.scrollbarProvider *)::-webkit-scrollbar {\n width: 12px;\n}\n\n:where(.scrollbarProvider *)::-webkit-scrollbar-track {\n background: transparent;\n}\n\n:where(.scrollbarProvider *)::-webkit-scrollbar-thumb {\n background-color: var(--background-accent-light);\n border-radius: 10px;\n border: 3px solid transparent;\n background-clip: padding-box;\n}\n\n/* The page's own scrollbar: <html> is outside every provider, so AhroweProvider sets the thumb\n colour on it. Without that property the value is invalid, and the browser's own stays. */\n:where(html) {\n scrollbar-color: var(--ahrowe-page-scrollbar-thumb) transparent;\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use client";import{ThemeVariablesContext as e}from"./themeVariablesContext.mjs";import{defaultTheme as t}from"./defaultTheme.mjs";import n from"./theme.module.mjs";import r,{Component as i}from"react";import a from"classnames";import{jsx as o}from"react/jsx-runtime";function s(e,t){let n=e.find(e=>e.id===t);return n?[...n.baseThemeId?s(e,n.baseThemeId):[],n.variables]:[]}function c({themes:e=[t],currentThemeId:n}){let r=e.find(e=>!e.baseThemeId)??e[0],i=r?r.variables:{},a=n?s(e,n):[];return Object.assign({},i,...a)}var l=class extends i{constructor(...e){super(...e),this.state={resolvedVariables:c(this.props),colorFadeActive:!1,currentThemeColor:null,resolvedFrom:{themes:this.props.themes,currentThemeId:this.props.currentThemeId}},this.colorFadeTimeout=null,this.wrapperRef=r.createRef(),this.themeColorMeta=null,this.createdThemeColorMeta=!1}static getDerivedStateFromProps(e,t){let{themes:n,currentThemeId:r}=t.resolvedFrom;return e.themes===n&&e.currentThemeId===r?null:{resolvedVariables:c(e),colorFadeActive:!0,resolvedFrom:{themes:e.themes,currentThemeId:e.currentThemeId}}}componentDidMount(){this.readThemeColor()}componentDidUpdate(e,t){t.resolvedVariables!==this.state.resolvedVariables&&(this.readThemeColor(),this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.colorFadeTimeout=setTimeout(()=>{this.setState({colorFadeActive:!1}),this.colorFadeTimeout=null},500)),t.currentThemeColor!==this.state.currentThemeColor&&this.syncThemeColorMeta(this.state.currentThemeColor??``)}componentWillUnmount(){this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.createdThemeColorMeta&&=(this.themeColorMeta?.remove(),this.themeColorMeta=null,!1)}syncThemeColorMeta(e){if(typeof document<`u`){if(!this.themeColorMeta){let e=document.head.querySelector(`meta[name="theme-color"]`);if(e)this.themeColorMeta=e;else{let e=document.createElement(`meta`);e.setAttribute(`name`,`theme-color`),document.head.appendChild(e),this.themeColorMeta=e,this.createdThemeColorMeta=!0}}this.themeColorMeta.setAttribute(`content`,e)}}readThemeColor(){if(!this.wrapperRef.current)return;let e=getComputedStyle(this.wrapperRef.current).getPropertyValue(`--primary-color`).trim();e!==this.state.currentThemeColor&&this.setState({currentThemeColor:e})}render(){let{children:t}=this.props,{resolvedVariables:r,colorFadeActive:i}=this.state;return o(`div`,{ref:this.wrapperRef,id:`themeProvider`,className:a(n.themeRoot,i&&n.colorFade),style:r,children:o(e.Provider,{value:r,children:t})})}};export{l as default};
1
+ "use client";import{defaultTheme as e}from"./defaultTheme.mjs";import{ThemeVariablesContext as t}from"./themeVariablesContext.mjs";import n from"./theme.module.mjs";import r,{Component as i}from"react";import a from"classnames";import{jsx as o}from"react/jsx-runtime";function s(e,t){let n=e.find(e=>e.id===t);return n?[...n.baseThemeId?s(e,n.baseThemeId):[],n.variables]:[]}function c({themes:t=[e],currentThemeId:n}){let r=t.find(e=>!e.baseThemeId)??t[0],i=r?r.variables:{},a=n?s(t,n):[];return Object.assign({},i,...a)}var l=class extends i{constructor(...e){super(...e),this.state={resolvedVariables:c(this.props),colorFadeActive:!1,currentThemeColor:null,resolvedFrom:{themes:this.props.themes,currentThemeId:this.props.currentThemeId}},this.colorFadeTimeout=null,this.wrapperRef=r.createRef(),this.themeColorMeta=null,this.createdThemeColorMeta=!1}static{this.contextType=t}static getDerivedStateFromProps(e,t){let{themes:n,currentThemeId:r}=t.resolvedFrom;return e.themes===n&&e.currentThemeId===r?null:{resolvedVariables:c(e),colorFadeActive:!0,resolvedFrom:{themes:e.themes,currentThemeId:e.currentThemeId}}}componentDidMount(){this.readThemeColor()}componentDidUpdate(e,t){t.resolvedVariables!==this.state.resolvedVariables&&(this.readThemeColor(),this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.colorFadeTimeout=setTimeout(()=>{this.setState({colorFadeActive:!1}),this.colorFadeTimeout=null},500)),t.currentThemeColor!==this.state.currentThemeColor&&this.syncThemeColorMeta(this.state.currentThemeColor??``)}componentWillUnmount(){this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.createdThemeColorMeta&&=(this.themeColorMeta?.remove(),this.themeColorMeta=null,!1)}syncThemeColorMeta(e){if(typeof document<`u`){if(!this.themeColorMeta){let e=document.head.querySelector(`meta[name="theme-color"]`);if(e)this.themeColorMeta=e;else{let e=document.createElement(`meta`);e.setAttribute(`name`,`theme-color`),document.head.appendChild(e),this.themeColorMeta=e,this.createdThemeColorMeta=!0}}this.themeColorMeta.setAttribute(`content`,e)}}readThemeColor(){if(this.context||!this.wrapperRef.current)return;let e=getComputedStyle(this.wrapperRef.current).getPropertyValue(`--primary-color`).trim();e!==this.state.currentThemeColor&&this.setState({currentThemeColor:e})}render(){let{children:e}=this.props,{resolvedVariables:r,colorFadeActive:i}=this.state;return o(`div`,{ref:this.wrapperRef,id:`themeProvider`,className:a(n.themeRoot,i&&n.colorFade),style:r,children:o(t.Provider,{value:r,children:e})})}};export{l as default};
2
2
  //# sourceMappingURL=themeProvider.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"themeProvider.mjs","names":[],"sources":["../../../../package/common/themeProvider/themeProvider.tsx"],"sourcesContent":["import React, { Component } from 'react';\nimport cx from 'classnames';\n\nimport type { Theme } from './theme.types';\nimport { defaultTheme } from './defaultTheme';\nimport { ThemeVariablesContext } from './themeVariablesContext';\nimport styles from './theme.module.pcss';\n\ninterface Props {\n children: React.ReactNode;\n themes?: Theme[];\n currentThemeId?: string;\n}\n\ninterface State {\n resolvedVariables: Record<string, string>;\n colorFadeActive: boolean;\n currentThemeColor: string | null;\n /** What `resolvedVariables` was resolved from, to tell a theme change from any other render. */\n resolvedFrom: { themes: Theme[] | undefined; currentThemeId: string | undefined };\n}\n\n/**\n * Resolves a theme ID's variables by walking up its baseThemeId chain, ordered from deepest\n * ancestor to the requested theme so they merge in order (later entries win).\n */\nfunction resolveChain(themes: Theme[], id: string): Record<string, string>[] {\n const theme = themes.find((t) => t.id === id);\n if (!theme) return [];\n const parentVars = theme.baseThemeId ? resolveChain(themes, theme.baseThemeId) : [];\n return [...parentVars, theme.variables as Record<string, string>];\n}\n\nfunction resolveVariables({ themes = [defaultTheme], currentThemeId }: Props): Record<string, string> {\n // The root theme, the first without a baseThemeId, is always the foundation.\n const rootTheme = themes.find((t) => !t.baseThemeId) ?? themes[0];\n const rootVars = rootTheme ? (rootTheme.variables as Record<string, string>) : {};\n const chainVars = currentThemeId ? resolveChain(themes, currentThemeId) : [];\n return Object.assign({}, rootVars, ...chainVars);\n}\n\nclass ThemeProvider extends Component<Props, State> {\n // Resolved before the first render rather than after mount: resolved in componentDidMount, every\n // component rendered and measured itself unthemed first, and on each page load some 50\n // backgrounds then faded in from nothing.\n state: State = {\n resolvedVariables: resolveVariables(this.props),\n colorFadeActive: false,\n currentThemeColor: null,\n resolvedFrom: { themes: this.props.themes, currentThemeId: this.props.currentThemeId },\n };\n\n private colorFadeTimeout: ReturnType<typeof setTimeout> | null = null;\n private wrapperRef = React.createRef<HTMLDivElement>();\n private themeColorMeta: HTMLMetaElement | null = null;\n private createdThemeColorMeta = false;\n\n // The fade and the new variables have to land in the same render: added a render later, the\n // browser may already have painted the new colours without it.\n static getDerivedStateFromProps(props: Props, state: State): Partial<State> | null {\n const { themes, currentThemeId } = state.resolvedFrom;\n if (props.themes === themes && props.currentThemeId === currentThemeId) return null;\n return {\n resolvedVariables: resolveVariables(props),\n colorFadeActive: true,\n resolvedFrom: { themes: props.themes, currentThemeId: props.currentThemeId },\n };\n }\n\n componentDidMount() {\n this.readThemeColor();\n }\n\n componentDidUpdate(prevProps: Props, prevState: State) {\n if (prevState.resolvedVariables !== this.state.resolvedVariables) {\n this.readThemeColor();\n if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n this.colorFadeTimeout = setTimeout(() => {\n this.setState({ colorFadeActive: false });\n this.colorFadeTimeout = null;\n }, 500);\n }\n if (prevState.currentThemeColor !== this.state.currentThemeColor) {\n this.syncThemeColorMeta(this.state.currentThemeColor ?? '');\n }\n }\n\n componentWillUnmount() {\n if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n // Only remove the meta tag if we created it; leave a host-app tag intact.\n // Also clear the cached refs — StrictMode's dev-only mount/unmount/remount\n // cycle reuses this instance, and a stale ref would otherwise point at a\n // detached element on remount instead of re-inserting into <head>.\n if (this.createdThemeColorMeta) {\n this.themeColorMeta?.remove();\n this.themeColorMeta = null;\n this.createdThemeColorMeta = false;\n }\n }\n\n /**\n * Replaces the former react-helmet usage: keep a single\n * <meta name=\"theme-color\"> in sync with the active theme's primary colour.\n * Reuses a tag the host app already declares; otherwise creates its own\n * (and removes it again on unmount).\n */\n private syncThemeColorMeta(color: string) {\n if (typeof document === 'undefined') return;\n\n if (!this.themeColorMeta) {\n const existing = document.head.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (existing) {\n this.themeColorMeta = existing;\n } else {\n const meta = document.createElement('meta');\n meta.setAttribute('name', 'theme-color');\n document.head.appendChild(meta);\n this.themeColorMeta = meta;\n this.createdThemeColorMeta = true;\n }\n }\n\n this.themeColorMeta.setAttribute('content', color);\n }\n\n /** `--primary-color` as the browser resolved it, which may be a `var()` of another variable. */\n private readThemeColor() {\n if (!this.wrapperRef.current) return;\n const color = getComputedStyle(this.wrapperRef.current).getPropertyValue('--primary-color').trim();\n if (color !== this.state.currentThemeColor) this.setState({ currentThemeColor: color });\n }\n\n render() {\n const { children } = this.props;\n const { resolvedVariables, colorFadeActive } = this.state;\n\n return (\n <div\n ref={this.wrapperRef}\n id='themeProvider'\n className={cx(styles.themeRoot, colorFadeActive && styles.colorFade)}\n style={resolvedVariables as React.CSSProperties}\n >\n <ThemeVariablesContext.Provider value={resolvedVariables}>{children}</ThemeVariablesContext.Provider>\n </div>\n );\n }\n}\n\nexport default ThemeProvider;\n"],"mappings":"4QA0BA,SAAS,EAAa,EAAiB,EAAsC,CAC3E,IAAM,EAAQ,EAAO,KAAM,GAAM,EAAE,KAAO,CAAE,EAG5C,OAFK,EAEE,CAAC,GADW,EAAM,YAAc,EAAa,EAAQ,EAAM,WAAW,EAAI,CAAC,EAC3D,EAAM,SAAmC,EAF7C,CAAC,CAGtB,CAEA,SAAS,EAAiB,CAAE,SAAS,CAAC,CAAY,EAAG,kBAAiD,CAEpG,IAAM,EAAY,EAAO,KAAM,GAAM,CAAC,EAAE,WAAW,GAAK,EAAO,GACzD,EAAW,EAAa,EAAU,UAAuC,CAAC,EAC1E,EAAY,EAAiB,EAAa,EAAQ,CAAc,EAAI,CAAC,EAC3E,OAAO,OAAO,OAAO,CAAC,EAAG,EAAU,GAAG,CAAS,CACjD,CAEA,IAAM,EAAN,cAA4B,CAAwB,+BAInC,KAAA,MAAA,CACb,kBAAmB,EAAiB,KAAK,KAAK,EAC9C,gBAAiB,GACjB,kBAAmB,KACnB,aAAc,CAAE,OAAQ,KAAK,MAAM,OAAQ,eAAgB,KAAK,MAAM,cAAe,CACvF,EAEiE,KAAA,iBAAA,KAC5C,KAAA,WAAA,EAAM,UAA0B,EACJ,KAAA,eAAA,KACjB,KAAA,sBAAA,GAIhC,OAAO,yBAAyB,EAAc,EAAqC,CACjF,GAAM,CAAE,SAAQ,kBAAmB,EAAM,aAEzC,OADI,EAAM,SAAW,GAAU,EAAM,iBAAmB,EAAuB,KACxE,CACL,kBAAmB,EAAiB,CAAK,EACzC,gBAAiB,GACjB,aAAc,CAAE,OAAQ,EAAM,OAAQ,eAAgB,EAAM,cAAe,CAC7E,CACF,CAEA,mBAAoB,CAClB,KAAK,eAAe,CACtB,CAEA,mBAAmB,EAAkB,EAAkB,CACjD,EAAU,oBAAsB,KAAK,MAAM,oBAC7C,KAAK,eAAe,EAChB,KAAK,kBAAkB,aAAa,KAAK,gBAAgB,EAC7D,KAAK,iBAAmB,eAAiB,CACvC,KAAK,SAAS,CAAE,gBAAiB,EAAM,CAAC,EACxC,KAAK,iBAAmB,IAC1B,EAAG,GAAG,GAEJ,EAAU,oBAAsB,KAAK,MAAM,mBAC7C,KAAK,mBAAmB,KAAK,MAAM,mBAAqB,EAAE,CAE9D,CAEA,sBAAuB,CACjB,KAAK,kBAAkB,aAAa,KAAK,gBAAgB,EAK7D,AAGE,KAAK,yBAFL,KAAK,gBAAgB,OAAO,EAC5B,KAAK,eAAiB,KACO,GAEjC,CAQA,mBAA2B,EAAe,CACpC,UAAO,SAAa,IAExB,IAAI,CAAC,KAAK,eAAgB,CACxB,IAAM,EAAW,SAAS,KAAK,cAA+B,0BAA0B,EACxF,GAAI,EACF,KAAK,eAAiB,MACjB,CACL,IAAM,EAAO,SAAS,cAAc,MAAM,EAC1C,EAAK,aAAa,OAAQ,aAAa,EACvC,SAAS,KAAK,YAAY,CAAI,EAC9B,KAAK,eAAiB,EACtB,KAAK,sBAAwB,EAC/B,CACF,CAEA,KAAK,eAAe,aAAa,UAAW,CAAK,CAFjD,CAGF,CAGA,gBAAyB,CACvB,GAAI,CAAC,KAAK,WAAW,QAAS,OAC9B,IAAM,EAAQ,iBAAiB,KAAK,WAAW,OAAO,CAAC,CAAC,iBAAiB,iBAAiB,CAAC,CAAC,KAAK,EAC7F,IAAU,KAAK,MAAM,mBAAmB,KAAK,SAAS,CAAE,kBAAmB,CAAM,CAAC,CACxF,CAEA,QAAS,CACP,GAAM,CAAE,YAAa,KAAK,MACpB,CAAE,oBAAmB,mBAAoB,KAAK,MAEpD,OACE,EAAC,MAAD,CACE,IAAK,KAAK,WACV,GAAG,gBACH,UAAW,EAAG,EAAO,UAAW,GAAmB,EAAO,SAAS,EACnE,MAAO,EAEP,SAAA,EAAC,EAAsB,SAAvB,CAAgC,MAAO,EAAoB,UAAyC,CAAA,CACjG,CAAA,CAET,CACF"}
1
+ {"version":3,"file":"themeProvider.mjs","names":[],"sources":["../../../../package/common/themeProvider/themeProvider.tsx"],"sourcesContent":["import React, { Component } from 'react';\nimport cx from 'classnames';\n\nimport type { Theme, ThemeProviderProps } from './theme.types';\nimport { defaultTheme } from './defaultTheme';\nimport { ThemeVariablesContext } from './themeVariablesContext';\nimport styles from './theme.module.pcss';\n\ninterface State {\n resolvedVariables: Record<string, string>;\n colorFadeActive: boolean;\n currentThemeColor: string | null;\n /** What `resolvedVariables` was resolved from, to tell a theme change from any other render. */\n resolvedFrom: { themes: Theme[] | undefined; currentThemeId: string | undefined };\n}\n\n/**\n * Resolves a theme ID's variables by walking up its baseThemeId chain, ordered from deepest\n * ancestor to the requested theme so they merge in order (later entries win).\n */\nfunction resolveChain(themes: Theme[], id: string): Record<string, string>[] {\n const theme = themes.find((t) => t.id === id);\n if (!theme) return [];\n const parentVars = theme.baseThemeId ? resolveChain(themes, theme.baseThemeId) : [];\n return [...parentVars, theme.variables as Record<string, string>];\n}\n\nfunction resolveVariables({ themes = [defaultTheme], currentThemeId }: ThemeProviderProps): Record<string, string> {\n // The root theme, the first without a baseThemeId, is always the foundation.\n const rootTheme = themes.find((t) => !t.baseThemeId) ?? themes[0];\n const rootVars = rootTheme ? (rootTheme.variables as Record<string, string>) : {};\n const chainVars = currentThemeId ? resolveChain(themes, currentThemeId) : [];\n return Object.assign({}, rootVars, ...chainVars);\n}\n\nclass ThemeProvider extends Component<ThemeProviderProps, State> {\n // A nested provider themes a section, not the page, so it leaves the page's `theme-color` alone.\n static contextType = ThemeVariablesContext;\n declare context: React.ContextType<typeof ThemeVariablesContext>;\n\n // Resolved before the first render rather than after mount: resolved in componentDidMount, every\n // component rendered and measured itself unthemed first, and on each page load some 50\n // backgrounds then faded in from nothing.\n state: State = {\n resolvedVariables: resolveVariables(this.props),\n colorFadeActive: false,\n currentThemeColor: null,\n resolvedFrom: { themes: this.props.themes, currentThemeId: this.props.currentThemeId },\n };\n\n private colorFadeTimeout: ReturnType<typeof setTimeout> | null = null;\n private wrapperRef = React.createRef<HTMLDivElement>();\n private themeColorMeta: HTMLMetaElement | null = null;\n private createdThemeColorMeta = false;\n\n // The fade and the new variables have to land in the same render: added a render later, the\n // browser may already have painted the new colours without it.\n static getDerivedStateFromProps(props: ThemeProviderProps, state: State): Partial<State> | null {\n const { themes, currentThemeId } = state.resolvedFrom;\n if (props.themes === themes && props.currentThemeId === currentThemeId) return null;\n return {\n resolvedVariables: resolveVariables(props),\n colorFadeActive: true,\n resolvedFrom: { themes: props.themes, currentThemeId: props.currentThemeId },\n };\n }\n\n componentDidMount() {\n this.readThemeColor();\n }\n\n componentDidUpdate(prevProps: ThemeProviderProps, prevState: State) {\n if (prevState.resolvedVariables !== this.state.resolvedVariables) {\n this.readThemeColor();\n if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n this.colorFadeTimeout = setTimeout(() => {\n this.setState({ colorFadeActive: false });\n this.colorFadeTimeout = null;\n }, 500);\n }\n if (prevState.currentThemeColor !== this.state.currentThemeColor) {\n this.syncThemeColorMeta(this.state.currentThemeColor ?? '');\n }\n }\n\n componentWillUnmount() {\n if (this.colorFadeTimeout) clearTimeout(this.colorFadeTimeout);\n // Only remove the meta tag if we created it; leave a host-app tag intact.\n // Also clear the cached refs — StrictMode's dev-only mount/unmount/remount\n // cycle reuses this instance, and a stale ref would otherwise point at a\n // detached element on remount instead of re-inserting into <head>.\n if (this.createdThemeColorMeta) {\n this.themeColorMeta?.remove();\n this.themeColorMeta = null;\n this.createdThemeColorMeta = false;\n }\n }\n\n /**\n * Replaces the former react-helmet usage: keep a single\n * <meta name=\"theme-color\"> in sync with the active theme's primary colour.\n * Reuses a tag the host app already declares; otherwise creates its own\n * (and removes it again on unmount).\n */\n private syncThemeColorMeta(color: string) {\n if (typeof document === 'undefined') return;\n\n if (!this.themeColorMeta) {\n const existing = document.head.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (existing) {\n this.themeColorMeta = existing;\n } else {\n const meta = document.createElement('meta');\n meta.setAttribute('name', 'theme-color');\n document.head.appendChild(meta);\n this.themeColorMeta = meta;\n this.createdThemeColorMeta = true;\n }\n }\n\n this.themeColorMeta.setAttribute('content', color);\n }\n\n /** `--primary-color` as the browser resolved it, which may be a `var()` of another variable. */\n private readThemeColor() {\n if (this.context || !this.wrapperRef.current) return;\n const color = getComputedStyle(this.wrapperRef.current).getPropertyValue('--primary-color').trim();\n if (color !== this.state.currentThemeColor) this.setState({ currentThemeColor: color });\n }\n\n render() {\n const { children } = this.props;\n const { resolvedVariables, colorFadeActive } = this.state;\n\n return (\n <div\n ref={this.wrapperRef}\n id='themeProvider'\n className={cx(styles.themeRoot, colorFadeActive && styles.colorFade)}\n style={resolvedVariables as React.CSSProperties}\n >\n <ThemeVariablesContext.Provider value={resolvedVariables}>{children}</ThemeVariablesContext.Provider>\n </div>\n );\n }\n}\n\nexport default ThemeProvider;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"themeVariablesContext.mjs","names":[],"sources":["../../../../package/common/themeProvider/themeVariablesContext.ts"],"sourcesContent":["import { createContext } from 'react';\n\n/**\n * The nearest ThemeProvider's resolved variables, for content that renders outside its DOM\n * subtree. Theme variables reach a component by CSS inheritance, which a portal breaks: a\n * `BodyEnd` child lands wherever `#bodyEnd` happens to be, so it picks up whatever provider\n * wraps *that* node rather than the one it was written under.\n *\n * Deliberately its own module rather than a member of `themeProvider.tsx`: BodyEnd needs the\n * context and nothing else, and importing the component for it would pull the whole\n * ThemeProvider into every bundle that uses a portal.\n */\nexport const ThemeVariablesContext = createContext<Record<string, string> | null>(null);\n"],"mappings":"mDAYA,IAAa,EAAwB,EAA6C,IAAI"}
1
+ {"version":3,"file":"themeVariablesContext.mjs","names":[],"sources":["../../../../package/common/themeProvider/themeVariablesContext.ts"],"sourcesContent":["import { createContext } from 'react';\n\n/**\n * The nearest ThemeProvider's resolved variables, for content that renders outside its DOM\n * subtree. Theme variables reach a component by CSS inheritance, which a portal breaks: a\n * `BodyEnd` child lands wherever the portal target is, so it picks up whatever provider wraps\n * *that* node rather than the one it was written under.\n *\n * Deliberately its own module rather than a member of `themeProvider.tsx`: BodyEnd needs the\n * context and nothing else, and importing the component for it would pull the whole\n * ThemeProvider into every bundle that uses a portal.\n */\nexport const ThemeVariablesContext = createContext<Record<string, string> | null>(null);\n"],"mappings":"mDAYA,IAAa,EAAwB,EAA6C,IAAI"}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ButtonSize as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import i from"../actionIcon/actionIcon.mjs";import"./timer.chime.mjs";import a from"./timer.module.mjs";import{useEffect as o,useImperativeHandle as ee,useMemo as te,useRef as s,useState as c}from"react";import l from"classnames";import{faMinus as ne,faPause as re,faPlay as ie,faPlus as ae,faXmark as oe}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var se=200,ce=1e3,le={cancel:`Cancel timer`};function ue(){return typeof window<`u`&&window.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches===!0}function de(e){let t=Math.ceil(e/1e3),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=String(r).padStart(2,`0`),o=String(i).padStart(2,`0`);return n>0?`${n}:${a}:${o}`:`${a}:${o}`}function f(f){let{ref:fe,durationSeconds:p,autoStart:m=!0,resetOnDurationChange:pe=!0,onTick:h,onComplete:me,onStart:he,onPause:ge,onResume:_e,onCancel:ve,onAddTime:g,onRemoveTime:ye,showControls:_=!0,hideAddButton:be=!1,hideRemoveButton:xe=!1,hidePauseButton:Se=!1,hideCancelButton:Ce=!1,adjustSeconds:v=30,maxSeconds:y,label:b,formatTime:we=de,size:x,strokeWidth:S=10,counterClockwise:Te=!1,urgentThreshold:Ee=.1,urgentMinSeconds:De=3,pulseOnUrgent:Oe=!0,showRunningRipples:C=!1,playSoundOnComplete:w=!1,soundUrl:T,soundVolume:ke=.5,notifyOnComplete:E=!1,notificationTitle:Ae,notificationBody:je,className:Me,style:Ne,classNames:D,styles:O,labels:Pe,...Fe}=e(`Timer`,f),Ie=t(`Timer`,Pe,le),k=p*1e3,[A,Le]=c(m?`running`:`idle`),[j,Re]=c(k),[ze,Be]=c(0),[M,N]=c([]),P=s(j),F=s(A),I=s(k),L=s(!1),R=s(!1),z=s(!1),Ve=s(0);function B(e){P.current=e,Re(e)}function V(e){F.current=e,Le(e)}function H(){Be(e=>e+1)}o(()=>{if(A!==`running`)return;let e=Date.now(),t=P.current,n=setInterval(()=>{let n=Math.max(0,t-(Date.now()-e));B(n),h?.(n),n<=0&&V(`completed`)},se);return()=>clearInterval(n)},[A,ze]),o(()=>{if(A===`completed`&&!L.current){if(L.current=!0,me?.(),w)try{let e=new Audio(T||`data:audio/wav;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANACUAKwAKAMr/kv+R/9n/TwCxALwAVwCs/xb/8/5p/0YAFgFfAeYA3f/N/lP+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`);e.volume=ke,e.play()?.catch(()=>{})}catch{}if(E&&typeof Notification<`u`&&Notification.permission===`granted`)try{new Notification(Ae??`Timer done`,{body:je??(typeof b==`string`?b:void 0)})}catch{}}A!==`completed`&&(L.current=!1)},[A]),o(()=>{if(!z.current){z.current=!0;return}let e=p*1e3;I.current=e,B(e),L.current=!1,H(),pe&&V(m?`running`:`idle`)},[p]),o(()=>{if(!C||A!==`running`||ue())return;function e(){let e=++Ve.current;N(t=>[...t,e])}e();let t=setInterval(e,ce);return()=>clearInterval(t)},[C,A]);function He(e){N(t=>t.filter(t=>t!==e))}function Ue(){E&&!R.current&&typeof Notification<`u`&&Notification.permission==="default"&&(R.current=!0,Notification.requestPermission().catch(()=>{}))}function U(){Ue(),(F.current===`completed`||F.current===`cancelled`)&&(B(I.current),L.current=!1),H(),V(`running`),he?.()}function W(){F.current===`running`&&(V(`paused`),ge?.())}function G(){F.current===`paused`&&(H(),V(`running`),_e?.())}function K(e){let t=y==null?1/0:y*1e3,n=Math.min(P.current+e*1e3,t);B(n),H(),g?.(e,Math.round(n/1e3)),F.current===`completed`&&n>0&&(L.current=!1,V(`running`))}function q(e){let t=Math.max(0,P.current-e*1e3);B(t),H(),ye?.(e,Math.round(t/1e3)),t<=0&&F.current===`running`&&V(`completed`)}function J(){B(I.current),V(`cancelled`),ve?.()}function We(){B(I.current),L.current=!1,V(`idle`)}ee(fe,()=>({start:U,pause:W,resume:G,addTime:K,removeTime:q,cancel:J,reset:We,getStatus:()=>F.current,getRemainingMs:()=>P.current}));let Y=50-S/2,X=te(()=>2*Math.PI*Y,[Y]),Ge=X*(1-(I.current>0?Math.min(1,j/I.current):0)),Z=Math.max(I.current*Ee,De*1e3),Q=A===`running`&&j<=Z,Ke=Q&&j<=Math.min(5e3,Z),$=A===`paused`,qe=_&&!be,Je=_&&!xe,Ye=_&&!Se,Xe=A===`running`?re:ie,Ze=A===`running`?`Pause`:$?`Resume`:A===`idle`?`Start`:`Restart`,Qe=A===`running`?W:$?G:U,$e=_&&!Ce;return d(`div`,{...Fe,className:l(a.timer,Oe&&Q&&a.timerPulsing,Ke&&a.timerCritical,Me,D?.root),style:{...x?{"--timer-size":x}:null,"--timer-stroke-width":`${S}px`,...Ne,...O?.root},children:[d(`div`,{className:l(a.timerRingWrapper,D?.ringWrapper),style:O?.ringWrapper,children:[M.length>0&&u(`div`,{className:l(a.timerRipples,D?.ripples),style:O?.ripples,children:M.map(e=>u(`span`,{className:l(a.timerRipple,D?.ripple),style:O?.ripple,onAnimationEnd:()=>He(e)},e))}),d(`svg`,{className:l(a.timerRingSvg,D?.ringSvg),viewBox:`0 0 100 100`,style:O?.ringSvg,children:[u(`circle`,{className:l(a.timerRingTrack,D?.ringTrack),cx:`50`,cy:`50`,r:Y,style:O?.ringTrack}),u(`circle`,{className:l(a.timerRingProgress,Q&&a.timerRingProgressUrgent,D?.ringProgress),cx:`50`,cy:`50`,r:Y,strokeDasharray:X,strokeDashoffset:Ge,style:{transition:$?`none`:void 0,transform:Te?`rotate(-90deg)`:void 0,...O?.ringProgress}})]}),d(`div`,{className:l(a.timerDisplay,D?.display),style:O?.display,children:[u(`span`,{className:l(a.timerTime,D?.time),style:O?.time,children:we(j)}),b&&u(`span`,{className:l(a.timerLabel,D?.label),style:O?.label,children:b})]})]}),_&&d(`div`,{className:l(a.timerControls,D?.controls),style:O?.controls,children:[d(`div`,{className:a.timerControlsRow,children:[Je&&u(i,{icon:ne,title:`Remove ${v}s`,onClick:()=>q(v),className:l(a.timerAdjustButton,D?.removeButton),style:O?.removeButton}),Ye&&u(r,{size:n.Small,icon:Xe,title:Ze,onClick:Qe,className:D?.pauseButton,style:O?.pauseButton}),qe&&u(i,{icon:ae,title:`Add ${v}s`,onClick:()=>K(v),className:l(a.timerAdjustButton,D?.addButton),style:O?.addButton})]}),$e&&u(i,{icon:oe,title:Ie.cancel,onClick:J,className:l(a.timerCancelButton,D?.cancelButton),style:O?.cancelButton})]})]})}export{f as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ButtonSize as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import i from"../actionIcon/actionIcon.mjs";import"./timer.chime.mjs";import a from"./timer.module.mjs";import{useEffect as o,useImperativeHandle as ee,useMemo as te,useRef as s,useState as c}from"react";import l from"classnames";import{faMinus as ne,faPause as re,faPlay as ie,faPlus as ae,faXmark as oe}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var se=200,ce=1e3,le={cancel:`Cancel timer`};function ue(){return typeof window<`u`&&window.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches===!0}function de(e){let t=Math.ceil(e/1e3),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=String(r).padStart(2,`0`),o=String(i).padStart(2,`0`);return n>0?`${n}:${a}:${o}`:`${a}:${o}`}function f(f){let{ref:fe,durationSeconds:p,autoStart:m=!0,resetOnDurationChange:pe=!0,onTick:h,onComplete:me,onStart:he,onPause:ge,onResume:_e,onCancel:ve,onAddTime:g,onRemoveTime:ye,showControls:_=!0,hideAddButton:be=!1,hideRemoveButton:xe=!1,hidePauseButton:Se=!1,hideCancelButton:Ce=!1,adjustSeconds:v=30,maxSeconds:y,label:b,formatTime:we=de,size:x,strokeWidth:S=10,counterClockwise:Te=!1,urgentThreshold:Ee=.1,urgentMinSeconds:De=3,pulseOnUrgent:Oe=!0,showRunningRipples:C=!1,playSoundOnComplete:w=!1,soundUrl:T,soundVolume:ke=.5,notifyOnComplete:E=!1,notificationTitle:Ae,notificationBody:je,className:Me,style:Ne,classNames:D,styles:O,labels:Pe,...Fe}=e(`Timer`,f),Ie=t(`Timer`,Pe,le),k=p*1e3,[A,Le]=c(m?`running`:`idle`),[j,Re]=c(k),[ze,Be]=c(0),[M,N]=c([]),P=s(j),F=s(A),I=s(k),L=s(!1),R=s(!1),z=s(!1),Ve=s(0);function B(e){P.current=e,Re(e)}function V(e){F.current=e,Le(e)}function H(){Be(e=>e+1)}o(()=>{if(A!==`running`)return;let e=Date.now(),t=P.current,n=setInterval(()=>{let n=Math.max(0,t-(Date.now()-e));B(n),h?.(n),n<=0&&V(`completed`)},se);return()=>clearInterval(n)},[A,ze]),o(()=>{if(A===`completed`&&!L.current){if(L.current=!0,me?.(),w)try{let e=new Audio(T||`data:audio/wav;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANACUAKwAKAMr/kv+R/9n/TwCxALwAVwCs/xb/8/5p/0YAFgFfAeYA3f/N/lP+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`);e.volume=ke,e.play()?.catch(()=>{})}catch{}if(E&&typeof Notification<`u`&&Notification.permission===`granted`)try{new Notification(Ae??`Timer done`,{body:je??(typeof b==`string`?b:void 0)})}catch{}}A!==`completed`&&(L.current=!1)},[A]),o(()=>{if(!z.current){z.current=!0;return}let e=p*1e3;I.current=e,B(e),L.current=!1,H(),pe&&V(m?`running`:`idle`)},[p]),o(()=>{if(!C||A!==`running`||ue())return;function e(){let e=++Ve.current;N(t=>[...t,e])}let t=setTimeout(e,0),n=setInterval(e,ce);return()=>{clearTimeout(t),clearInterval(n)}},[C,A]);function He(e){N(t=>t.filter(t=>t!==e))}function Ue(){E&&!R.current&&typeof Notification<`u`&&Notification.permission==="default"&&(R.current=!0,Notification.requestPermission().catch(()=>{}))}function U(){Ue(),(F.current===`completed`||F.current===`cancelled`)&&(B(I.current),L.current=!1),H(),V(`running`),he?.()}function W(){F.current===`running`&&(V(`paused`),ge?.())}function G(){F.current===`paused`&&(H(),V(`running`),_e?.())}function K(e){let t=y==null?1/0:y*1e3,n=Math.min(P.current+e*1e3,t);B(n),H(),g?.(e,Math.round(n/1e3)),F.current===`completed`&&n>0&&(L.current=!1,V(`running`))}function q(e){let t=Math.max(0,P.current-e*1e3);B(t),H(),ye?.(e,Math.round(t/1e3)),t<=0&&F.current===`running`&&V(`completed`)}function J(){B(I.current),V(`cancelled`),ve?.()}function We(){B(I.current),L.current=!1,V(`idle`)}ee(fe,()=>({start:U,pause:W,resume:G,addTime:K,removeTime:q,cancel:J,reset:We,getStatus:()=>F.current,getRemainingMs:()=>P.current}));let Y=50-S/2,X=te(()=>2*Math.PI*Y,[Y]),Ge=X*(1-(I.current>0?Math.min(1,j/I.current):0)),Z=Math.max(I.current*Ee,De*1e3),Q=A===`running`&&j<=Z,Ke=Q&&j<=Math.min(5e3,Z),$=A===`paused`,qe=_&&!be,Je=_&&!xe,Ye=_&&!Se,Xe=A===`running`?re:ie,Ze=A===`running`?`Pause`:$?`Resume`:A===`idle`?`Start`:`Restart`,Qe=A===`running`?W:$?G:U,$e=_&&!Ce;return d(`div`,{...Fe,className:l(a.timer,Oe&&Q&&a.timerPulsing,Ke&&a.timerCritical,Me,D?.root),style:{...x?{"--timer-size":x}:null,"--timer-stroke-width":`${S}px`,...Ne,...O?.root},children:[d(`div`,{className:l(a.timerRingWrapper,D?.ringWrapper),style:O?.ringWrapper,children:[M.length>0&&u(`div`,{className:l(a.timerRipples,D?.ripples),style:O?.ripples,children:M.map(e=>u(`span`,{className:l(a.timerRipple,D?.ripple),style:O?.ripple,onAnimationEnd:()=>He(e)},e))}),d(`svg`,{className:l(a.timerRingSvg,D?.ringSvg),viewBox:`0 0 100 100`,style:O?.ringSvg,children:[u(`circle`,{className:l(a.timerRingTrack,D?.ringTrack),cx:`50`,cy:`50`,r:Y,style:O?.ringTrack}),u(`circle`,{className:l(a.timerRingProgress,Q&&a.timerRingProgressUrgent,D?.ringProgress),cx:`50`,cy:`50`,r:Y,strokeDasharray:X,strokeDashoffset:Ge,style:{transition:$?`none`:void 0,transform:Te?`rotate(-90deg)`:void 0,...O?.ringProgress}})]}),d(`div`,{className:l(a.timerDisplay,D?.display),style:O?.display,children:[u(`span`,{className:l(a.timerTime,D?.time),style:O?.time,children:we(j)}),b&&u(`span`,{className:l(a.timerLabel,D?.label),style:O?.label,children:b})]})]}),_&&d(`div`,{className:l(a.timerControls,D?.controls),style:O?.controls,children:[d(`div`,{className:a.timerControlsRow,children:[Je&&u(i,{icon:ne,title:`Remove ${v}s`,onClick:()=>q(v),className:l(a.timerAdjustButton,D?.removeButton),style:O?.removeButton}),Ye&&u(r,{size:n.Small,icon:Xe,title:Ze,onClick:Qe,className:D?.pauseButton,style:O?.pauseButton}),qe&&u(i,{icon:ae,title:`Add ${v}s`,onClick:()=>K(v),className:l(a.timerAdjustButton,D?.addButton),style:O?.addButton})]}),$e&&u(i,{icon:oe,title:Ie.cancel,onClick:J,className:l(a.timerCancelButton,D?.cancelButton),style:O?.cancelButton})]})]})}export{f as default};
2
2
  //# sourceMappingURL=timer.mjs.map