tempest-react-sdk 0.65.0 → 0.67.0

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 (157) hide show
  1. package/dist/auth/create-tempest-auth.cjs +1 -1
  2. package/dist/auth/create-tempest-auth.cjs.map +1 -1
  3. package/dist/auth/create-tempest-auth.js +31 -36
  4. package/dist/auth/create-tempest-auth.js.map +1 -1
  5. package/dist/components/BottomSheet/BottomSheet.cjs +1 -1
  6. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  7. package/dist/components/BottomSheet/BottomSheet.js +31 -40
  8. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  9. package/dist/components/Combobox/Combobox.cjs +1 -1
  10. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  11. package/dist/components/Combobox/Combobox.js +94 -73
  12. package/dist/components/Combobox/Combobox.js.map +1 -1
  13. package/dist/components/Combobox/Combobox.module.cjs +1 -1
  14. package/dist/components/Combobox/Combobox.module.cjs.map +1 -1
  15. package/dist/components/Combobox/Combobox.module.js +4 -3
  16. package/dist/components/Combobox/Combobox.module.js.map +1 -1
  17. package/dist/components/ContextMenu/ContextMenu.cjs +1 -1
  18. package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
  19. package/dist/components/ContextMenu/ContextMenu.js +80 -83
  20. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  21. package/dist/components/Drawer/Drawer.cjs +1 -1
  22. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  23. package/dist/components/Drawer/Drawer.js +43 -46
  24. package/dist/components/Drawer/Drawer.js.map +1 -1
  25. package/dist/components/DropdownMenu/DropdownMenu.cjs +1 -1
  26. package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  27. package/dist/components/DropdownMenu/DropdownMenu.js +129 -105
  28. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  29. package/dist/components/DropdownMenu/DropdownMenu.module.cjs +1 -1
  30. package/dist/components/DropdownMenu/DropdownMenu.module.cjs.map +1 -1
  31. package/dist/components/DropdownMenu/DropdownMenu.module.js +12 -11
  32. package/dist/components/DropdownMenu/DropdownMenu.module.js.map +1 -1
  33. package/dist/components/HoverCard/HoverCard.cjs +1 -1
  34. package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
  35. package/dist/components/HoverCard/HoverCard.js +54 -29
  36. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  37. package/dist/components/HoverCard/HoverCard.module.cjs +1 -1
  38. package/dist/components/HoverCard/HoverCard.module.cjs.map +1 -1
  39. package/dist/components/HoverCard/HoverCard.module.js +4 -3
  40. package/dist/components/HoverCard/HoverCard.module.js.map +1 -1
  41. package/dist/components/Lightbox/Lightbox.cjs +1 -1
  42. package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
  43. package/dist/components/Lightbox/Lightbox.js +86 -89
  44. package/dist/components/Lightbox/Lightbox.js.map +1 -1
  45. package/dist/components/Menubar/Menubar.cjs +1 -1
  46. package/dist/components/Menubar/Menubar.cjs.map +1 -1
  47. package/dist/components/Menubar/Menubar.js +86 -65
  48. package/dist/components/Menubar/Menubar.js.map +1 -1
  49. package/dist/components/Menubar/Menubar.module.cjs +1 -1
  50. package/dist/components/Menubar/Menubar.module.cjs.map +1 -1
  51. package/dist/components/Menubar/Menubar.module.js +4 -3
  52. package/dist/components/Menubar/Menubar.module.js.map +1 -1
  53. package/dist/components/Modal/Modal.cjs +1 -1
  54. package/dist/components/Modal/Modal.cjs.map +1 -1
  55. package/dist/components/Modal/Modal.js +44 -51
  56. package/dist/components/Modal/Modal.js.map +1 -1
  57. package/dist/components/MultiSelect/MultiSelect.cjs +1 -1
  58. package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
  59. package/dist/components/MultiSelect/MultiSelect.js +113 -96
  60. package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
  61. package/dist/components/MultiSelect/MultiSelect.module.cjs +1 -1
  62. package/dist/components/MultiSelect/MultiSelect.module.cjs.map +1 -1
  63. package/dist/components/MultiSelect/MultiSelect.module.js +4 -3
  64. package/dist/components/MultiSelect/MultiSelect.module.js.map +1 -1
  65. package/dist/components/NavigationMenu/NavigationMenu.cjs +1 -1
  66. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  67. package/dist/components/NavigationMenu/NavigationMenu.js +83 -61
  68. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  69. package/dist/components/NavigationMenu/NavigationMenu.module.cjs +1 -1
  70. package/dist/components/NavigationMenu/NavigationMenu.module.cjs.map +1 -1
  71. package/dist/components/NavigationMenu/NavigationMenu.module.js +4 -3
  72. package/dist/components/NavigationMenu/NavigationMenu.module.js.map +1 -1
  73. package/dist/components/Popover/Popover.cjs +1 -1
  74. package/dist/components/Popover/Popover.cjs.map +1 -1
  75. package/dist/components/Popover/Popover.js +57 -36
  76. package/dist/components/Popover/Popover.js.map +1 -1
  77. package/dist/components/Popover/Popover.module.cjs +1 -1
  78. package/dist/components/Popover/Popover.module.cjs.map +1 -1
  79. package/dist/components/Popover/Popover.module.js +7 -6
  80. package/dist/components/Popover/Popover.module.js.map +1 -1
  81. package/dist/components/Portal/anchor-position.cjs +2 -0
  82. package/dist/components/Portal/anchor-position.cjs.map +1 -0
  83. package/dist/components/Portal/anchor-position.js +99 -0
  84. package/dist/components/Portal/anchor-position.js.map +1 -0
  85. package/dist/components/Portal/escape-layer.cjs +2 -0
  86. package/dist/components/Portal/escape-layer.cjs.map +1 -0
  87. package/dist/components/Portal/escape-layer.js +30 -0
  88. package/dist/components/Portal/escape-layer.js.map +1 -0
  89. package/dist/components/Portal/tab-bridge.cjs +2 -0
  90. package/dist/components/Portal/tab-bridge.cjs.map +1 -0
  91. package/dist/components/Portal/tab-bridge.js +39 -0
  92. package/dist/components/Portal/tab-bridge.js.map +1 -0
  93. package/dist/components/Tooltip/Tooltip.cjs +1 -1
  94. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  95. package/dist/components/Tooltip/Tooltip.js +43 -29
  96. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  97. package/dist/components/Tooltip/Tooltip.module.cjs +1 -1
  98. package/dist/components/Tooltip/Tooltip.module.cjs.map +1 -1
  99. package/dist/components/Tooltip/Tooltip.module.js +4 -3
  100. package/dist/components/Tooltip/Tooltip.module.js.map +1 -1
  101. package/dist/hooks/use-focus-trap.cjs +1 -1
  102. package/dist/hooks/use-focus-trap.cjs.map +1 -1
  103. package/dist/hooks/use-focus-trap.js +2 -2
  104. package/dist/hooks/use-focus-trap.js.map +1 -1
  105. package/dist/hooks/use-sortable.cjs +1 -1
  106. package/dist/hooks/use-sortable.cjs.map +1 -1
  107. package/dist/hooks/use-sortable.js +3 -3
  108. package/dist/hooks/use-sortable.js.map +1 -1
  109. package/dist/http/api-client.cjs +1 -1
  110. package/dist/http/api-client.cjs.map +1 -1
  111. package/dist/http/api-client.js +87 -85
  112. package/dist/http/api-client.js.map +1 -1
  113. package/dist/http/credential-scope.cjs +2 -0
  114. package/dist/http/credential-scope.cjs.map +1 -0
  115. package/dist/http/credential-scope.js +23 -0
  116. package/dist/http/credential-scope.js.map +1 -0
  117. package/dist/http/describe-api-error.cjs +1 -1
  118. package/dist/http/describe-api-error.cjs.map +1 -1
  119. package/dist/http/describe-api-error.js +4 -2
  120. package/dist/http/describe-api-error.js.map +1 -1
  121. package/dist/http/resumable-upload.cjs +1 -1
  122. package/dist/http/resumable-upload.cjs.map +1 -1
  123. package/dist/http/resumable-upload.js +121 -120
  124. package/dist/http/resumable-upload.js.map +1 -1
  125. package/dist/http/upload-with-progress.cjs +1 -1
  126. package/dist/http/upload-with-progress.cjs.map +1 -1
  127. package/dist/http/upload-with-progress.js +35 -34
  128. package/dist/http/upload-with-progress.js.map +1 -1
  129. package/dist/styles/actions.css +3 -0
  130. package/dist/styles/advanced.css +1 -0
  131. package/dist/styles/component/Combobox.css +1 -0
  132. package/dist/styles/component/DropdownMenu.css +1 -0
  133. package/dist/styles/component/HoverCard.css +1 -0
  134. package/dist/styles/component/Menubar.css +1 -0
  135. package/dist/styles/component/MultiSelect.css +1 -0
  136. package/dist/styles/component/NavigationMenu.css +1 -0
  137. package/dist/styles/component/Popover.css +1 -0
  138. package/dist/styles/component/Tooltip.css +1 -0
  139. package/dist/styles/forms.css +2 -0
  140. package/dist/styles/navigation.css +2 -0
  141. package/dist/styles.css +1 -1
  142. package/dist/tempest-react-sdk.cjs +1 -1
  143. package/dist/tempest-react-sdk.d.ts +364 -29
  144. package/dist/tempest-react-sdk.js +335 -333
  145. package/dist/utils/absent.cjs +2 -0
  146. package/dist/utils/absent.cjs.map +1 -0
  147. package/dist/utils/absent.js +12 -0
  148. package/dist/utils/absent.js.map +1 -0
  149. package/dist/utils/format.cjs +1 -1
  150. package/dist/utils/format.cjs.map +1 -1
  151. package/dist/utils/format.js +42 -37
  152. package/dist/utils/format.js.map +1 -1
  153. package/dist/utils/numbers.cjs +1 -1
  154. package/dist/utils/numbers.cjs.map +1 -1
  155. package/dist/utils/numbers.js +14 -12
  156. package/dist/utils/numbers.js.map +1 -1
  157. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Menubar.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n}\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n triggerRefs.current[openIndex]?.focus();\n return;\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [openIndex, menus.length, close]);\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>\n {item.label}\n </span>\n {item.shortcut && (\n <span className={styles.shortcut}>\n {item.shortcut}\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"8HAmCA,SAAS,EAAY,EAAgD,CACjE,MAAO,cAAe,CAC1B,CAwBA,SAAgB,EAAQ,CAAE,QAAO,YAAW,GAAG,GAAuB,CAClE,GAAM,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAiB,EAAE,EAC/C,GAAA,EAAS,EAAA,MAAA,CAAM,EACf,GAAA,EAAU,EAAA,OAAA,CAAuB,IAAI,EACrC,GAAA,EAAc,EAAA,OAAA,CAAwC,CAAC,CAAC,EAExD,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,EAAa,EAAE,CACnB,EAAG,CAAC,CAAC,EAkCL,OAhCA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,IAAc,GAAI,OACtB,IAAM,EAAS,GAA+B,CAC1C,GAAI,EAAM,MAAQ,SAAU,CACxB,EAAM,EACN,EAAY,QAAQ,EAAU,EAAE,MAAM,EACtC,MACJ,CACA,GAAI,EAAM,MAAQ,aAAc,CAC5B,EAAM,eAAe,EACrB,IAAM,GAAQ,EAAY,GAAK,EAAM,OACrC,EAAa,CAAI,EACjB,EAAY,QAAQ,EAAK,EAAE,MAAM,CACrC,CACA,GAAI,EAAM,MAAQ,YAAa,CAC3B,EAAM,eAAe,EACrB,IAAM,GAAQ,EAAY,EAAI,EAAM,QAAU,EAAM,OACpD,EAAa,CAAI,EACjB,EAAY,QAAQ,EAAK,EAAE,MAAM,CACrC,CACJ,EACM,EAAU,GAA4B,CACpC,EAAQ,SAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,GAAG,EAAM,CAClF,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAK,EACxC,OAAO,iBAAiB,YAAa,CAAM,MAC9B,CACT,OAAO,oBAAoB,UAAW,CAAK,EAC3C,OAAO,oBAAoB,YAAa,CAAM,CAClD,CACJ,EAAG,CAAC,EAAW,EAAM,OAAQ,CAAK,CAAC,GAG/B,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAS,KAAK,UAAU,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,GAAI,EACxE,SAAA,EAAM,KAAK,EAAM,IAAU,CACxB,IAAM,EAAO,IAAc,EACrB,EAAU,GAAG,EAAO,SAAS,IACnC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAiB,UAAW,EAAA,QAAO,KAAnC,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,IAAM,GAAO,CACT,EAAY,QAAQ,GAAS,CACjC,EACA,KAAK,SACL,KAAK,WACL,gBAAc,OACd,gBAAe,EACf,gBAAe,EAAO,EAAU,IAAA,GAChC,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAQ,EAAA,QAAO,WAAW,EACxD,YAAe,EAAc,GAAU,IAAS,EAAQ,GAAK,CAAM,EAElE,SAAA,EAAK,KACF,CAAA,EACP,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,KAAK,OAAO,UAAW,EAAA,QAAO,MAC1C,SAAA,EAAK,MAAM,KAAK,EAAM,IACf,EAAY,CAAI,GAEZ,EAAA,EAAA,IAAA,CAAC,KAAD,CAEI,KAAK,YACL,UAAW,EAAA,QAAO,UAClB,cAAA,EACH,EAJQ,CAIR,GAIL,EAAA,EAAA,IAAA,CAAC,KAAD,CAAoB,KAAK,OACrB,UAAA,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAK,WACL,UAAW,EAAA,QAAO,KAClB,SAAU,EAAK,SACf,YAAe,CACX,EAAK,WAAW,EAChB,EAAM,CACV,EARJ,SAAA,EAUI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,UACnB,SAAA,EAAK,KACJ,CAAA,EACL,EAAK,WACF,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SACnB,SAAA,EAAK,QACJ,CAAA,CAEN,GACR,EApBK,CAoBL,CAEX,CACD,CAAA,CAEP,CAtDK,EAAA,CAsDL,CAEb,CAAC,CACA,CAAA,CAEb"}
1
+ {"version":3,"file":"Menubar.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { usePortalTabOrder } from \"@/components/Portal/tab-bridge\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n /**\n * Render the open panel in a portal, positioned under its trigger. Default\n * `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the panel,\n * and the SDK's own `Navbar` is one: its `nav` slot scrolls horizontally, and\n * `overflow-x: auto` forces `overflow-y` to `auto` too — a menu showed\n * 0% of its panel there (measured in Chrome at 1440 px). In a portal the\n * panel escapes the clip, flips at the viewport edge, stays in the tab order\n * right after its trigger, and follows it on scroll and resize.\n */\n portal?: boolean;\n}\n\n/** Gap between trigger and panel, in px — the in-flow CSS uses the same 4 px. */\nconst PANEL_OFFSET = 4;\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, portal = true, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n const [anchorNode, setAnchorNode] = useState<HTMLDivElement | null>(null);\n const [panelNode, setPanelNode] = useState<HTMLUListElement | null>(null);\n const floating = portal && openIndex !== -1;\n const floatingStyle = useAnchorPosition({\n anchor: anchorNode,\n floating: panelNode,\n side: \"bottom\",\n align: \"start\",\n offset: PANEL_OFFSET,\n enabled: floating,\n });\n usePortalTabOrder({ enabled: floating, anchor: anchorNode, panel: panelNode });\n\n useEscapeLayer(openIndex !== -1, () => {\n close();\n triggerRefs.current[openIndex]?.focus();\n });\n\n /**\n * Arrow keys move between menus while one is open; a press outside both the\n * menubar and the open panel closes it.\n *\n * The panel is checked on its own because in a portal it is not inside the\n * menubar's root: a press on one of its items would otherwise count as\n * outside, close the panel on `mousedown`, and swallow the item's click.\n */\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || panelNode?.contains(target)) return;\n close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [openIndex, menus.length, close, panelNode]);\n\n /**\n * The dropdown of one menu.\n *\n * @param menu - The menu whose items are listed.\n * @param panelId - The id the trigger's `aria-controls` points at.\n * @returns The panel element, in flow or for the portal.\n */\n const panel = (menu: MenubarMenu, panelId: string): ReactNode => (\n <ul\n ref={setPanelNode}\n id={panelId}\n role=\"menu\"\n className={cn(styles.panel, portal && styles.portalled)}\n style={floatingStyle}\n >\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>{item.label}</span>\n {item.shortcut && (\n <span className={styles.shortcut}>{item.shortcut}</span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n );\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} ref={open ? setAnchorNode : undefined} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open &&\n (portal ? (\n <Portal>{panel(menu, panelId)}</Portal>\n ) : (\n panel(menu, panelId)\n ))}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,96 +1,117 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
- import t from "./Menubar.module.js";
3
- import { useCallback as n, useEffect as r, useId as i, useRef as a, useState as o } from "react";
4
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
2
+ import { useEscapeLayer as t } from "../Portal/escape-layer.js";
3
+ import { Portal as n } from "../Portal/Portal.js";
4
+ import { useAnchorPosition as r } from "../Portal/anchor-position.js";
5
+ import { usePortalTabOrder as i } from "../Portal/tab-bridge.js";
6
+ import a from "./Menubar.module.js";
7
+ import { useCallback as o, useEffect as s, useId as c, useRef as l, useState as u } from "react";
8
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
5
9
  //#region src/components/Menubar/Menubar.tsx
6
- function l(e) {
10
+ var p = 4;
11
+ function m(e) {
7
12
  return "separator" in e;
8
13
  }
9
- function u({ menus: u, className: d, ...f }) {
10
- let [p, m] = o(-1), h = i(), g = a(null), _ = a([]), v = n(() => {
11
- m(-1);
12
- }, []);
13
- return r(() => {
14
- if (p === -1) return;
14
+ function h({ menus: h, portal: g = !0, className: _, ...v }) {
15
+ let [y, b] = u(-1), x = c(), S = l(null), C = l([]), w = o(() => {
16
+ b(-1);
17
+ }, []), [T, E] = u(null), [D, O] = u(null), k = g && y !== -1, A = r({
18
+ anchor: T,
19
+ floating: D,
20
+ side: "bottom",
21
+ align: "start",
22
+ offset: p,
23
+ enabled: k
24
+ });
25
+ i({
26
+ enabled: k,
27
+ anchor: T,
28
+ panel: D
29
+ }), t(y !== -1, () => {
30
+ w(), C.current[y]?.focus();
31
+ }), s(() => {
32
+ if (y === -1) return;
15
33
  let e = (e) => {
16
- if (e.key === "Escape") {
17
- v(), _.current[p]?.focus();
18
- return;
19
- }
20
34
  if (e.key === "ArrowRight") {
21
35
  e.preventDefault();
22
- let t = (p + 1) % u.length;
23
- m(t), _.current[t]?.focus();
36
+ let t = (y + 1) % h.length;
37
+ b(t), C.current[t]?.focus();
24
38
  }
25
39
  if (e.key === "ArrowLeft") {
26
40
  e.preventDefault();
27
- let t = (p - 1 + u.length) % u.length;
28
- m(t), _.current[t]?.focus();
41
+ let t = (y - 1 + h.length) % h.length;
42
+ b(t), C.current[t]?.focus();
29
43
  }
30
44
  }, t = (e) => {
31
- g.current && !g.current.contains(e.target) && v();
45
+ let t = e.target;
46
+ S.current?.contains(t) || D?.contains(t) || w();
32
47
  };
33
48
  return window.addEventListener("keydown", e), window.addEventListener("mousedown", t), () => {
34
49
  window.removeEventListener("keydown", e), window.removeEventListener("mousedown", t);
35
50
  };
36
51
  }, [
37
- p,
38
- u.length,
39
- v
40
- ]), /* @__PURE__ */ s("div", {
41
- ref: g,
52
+ y,
53
+ h.length,
54
+ w,
55
+ D
56
+ ]);
57
+ let j = (t, n) => /* @__PURE__ */ d("ul", {
58
+ ref: O,
59
+ id: n,
60
+ role: "menu",
61
+ className: e(a.panel, g && a.portalled),
62
+ style: A,
63
+ children: t.items.map((e, t) => m(e) ? /* @__PURE__ */ d("li", {
64
+ role: "separator",
65
+ className: a.separator,
66
+ "aria-hidden": !0
67
+ }, t) : /* @__PURE__ */ d("li", {
68
+ role: "none",
69
+ children: /* @__PURE__ */ f("button", {
70
+ type: "button",
71
+ role: "menuitem",
72
+ className: a.item,
73
+ disabled: e.disabled,
74
+ onClick: () => {
75
+ e.onSelect?.(), w();
76
+ },
77
+ children: [/* @__PURE__ */ d("span", {
78
+ className: a.itemLabel,
79
+ children: e.label
80
+ }), e.shortcut && /* @__PURE__ */ d("span", {
81
+ className: a.shortcut,
82
+ children: e.shortcut
83
+ })]
84
+ })
85
+ }, t))
86
+ });
87
+ return /* @__PURE__ */ d("div", {
88
+ ref: S,
42
89
  role: "menubar",
43
- className: e(t.root, d),
44
- ...f,
45
- children: u.map((n, r) => {
46
- let i = p === r, a = `${h}-panel-${r}`;
47
- return /* @__PURE__ */ c("div", {
48
- className: t.menu,
49
- children: [/* @__PURE__ */ s("button", {
90
+ className: e(a.root, _),
91
+ ...v,
92
+ children: h.map((t, r) => {
93
+ let i = y === r, o = `${x}-panel-${r}`;
94
+ return /* @__PURE__ */ f("div", {
95
+ ref: i ? E : void 0,
96
+ className: a.menu,
97
+ children: [/* @__PURE__ */ d("button", {
50
98
  ref: (e) => {
51
- _.current[r] = e;
99
+ C.current[r] = e;
52
100
  },
53
101
  type: "button",
54
102
  role: "menuitem",
55
103
  "aria-haspopup": "menu",
56
104
  "aria-expanded": i,
57
- "aria-controls": i ? a : void 0,
58
- className: e(t.trigger, i && t.triggerOpen),
59
- onClick: () => m((e) => e === r ? -1 : r),
60
- children: n.label
61
- }), i && /* @__PURE__ */ s("ul", {
62
- id: a,
63
- role: "menu",
64
- className: t.panel,
65
- children: n.items.map((e, n) => l(e) ? /* @__PURE__ */ s("li", {
66
- role: "separator",
67
- className: t.separator,
68
- "aria-hidden": !0
69
- }, n) : /* @__PURE__ */ s("li", {
70
- role: "none",
71
- children: /* @__PURE__ */ c("button", {
72
- type: "button",
73
- role: "menuitem",
74
- className: t.item,
75
- disabled: e.disabled,
76
- onClick: () => {
77
- e.onSelect?.(), v();
78
- },
79
- children: [/* @__PURE__ */ s("span", {
80
- className: t.itemLabel,
81
- children: e.label
82
- }), e.shortcut && /* @__PURE__ */ s("span", {
83
- className: t.shortcut,
84
- children: e.shortcut
85
- })]
86
- })
87
- }, n))
88
- })]
105
+ "aria-controls": i ? o : void 0,
106
+ className: e(a.trigger, i && a.triggerOpen),
107
+ onClick: () => b((e) => e === r ? -1 : r),
108
+ children: t.label
109
+ }), i && (g ? /* @__PURE__ */ d(n, { children: j(t, o) }) : j(t, o))]
89
110
  }, r);
90
111
  })
91
112
  });
92
113
  }
93
114
  //#endregion
94
- export { u as Menubar };
115
+ export { h as Menubar };
95
116
 
96
117
  //# sourceMappingURL=Menubar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Menubar.js","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n}\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n triggerRefs.current[openIndex]?.focus();\n return;\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [openIndex, menus.length, close]);\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>\n {item.label}\n </span>\n {item.shortcut && (\n <span className={styles.shortcut}>\n {item.shortcut}\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;AAmCA,SAAS,EAAY,GAAgD;CACjE,OAAO,eAAe;AAC1B;AAwBA,SAAgB,EAAQ,EAAE,UAAO,cAAW,GAAG,KAAuB;CAClE,IAAM,CAAC,GAAW,KAAgB,EAAiB,EAAE,GAC/C,IAAS,EAAM,GACf,IAAU,EAAuB,IAAI,GACrC,IAAc,EAAwC,CAAC,CAAC,GAExD,IAAQ,QAAwB;EAClC,EAAa,EAAE;CACnB,GAAG,CAAC,CAAC;CAkCL,OAhCA,QAAgB;EACZ,IAAI,MAAc,IAAI;EACtB,IAAM,KAAS,MAA+B;GAC1C,IAAI,EAAM,QAAQ,UAAU;IAExB,AADA,EAAM,GACN,EAAY,QAAQ,EAAU,EAAE,MAAM;IACtC;GACJ;GACA,IAAI,EAAM,QAAQ,cAAc;IAC5B,EAAM,eAAe;IACrB,IAAM,KAAQ,IAAY,KAAK,EAAM;IAErC,AADA,EAAa,CAAI,GACjB,EAAY,QAAQ,EAAK,EAAE,MAAM;GACrC;GACA,IAAI,EAAM,QAAQ,aAAa;IAC3B,EAAM,eAAe;IACrB,IAAM,KAAQ,IAAY,IAAI,EAAM,UAAU,EAAM;IAEpD,AADA,EAAa,CAAI,GACjB,EAAY,QAAQ,EAAK,EAAE,MAAM;GACrC;EACJ,GACM,KAAU,MAA4B;GACxC,AAAI,EAAQ,WAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,KAAG,EAAM;EAClF;EAGA,OAFA,OAAO,iBAAiB,WAAW,CAAK,GACxC,OAAO,iBAAiB,aAAa,CAAM,SAC9B;GAET,AADA,OAAO,oBAAoB,WAAW,CAAK,GAC3C,OAAO,oBAAoB,aAAa,CAAM;EAClD;CACJ,GAAG;EAAC;EAAW,EAAM;EAAQ;CAAK,CAAC,GAG/B,kBAAC,OAAD;EAAK,KAAK;EAAS,MAAK;EAAU,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;EACxE,UAAA,EAAM,KAAK,GAAM,MAAU;GACxB,IAAM,IAAO,MAAc,GACrB,IAAU,GAAG,EAAO,SAAS;GACnC,OACI,kBAAC,OAAD;IAAiB,WAAW,EAAO;IAAnC,UAAA,CACI,kBAAC,UAAD;KACI,MAAM,MAAO;MACT,EAAY,QAAQ,KAAS;KACjC;KACA,MAAK;KACL,MAAK;KACL,iBAAc;KACd,iBAAe;KACf,iBAAe,IAAO,IAAU,KAAA;KAChC,WAAW,EAAG,EAAO,SAAS,KAAQ,EAAO,WAAW;KACxD,eAAe,GAAc,MAAU,MAAS,IAAQ,KAAK,CAAM;KAElE,UAAA,EAAK;IACF,CAAA,GACP,KACG,kBAAC,MAAD;KAAI,IAAI;KAAS,MAAK;KAAO,WAAW,EAAO;KAC1C,UAAA,EAAK,MAAM,KAAK,GAAM,MACf,EAAY,CAAI,IAEZ,kBAAC,MAAD;MAEI,MAAK;MACL,WAAW,EAAO;MAClB,eAAA;KACH,GAJQ,CAIR,IAIL,kBAAC,MAAD;MAAoB,MAAK;MACrB,UAAA,kBAAC,UAAD;OACI,MAAK;OACL,MAAK;OACL,WAAW,EAAO;OAClB,UAAU,EAAK;OACf,eAAe;QAEX,AADA,EAAK,WAAW,GAChB,EAAM;OACV;OARJ,UAAA,CAUI,kBAAC,QAAD;QAAM,WAAW,EAAO;QACnB,UAAA,EAAK;OACJ,CAAA,GACL,EAAK,YACF,kBAAC,QAAD;QAAM,WAAW,EAAO;QACnB,UAAA,EAAK;OACJ,CAAA,CAEN;;KACR,GApBK,CAoBL,CAEX;IACD,CAAA,CAEP;GAtDK,GAAA,CAsDL;EAEb,CAAC;CACA,CAAA;AAEb"}
1
+ {"version":3,"file":"Menubar.js","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { Portal } from \"@/components/Portal\";\nimport { useAnchorPosition } from \"@/components/Portal/anchor-position\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { usePortalTabOrder } from \"@/components/Portal/tab-bridge\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n /**\n * Render the open panel in a portal, positioned under its trigger. Default\n * `true`.\n *\n * In flow, an ancestor with `overflow` other than `visible` clips the panel,\n * and the SDK's own `Navbar` is one: its `nav` slot scrolls horizontally, and\n * `overflow-x: auto` forces `overflow-y` to `auto` too — a menu showed\n * 0% of its panel there (measured in Chrome at 1440 px). In a portal the\n * panel escapes the clip, flips at the viewport edge, stays in the tab order\n * right after its trigger, and follows it on scroll and resize.\n */\n portal?: boolean;\n}\n\n/** Gap between trigger and panel, in px — the in-flow CSS uses the same 4 px. */\nconst PANEL_OFFSET = 4;\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, portal = true, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n const [anchorNode, setAnchorNode] = useState<HTMLDivElement | null>(null);\n const [panelNode, setPanelNode] = useState<HTMLUListElement | null>(null);\n const floating = portal && openIndex !== -1;\n const floatingStyle = useAnchorPosition({\n anchor: anchorNode,\n floating: panelNode,\n side: \"bottom\",\n align: \"start\",\n offset: PANEL_OFFSET,\n enabled: floating,\n });\n usePortalTabOrder({ enabled: floating, anchor: anchorNode, panel: panelNode });\n\n useEscapeLayer(openIndex !== -1, () => {\n close();\n triggerRefs.current[openIndex]?.focus();\n });\n\n /**\n * Arrow keys move between menus while one is open; a press outside both the\n * menubar and the open panel closes it.\n *\n * The panel is checked on its own because in a portal it is not inside the\n * menubar's root: a press on one of its items would otherwise count as\n * outside, close the panel on `mousedown`, and swallow the item's click.\n */\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n const target = event.target as Node;\n if (rootRef.current?.contains(target) || panelNode?.contains(target)) return;\n close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [openIndex, menus.length, close, panelNode]);\n\n /**\n * The dropdown of one menu.\n *\n * @param menu - The menu whose items are listed.\n * @param panelId - The id the trigger's `aria-controls` points at.\n * @returns The panel element, in flow or for the portal.\n */\n const panel = (menu: MenubarMenu, panelId: string): ReactNode => (\n <ul\n ref={setPanelNode}\n id={panelId}\n role=\"menu\"\n className={cn(styles.panel, portal && styles.portalled)}\n style={floatingStyle}\n >\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>{item.label}</span>\n {item.shortcut && (\n <span className={styles.shortcut}>{item.shortcut}</span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n );\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} ref={open ? setAnchorNode : undefined} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open &&\n (portal ? (\n <Portal>{panel(menu, panelId)}</Portal>\n ) : (\n panel(menu, panelId)\n ))}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  require("../../styles/component/Menubar.css");
2
- var e=`tempest_root_PGUO2`,t=`tempest_menu_hEVLT`,n=`tempest_trigger_ZlZBx`,r=`tempest_triggerOpen_Hpfbu`,i=`tempest_panel_6ta6u`,a=`tempest_tempest-menubar-in_gOQQD`,o=`tempest_item_wM-61`,s=`tempest_itemLabel_wXfB2`,c=`tempest_shortcut_qNlmN`,l=`tempest_separator_LfK3t`,u={root:e,menu:t,trigger:n,triggerOpen:r,panel:i,tempestMenubarIn:a,item:o,itemLabel:s,shortcut:c,separator:l};exports.default=u,exports.item=o,exports.itemLabel=s,exports.menu=t,exports.panel=i,exports.root=e,exports.separator=l,exports.shortcut=c,exports.tempestMenubarIn=a,exports.trigger=n,exports.triggerOpen=r;
2
+ var e=`tempest_root_PGUO2`,t=`tempest_menu_hEVLT`,n=`tempest_trigger_ZlZBx`,r=`tempest_triggerOpen_Hpfbu`,i=`tempest_panel_6ta6u`,a=`tempest_tempest-menubar-in_gOQQD`,o=`tempest_item_wM-61`,s=`tempest_itemLabel_wXfB2`,c=`tempest_shortcut_qNlmN`,l=`tempest_separator_LfK3t`,u=`tempest_portalled_tdHc1`,d={root:e,menu:t,trigger:n,triggerOpen:r,panel:i,tempestMenubarIn:a,item:o,itemLabel:s,shortcut:c,separator:l,portalled:u};exports.default=d,exports.item=o,exports.itemLabel=s,exports.menu=t,exports.panel=i,exports.portalled=u,exports.root=e,exports.separator=l,exports.shortcut=c,exports.tempestMenubarIn=a,exports.trigger=n,exports.triggerOpen=r;
3
3
  //# sourceMappingURL=Menubar.module.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Menubar.module.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.triggerOpen {\n background-color: var(--tempest-surface);\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 200px;\n max-width: 360px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-menubar-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-4);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus {\n outline: none;\n background-color: var(--tempest-surface);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.itemLabel {\n flex: 1;\n}\n\n.shortcut {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n@keyframes tempest-menubar-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"Menubar.module.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.triggerOpen {\n background-color: var(--tempest-surface);\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 200px;\n max-width: 360px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-menubar-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-4);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus {\n outline: none;\n background-color: var(--tempest-surface);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.itemLabel {\n flex: 1;\n}\n\n.shortcut {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n@keyframes tempest-menubar-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
@@ -1,6 +1,6 @@
1
1
  import "../../styles/component/Menubar.css";
2
2
  //#region src/components/Menubar/Menubar.module.css
3
- var e = "tempest_root_PGUO2", t = "tempest_menu_hEVLT", n = "tempest_trigger_ZlZBx", r = "tempest_triggerOpen_Hpfbu", i = "tempest_panel_6ta6u", a = "tempest_tempest-menubar-in_gOQQD", o = "tempest_item_wM-61", s = "tempest_itemLabel_wXfB2", c = "tempest_shortcut_qNlmN", l = "tempest_separator_LfK3t", u = {
3
+ var e = "tempest_root_PGUO2", t = "tempest_menu_hEVLT", n = "tempest_trigger_ZlZBx", r = "tempest_triggerOpen_Hpfbu", i = "tempest_panel_6ta6u", a = "tempest_tempest-menubar-in_gOQQD", o = "tempest_item_wM-61", s = "tempest_itemLabel_wXfB2", c = "tempest_shortcut_qNlmN", l = "tempest_separator_LfK3t", u = "tempest_portalled_tdHc1", d = {
4
4
  root: e,
5
5
  menu: t,
6
6
  trigger: n,
@@ -10,9 +10,10 @@ var e = "tempest_root_PGUO2", t = "tempest_menu_hEVLT", n = "tempest_trigger_ZlZ
10
10
  item: o,
11
11
  itemLabel: s,
12
12
  shortcut: c,
13
- separator: l
13
+ separator: l,
14
+ portalled: u
14
15
  };
15
16
  //#endregion
16
- export { u as default, o as item, s as itemLabel, t as menu, i as panel, e as root, l as separator, c as shortcut, a as tempestMenubarIn, n as trigger, r as triggerOpen };
17
+ export { d as default, o as item, s as itemLabel, t as menu, i as panel, u as portalled, e as root, l as separator, c as shortcut, a as tempestMenubarIn, n as trigger, r as triggerOpen };
17
18
 
18
19
  //# sourceMappingURL=Menubar.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Menubar.module.js","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.triggerOpen {\n background-color: var(--tempest-surface);\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 200px;\n max-width: 360px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-menubar-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-4);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus {\n outline: none;\n background-color: var(--tempest-surface);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.itemLabel {\n flex: 1;\n}\n\n.shortcut {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n@keyframes tempest-menubar-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n"],"mappings":";"}
1
+ {"version":3,"file":"Menubar.module.js","names":[],"sources":["../../../src/components/Menubar/Menubar.module.css"],"sourcesContent":[".root {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n}\n\n.menu {\n position: relative;\n}\n\n.trigger {\n display: inline-flex;\n align-items: center;\n padding: var(--tempest-space-1) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .trigger:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n\n.triggerOpen {\n background-color: var(--tempest-surface);\n}\n\n.panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--tempest-z-dropdown);\n min-width: 200px;\n max-width: 360px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n animation: tempest-menubar-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-4);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus {\n outline: none;\n background-color: var(--tempest-surface);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.itemLabel {\n flex: 1;\n}\n\n.shortcut {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n font-variant-numeric: tabular-nums;\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n@keyframes tempest-menubar-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panel {\n animation: none;\n }\n}\n\n.portalled {\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n z-index: var(--tempest-z-popover);\n}\n"],"mappings":";"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../Portal/portal-host.cjs"),n=require("./Modal.module.cjs");let r=require("react"),i=require("react/jsx-runtime"),a=require("react-dom");function o({open:o,onClose:l,title:u,children:d,footer:f,size:p=`md`,closeOnBackdrop:m=!0,closeOnEsc:h=!0,className:g,hideCloseButton:_=!1,fullscreen:v=!1,fullscreenOnMobile:y=!1,"aria-label":b}){let x=(0,r.useId)();(0,r.useEffect)(()=>{if(!o)return;let e=document.body.style.overflow;document.body.style.overflow=`hidden`;let t=e=>{h&&e.key===`Escape`&&l()};return window.addEventListener(`keydown`,t),()=>{document.body.style.overflow=e,window.removeEventListener(`keydown`,t)}},[o,h,l]);let S=t.usePortalHost();return!o||!S?null:(0,a.createPortal)((0,i.jsx)(`div`,{className:n.default.overlay,role:`presentation`,onClick:()=>{m&&l()},children:(0,i.jsxs)(`div`,{role:`dialog`,"aria-modal":`true`,"aria-labelledby":u?x:void 0,"aria-label":u?void 0:b,className:e.cn(n.default.dialog,s(p),v&&n.default.fullscreen,y&&n.default.fullscreenOnMobile,g),onClick:e=>e.stopPropagation(),children:[(u||!_)&&(0,i.jsxs)(`header`,{className:n.default.header,children:[u&&(0,i.jsx)(`h3`,{id:x,className:n.default.title,children:u}),!_&&(0,i.jsx)(`button`,{type:`button`,className:n.default.close,"aria-label":`Fechar`,onClick:l,children:(0,i.jsx)(c,{})})]}),(0,i.jsx)(`div`,{className:n.default.body,children:d}),f&&(0,i.jsx)(`footer`,{className:n.default.footer,children:f})]})}),S)}function s(e){return e===`2xl`?n.default.size2xl:e===`3xl`?n.default.size3xl:n.default[e]}function c(){return(0,i.jsx)(`svg`,{width:`18`,height:`18`,viewBox:`0 0 24 24`,fill:`none`,children:(0,i.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.Modal=o;
1
+ const e=require("../../utils/cn.cjs"),t=require("../Portal/escape-layer.cjs"),n=require("../Portal/portal-host.cjs"),r=require("./Modal.module.cjs");let i=require("react"),a=require("react/jsx-runtime"),o=require("react-dom");function s({open:s,onClose:u,title:d,children:f,footer:p,size:m=`md`,closeOnBackdrop:h=!0,closeOnEsc:g=!0,className:_,hideCloseButton:v=!1,fullscreen:y=!1,fullscreenOnMobile:b=!1,"aria-label":x}){let S=(0,i.useId)();t.useEscapeLayer(s,g?u:null),(0,i.useEffect)(()=>{if(!s)return;let e=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{document.body.style.overflow=e}},[s]);let C=n.usePortalHost();return!s||!C?null:(0,o.createPortal)((0,a.jsx)(`div`,{className:r.default.overlay,role:`presentation`,onClick:()=>{h&&u()},children:(0,a.jsxs)(`div`,{role:`dialog`,"aria-modal":`true`,"aria-labelledby":d?S:void 0,"aria-label":d?void 0:x,className:e.cn(r.default.dialog,c(m),y&&r.default.fullscreen,b&&r.default.fullscreenOnMobile,_),onClick:e=>e.stopPropagation(),children:[(d||!v)&&(0,a.jsxs)(`header`,{className:r.default.header,children:[d&&(0,a.jsx)(`h3`,{id:S,className:r.default.title,children:d}),!v&&(0,a.jsx)(`button`,{type:`button`,className:r.default.close,"aria-label":`Fechar`,onClick:u,children:(0,a.jsx)(l,{})})]}),(0,a.jsx)(`div`,{className:r.default.body,children:f}),p&&(0,a.jsx)(`footer`,{className:r.default.footer,children:p})]})}),C)}function c(e){return e===`2xl`?r.default.size2xl:e===`3xl`?r.default.size3xl:r.default[e]}function l(){return(0,a.jsx)(`svg`,{width:`18`,height:`18`,viewBox:`0 0 24 24`,fill:`none`,children:(0,a.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.Modal=s;
2
2
  //# sourceMappingURL=Modal.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.cjs","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </div>\n </div>,\n portalHost,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"0LA+CA,SAAgB,EAAM,CAClB,OACA,UACA,QACA,WACA,SACA,OAAO,KACP,kBAAkB,GAClB,aAAa,GACb,YACA,kBAAkB,GAClB,aAAa,GACb,qBAAqB,GACrB,aAAc,GACH,CACX,IAAM,GAAA,EAAU,EAAA,MAAA,CAAM,GACtB,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAC7C,SAAS,KAAK,MAAM,SAAW,SAE/B,IAAM,EAAa,GAA+B,CAC1C,GAAc,EAAM,MAAQ,UAAU,EAAQ,CACtD,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAS,MAE/B,CACT,SAAS,KAAK,MAAM,SAAW,EAC/B,OAAO,oBAAoB,UAAW,CAAS,CACnD,CACJ,EAAG,CAAC,EAAM,EAAY,CAAO,CAAC,EAE9B,IAAM,EAAa,EAAA,cAAc,EAIjC,MAFI,CAAC,GAAQ,CAAC,EAAmB,MAEjC,EAAO,EAAA,aAAA,EACH,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,KAAK,eACL,YAAe,CACP,GAAiB,EAAQ,CACjC,EAEA,UAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,KAAK,SACL,aAAW,OACX,kBAAiB,EAAQ,EAAU,IAAA,GACnC,aAAY,EAAQ,IAAA,GAAY,EAChC,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAc,CAAI,EAClB,GAAc,EAAA,QAAO,WACrB,GAAsB,EAAA,QAAO,mBAC7B,CACJ,EACA,QAAU,GAAU,EAAM,gBAAgB,EAZ9C,SAAA,EAcM,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAA1B,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,UAAW,EAAA,QAAO,MAC9B,SAAA,CACD,CAAA,EAEP,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,MAClB,aAAW,SACX,QAAS,EAET,UAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAAY,CAAA,CACR,CAAA,CAER,KAEZ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,UAAc,CAAA,EAC3C,IAAU,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAAS,SAAA,CAAe,CAAA,CAC5D,GACJ,CAAA,EACL,CACJ,CACJ,CAEA,SAAS,EAAc,EAAqC,CAGxD,OAFI,IAAS,MAAc,EAAA,QAAO,QAC9B,IAAS,MAAc,EAAA,QAAO,QAC3B,EAAA,QAAO,EAClB,CAEA,SAAS,GAAY,CACjB,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OACjD,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,EAAE,uBACF,OAAO,eACP,YAAY,IACZ,cAAc,OACjB,CAAA,CACA,CAAA,CAEb"}
1
+ {"version":3,"file":"Modal.cjs","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEscapeLayer(open, closeOnEsc ? onClose : null);\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = previousOverflow;\n };\n }, [open]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </div>\n </div>,\n portalHost,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"kOAgDA,SAAgB,EAAM,CAClB,OACA,UACA,QACA,WACA,SACA,OAAO,KACP,kBAAkB,GAClB,aAAa,GACb,YACA,kBAAkB,GAClB,aAAa,GACb,qBAAqB,GACrB,aAAc,GACH,CACX,IAAM,GAAA,EAAU,EAAA,MAAA,CAAM,EACtB,EAAA,eAAe,EAAM,EAAa,EAAU,IAAI,GAChD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAE7C,MADA,UAAS,KAAK,MAAM,SAAW,aAClB,CACT,SAAS,KAAK,MAAM,SAAW,CACnC,CACJ,EAAG,CAAC,CAAI,CAAC,EAET,IAAM,EAAa,EAAA,cAAc,EAIjC,MAFI,CAAC,GAAQ,CAAC,EAAmB,MAEjC,EAAO,EAAA,aAAA,EACH,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,KAAK,eACL,YAAe,CACP,GAAiB,EAAQ,CACjC,EAEA,UAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,KAAK,SACL,aAAW,OACX,kBAAiB,EAAQ,EAAU,IAAA,GACnC,aAAY,EAAQ,IAAA,GAAY,EAChC,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAc,CAAI,EAClB,GAAc,EAAA,QAAO,WACrB,GAAsB,EAAA,QAAO,mBAC7B,CACJ,EACA,QAAU,GAAU,EAAM,gBAAgB,EAZ9C,SAAA,EAcM,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAA1B,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,UAAW,EAAA,QAAO,MAC9B,SAAA,CACD,CAAA,EAEP,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,MAClB,aAAW,SACX,QAAS,EAET,UAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAAY,CAAA,CACR,CAAA,CAER,KAEZ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,UAAc,CAAA,EAC3C,IAAU,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAAS,SAAA,CAAe,CAAA,CAC5D,GACJ,CAAA,EACL,CACJ,CACJ,CAEA,SAAS,EAAc,EAAqC,CAGxD,OAFI,IAAS,MAAc,EAAA,QAAO,QAC9B,IAAS,MAAc,EAAA,QAAO,QAC3B,EAAA,QAAO,EAClB,CAEA,SAAS,GAAY,CACjB,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OACjD,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,EAAE,uBACF,OAAO,eACP,YAAY,IACZ,cAAc,OACjB,CAAA,CACA,CAAA,CAEb"}
@@ -1,78 +1,71 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
- import { usePortalHost as t } from "../Portal/portal-host.js";
3
- import n from "./Modal.module.js";
4
- import { useEffect as r, useId as i } from "react";
5
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
- import { createPortal as s } from "react-dom";
2
+ import { useEscapeLayer as t } from "../Portal/escape-layer.js";
3
+ import { usePortalHost as n } from "../Portal/portal-host.js";
4
+ import r from "./Modal.module.js";
5
+ import { useEffect as i, useId as a } from "react";
6
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ import { createPortal as c } from "react-dom";
7
8
  //#region src/components/Modal/Modal.tsx
8
- function c({ open: c, onClose: d, title: f, children: p, footer: m, size: h = "md", closeOnBackdrop: g = !0, closeOnEsc: _ = !0, className: v, hideCloseButton: y = !1, fullscreen: b = !1, fullscreenOnMobile: x = !1, "aria-label": S }) {
9
- let C = i();
10
- r(() => {
11
- if (!c) return;
9
+ function l({ open: l, onClose: f, title: p, children: m, footer: h, size: g = "md", closeOnBackdrop: _ = !0, closeOnEsc: v = !0, className: y, hideCloseButton: b = !1, fullscreen: x = !1, fullscreenOnMobile: S = !1, "aria-label": C }) {
10
+ let w = a();
11
+ t(l, v ? f : null), i(() => {
12
+ if (!l) return;
12
13
  let e = document.body.style.overflow;
13
- document.body.style.overflow = "hidden";
14
- let t = (e) => {
15
- _ && e.key === "Escape" && d();
14
+ return document.body.style.overflow = "hidden", () => {
15
+ document.body.style.overflow = e;
16
16
  };
17
- return window.addEventListener("keydown", t), () => {
18
- document.body.style.overflow = e, window.removeEventListener("keydown", t);
19
- };
20
- }, [
21
- c,
22
- _,
23
- d
24
- ]);
25
- let w = t();
26
- return !c || !w ? null : s(/* @__PURE__ */ a("div", {
27
- className: n.overlay,
17
+ }, [l]);
18
+ let T = n();
19
+ return !l || !T ? null : c(/* @__PURE__ */ o("div", {
20
+ className: r.overlay,
28
21
  role: "presentation",
29
22
  onClick: () => {
30
- g && d();
23
+ _ && f();
31
24
  },
32
- children: /* @__PURE__ */ o("div", {
25
+ children: /* @__PURE__ */ s("div", {
33
26
  role: "dialog",
34
27
  "aria-modal": "true",
35
- "aria-labelledby": f ? C : void 0,
36
- "aria-label": f ? void 0 : S,
37
- className: e(n.dialog, l(h), b && n.fullscreen, x && n.fullscreenOnMobile, v),
28
+ "aria-labelledby": p ? w : void 0,
29
+ "aria-label": p ? void 0 : C,
30
+ className: e(r.dialog, u(g), x && r.fullscreen, S && r.fullscreenOnMobile, y),
38
31
  onClick: (e) => e.stopPropagation(),
39
32
  children: [
40
- (f || !y) && /* @__PURE__ */ o("header", {
41
- className: n.header,
42
- children: [f && /* @__PURE__ */ a("h3", {
43
- id: C,
44
- className: n.title,
45
- children: f
46
- }), !y && /* @__PURE__ */ a("button", {
33
+ (p || !b) && /* @__PURE__ */ s("header", {
34
+ className: r.header,
35
+ children: [p && /* @__PURE__ */ o("h3", {
36
+ id: w,
37
+ className: r.title,
38
+ children: p
39
+ }), !b && /* @__PURE__ */ o("button", {
47
40
  type: "button",
48
- className: n.close,
41
+ className: r.close,
49
42
  "aria-label": "Fechar",
50
- onClick: d,
51
- children: /* @__PURE__ */ a(u, {})
43
+ onClick: f,
44
+ children: /* @__PURE__ */ o(d, {})
52
45
  })]
53
46
  }),
54
- /* @__PURE__ */ a("div", {
55
- className: n.body,
56
- children: p
57
- }),
58
- m && /* @__PURE__ */ a("footer", {
59
- className: n.footer,
47
+ /* @__PURE__ */ o("div", {
48
+ className: r.body,
60
49
  children: m
50
+ }),
51
+ h && /* @__PURE__ */ o("footer", {
52
+ className: r.footer,
53
+ children: h
61
54
  })
62
55
  ]
63
56
  })
64
- }), w);
57
+ }), T);
65
58
  }
66
- function l(e) {
67
- return e === "2xl" ? n.size2xl : e === "3xl" ? n.size3xl : n[e];
59
+ function u(e) {
60
+ return e === "2xl" ? r.size2xl : e === "3xl" ? r.size3xl : r[e];
68
61
  }
69
- function u() {
70
- return /* @__PURE__ */ a("svg", {
62
+ function d() {
63
+ return /* @__PURE__ */ o("svg", {
71
64
  width: "18",
72
65
  height: "18",
73
66
  viewBox: "0 0 24 24",
74
67
  fill: "none",
75
- children: /* @__PURE__ */ a("path", {
68
+ children: /* @__PURE__ */ o("path", {
76
69
  d: "M6 6l12 12M6 18L18 6",
77
70
  stroke: "currentColor",
78
71
  strokeWidth: "2",
@@ -81,6 +74,6 @@ function u() {
81
74
  });
82
75
  }
83
76
  //#endregion
84
- export { c as Modal };
77
+ export { l as Modal };
85
78
 
86
79
  //# sourceMappingURL=Modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </div>\n </div>,\n portalHost,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":";;;;;;;AA+CA,SAAgB,EAAM,EAClB,SACA,YACA,UACA,aACA,WACA,UAAO,MACP,qBAAkB,IAClB,gBAAa,IACb,cACA,qBAAkB,IAClB,gBAAa,IACb,wBAAqB,IACrB,cAAc,KACH;CACX,IAAM,IAAU,EAAM;CACtB,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAC7C,SAAS,KAAK,MAAM,WAAW;EAE/B,IAAM,KAAa,MAA+B;GAC9C,AAAI,KAAc,EAAM,QAAQ,YAAU,EAAQ;EACtD;EAGA,OAFA,OAAO,iBAAiB,WAAW,CAAS,SAE/B;GAET,AADA,SAAS,KAAK,MAAM,WAAW,GAC/B,OAAO,oBAAoB,WAAW,CAAS;EACnD;CACJ,GAAG;EAAC;EAAM;EAAY;CAAO,CAAC;CAE9B,IAAM,IAAa,EAAc;CAIjC,OAFI,CAAC,KAAQ,CAAC,IAAmB,OAE1B,EACH,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,MAAK;EACL,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;EAEA,UAAA,kBAAC,OAAD;GACI,MAAK;GACL,cAAW;GACX,mBAAiB,IAAQ,IAAU,KAAA;GACnC,cAAY,IAAQ,KAAA,IAAY;GAChC,WAAW,EACP,EAAO,QACP,EAAc,CAAI,GAClB,KAAc,EAAO,YACrB,KAAsB,EAAO,oBAC7B,CACJ;GACA,UAAU,MAAU,EAAM,gBAAgB;GAZ9C,UAAA;KAcM,KAAS,CAAC,MACR,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAA1B,UAAA,CACK,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,WAAW,EAAO;MAC9B,UAAA;KACD,CAAA,GAEP,CAAC,KACE,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,cAAW;MACX,SAAS;MAET,UAAA,kBAAC,GAAD,CAAY,CAAA;KACR,CAAA,CAER;;IAEZ,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAO;IAAc,CAAA;IAC3C,KAAU,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAAS,UAAA;IAAe,CAAA;GAC5D;;CACJ,CAAA,GACL,CACJ;AACJ;AAEA,SAAS,EAAc,GAAqC;CAGxD,OAFI,MAAS,QAAc,EAAO,UAC9B,MAAS,QAAc,EAAO,UAC3B,EAAO;AAClB;AAEA,SAAS,IAAY;CACjB,OACI,kBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EACjD,UAAA,kBAAC,QAAD;GACI,GAAE;GACF,QAAO;GACP,aAAY;GACZ,eAAc;EACjB,CAAA;CACA,CAAA;AAEb"}
1
+ {"version":3,"file":"Modal.js","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { useEscapeLayer } from \"@/components/Portal/escape-layer\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEscapeLayer(open, closeOnEsc ? onClose : null);\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = previousOverflow;\n };\n }, [open]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </div>\n </div>,\n portalHost,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":";;;;;;;;AAgDA,SAAgB,EAAM,EAClB,SACA,YACA,UACA,aACA,WACA,UAAO,MACP,qBAAkB,IAClB,gBAAa,IACb,cACA,qBAAkB,IAClB,gBAAa,IACb,wBAAqB,IACrB,cAAc,KACH;CACX,IAAM,IAAU,EAAM;CAEtB,AADA,EAAe,GAAM,IAAa,IAAU,IAAI,GAChD,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAE7C,OADA,SAAS,KAAK,MAAM,WAAW,gBAClB;GACT,SAAS,KAAK,MAAM,WAAW;EACnC;CACJ,GAAG,CAAC,CAAI,CAAC;CAET,IAAM,IAAa,EAAc;CAIjC,OAFI,CAAC,KAAQ,CAAC,IAAmB,OAE1B,EACH,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,MAAK;EACL,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;EAEA,UAAA,kBAAC,OAAD;GACI,MAAK;GACL,cAAW;GACX,mBAAiB,IAAQ,IAAU,KAAA;GACnC,cAAY,IAAQ,KAAA,IAAY;GAChC,WAAW,EACP,EAAO,QACP,EAAc,CAAI,GAClB,KAAc,EAAO,YACrB,KAAsB,EAAO,oBAC7B,CACJ;GACA,UAAU,MAAU,EAAM,gBAAgB;GAZ9C,UAAA;KAcM,KAAS,CAAC,MACR,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAA1B,UAAA,CACK,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,WAAW,EAAO;MAC9B,UAAA;KACD,CAAA,GAEP,CAAC,KACE,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,cAAW;MACX,SAAS;MAET,UAAA,kBAAC,GAAD,CAAY,CAAA;KACR,CAAA,CAER;;IAEZ,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAO;IAAc,CAAA;IAC3C,KAAU,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAAS,UAAA;IAAe,CAAA;GAC5D;;CACJ,CAAA,GACL,CACJ;AACJ;AAEA,SAAS,EAAc,GAAqC;CAGxD,OAFI,MAAS,QAAc,EAAO,UAC9B,MAAS,QAAc,EAAO,UAC3B,EAAO;AAClB;AAEA,SAAS,IAAY;CACjB,OACI,kBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EACjD,UAAA,kBAAC,QAAD;GACI,GAAE;GACF,QAAO;GACP,aAAY;GACZ,eAAc;EACjB,CAAA;CACA,CAAA;AAEb"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./MultiSelect.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i(e,t){return!t||e.label.toLowerCase().includes(t.toLowerCase())}function a({options:a,value:o,onChange:s,label:c,placeholder:l=`Selecione`,helperText:u,error:d,disabled:f,maxItems:p,filter:m=i,emptyMessage:h=`Nenhuma opção encontrada`,className:g}){let _=(0,n.useId)(),v=(0,n.useRef)(null),y=(0,n.useRef)(null),[b,x]=(0,n.useState)(!1),[S,C]=(0,n.useState)(``),[w,T]=(0,n.useState)(0),E=(0,n.useMemo)(()=>new Set(o),[o]),D=(0,n.useMemo)(()=>o.map(e=>a.find(t=>t.value===e)).filter(e=>!!e),[o,a]),O=(0,n.useMemo)(()=>a.filter(e=>m(e,S)),[a,m,S]),k=p!==void 0&&o.length>=p,A=(0,n.useCallback)(e=>{if(!e.disabled){if(E.has(e.value))s(o.filter(t=>t!==e.value));else{if(k)return;s([...o,e.value])}C(``),y.current?.focus()}},[E,s,o,k]),j=(0,n.useCallback)(e=>{s(o.filter(t=>t!==e))},[s,o]);return(0,n.useEffect)(()=>{if(!b)return;let e=e=>{v.current&&!v.current.contains(e.target)&&(x(!1),C(``))};return window.addEventListener(`mousedown`,e),()=>window.removeEventListener(`mousedown`,e)},[b]),(0,r.jsxs)(`div`,{ref:v,className:e.cn(t.default.wrapper,d&&t.default.error,g),children:[c&&(0,r.jsx)(`label`,{htmlFor:_,className:t.default.label,children:c}),(0,r.jsxs)(`div`,{className:e.cn(t.default.field,f&&t.default.disabled),onMouseDown:e=>{f||e.target===e.currentTarget&&(e.preventDefault(),y.current?.focus(),x(!0))},children:[D.map(e=>(0,r.jsxs)(`span`,{className:t.default.chip,children:[e.label,(0,r.jsx)(`button`,{type:`button`,className:t.default.chipRemove,"aria-label":`Remover ${e.label}`,disabled:f,onMouseDown:t=>{t.preventDefault(),j(e.value)},children:`×`})]},e.value)),(0,r.jsx)(`input`,{id:_,ref:y,type:`text`,role:`combobox`,"aria-expanded":b,"aria-controls":`${_}-listbox`,"aria-autocomplete":`list`,className:t.default.input,placeholder:o.length===0?l:``,disabled:f,value:S,onFocus:()=>x(!0),onChange:e=>{C(e.target.value),x(!0),T(0)},onKeyDown:e=>{if(e.key===`ArrowDown`)e.preventDefault(),x(!0),T(e=>Math.min(O.length-1,e+1));else if(e.key===`ArrowUp`)e.preventDefault(),T(e=>Math.max(0,e-1));else if(e.key===`Enter`){e.preventDefault();let t=O[w];t&&A(t)}else e.key===`Escape`?(x(!1),C(``)):e.key===`Backspace`&&S===``&&o.length>0&&j(o[o.length-1])}})]}),b&&(0,r.jsx)(`ul`,{id:`${_}-listbox`,role:`listbox`,"aria-multiselectable":!0,className:t.default.menu,children:O.length===0?(0,r.jsx)(`li`,{className:t.default.empty,children:h}):O.map((n,i)=>{let a=E.has(n.value);return(0,r.jsxs)(`li`,{role:`option`,"aria-selected":a,"aria-disabled":n.disabled||!a&&k,className:e.cn(t.default.option,i===w&&t.default.active,a&&t.default.selected),onMouseEnter:()=>T(i),onMouseDown:e=>{e.preventDefault(),A(n)},children:[(0,r.jsx)(`span`,{className:t.default.check,"aria-hidden":!0,children:a?`✓`:``}),n.label]},n.value)})}),d?(0,r.jsx)(`span`,{className:t.default.errorText,children:d}):u?(0,r.jsx)(`span`,{className:t.default.helper,children:u}):null]})}exports.MultiSelect=a;
1
+ const e=require("../../utils/cn.cjs"),t=require("../Portal/Portal.cjs"),n=require("../Portal/anchor-position.cjs"),r=require("./MultiSelect.module.cjs");let i=require("react"),a=require("react/jsx-runtime");function o(e,t){return!t||e.label.toLowerCase().includes(t.toLowerCase())}var s=4;function c({options:c,value:l,onChange:u,label:d,placeholder:f=`Selecione`,helperText:p,error:m,disabled:h,maxItems:g,filter:_=o,emptyMessage:v=`Nenhuma opção encontrada`,portal:y=!0,className:b}){let x=(0,i.useId)(),S=(0,i.useRef)(null),C=(0,i.useRef)(null),[w,T]=(0,i.useState)(!1),[E,D]=(0,i.useState)(``),[O,k]=(0,i.useState)(0),[A,j]=(0,i.useState)(null),[M,N]=(0,i.useState)(null),P=n.useAnchorPosition({anchor:A,floating:M,side:`bottom`,align:`start`,offset:s,enabled:y&&w,matchWidth:!0}),F=(0,i.useMemo)(()=>new Set(l),[l]),I=(0,i.useMemo)(()=>l.map(e=>c.find(t=>t.value===e)).filter(e=>!!e),[l,c]),L=(0,i.useMemo)(()=>c.filter(e=>_(e,E)),[c,_,E]),R=g!==void 0&&l.length>=g,z=(0,i.useCallback)(e=>{if(!e.disabled){if(F.has(e.value))u(l.filter(t=>t!==e.value));else{if(R)return;u([...l,e.value])}D(``),C.current?.focus()}},[F,u,l,R]),B=(0,i.useCallback)(e=>{u(l.filter(t=>t!==e))},[u,l]);(0,i.useEffect)(()=>{if(!w)return;let e=e=>{let t=e.target;S.current?.contains(t)||M?.contains(t)||(T(!1),D(``))};return window.addEventListener(`mousedown`,e),()=>window.removeEventListener(`mousedown`,e)},[w,M]);let V=e=>{h||e.target!==e.currentTarget||(e.preventDefault(),C.current?.focus(),T(!0))},H=e=>{if(e.key===`ArrowDown`)e.preventDefault(),T(!0),k(e=>Math.min(L.length-1,e+1));else if(e.key===`ArrowUp`)e.preventDefault(),k(e=>Math.max(0,e-1));else if(e.key===`Enter`){e.preventDefault();let t=L[O];t&&z(t)}else e.key===`Escape`?(w&&e.preventDefault(),T(!1),D(``)):e.key===`Backspace`&&E===``&&l.length>0&&B(l[l.length-1])},U=(0,a.jsx)(`ul`,{ref:N,id:`${x}-listbox`,role:`listbox`,"aria-multiselectable":!0,className:e.cn(r.default.menu,y&&r.default.portalled),style:P,children:L.length===0?(0,a.jsx)(`li`,{className:r.default.empty,children:v}):L.map((t,n)=>{let i=F.has(t.value);return(0,a.jsxs)(`li`,{role:`option`,"aria-selected":i,"aria-disabled":t.disabled||!i&&R,className:e.cn(r.default.option,n===O&&r.default.active,i&&r.default.selected),onMouseEnter:()=>k(n),onMouseDown:e=>{e.preventDefault(),z(t)},children:[(0,a.jsx)(`span`,{className:r.default.check,"aria-hidden":!0,children:i?`✓`:``}),t.label]},t.value)})});return(0,a.jsxs)(`div`,{ref:S,className:e.cn(r.default.wrapper,m&&r.default.error,b),children:[d&&(0,a.jsx)(`label`,{htmlFor:x,className:r.default.label,children:d}),(0,a.jsxs)(`div`,{ref:j,className:e.cn(r.default.field,h&&r.default.disabled),onMouseDown:V,children:[I.map(e=>(0,a.jsxs)(`span`,{className:r.default.chip,children:[e.label,(0,a.jsx)(`button`,{type:`button`,className:r.default.chipRemove,"aria-label":`Remover ${e.label}`,disabled:h,onMouseDown:t=>{t.preventDefault(),B(e.value)},children:`×`})]},e.value)),(0,a.jsx)(`input`,{id:x,ref:C,type:`text`,role:`combobox`,"aria-expanded":w,"aria-controls":`${x}-listbox`,"aria-autocomplete":`list`,className:r.default.input,placeholder:l.length===0?f:``,disabled:h,value:E,onFocus:()=>T(!0),onChange:e=>{D(e.target.value),T(!0),k(0)},onKeyDown:H})]}),w&&(y?(0,a.jsx)(t.Portal,{children:U}):U),m?(0,a.jsx)(`span`,{className:r.default.errorText,children:m}):p?(0,a.jsx)(`span`,{className:r.default.helper,children:p}):null]})}exports.MultiSelect=c;
2
2
  //# sourceMappingURL=MultiSelect.cjs.map