@mittwald/flow-react-components 1.3.0-next.3 → 1.3.0-next.31

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 (188) hide show
  1. package/AGENTS.md +56 -1
  2. package/MIGRATION.md +24 -0
  3. package/USAGE.md +14 -6
  4. package/dist/assets/component-index.json +1 -1
  5. package/dist/assets/doc-properties.json +1 -1
  6. package/dist/css/all-layered.css +1 -1
  7. package/dist/css/all.css +1 -1
  8. package/dist/js/_virtual/_24.locale.mjs +10 -2
  9. package/dist/js/_virtual/_28.locale.mjs +10 -0
  10. package/dist/js/packages/components/src/components/Accordion/Accordion.mjs +26 -15
  11. package/dist/js/packages/components/src/components/Accordion/Accordion.mjs.map +1 -1
  12. package/dist/js/packages/components/src/components/Avatar/Avatar.module.mjs.map +1 -1
  13. package/dist/js/packages/components/src/components/Button/Button.module.mjs +1 -0
  14. package/dist/js/packages/components/src/components/Button/Button.module.mjs.map +1 -1
  15. package/dist/js/packages/components/src/components/Calendar/Calendar.module.mjs +3 -1
  16. package/dist/js/packages/components/src/components/Calendar/Calendar.module.mjs.map +1 -1
  17. package/dist/js/packages/components/src/components/Calendar/components/CalendarHeader/CalendarHeader.mjs +19 -15
  18. package/dist/js/packages/components/src/components/Calendar/components/CalendarHeader/CalendarHeader.mjs.map +1 -1
  19. package/dist/js/packages/components/src/components/CartesianChart/CartesianChart.mjs +6 -2
  20. package/dist/js/packages/components/src/components/CartesianChart/CartesianChart.mjs.map +1 -1
  21. package/dist/js/packages/components/src/components/CartesianChart/CartesianChart.module.mjs.map +1 -1
  22. package/dist/js/packages/components/src/components/CartesianChart/components/Area/Area.mjs +7 -2
  23. package/dist/js/packages/components/src/components/CartesianChart/components/Area/Area.mjs.map +1 -1
  24. package/dist/js/packages/components/src/components/CartesianChart/components/Bar/Bar.mjs +15 -3
  25. package/dist/js/packages/components/src/components/CartesianChart/components/Bar/Bar.mjs.map +1 -1
  26. package/dist/js/packages/components/src/components/CartesianChart/components/Line/Line.mjs +4 -1
  27. package/dist/js/packages/components/src/components/CartesianChart/components/Line/Line.mjs.map +1 -1
  28. package/dist/js/packages/components/src/components/CartesianChart/context.mjs +4 -1
  29. package/dist/js/packages/components/src/components/CartesianChart/context.mjs.map +1 -1
  30. package/dist/js/packages/components/src/components/CartesianChart/hooks/useChartAnimation.mjs +27 -0
  31. package/dist/js/packages/components/src/components/CartesianChart/hooks/useChartAnimation.mjs.map +1 -0
  32. package/dist/js/packages/components/src/components/CartesianChart/hooks/useScopedStackId.mjs +16 -0
  33. package/dist/js/packages/components/src/components/CartesianChart/hooks/useScopedStackId.mjs.map +1 -0
  34. package/dist/js/packages/components/src/components/Chat/Chat.module.mjs.map +1 -1
  35. package/dist/js/packages/components/src/components/Checkbox/Checkbox.module.mjs +2 -1
  36. package/dist/js/packages/components/src/components/Checkbox/Checkbox.module.mjs.map +1 -1
  37. package/dist/js/packages/components/src/components/CheckboxGroup/CheckboxGroup.module.mjs.map +1 -1
  38. package/dist/js/packages/components/src/components/CodeBlock/CodeBlock.mjs.map +1 -1
  39. package/dist/js/packages/components/src/components/CodeBlock/CodeBlock.module.mjs.map +1 -1
  40. package/dist/js/packages/components/src/components/Combine/Combine.mjs +4 -1
  41. package/dist/js/packages/components/src/components/Combine/Combine.mjs.map +1 -1
  42. package/dist/js/packages/components/src/components/Combine/Combine.module.mjs.map +1 -1
  43. package/dist/js/packages/components/src/components/ComboBox/ComboBox.module.mjs.map +1 -1
  44. package/dist/js/packages/components/src/components/ContextMenu/ContextMenu.module.mjs.map +1 -1
  45. package/dist/js/packages/components/src/components/CopyButton/CopyButton.mjs +4 -3
  46. package/dist/js/packages/components/src/components/CopyButton/CopyButton.mjs.map +1 -1
  47. package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.mjs +14 -11
  48. package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.mjs.map +1 -1
  49. package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.module.mjs +4 -2
  50. package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.module.mjs.map +1 -1
  51. package/dist/js/packages/components/src/components/DonutChart/DonutChart.module.mjs +6 -3
  52. package/dist/js/packages/components/src/components/DonutChart/DonutChart.module.mjs.map +1 -1
  53. package/dist/js/packages/components/src/components/DonutChart/components/DonutChartFill.mjs +13 -2
  54. package/dist/js/packages/components/src/components/DonutChart/components/DonutChartFill.mjs.map +1 -1
  55. package/dist/js/packages/components/src/components/FileDropZone/FileDropZone.mjs +2 -2
  56. package/dist/js/packages/components/src/components/FileDropZone/FileDropZone.mjs.map +1 -1
  57. package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.mjs +9 -6
  58. package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.mjs.map +1 -1
  59. package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.module.mjs +5 -1
  60. package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.module.mjs.map +1 -1
  61. package/dist/js/packages/components/src/components/Heading/Heading.mjs +1 -1
  62. package/dist/js/packages/components/src/components/Heading/Heading.mjs.map +1 -1
  63. package/dist/js/packages/components/src/components/Heading/Heading.module.mjs.map +1 -1
  64. package/dist/js/packages/components/src/components/Image/Image.mjs +2 -2
  65. package/dist/js/packages/components/src/components/Image/Image.mjs.map +1 -1
  66. package/dist/js/packages/components/src/components/Image/Image.module.mjs +3 -1
  67. package/dist/js/packages/components/src/components/Image/Image.module.mjs.map +1 -1
  68. package/dist/js/packages/components/src/components/LayoutCard/LayoutCard.module.mjs.map +1 -1
  69. package/dist/js/packages/components/src/components/LightBox/LightBox.module.mjs.map +1 -1
  70. package/dist/js/packages/components/src/components/LightBox/components/LightBoxGallery/Controls.mjs +1 -1
  71. package/dist/js/packages/components/src/components/LightBox/components/LightBoxGallery/Controls.mjs.map +1 -1
  72. package/dist/js/packages/components/src/components/List/List.module.mjs.map +1 -1
  73. package/dist/js/packages/components/src/components/List/components/Header/components/SearchField/SearchField.module.mjs.map +1 -1
  74. package/dist/js/packages/components/src/components/List/components/Items/Items.mjs +3 -6
  75. package/dist/js/packages/components/src/components/List/components/Items/Items.mjs.map +1 -1
  76. package/dist/js/packages/components/src/components/List/components/Items/components/Item/Item.module.mjs.map +1 -1
  77. package/dist/js/packages/components/src/components/List/components/ListItemView/ListItemView.module.mjs.map +1 -1
  78. package/dist/js/packages/components/src/components/List/components/Table/Table.mjs +6 -2
  79. package/dist/js/packages/components/src/components/List/components/Table/Table.mjs.map +1 -1
  80. package/dist/js/packages/components/src/components/List/{components/Items/hooks → hooks}/useInfiniteScrollTrigger.mjs +11 -12
  81. package/dist/js/packages/components/src/components/List/hooks/useInfiniteScrollTrigger.mjs.map +1 -0
  82. package/dist/js/packages/components/src/components/Markdown/Markdown.module.mjs.map +1 -1
  83. package/dist/js/packages/components/src/components/MarkdownEditor/MarkdownEditor.mjs +16 -3
  84. package/dist/js/packages/components/src/components/MarkdownEditor/MarkdownEditor.mjs.map +1 -1
  85. package/dist/js/packages/components/src/components/MarkdownEditor/MarkdownEditor.module.mjs +4 -2
  86. package/dist/js/packages/components/src/components/MarkdownEditor/MarkdownEditor.module.mjs.map +1 -1
  87. package/dist/js/packages/components/src/components/MarkdownEditor/components/AttachmentButton.mjs +47 -0
  88. package/dist/js/packages/components/src/components/MarkdownEditor/components/AttachmentButton.mjs.map +1 -0
  89. package/dist/js/packages/components/src/components/MarkdownEditor/components/Toolbar.mjs +21 -20
  90. package/dist/js/packages/components/src/components/MarkdownEditor/components/Toolbar.mjs.map +1 -1
  91. package/dist/js/packages/components/src/components/MarkdownEditor/components/ToolbarButton.mjs +1 -1
  92. package/dist/js/packages/components/src/components/MarkdownEditor/components/ToolbarButton.mjs.map +1 -1
  93. package/dist/js/packages/components/src/components/MarkdownEditor/lib/fileUpload.mjs +73 -0
  94. package/dist/js/packages/components/src/components/MarkdownEditor/lib/fileUpload.mjs.map +1 -0
  95. package/dist/js/packages/components/src/components/MarkdownEditor/lib/useFileUpload.mjs +141 -0
  96. package/dist/js/packages/components/src/components/MarkdownEditor/lib/useFileUpload.mjs.map +1 -0
  97. package/dist/js/packages/components/src/components/MessageThread/MessageThread.module.mjs.map +1 -1
  98. package/dist/js/packages/components/src/components/Modal/Modal.module.mjs.map +1 -1
  99. package/dist/js/packages/components/src/components/Navigation/Navigation.module.mjs +3 -1
  100. package/dist/js/packages/components/src/components/Navigation/Navigation.module.mjs.map +1 -1
  101. package/dist/js/packages/components/src/components/Navigation/components/LinkListTunnelExit/LinkListTunnelExit.mjs +5 -1
  102. package/dist/js/packages/components/src/components/Navigation/components/LinkListTunnelExit/LinkListTunnelExit.mjs.map +1 -1
  103. package/dist/js/packages/components/src/components/Navigation/components/NavigationGroup/NavigationGroup.module.mjs.map +1 -1
  104. package/dist/js/packages/components/src/components/NumberField/NumberField.module.mjs.map +1 -1
  105. package/dist/js/packages/components/src/components/Overlay/Overlay.module.mjs +3 -1
  106. package/dist/js/packages/components/src/components/Overlay/Overlay.module.mjs.map +1 -1
  107. package/dist/js/packages/components/src/components/Overlay/components/OverlayContent.mjs +47 -9
  108. package/dist/js/packages/components/src/components/Overlay/components/OverlayContent.mjs.map +1 -1
  109. package/dist/js/packages/components/src/components/PasswordCreationField/PasswordCreationField.mjs +8 -1
  110. package/dist/js/packages/components/src/components/PasswordCreationField/PasswordCreationField.mjs.map +1 -1
  111. package/dist/js/packages/components/src/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.mjs +2 -0
  112. package/dist/js/packages/components/src/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.mjs.map +1 -1
  113. package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.mjs +13 -7
  114. package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.mjs.map +1 -1
  115. package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.module.mjs +9 -0
  116. package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.module.mjs.map +1 -0
  117. package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry.mjs +12 -4
  118. package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry.mjs.map +1 -1
  119. package/dist/js/packages/components/src/components/PasswordCreationField/lib/ariaAnnounce.mjs +52 -0
  120. package/dist/js/packages/components/src/components/PasswordCreationField/lib/ariaAnnounce.mjs.map +1 -0
  121. package/dist/js/packages/components/src/components/PasswordCreationField/lib/generateValidationTranslation.mjs +1 -0
  122. package/dist/js/packages/components/src/components/PasswordCreationField/lib/generateValidationTranslation.mjs.map +1 -1
  123. package/dist/js/packages/components/src/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.mjs +1 -0
  124. package/dist/js/packages/components/src/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.mjs.map +1 -1
  125. package/dist/js/packages/components/src/components/PasswordCreationField/lib/usePolicyValidationResult.mjs +1 -0
  126. package/dist/js/packages/components/src/components/PasswordCreationField/lib/usePolicyValidationResult.mjs.map +1 -1
  127. package/dist/js/packages/components/src/components/Popover/Popover.module.mjs +3 -1
  128. package/dist/js/packages/components/src/components/Popover/Popover.module.mjs.map +1 -1
  129. package/dist/js/packages/components/src/components/Popover/components/NonModalPopoverContent/NonModalPopoverContent.mjs +20 -11
  130. package/dist/js/packages/components/src/components/Popover/components/NonModalPopoverContent/NonModalPopoverContent.mjs.map +1 -1
  131. package/dist/js/packages/components/src/components/Popover/components/PopoverContent/PopoverContent.mjs +1 -0
  132. package/dist/js/packages/components/src/components/Popover/components/PopoverContent/PopoverContent.mjs.map +1 -1
  133. package/dist/js/packages/components/src/components/ProgressBar/ProgressBar.module.mjs +1 -0
  134. package/dist/js/packages/components/src/components/ProgressBar/ProgressBar.module.mjs.map +1 -1
  135. package/dist/js/packages/components/src/components/RadioGroup/components/Radio/Radio.module.mjs +2 -1
  136. package/dist/js/packages/components/src/components/RadioGroup/components/Radio/Radio.module.mjs.map +1 -1
  137. package/dist/js/packages/components/src/components/SearchField/SearchField.module.mjs +3 -3
  138. package/dist/js/packages/components/src/components/SearchField/SearchField.module.mjs.map +1 -1
  139. package/dist/js/packages/components/src/components/Section/Section.module.mjs.map +1 -1
  140. package/dist/js/packages/components/src/components/Section/components/SectionHeader/SectionHeader.module.mjs.map +1 -1
  141. package/dist/js/packages/components/src/components/Slider/Slider.module.mjs.map +1 -1
  142. package/dist/js/packages/components/src/components/Switch/Switch.module.mjs.map +1 -1
  143. package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.mjs +2 -1
  144. package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.mjs.map +1 -1
  145. package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.module.mjs +5 -1
  146. package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.module.mjs.map +1 -1
  147. package/dist/js/packages/components/src/components/Table/Table.module.mjs.map +1 -1
  148. package/dist/js/packages/components/src/components/Tabs/components/TabTitle/TabTitle.module.mjs.map +1 -1
  149. package/dist/js/packages/components/src/components/Text/Text.module.mjs.map +1 -1
  150. package/dist/js/packages/components/src/components/TextArea/TextArea.mjs +14 -13
  151. package/dist/js/packages/components/src/components/TextArea/TextArea.mjs.map +1 -1
  152. package/dist/js/packages/components/src/components/Tooltip/Tooltip.mjs +1 -0
  153. package/dist/js/packages/components/src/components/Tooltip/Tooltip.mjs.map +1 -1
  154. package/dist/js/packages/components/src/components/Tooltip/Tooltip.module.mjs +3 -1
  155. package/dist/js/packages/components/src/components/Tooltip/Tooltip.module.mjs.map +1 -1
  156. package/dist/js/packages/components/src/lib/files/acceptedFiles.mjs +34 -0
  157. package/dist/js/packages/components/src/lib/files/acceptedFiles.mjs.map +1 -0
  158. package/dist/js/packages/components/src/lib/hooks/dom/useKeepBrowserExtensionsInteractive.mjs +1 -1
  159. package/dist/js/packages/components/src/lib/hooks/dom/useKeepBrowserExtensionsInteractive.mjs.map +1 -1
  160. package/dist/js/packages/design-tokens/dist/json-runtime/all-dark.mjs +13 -1
  161. package/dist/js/packages/design-tokens/dist/json-runtime/all-dark.mjs.map +1 -1
  162. package/dist/js/packages/design-tokens/dist/json-runtime/all-light.mjs +13 -1
  163. package/dist/js/packages/design-tokens/dist/json-runtime/all-light.mjs.map +1 -1
  164. package/dist/types/components/CartesianChart/context.d.ts +6 -0
  165. package/dist/types/components/CartesianChart/hooks/useChartAnimation.d.ts +1 -0
  166. package/dist/types/components/CartesianChart/hooks/useScopedStackId.d.ts +6 -0
  167. package/dist/types/components/CopyButton/CopyButton.d.ts +5 -0
  168. package/dist/types/components/Image/Image.d.ts +7 -1
  169. package/dist/types/components/List/hooks/useInfiniteScrollTrigger.d.ts +17 -0
  170. package/dist/types/components/MarkdownEditor/MarkdownEditor.d.ts +20 -0
  171. package/dist/types/components/MarkdownEditor/components/AttachmentButton.d.ts +1 -0
  172. package/dist/types/components/MarkdownEditor/components/Toolbar.d.ts +11 -2
  173. package/dist/types/components/MarkdownEditor/components/ToolbarButton.d.ts +8 -2
  174. package/dist/types/components/MarkdownEditor/index.d.ts +1 -1
  175. package/dist/types/components/MarkdownEditor/lib/fileUpload.d.ts +19 -0
  176. package/dist/types/components/MarkdownEditor/lib/useFileUpload.d.ts +1 -0
  177. package/dist/types/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.d.ts +0 -7
  178. package/dist/types/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.d.ts +0 -9
  179. package/dist/types/components/PasswordCreationField/lib/ariaAnnounce.d.ts +1 -0
  180. package/dist/types/components/PasswordCreationField/lib/generateValidationTranslation.d.ts +0 -2
  181. package/dist/types/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.d.ts +0 -3
  182. package/dist/types/components/PasswordCreationField/lib/usePolicyValidationResult.d.ts +1 -7
  183. package/dist/types/lib/files/acceptedFiles.d.ts +1 -0
  184. package/dist/types/lib/hooks/dom/useKeepBrowserExtensionsInteractive.d.ts +1 -0
  185. package/dist/types/lib/theming/hooks/useDesignTokens.d.ts +14 -0
  186. package/package.json +10 -10
  187. package/dist/js/packages/components/src/components/List/components/Items/hooks/useInfiniteScrollTrigger.mjs.map +0 -1
  188. package/dist/types/components/List/components/Items/hooks/useInfiniteScrollTrigger.d.ts +0 -3
@@ -4,7 +4,7 @@ import { useIsActivityActive } from "../../Activity/context.mjs";
4
4
  import DivView from "../../../views/DivView.mjs";
5
5
  import Overlay_module_default from "../Overlay.module.mjs";
6
6
  import { OverlaySuspenseFallback } from "./OverlaySuspenseFallback.mjs";
7
- import useKeepBrowserExtensionsInteractive from "../../../lib/hooks/dom/useKeepBrowserExtensionsInteractive.mjs";
7
+ import useKeepBrowserExtensionsInteractive, { isBrowserExtensionNode } from "../../../lib/hooks/dom/useKeepBrowserExtensionsInteractive.mjs";
8
8
  import { Suspense } from "react";
9
9
  import { jsx } from "react/jsx-runtime";
10
10
  import * as Aria from "react-aria-components";
@@ -12,6 +12,41 @@ import { UNSAFE_PortalProvider } from "react-aria";
12
12
  //#region src/components/Overlay/components/OverlayContent.tsx
13
13
  var overlayContainerAttribute = "data-flow-overlays";
14
14
  /**
15
+ * Nodes a browser extension appends (e.g. 1Password's inline menu) don't count
16
+ * as covering: they belong on top anyway, and moving the container for them
17
+ * blurs a focused field inside it (#3268). The same heuristic also spares
18
+ * custom elements and shadow hosts the page itself appends, which then render
19
+ * above open overlays.
20
+ */
21
+ var isCoveredByPageContent = (container) => {
22
+ for (let node = container.nextElementSibling; node; node = node.nextElementSibling) if (!isBrowserExtensionNode(node)) return true;
23
+ return false;
24
+ };
25
+ /**
26
+ * Only the container moves, never the nodes appended after it: those are
27
+ * foreign, and moving them costs state we cannot restore (an iframe reloads).
28
+ * The container's own state we can. Moving a node blurs the focused element
29
+ * inside it and resets every scroll container inside to the top. `moveBefore`
30
+ * keeps the focus (and, in Chromium, the scroll positions); the rest is
31
+ * restored afterwards.
32
+ */
33
+ var moveToEnd = (container) => {
34
+ const body = document.body;
35
+ const focused = document.activeElement;
36
+ const scrolled = Array.from(container.querySelectorAll("*")).filter((element) => element.scrollTop !== 0 || element.scrollLeft !== 0).map((element) => [
37
+ element,
38
+ element.scrollTop,
39
+ element.scrollLeft
40
+ ]);
41
+ if (typeof body.moveBefore === "function") body.moveBefore(container, null);
42
+ else body.append(container);
43
+ for (const [element, scrollTop, scrollLeft] of scrolled) {
44
+ element.scrollTop = scrollTop;
45
+ element.scrollLeft = scrollLeft;
46
+ }
47
+ if (focused instanceof HTMLElement && container.contains(focused) && document.activeElement !== focused) focused.focus({ preventScroll: true });
48
+ };
49
+ /**
15
50
  * Render overlays into a dedicated container instead of `document.body`.
16
51
  *
17
52
  * React Aria `FocusScope` relies on nearby sibling nodes (sentinels + overlay).
@@ -33,7 +68,7 @@ var getOverlayContainer = () => {
33
68
  if (typeof document === "undefined") return null;
34
69
  const existingContainer = document.querySelector(`body > [${overlayContainerAttribute}]`);
35
70
  if (existingContainer) {
36
- if (existingContainer !== document.body.lastElementChild) document.body.append(existingContainer);
71
+ if (isCoveredByPageContent(existingContainer)) moveToEnd(existingContainer);
37
72
  return existingContainer;
38
73
  }
39
74
  const container = document.createElement("div");
@@ -58,13 +93,16 @@ var OverlayContent = (props) => {
58
93
  children: /* @__PURE__ */ jsx(Aria.ModalOverlay, {
59
94
  ...restProps,
60
95
  className,
61
- children: /* @__PURE__ */ jsx(DivView, { children: /* @__PURE__ */ jsx(Aria.Modal, { children: /* @__PURE__ */ jsx(Suspense, {
62
- fallback: /* @__PURE__ */ jsx(Fallback, {}),
63
- children: /* @__PURE__ */ jsx(Aria.Dialog, {
64
- "aria-labelledby": ariaLabelledBy,
65
- children
66
- })
67
- }) }) })
96
+ children: /* @__PURE__ */ jsx(DivView, {
97
+ className: Overlay_module_default.viewport,
98
+ children: /* @__PURE__ */ jsx(Aria.Modal, { children: /* @__PURE__ */ jsx(Suspense, {
99
+ fallback: /* @__PURE__ */ jsx(Fallback, {}),
100
+ children: /* @__PURE__ */ jsx(Aria.Dialog, {
101
+ "aria-labelledby": ariaLabelledBy,
102
+ children
103
+ })
104
+ }) })
105
+ })
68
106
  })
69
107
  });
70
108
  };
@@ -1 +1 @@
1
- {"version":3,"file":"OverlayContent.mjs","names":[],"sources":["../../../../../../../../src/components/Overlay/components/OverlayContent.tsx"],"sourcesContent":["import * as Aria from \"react-aria-components\";\nimport { type FC, type PropsWithChildren, type Ref, Suspense } from \"react\";\nimport { UNSAFE_PortalProvider } from \"react-aria\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport { OverlaySuspenseFallback } from \"@/components/Overlay/components/OverlaySuspenseFallback\";\nimport styles from \"../Overlay.module.scss\";\nimport DivView from \"@/views/DivView\";\nimport { useKeepBrowserExtensionsInteractive } from \"@/lib/hooks/dom/useKeepBrowserExtensionsInteractive\";\nimport { useIsActivityActive } from \"@/components/Activity/context\";\n\nconst overlayContainerAttribute = \"data-flow-overlays\";\n\n/**\n * Render overlays into a dedicated container instead of `document.body`.\n *\n * React Aria `FocusScope` relies on nearby sibling nodes (sentinels + overlay).\n * Browser extensions (e.g. password managers) inject/reorder `body` children,\n * which can separate those nodes. Then focus scope detection breaks and may\n * cause recursive focus restoration.\n *\n * Keep the overlay container as the last child of body.\n *\n * React Aria normally portals overlays directly into body, so they are appended\n * last and render on top.\n *\n * If we create one persistent container only once, anything appended to body\n * later (toasts, third-party widgets, remounted app root) can render above open\n * overlays. Then overlays can appear behind page content, and the backdrop blur\n * no longer covers the page.\n */\nconst getOverlayContainer = (): HTMLElement | null => {\n if (typeof document === \"undefined\") {\n return null;\n }\n\n const existingContainer = document.querySelector<HTMLElement>(\n `body > [${overlayContainerAttribute}]`,\n );\n\n if (existingContainer) {\n if (existingContainer !== document.body.lastElementChild) {\n document.body.append(existingContainer);\n }\n return existingContainer;\n }\n\n const container = document.createElement(\"div\");\n container.setAttribute(overlayContainerAttribute, \"\");\n document.body.append(container);\n\n return container;\n};\n\nexport interface OverlayContentProps\n extends\n PropsWithChildren,\n PropsWithClassName,\n Pick<Aria.DialogProps, \"aria-labelledby\"> {\n ref?: Ref<HTMLDivElement>;\n onOpenChange: (isOpen: boolean) => void;\n isDismissable?: boolean;\n isOpen?: boolean;\n}\n\n/** @flr-generate all */\nexport const OverlayContent: FC<OverlayContentProps> = (props) => {\n const {\n children,\n className,\n \"aria-labelledby\": ariaLabelledBy,\n ...restProps\n } = props;\n\n const isActivityActive = useIsActivityActive();\n\n useKeepBrowserExtensionsInteractive(restProps.isOpen ?? false);\n\n const Fallback = () => {\n return (\n <DivView className={styles.suspense}>\n <OverlaySuspenseFallback {...restProps} />\n </DivView>\n );\n };\n\n if (!isActivityActive) {\n return null;\n }\n\n return (\n <UNSAFE_PortalProvider getContainer={getOverlayContainer}>\n <Aria.ModalOverlay {...restProps} className={className}>\n <DivView>\n <Aria.Modal>\n <Suspense fallback={<Fallback />}>\n <Aria.Dialog aria-labelledby={ariaLabelledBy}>\n {children}\n </Aria.Dialog>\n </Suspense>\n </Aria.Modal>\n </DivView>\n </Aria.ModalOverlay>\n </UNSAFE_PortalProvider>\n );\n};\n\nexport default OverlayContent;\n"],"mappings":";;;;;;;;;;;;AAUA,IAAM,4BAA4B;;;;;;;;;;;;;;;;;;;AAoBlC,IAAM,4BAAgD;CACpD,IAAI,OAAO,aAAa,aACtB,OAAO;CAGT,MAAM,oBAAoB,SAAS,cACjC,WAAW,0BAA0B,EACvC;CAEA,IAAI,mBAAmB;EACrB,IAAI,sBAAsB,SAAS,KAAK,kBACtC,SAAS,KAAK,OAAO,iBAAiB;EAExC,OAAO;CACT;CAEA,MAAM,YAAY,SAAS,cAAc,KAAK;CAC9C,UAAU,aAAa,2BAA2B,EAAE;CACpD,SAAS,KAAK,OAAO,SAAS;CAE9B,OAAO;AACT;;AAcA,IAAa,kBAA2C,UAAU;CAChE,MAAM,EACJ,UACA,WACA,mBAAmB,gBACnB,GAAG,cACD;CAEJ,MAAM,mBAAmB,oBAAoB;CAE7C,oCAAoC,UAAU,UAAU,KAAK;CAE7D,MAAM,iBAAiB;EACrB,OACE,oBAAC,SAAD;GAAS,WAAW,uBAAO;GACzB,UAAA,oBAAC,yBAAD,EAAyB,GAAI,UAAY,CAAA;EAClC,CAAA;CAEb;CAEA,IAAI,CAAC,kBACH,OAAO;CAGT,OACE,oBAAC,uBAAD;EAAuB,cAAc;EACnC,UAAA,oBAAC,KAAK,cAAN;GAAmB,GAAI;GAAsB;GAC3C,UAAA,oBAAC,SAAD,EAAA,UACE,oBAAC,KAAK,OAAN,EAAA,UACE,oBAAC,UAAD;IAAU,UAAU,oBAAC,UAAD,CAAW,CAAA;IAC7B,UAAA,oBAAC,KAAK,QAAN;KAAa,mBAAiB;KAC3B;IACU,CAAA;GACL,CAAA,EACA,CAAA,EACL,CAAA;EACQ,CAAA;CACE,CAAA;AAE3B"}
1
+ {"version":3,"file":"OverlayContent.mjs","names":[],"sources":["../../../../../../../../src/components/Overlay/components/OverlayContent.tsx"],"sourcesContent":["import * as Aria from \"react-aria-components\";\nimport { type FC, type PropsWithChildren, type Ref, Suspense } from \"react\";\nimport { UNSAFE_PortalProvider } from \"react-aria\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport { OverlaySuspenseFallback } from \"@/components/Overlay/components/OverlaySuspenseFallback\";\nimport styles from \"../Overlay.module.scss\";\nimport DivView from \"@/views/DivView\";\nimport {\n isBrowserExtensionNode,\n useKeepBrowserExtensionsInteractive,\n} from \"@/lib/hooks/dom/useKeepBrowserExtensionsInteractive\";\nimport { useIsActivityActive } from \"@/components/Activity/context\";\n\nconst overlayContainerAttribute = \"data-flow-overlays\";\n\n/**\n * Nodes a browser extension appends (e.g. 1Password's inline menu) don't count\n * as covering: they belong on top anyway, and moving the container for them\n * blurs a focused field inside it (#3268). The same heuristic also spares\n * custom elements and shadow hosts the page itself appends, which then render\n * above open overlays.\n */\nconst isCoveredByPageContent = (container: HTMLElement): boolean => {\n for (\n let node = container.nextElementSibling;\n node;\n node = node.nextElementSibling\n ) {\n if (!isBrowserExtensionNode(node)) {\n return true;\n }\n }\n return false;\n};\n\n/**\n * Only the container moves, never the nodes appended after it: those are\n * foreign, and moving them costs state we cannot restore (an iframe reloads).\n * The container's own state we can. Moving a node blurs the focused element\n * inside it and resets every scroll container inside to the top. `moveBefore`\n * keeps the focus (and, in Chromium, the scroll positions); the rest is\n * restored afterwards.\n */\nconst moveToEnd = (container: HTMLElement): void => {\n const body = document.body;\n const focused = document.activeElement;\n const scrolled = Array.from(container.querySelectorAll<HTMLElement>(\"*\"))\n .filter((element) => element.scrollTop !== 0 || element.scrollLeft !== 0)\n .map(\n (element) => [element, element.scrollTop, element.scrollLeft] as const,\n );\n\n if (typeof body.moveBefore === \"function\") {\n body.moveBefore(container, null);\n } else {\n body.append(container);\n }\n\n for (const [element, scrollTop, scrollLeft] of scrolled) {\n element.scrollTop = scrollTop;\n element.scrollLeft = scrollLeft;\n }\n if (\n focused instanceof HTMLElement &&\n container.contains(focused) &&\n document.activeElement !== focused\n ) {\n focused.focus({ preventScroll: true });\n }\n};\n\n/**\n * Render overlays into a dedicated container instead of `document.body`.\n *\n * React Aria `FocusScope` relies on nearby sibling nodes (sentinels + overlay).\n * Browser extensions (e.g. password managers) inject/reorder `body` children,\n * which can separate those nodes. Then focus scope detection breaks and may\n * cause recursive focus restoration.\n *\n * Keep the overlay container as the last child of body.\n *\n * React Aria normally portals overlays directly into body, so they are appended\n * last and render on top.\n *\n * If we create one persistent container only once, anything appended to body\n * later (toasts, third-party widgets, remounted app root) can render above open\n * overlays. Then overlays can appear behind page content, and the backdrop blur\n * no longer covers the page.\n */\nconst getOverlayContainer = (): HTMLElement | null => {\n if (typeof document === \"undefined\") {\n return null;\n }\n\n const existingContainer = document.querySelector<HTMLElement>(\n `body > [${overlayContainerAttribute}]`,\n );\n\n if (existingContainer) {\n if (isCoveredByPageContent(existingContainer)) {\n moveToEnd(existingContainer);\n }\n return existingContainer;\n }\n\n const container = document.createElement(\"div\");\n container.setAttribute(overlayContainerAttribute, \"\");\n document.body.append(container);\n\n return container;\n};\n\nexport interface OverlayContentProps\n extends\n PropsWithChildren,\n PropsWithClassName,\n Pick<Aria.DialogProps, \"aria-labelledby\"> {\n ref?: Ref<HTMLDivElement>;\n onOpenChange: (isOpen: boolean) => void;\n isDismissable?: boolean;\n isOpen?: boolean;\n}\n\n/** @flr-generate all */\nexport const OverlayContent: FC<OverlayContentProps> = (props) => {\n const {\n children,\n className,\n \"aria-labelledby\": ariaLabelledBy,\n ...restProps\n } = props;\n\n const isActivityActive = useIsActivityActive();\n\n useKeepBrowserExtensionsInteractive(restProps.isOpen ?? false);\n\n const Fallback = () => {\n return (\n <DivView className={styles.suspense}>\n <OverlaySuspenseFallback {...restProps} />\n </DivView>\n );\n };\n\n if (!isActivityActive) {\n return null;\n }\n\n return (\n <UNSAFE_PortalProvider getContainer={getOverlayContainer}>\n <Aria.ModalOverlay {...restProps} className={className}>\n {/* The centering viewport between the backdrop and react-aria's modal\n wrapper. Classed so Modal/OffCanvas/LightBox can position it\n without selecting a bare `div`. */}\n <DivView className={styles.viewport}>\n <Aria.Modal>\n <Suspense fallback={<Fallback />}>\n <Aria.Dialog aria-labelledby={ariaLabelledBy}>\n {children}\n </Aria.Dialog>\n </Suspense>\n </Aria.Modal>\n </DivView>\n </Aria.ModalOverlay>\n </UNSAFE_PortalProvider>\n );\n};\n\nexport default OverlayContent;\n"],"mappings":";;;;;;;;;;;;AAaA,IAAM,4BAA4B;;;;;;;;AASlC,IAAM,0BAA0B,cAAoC;CAClE,KACE,IAAI,OAAO,UAAU,oBACrB,MACA,OAAO,KAAK,oBAEZ,IAAI,CAAC,uBAAuB,IAAI,GAC9B,OAAO;CAGX,OAAO;AACT;;;;;;;;;AAUA,IAAM,aAAa,cAAiC;CAClD,MAAM,OAAO,SAAS;CACtB,MAAM,UAAU,SAAS;CACzB,MAAM,WAAW,MAAM,KAAK,UAAU,iBAA8B,GAAG,CAAC,CAAC,CACtE,QAAQ,YAAY,QAAQ,cAAc,KAAK,QAAQ,eAAe,CAAC,CAAC,CACxE,KACE,YAAY;EAAC;EAAS,QAAQ;EAAW,QAAQ;CAAU,CAC9D;CAEF,IAAI,OAAO,KAAK,eAAe,YAC7B,KAAK,WAAW,WAAW,IAAI;MAE/B,KAAK,OAAO,SAAS;CAGvB,KAAK,MAAM,CAAC,SAAS,WAAW,eAAe,UAAU;EACvD,QAAQ,YAAY;EACpB,QAAQ,aAAa;CACvB;CACA,IACE,mBAAmB,eACnB,UAAU,SAAS,OAAO,KAC1B,SAAS,kBAAkB,SAE3B,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;AAoBA,IAAM,4BAAgD;CACpD,IAAI,OAAO,aAAa,aACtB,OAAO;CAGT,MAAM,oBAAoB,SAAS,cACjC,WAAW,0BAA0B,EACvC;CAEA,IAAI,mBAAmB;EACrB,IAAI,uBAAuB,iBAAiB,GAC1C,UAAU,iBAAiB;EAE7B,OAAO;CACT;CAEA,MAAM,YAAY,SAAS,cAAc,KAAK;CAC9C,UAAU,aAAa,2BAA2B,EAAE;CACpD,SAAS,KAAK,OAAO,SAAS;CAE9B,OAAO;AACT;;AAcA,IAAa,kBAA2C,UAAU;CAChE,MAAM,EACJ,UACA,WACA,mBAAmB,gBACnB,GAAG,cACD;CAEJ,MAAM,mBAAmB,oBAAoB;CAE7C,oCAAoC,UAAU,UAAU,KAAK;CAE7D,MAAM,iBAAiB;EACrB,OACE,oBAAC,SAAD;GAAS,WAAW,uBAAO;GACzB,UAAA,oBAAC,yBAAD,EAAyB,GAAI,UAAY,CAAA;EAClC,CAAA;CAEb;CAEA,IAAI,CAAC,kBACH,OAAO;CAGT,OACE,oBAAC,uBAAD;EAAuB,cAAc;EACnC,UAAA,oBAAC,KAAK,cAAN;GAAmB,GAAI;GAAsB;GAI3C,UAAA,oBAAC,SAAD;IAAS,WAAW,uBAAO;IACzB,UAAA,oBAAC,KAAK,OAAN,EAAA,UACE,oBAAC,UAAD;KAAU,UAAU,oBAAC,UAAD,CAAW,CAAA;KAC7B,UAAA,oBAAC,KAAK,QAAN;MAAa,mBAAiB;MAC3B;KACU,CAAA;IACL,CAAA,EACA,CAAA;GACL,CAAA;EACQ,CAAA;CACE,CAAA;AAE3B"}
@@ -21,6 +21,7 @@ import { TogglePasswordVisibilityButton } from "./components/TogglePasswordVisib
21
21
  import { ValidationResultButton } from "./components/ValidationResultButton/ValidationResultButton.mjs";
22
22
  import { PasswordGenerateButton } from "./components/PasswordGenerateButton/PasswordGenerateButton.mjs";
23
23
  import { usePolicyValidationResult } from "./lib/usePolicyValidationResult.mjs";
24
+ import { useAriaAnnouncePasswordVisibility, useAriaAnnounceValidationState } from "./lib/ariaAnnounce.mjs";
24
25
  import clsx from "clsx";
25
26
  import { useMemo, useRef, useState } from "react";
26
27
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -29,7 +30,6 @@ import * as Aria from "react-aria-components";
29
30
  /** @flr-generate all */
30
31
  var PasswordCreationField = flowComponent("PasswordCreationField", (props) => {
31
32
  const { children, className, ref, isDisabled, onValidationResult, isInvalid: invalidFromProps, validationPolicy: validationPolicyFromProps = defaultPasswordCreationPolicy, isRequired, value, onChange, ...rest } = useControlledHostValueProps(props, "");
32
- const { FieldErrorView, FieldErrorCaptureContext, fieldProps, fieldPropsContext } = useFieldComponent(props, "PasswordCreationField");
33
33
  const translate = useLocalizedStringFormatter(__locale_default, "PasswordCreationField");
34
34
  const validationPolicy = useMemo(() => Policy.fromDeclaration(validationPolicyFromProps), [validationPolicyFromProps]);
35
35
  const [isPasswordRevealed, setIsPasswordRevealed] = useState(false);
@@ -77,6 +77,12 @@ var PasswordCreationField = flowComponent("PasswordCreationField", (props) => {
77
77
  const isValidFromValidationResult = !isEmptyValue && stateFromValidationResult?.isValid;
78
78
  const isInvalidFromValidationResult = !isEmptyValue && stateFromValidationResult?.isValid === false;
79
79
  const isInvalid = invalidFromProps || isInvalidFromValidationResult;
80
+ const { FieldErrorView, FieldErrorCaptureContext, fieldProps, fieldPropsContext } = useFieldComponent({
81
+ ...props,
82
+ isInvalid
83
+ }, "PasswordCreationField");
84
+ useAriaAnnounceValidationState(latestValidationErrorText, !isEmptyValue && policyValidationResult.isValid !== "indeterminate");
85
+ useAriaAnnouncePasswordVisibility(isPasswordRevealed);
80
86
  const setOptimisticPolicyValidationResult = (state = {}) => {
81
87
  setPolicyValidationResult((currentState) => ({
82
88
  ...currentState,
@@ -149,6 +155,7 @@ var PasswordCreationField = flowComponent("PasswordCreationField", (props) => {
149
155
  };
150
156
  return /* @__PURE__ */ jsxs(Aria.TextField, {
151
157
  ...rest,
158
+ ...fieldProps,
152
159
  value,
153
160
  type: isPasswordRevealed ? "text" : "password",
154
161
  onChange,
@@ -1 +1 @@
1
- {"version":3,"file":"PasswordCreationField.mjs","names":[],"sources":["../../../../../../../src/components/PasswordCreationField/PasswordCreationField.tsx"],"sourcesContent":["import {\n type PropsWithChildren,\n useState,\n type ClipboardEvent,\n useMemo,\n useRef,\n} from \"react\";\nimport {\n dynamic,\n type PropsContext,\n PropsContextProvider,\n} from \"@/lib/propsContext\";\nimport {\n flowComponent,\n type FlowComponentProps,\n} from \"@/lib/componentFactory/flowComponent\";\nimport styles from \"./PasswordCreationField.module.scss\";\nimport * as Aria from \"react-aria-components\";\nimport clsx from \"clsx\";\nimport { type ActionFn } from \"@/components/Action\";\nimport getStateFromLatestPolicyValidationResult from \"@/components/PasswordCreationField/lib/getStateFromLatestPolicyValidationResult\";\nimport locales from \"./locales/*.locale.json\";\nimport generateValidationTranslation from \"@/components/PasswordCreationField/lib/generateValidationTranslation\";\nimport FieldDescription from \"@/components/FieldDescription\";\nimport ComplexityIndicator from \"@/components/PasswordCreationField/components/ComplexityIndicator/ComplexityIndicator\";\nimport { generatePassword } from \"@/components/PasswordCreationField/worker/generatePassword\";\nimport TogglePasswordVisibilityButton from \"@/components/PasswordCreationField/components/TogglePasswordVisibilityButton/TogglePasswordVisibilityButton\";\nimport { ValidationResultButton } from \"@/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton\";\nimport { PasswordGenerateButton } from \"@/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton\";\nimport type {\n PolicyValidationResult,\n PolicyGenericDeclaration,\n RuleValidationResult,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport {\n defaultPasswordCreationPolicy,\n Policy,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport { usePolicyValidationResult } from \"@/components/PasswordCreationField/lib/usePolicyValidationResult\";\nimport { useFieldComponent } from \"@/lib/hooks/useFieldComponent\";\nimport { FieldError } from \"@/components/FieldError\";\nimport { useControlledHostValueProps } from \"@/lib/remote/useControlledHostValueProps\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\n\nexport interface PasswordCreationFieldProps\n extends\n PropsWithChildren<\n Omit<Aria.TextFieldProps, \"children\" | \"value\" | \"defaultValue\"> &\n Partial<Pick<Aria.FieldErrorRenderProps, \"validationErrors\">>\n >,\n FlowComponentProps<HTMLInputElement> {\n /** The password of a controlled field. */\n value?: string;\n /** Called with the password and its validity whenever the password changes. */\n onValidationResult?: (result: { password: string; isValid: boolean }) => void;\n /** The initial password of an uncontrolled field. */\n defaultValue?: string;\n /** The placeholder shown while the field is empty. */\n placeholder?: string;\n /**\n * The policy the password is validated against.\n *\n * @default defaultPasswordCreationPolicy\n */\n validationPolicy?: PolicyGenericDeclaration;\n}\n\nexport interface ResolvedPolicyValidationResult extends Omit<\n PolicyValidationResult,\n \"isValid\"\n> {\n isValid: boolean | \"indeterminate\";\n ruleResults: RuleValidationResult[];\n}\n\n/** @flr-generate all */\nexport const PasswordCreationField = flowComponent(\n \"PasswordCreationField\",\n (props) => {\n const {\n children,\n className,\n ref,\n isDisabled,\n onValidationResult,\n isInvalid: invalidFromProps,\n validationPolicy:\n validationPolicyFromProps = defaultPasswordCreationPolicy,\n isRequired,\n value,\n onChange,\n ...rest\n } = useControlledHostValueProps(props, \"\");\n\n const {\n FieldErrorView,\n FieldErrorCaptureContext,\n fieldProps,\n fieldPropsContext,\n } = useFieldComponent(props, \"PasswordCreationField\");\n\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n const validationPolicy = useMemo(\n () => Policy.fromDeclaration(validationPolicyFromProps),\n [validationPolicyFromProps],\n );\n\n const [isPasswordRevealed, setIsPasswordRevealed] = useState(false);\n const initialPolicyValidationState: ResolvedPolicyValidationResult = {\n isValid: \"indeterminate\",\n complexity: {\n min: validationPolicy.minComplexity,\n actual: 4,\n warning: null,\n },\n ruleResults: [],\n };\n\n const [policyValidationResult, setPolicyValidationResult] = useState(\n initialPolicyValidationState,\n );\n\n const loadingRef = useRef<ReturnType<typeof setTimeout>>(null);\n usePolicyValidationResult(\n validationPolicy,\n value ?? \"\",\n () => {\n if (isEmptyValue) {\n return;\n }\n\n loadingRef.current = setTimeout(() => {\n setPolicyValidationResult((r) => ({\n ...r,\n isValid: \"indeterminate\",\n }));\n }, 150);\n },\n ({ password, isValid, results }) => {\n if (loadingRef.current) {\n clearTimeout(loadingRef.current);\n }\n\n if (isEmptyValue) {\n setPolicyValidationResult(() => ({\n ...results,\n isValid: true,\n }));\n return;\n }\n\n setPolicyValidationResult(() => results);\n onValidationResult?.({ password, isValid });\n },\n );\n\n const isEmptyValue = !value;\n const stateFromValidationResult = getStateFromLatestPolicyValidationResult(\n isEmptyValue,\n policyValidationResult,\n );\n let latestValidationErrorText = undefined;\n if (stateFromValidationResult) {\n const [translationKey, translationValues] = generateValidationTranslation(\n stateFromValidationResult,\n );\n latestValidationErrorText = translate.format(\n translationKey,\n translationValues,\n );\n }\n\n const isValidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid;\n\n const isInvalidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid === false;\n const isInvalid = invalidFromProps || isInvalidFromValidationResult;\n\n const setOptimisticPolicyValidationResult = (\n state: Partial<ResolvedPolicyValidationResult> = {},\n ) => {\n setPolicyValidationResult((currentState) => ({\n ...currentState,\n isValid: true,\n ...state,\n }));\n };\n\n const onPasswordGenerateHandler: ActionFn = async () => {\n const generatedPassword = await generatePassword(validationPolicy);\n setOptimisticPolicyValidationResult({\n ...initialPolicyValidationState,\n isValid: true,\n ruleResults: policyValidationResult.ruleResults.map((r) => ({\n ...r,\n isValid: true,\n })),\n });\n setIsPasswordRevealed(true);\n onChange(generatedPassword);\n };\n\n const onPasswordPasteHandler = (event: ClipboardEvent) => {\n const pastedValue = event.clipboardData?.getData(\"text\");\n if (typeof pastedValue === \"string\" && pastedValue !== value) {\n setOptimisticPolicyValidationResult({\n isValid: \"indeterminate\",\n });\n }\n };\n\n const togglePasswordVisibilityHandler = () => {\n setIsPasswordRevealed((old) => !old);\n };\n\n const propsContext: PropsContext = {\n ...fieldPropsContext,\n Button: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n },\n CopyButton: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n text: value,\n },\n Label: {\n ...fieldPropsContext.Label,\n children: dynamic((localProps) => {\n return (\n <>\n {localProps.children}\n <PasswordGenerateButton\n isDisabled={isDisabled}\n onGeneratePasswordAction={onPasswordGenerateHandler}\n />\n <ValidationResultButton\n isEmptyValue={isEmptyValue}\n isDisabled={isDisabled}\n policyValidationResult={policyValidationResult}\n />\n </>\n );\n }),\n },\n };\n\n return (\n <Aria.TextField\n {...rest}\n value={value}\n type={isPasswordRevealed ? \"text\" : \"password\"}\n onChange={onChange}\n onPaste={onPasswordPasteHandler}\n className={clsx(className, fieldProps.className)}\n isDisabled={isDisabled}\n isInvalid={isInvalid}\n isRequired={isRequired}\n >\n <FieldErrorCaptureContext>\n {latestValidationErrorText && (\n <FieldError>{latestValidationErrorText}</FieldError>\n )}\n <PropsContextProvider\n props={propsContext}\n dependencies={[\n isDisabled,\n isRequired,\n value,\n policyValidationResult,\n isEmptyValue,\n ]}\n >\n {children}\n <Aria.Group\n isDisabled={isDisabled}\n className={clsx(styles.inputGroup)}\n >\n <Aria.Input ref={ref} className={styles.input} />\n <Aria.Group className={styles.buttonContainer}>\n <TogglePasswordVisibilityButton\n className={styles.button}\n isVisible={isPasswordRevealed}\n isDisabled={isDisabled}\n onPress={togglePasswordVisibilityHandler}\n />\n <UiComponentTunnelExit\n id=\"button\"\n component=\"PasswordCreationField\"\n />\n </Aria.Group>\n <ComplexityIndicator\n key={value}\n isEmptyValue={isEmptyValue}\n isLoading={policyValidationResult.isValid === \"indeterminate\"}\n policyValidationResult={policyValidationResult}\n validationResultState={stateFromValidationResult}\n />\n </Aria.Group>\n {isValidFromValidationResult && (\n <FieldDescription>{latestValidationErrorText}</FieldDescription>\n )}\n </PropsContextProvider>\n </FieldErrorCaptureContext>\n <FieldErrorView />\n </Aria.TextField>\n );\n },\n);\n\nexport default PasswordCreationField;\n"],"mappings":"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"}
1
+ {"version":3,"file":"PasswordCreationField.mjs","names":[],"sources":["../../../../../../../src/components/PasswordCreationField/PasswordCreationField.tsx"],"sourcesContent":["import {\n type PropsWithChildren,\n useState,\n type ClipboardEvent,\n useMemo,\n useRef,\n} from \"react\";\nimport {\n dynamic,\n type PropsContext,\n PropsContextProvider,\n} from \"@/lib/propsContext\";\nimport {\n flowComponent,\n type FlowComponentProps,\n} from \"@/lib/componentFactory/flowComponent\";\nimport styles from \"./PasswordCreationField.module.scss\";\nimport * as Aria from \"react-aria-components\";\nimport clsx from \"clsx\";\nimport { type ActionFn } from \"@/components/Action\";\nimport getStateFromLatestPolicyValidationResult from \"@/components/PasswordCreationField/lib/getStateFromLatestPolicyValidationResult\";\nimport locales from \"./locales/*.locale.json\";\nimport generateValidationTranslation from \"@/components/PasswordCreationField/lib/generateValidationTranslation\";\nimport FieldDescription from \"@/components/FieldDescription\";\nimport ComplexityIndicator from \"@/components/PasswordCreationField/components/ComplexityIndicator/ComplexityIndicator\";\nimport { generatePassword } from \"@/components/PasswordCreationField/worker/generatePassword\";\nimport TogglePasswordVisibilityButton from \"@/components/PasswordCreationField/components/TogglePasswordVisibilityButton/TogglePasswordVisibilityButton\";\nimport { ValidationResultButton } from \"@/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton\";\nimport { PasswordGenerateButton } from \"@/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton\";\nimport type {\n PolicyValidationResult,\n PolicyGenericDeclaration,\n RuleValidationResult,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport {\n defaultPasswordCreationPolicy,\n Policy,\n} from \"@/integrations/@mittwald/password-tools-js\";\nimport { usePolicyValidationResult } from \"@/components/PasswordCreationField/lib/usePolicyValidationResult\";\nimport { useFieldComponent } from \"@/lib/hooks/useFieldComponent\";\nimport { FieldError } from \"@/components/FieldError\";\nimport { useControlledHostValueProps } from \"@/lib/remote/useControlledHostValueProps\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\nimport {\n useAriaAnnouncePasswordVisibility,\n useAriaAnnounceValidationState,\n} from \"@/components/PasswordCreationField/lib/ariaAnnounce\";\n\nexport interface PasswordCreationFieldProps\n extends\n PropsWithChildren<\n Omit<Aria.TextFieldProps, \"children\" | \"value\" | \"defaultValue\"> &\n Partial<Pick<Aria.FieldErrorRenderProps, \"validationErrors\">>\n >,\n FlowComponentProps<HTMLInputElement> {\n /** The password of a controlled field. */\n value?: string;\n /** Called with the password and its validity whenever the password changes. */\n onValidationResult?: (result: { password: string; isValid: boolean }) => void;\n /** The initial password of an uncontrolled field. */\n defaultValue?: string;\n /** The placeholder shown while the field is empty. */\n placeholder?: string;\n /**\n * The policy the password is validated against.\n *\n * @default defaultPasswordCreationPolicy\n */\n validationPolicy?: PolicyGenericDeclaration;\n}\n\nexport interface ResolvedPolicyValidationResult extends Omit<\n PolicyValidationResult,\n \"isValid\"\n> {\n isValid: boolean | \"indeterminate\";\n ruleResults: RuleValidationResult[];\n}\n\n/** @flr-generate all */\nexport const PasswordCreationField = flowComponent(\n \"PasswordCreationField\",\n (props) => {\n const {\n children,\n className,\n ref,\n isDisabled,\n onValidationResult,\n isInvalid: invalidFromProps,\n validationPolicy:\n validationPolicyFromProps = defaultPasswordCreationPolicy,\n isRequired,\n value,\n onChange,\n ...rest\n } = useControlledHostValueProps(props, \"\");\n\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n const validationPolicy = useMemo(\n () => Policy.fromDeclaration(validationPolicyFromProps),\n [validationPolicyFromProps],\n );\n\n const [isPasswordRevealed, setIsPasswordRevealed] = useState(false);\n const initialPolicyValidationState: ResolvedPolicyValidationResult = {\n isValid: \"indeterminate\",\n complexity: {\n min: validationPolicy.minComplexity,\n actual: 4,\n warning: null,\n },\n ruleResults: [],\n };\n\n const [policyValidationResult, setPolicyValidationResult] = useState(\n initialPolicyValidationState,\n );\n\n const loadingRef = useRef<ReturnType<typeof setTimeout>>(null);\n usePolicyValidationResult(\n validationPolicy,\n value ?? \"\",\n () => {\n if (isEmptyValue) {\n return;\n }\n\n loadingRef.current = setTimeout(() => {\n setPolicyValidationResult((r) => ({\n ...r,\n isValid: \"indeterminate\",\n }));\n }, 150);\n },\n ({ password, isValid, results }) => {\n if (loadingRef.current) {\n clearTimeout(loadingRef.current);\n }\n\n if (isEmptyValue) {\n setPolicyValidationResult(() => ({\n ...results,\n isValid: true,\n }));\n return;\n }\n\n setPolicyValidationResult(() => results);\n onValidationResult?.({ password, isValid });\n },\n );\n\n const isEmptyValue = !value;\n const stateFromValidationResult = getStateFromLatestPolicyValidationResult(\n isEmptyValue,\n policyValidationResult,\n );\n let latestValidationErrorText = undefined;\n if (stateFromValidationResult) {\n const [translationKey, translationValues] = generateValidationTranslation(\n stateFromValidationResult,\n );\n latestValidationErrorText = translate.format(\n translationKey,\n translationValues,\n );\n }\n\n const isValidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid;\n\n const isInvalidFromValidationResult =\n !isEmptyValue && stateFromValidationResult?.isValid === false;\n const isInvalid = invalidFromProps || isInvalidFromValidationResult;\n\n // The field derives its invalid state from the policy validation, so the\n // hook has to see that state — not just the `isInvalid` prop — to describe\n // the input with the field error.\n const {\n FieldErrorView,\n FieldErrorCaptureContext,\n fieldProps,\n fieldPropsContext,\n } = useFieldComponent({ ...props, isInvalid }, \"PasswordCreationField\");\n\n useAriaAnnounceValidationState(\n latestValidationErrorText,\n !isEmptyValue && policyValidationResult.isValid !== \"indeterminate\",\n );\n useAriaAnnouncePasswordVisibility(isPasswordRevealed);\n\n const setOptimisticPolicyValidationResult = (\n state: Partial<ResolvedPolicyValidationResult> = {},\n ) => {\n setPolicyValidationResult((currentState) => ({\n ...currentState,\n isValid: true,\n ...state,\n }));\n };\n\n const onPasswordGenerateHandler: ActionFn = async () => {\n const generatedPassword = await generatePassword(validationPolicy);\n setOptimisticPolicyValidationResult({\n ...initialPolicyValidationState,\n isValid: true,\n ruleResults: policyValidationResult.ruleResults.map((r) => ({\n ...r,\n isValid: true,\n })),\n });\n setIsPasswordRevealed(true);\n onChange(generatedPassword);\n };\n\n const onPasswordPasteHandler = (event: ClipboardEvent) => {\n const pastedValue = event.clipboardData?.getData(\"text\");\n if (typeof pastedValue === \"string\" && pastedValue !== value) {\n setOptimisticPolicyValidationResult({\n isValid: \"indeterminate\",\n });\n }\n };\n\n const togglePasswordVisibilityHandler = () => {\n setIsPasswordRevealed((old) => !old);\n };\n\n const propsContext: PropsContext = {\n ...fieldPropsContext,\n Button: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n },\n CopyButton: {\n tunnel: {\n id: \"button\",\n component: \"PasswordCreationField\",\n },\n size: \"m\",\n variant: \"plain\",\n color: \"secondary\",\n isDisabled: isDisabled,\n className: styles.button,\n text: value,\n },\n Label: {\n ...fieldPropsContext.Label,\n children: dynamic((localProps) => {\n return (\n <>\n {localProps.children}\n <PasswordGenerateButton\n isDisabled={isDisabled}\n onGeneratePasswordAction={onPasswordGenerateHandler}\n />\n <ValidationResultButton\n isEmptyValue={isEmptyValue}\n isDisabled={isDisabled}\n policyValidationResult={policyValidationResult}\n />\n </>\n );\n }),\n },\n };\n\n return (\n <Aria.TextField\n {...rest}\n {...fieldProps}\n value={value}\n type={isPasswordRevealed ? \"text\" : \"password\"}\n onChange={onChange}\n onPaste={onPasswordPasteHandler}\n className={clsx(className, fieldProps.className)}\n isDisabled={isDisabled}\n isInvalid={isInvalid}\n isRequired={isRequired}\n >\n <FieldErrorCaptureContext>\n {latestValidationErrorText && (\n <FieldError>{latestValidationErrorText}</FieldError>\n )}\n <PropsContextProvider\n props={propsContext}\n dependencies={[\n isDisabled,\n isRequired,\n value,\n policyValidationResult,\n isEmptyValue,\n ]}\n >\n {children}\n <Aria.Group\n isDisabled={isDisabled}\n className={clsx(styles.inputGroup)}\n >\n <Aria.Input ref={ref} className={styles.input} />\n <Aria.Group className={styles.buttonContainer}>\n <TogglePasswordVisibilityButton\n className={styles.button}\n isVisible={isPasswordRevealed}\n isDisabled={isDisabled}\n onPress={togglePasswordVisibilityHandler}\n />\n <UiComponentTunnelExit\n id=\"button\"\n component=\"PasswordCreationField\"\n />\n </Aria.Group>\n <ComplexityIndicator\n key={value}\n isEmptyValue={isEmptyValue}\n isLoading={policyValidationResult.isValid === \"indeterminate\"}\n policyValidationResult={policyValidationResult}\n validationResultState={stateFromValidationResult}\n />\n </Aria.Group>\n {isValidFromValidationResult && (\n <FieldDescription>{latestValidationErrorText}</FieldDescription>\n )}\n </PropsContextProvider>\n </FieldErrorCaptureContext>\n <FieldErrorView />\n </Aria.TextField>\n );\n },\n);\n\nexport default PasswordCreationField;\n"],"mappings":"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"}
@@ -7,6 +7,7 @@ import __locale_default from "../../../../../../../_virtual/_28.locale.mjs";
7
7
  import "react";
8
8
  import { jsx } from "react/jsx-runtime";
9
9
  //#region src/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.tsx
10
+ /** @internal */
10
11
  var PasswordGenerateButton = (props) => {
11
12
  const { isDisabled, onGeneratePasswordAction } = props;
12
13
  const translate = useLocalizedStringFormatter(__locale_default, "PasswordCreationField");
@@ -18,6 +19,7 @@ var PasswordGenerateButton = (props) => {
18
19
  isDisabled,
19
20
  variant: "plain",
20
21
  color: "secondary",
22
+ "aria-label": translate.format("button.generate.label"),
21
23
  children: translate.format("button.generate")
22
24
  })
23
25
  });
@@ -1 +1 @@
1
- {"version":3,"file":"PasswordGenerateButton.mjs","names":[],"sources":["../../../../../../../../../src/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.tsx"],"sourcesContent":["import { type FC } from \"react\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\nimport locales from \"./../../locales/*.locale.json\";\nimport { Button } from \"@/components/Button\";\nimport { Action, type ActionFn } from \"@/components/Action\";\n\ninterface Props {\n isDisabled?: boolean;\n onGeneratePasswordAction?: ActionFn;\n}\n\nexport const PasswordGenerateButton: FC<Props> = (props) => {\n const { isDisabled, onGeneratePasswordAction } = props;\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n return (\n <Action onAction={onGeneratePasswordAction} showFeedback={false}>\n <Button\n data-component=\"generatePassword\"\n isDisabled={isDisabled}\n variant=\"plain\"\n color=\"secondary\"\n >\n {translate.format(\"button.generate\")}\n </Button>\n </Action>\n );\n};\n"],"mappings":";;;;;;;;;AAWA,IAAa,0BAAqC,UAAU;CAC1D,MAAM,EAAE,YAAY,6BAA6B;CACjD,MAAM,YAAY,4BAChB,kBACA,uBACF;CAEA,OACE,oBAAC,QAAD;EAAQ,UAAU;EAA0B,cAAc;EACxD,UAAA,oBAAC,QAAD;GACE,kBAAe;GACH;GACZ,SAAQ;GACR,OAAM;GAEL,UAAA,UAAU,OAAO,iBAAiB;EAC7B,CAAA;CACF,CAAA;AAEZ"}
1
+ {"version":3,"file":"PasswordGenerateButton.mjs","names":[],"sources":["../../../../../../../../../src/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.tsx"],"sourcesContent":["import { type FC } from \"react\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\nimport locales from \"./../../locales/*.locale.json\";\nimport { Button } from \"@/components/Button\";\nimport { Action, type ActionFn } from \"@/components/Action\";\n\ninterface Props {\n isDisabled?: boolean;\n onGeneratePasswordAction?: ActionFn;\n}\n\n/** @internal */\nexport const PasswordGenerateButton: FC<Props> = (props) => {\n const { isDisabled, onGeneratePasswordAction } = props;\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n return (\n <Action onAction={onGeneratePasswordAction} showFeedback={false}>\n <Button\n data-component=\"generatePassword\"\n isDisabled={isDisabled}\n variant=\"plain\"\n color=\"secondary\"\n // The button sits in the label, where \"Generate\" alone does not say\n // what is generated.\n aria-label={translate.format(\"button.generate.label\")}\n >\n {translate.format(\"button.generate\")}\n </Button>\n </Action>\n );\n};\n"],"mappings":";;;;;;;;;;AAYA,IAAa,0BAAqC,UAAU;CAC1D,MAAM,EAAE,YAAY,6BAA6B;CACjD,MAAM,YAAY,4BAChB,kBACA,uBACF;CAEA,OACE,oBAAC,QAAD;EAAQ,UAAU;EAA0B,cAAc;EACxD,UAAA,oBAAC,QAAD;GACE,kBAAe;GACH;GACZ,SAAQ;GACR,OAAM;GAGN,cAAY,UAAU,OAAO,uBAAuB;GAEnD,UAAA,UAAU,OAAO,iBAAiB;EAC7B,CAAA;CACF,CAAA;AAEZ"}
@@ -7,12 +7,15 @@ import { Heading } from "../../../Heading/Heading.mjs";
7
7
  import { ContextualHelp } from "../../../ContextualHelp/ContextualHelp.mjs";
8
8
  import __locale_default from "../../../../../../../_virtual/_28.locale.mjs";
9
9
  import { ValidationResultEntry } from "../ValidationResultEntry/ValidationResultEntry.mjs";
10
- import "react";
10
+ import ValidationResultButton_module_default from "./ValidationResultButton.module.mjs";
11
+ import { useId } from "react";
11
12
  import { jsx, jsxs } from "react/jsx-runtime";
12
13
  //#region src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.tsx
14
+ /** @internal */
13
15
  var ValidationResultButton = (props) => {
14
16
  const { policyValidationResult, isDisabled, isEmptyValue, className } = props;
15
17
  const translate = useLocalizedStringFormatter(__locale_default, "PasswordCreationField");
18
+ const headingId = useId();
16
19
  let validationResults = policyValidationResult?.ruleResults?.filter((r) => {
17
20
  return isEmptyValue ? !r.isValid : true;
18
21
  });
@@ -26,14 +29,17 @@ var ValidationResultButton = (props) => {
26
29
  "data-component": "showPasswordRules",
27
30
  isDisabled,
28
31
  className
29
- }), /* @__PURE__ */ jsxs(ContextualHelp, { children: [
30
- /* @__PURE__ */ jsx(Heading, { children: translate.format("password.requirements.heading") }),
31
- validationResultComponents.length === 0 && /* @__PURE__ */ jsx(ValidationResultEntry, {
32
+ }), /* @__PURE__ */ jsxs(ContextualHelp, { children: [/* @__PURE__ */ jsx(Heading, {
33
+ id: headingId,
34
+ children: translate.format("password.requirements.heading")
35
+ }), /* @__PURE__ */ jsx("ul", {
36
+ "aria-labelledby": headingId,
37
+ className: ValidationResultButton_module_default.validationResultList,
38
+ children: validationResultComponents.length === 0 ? /* @__PURE__ */ jsx(ValidationResultEntry, {
32
39
  result: { isValid: true },
33
40
  unspecifiedRules: true
34
- }),
35
- validationResultComponents
36
- ] })]
41
+ }) : validationResultComponents
42
+ })] })]
37
43
  });
38
44
  };
39
45
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"ValidationResultButton.mjs","names":[],"sources":["../../../../../../../../../src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.tsx"],"sourcesContent":["import { type FC } from \"react\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\n\nimport locales from \"./../../locales/*.locale.json\";\nimport type { ResolvedPolicyValidationResult } from \"@/components/PasswordCreationField/PasswordCreationField\";\nimport ValidationResultEntry from \"@/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry\";\nimport { Button } from \"@/components/Button\";\nimport {\n ContextualHelp,\n ContextualHelpTrigger,\n} from \"@/components/ContextualHelp\";\nimport { Heading } from \"@/components/Heading\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\n\ninterface Props extends PropsWithClassName {\n policyValidationResult?: ResolvedPolicyValidationResult;\n isDisabled?: boolean;\n isEmptyValue: boolean;\n}\n\nexport const ValidationResultButton: FC<Props> = (props) => {\n const { policyValidationResult, isDisabled, isEmptyValue, className } = props;\n\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n let validationResults = policyValidationResult?.ruleResults?.filter((r) => {\n return isEmptyValue ? !r.isValid : true;\n });\n if (validationResults && validationResults.length === 0 && isEmptyValue) {\n // if we have no rules to show on first info - just list them all\n validationResults = policyValidationResult?.ruleResults;\n }\n\n const validationResultComponents =\n validationResults?.map((result, index) => {\n return (\n <ValidationResultEntry\n key={`${result.identifier}-${index}`}\n result={result}\n />\n );\n }) ?? [];\n\n return (\n <ContextualHelpTrigger\n subject={translate.format(\"password.requirements.subject\")}\n >\n <Button\n data-component=\"showPasswordRules\"\n isDisabled={isDisabled}\n className={className}\n />\n <ContextualHelp>\n <Heading>{translate.format(\"password.requirements.heading\")}</Heading>\n {validationResultComponents.length === 0 && (\n <ValidationResultEntry result={{ isValid: true }} unspecifiedRules />\n )}\n {validationResultComponents}\n </ContextualHelp>\n </ContextualHelpTrigger>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAoBA,IAAa,0BAAqC,UAAU;CAC1D,MAAM,EAAE,wBAAwB,YAAY,cAAc,cAAc;CAExE,MAAM,YAAY,4BAChB,kBACA,uBACF;CAEA,IAAI,oBAAoB,wBAAwB,aAAa,QAAQ,MAAM;EACzE,OAAO,eAAe,CAAC,EAAE,UAAU;CACrC,CAAC;CACD,IAAI,qBAAqB,kBAAkB,WAAW,KAAK,cAEzD,oBAAoB,wBAAwB;CAG9C,MAAM,6BACJ,mBAAmB,KAAK,QAAQ,UAAU;EACxC,OACE,oBAAC,uBAAD,EAEU,OACT,GAFM,GAAG,OAAO,WAAW,GAAG,OAE9B;CAEL,CAAC,KAAK,CAAC;CAET,OACE,qBAAC,uBAAD;EACE,SAAS,UAAU,OAAO,+BAA+B;EAD3D,UAAA,CAGE,oBAAC,QAAD;GACE,kBAAe;GACH;GACD;EACZ,CAAA,GACD,qBAAC,gBAAD,EAAA,UAAA;GACE,oBAAC,SAAD,EAAA,UAAU,UAAU,OAAO,+BAA+B,EAAW,CAAA;GACpE,2BAA2B,WAAW,KACrC,oBAAC,uBAAD;IAAuB,QAAQ,EAAE,SAAS,KAAK;IAAG,kBAAA;GAAkB,CAAA;GAErE;EACa,EAAA,CAAA,CACK;;AAE3B"}
1
+ {"version":3,"file":"ValidationResultButton.mjs","names":[],"sources":["../../../../../../../../../src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.tsx"],"sourcesContent":["import { type FC, useId } from \"react\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\n\nimport locales from \"./../../locales/*.locale.json\";\nimport type { ResolvedPolicyValidationResult } from \"@/components/PasswordCreationField/PasswordCreationField\";\nimport ValidationResultEntry from \"@/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry\";\nimport { Button } from \"@/components/Button\";\nimport {\n ContextualHelp,\n ContextualHelpTrigger,\n} from \"@/components/ContextualHelp\";\nimport { Heading } from \"@/components/Heading\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport styles from \"./ValidationResultButton.module.scss\";\n\ninterface Props extends PropsWithClassName {\n policyValidationResult?: ResolvedPolicyValidationResult;\n isDisabled?: boolean;\n isEmptyValue: boolean;\n}\n\n/** @internal */\nexport const ValidationResultButton: FC<Props> = (props) => {\n const { policyValidationResult, isDisabled, isEmptyValue, className } = props;\n\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n const headingId = useId();\n\n let validationResults = policyValidationResult?.ruleResults?.filter((r) => {\n return isEmptyValue ? !r.isValid : true;\n });\n if (validationResults && validationResults.length === 0 && isEmptyValue) {\n // if we have no rules to show on first info - just list them all\n validationResults = policyValidationResult?.ruleResults;\n }\n\n const validationResultComponents =\n validationResults?.map((result, index) => {\n return (\n <ValidationResultEntry\n key={`${result.identifier}-${index}`}\n result={result}\n />\n );\n }) ?? [];\n\n return (\n <ContextualHelpTrigger\n subject={translate.format(\"password.requirements.subject\")}\n >\n <Button\n data-component=\"showPasswordRules\"\n isDisabled={isDisabled}\n className={className}\n />\n <ContextualHelp>\n <Heading id={headingId}>\n {translate.format(\"password.requirements.heading\")}\n </Heading>\n {/* A list, so a screen reader announces the requirements as one unit\n with their number — and each rule with its status. */}\n <ul aria-labelledby={headingId} className={styles.validationResultList}>\n {validationResultComponents.length === 0 ? (\n <ValidationResultEntry\n result={{ isValid: true }}\n unspecifiedRules\n />\n ) : (\n validationResultComponents\n )}\n </ul>\n </ContextualHelp>\n </ContextualHelpTrigger>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AAsBA,IAAa,0BAAqC,UAAU;CAC1D,MAAM,EAAE,wBAAwB,YAAY,cAAc,cAAc;CAExE,MAAM,YAAY,4BAChB,kBACA,uBACF;CACA,MAAM,YAAY,MAAM;CAExB,IAAI,oBAAoB,wBAAwB,aAAa,QAAQ,MAAM;EACzE,OAAO,eAAe,CAAC,EAAE,UAAU;CACrC,CAAC;CACD,IAAI,qBAAqB,kBAAkB,WAAW,KAAK,cAEzD,oBAAoB,wBAAwB;CAG9C,MAAM,6BACJ,mBAAmB,KAAK,QAAQ,UAAU;EACxC,OACE,oBAAC,uBAAD,EAEU,OACT,GAFM,GAAG,OAAO,WAAW,GAAG,OAE9B;CAEL,CAAC,KAAK,CAAC;CAET,OACE,qBAAC,uBAAD;EACE,SAAS,UAAU,OAAO,+BAA+B;EAD3D,UAAA,CAGE,oBAAC,QAAD;GACE,kBAAe;GACH;GACD;EACZ,CAAA,GACD,qBAAC,gBAAD,EAAA,UAAA,CACE,oBAAC,SAAD;GAAS,IAAI;GACV,UAAA,UAAU,OAAO,+BAA+B;EAC1C,CAAA,GAGT,oBAAC,MAAD;GAAI,mBAAiB;GAAW,WAAW,sCAAO;GAC/C,UAAA,2BAA2B,WAAW,IACrC,oBAAC,uBAAD;IACE,QAAQ,EAAE,SAAS,KAAK;IACxB,kBAAA;GACD,CAAA,IAED;EAEA,CAAA,CACU,EAAA,CAAA,CACK;;AAE3B"}
@@ -0,0 +1,9 @@
1
+ "use client"
2
+ /* */
3
+ //#region src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.module.scss
4
+ var validationResultList = "flow--password-creation-field--validation-result-button--validation-result-list";
5
+ var ValidationResultButton_module_default = { validationResultList };
6
+ //#endregion
7
+ export { ValidationResultButton_module_default as default, validationResultList };
8
+
9
+ //# sourceMappingURL=ValidationResultButton.module.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValidationResultButton.module.mjs","names":[],"sources":["../../../../../../../../../src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.module.scss"],"sourcesContent":[".validationResultList {\n display: flex;\n flex-direction: column;\n row-gap: var(--password-creation-field--rule-to-rule-spacing);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n"],"mappings":""}
@@ -7,23 +7,31 @@ import __locale_default from "../../../../../../../_virtual/_28.locale.mjs";
7
7
  import generateValidationTranslation from "../../lib/generateValidationTranslation.mjs";
8
8
  import ValidationResultEntry_module_default from "./ValidationResultEntry.module.mjs";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
+ import * as Aria from "react-aria-components";
10
11
  //#region src/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry.tsx
11
12
  /** @internal */
12
13
  var ValidationResultEntry = (props) => {
13
14
  const { result, unspecifiedRules = false } = props;
14
15
  const translate = useLocalizedStringFormatter(__locale_default, "PasswordCreationField");
15
- const icon = /* @__PURE__ */ jsx(AlertIcon, { status: result.isValid ? "success" : "warning" });
16
+ const icon = /* @__PURE__ */ jsx(AlertIcon, {
17
+ status: result.isValid ? "success" : "warning",
18
+ "aria-label": void 0
19
+ });
20
+ const status = translate.format(result.isValid ? "password.requirements.rule.fulfilled" : "password.requirements.rule.unfulfilled");
16
21
  let [translationKey, translationValues] = generateValidationTranslation(result, true);
17
22
  if (unspecifiedRules) {
18
23
  translationKey = `${translationKey}.unspecified`;
19
24
  translationValues = {};
20
25
  }
21
- return /* @__PURE__ */ jsxs(Text, {
26
+ return /* @__PURE__ */ jsxs("li", {
22
27
  className: ValidationResultEntry_module_default.validationResultEntry,
23
28
  "data-rule": result.ruleType,
24
29
  "data-rule-valid": result.isValid,
25
- children: [icon, translate.format(translationKey, translationValues)]
26
- }, translationKey);
30
+ children: [icon, /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Aria.VisuallyHidden, {
31
+ elementType: "span",
32
+ children: `${status}: `
33
+ }), translate.format(translationKey, translationValues)] })]
34
+ });
27
35
  };
28
36
  //#endregion
29
37
  export { ValidationResultEntry, ValidationResultEntry as default };
@@ -1 +1 @@
1
- {"version":3,"file":"ValidationResultEntry.mjs","names":[],"sources":["../../../../../../../../../src/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { Text } from \"@/components/Text\";\nimport generateValidationTranslation from \"@/components/PasswordCreationField/lib/generateValidationTranslation\";\nimport locales from \"./../../locales/*.locale.json\";\nimport styles from \"./ValidationResultEntry.module.scss\";\nimport type { RuleValidationResult } from \"@/integrations/@mittwald/password-tools-js\";\nimport AlertIcon from \"@/components/AlertIcon\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\n\ninterface Props {\n result: Partial<RuleValidationResult>;\n unspecifiedRules?: boolean;\n}\n\n/** @internal */\nexport const ValidationResultEntry: FC<Props> = (props) => {\n const { result, unspecifiedRules = false } = props;\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n const icon = <AlertIcon status={result.isValid ? \"success\" : \"warning\"} />;\n\n let [translationKey, translationValues] = generateValidationTranslation(\n result,\n true,\n );\n\n if (unspecifiedRules) {\n translationKey = `${translationKey}.unspecified`;\n translationValues = {};\n }\n\n return (\n <Text\n className={styles.validationResultEntry}\n data-rule={result.ruleType}\n data-rule-valid={result.isValid}\n key={translationKey}\n >\n {icon}\n {translate.format(translationKey, translationValues)}\n </Text>\n );\n};\n\nexport default ValidationResultEntry;\n"],"mappings":";;;;;;;;;;;AAeA,IAAa,yBAAoC,UAAU;CACzD,MAAM,EAAE,QAAQ,mBAAmB,UAAU;CAC7C,MAAM,YAAY,4BAChB,kBACA,uBACF;CAEA,MAAM,OAAO,oBAAC,WAAD,EAAW,QAAQ,OAAO,UAAU,YAAY,UAAY,CAAA;CAEzE,IAAI,CAAC,gBAAgB,qBAAqB,8BACxC,QACA,IACF;CAEA,IAAI,kBAAkB;EACpB,iBAAiB,GAAG,eAAe;EACnC,oBAAoB,CAAC;CACvB;CAEA,OACE,qBAAC,MAAD;EACE,WAAW,qCAAO;EAClB,aAAW,OAAO;EAClB,mBAAiB,OAAO;EAH1B,UAAA,CAMG,MACA,UAAU,OAAO,gBAAgB,iBAAiB,CAC/C;CAJC,GAAA,cAID;AAEV"}
1
+ {"version":3,"file":"ValidationResultEntry.mjs","names":[],"sources":["../../../../../../../../../src/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport * as Aria from \"react-aria-components\";\nimport { Text } from \"@/components/Text\";\nimport generateValidationTranslation from \"@/components/PasswordCreationField/lib/generateValidationTranslation\";\nimport locales from \"./../../locales/*.locale.json\";\nimport styles from \"./ValidationResultEntry.module.scss\";\nimport type { RuleValidationResult } from \"@/integrations/@mittwald/password-tools-js\";\nimport AlertIcon from \"@/components/AlertIcon\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\n\ninterface Props {\n result: Partial<RuleValidationResult>;\n unspecifiedRules?: boolean;\n}\n\n/** @internal */\nexport const ValidationResultEntry: FC<Props> = (props) => {\n const { result, unspecifiedRules = false } = props;\n const translate = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n\n const icon = (\n <AlertIcon\n status={result.isValid ? \"success\" : \"warning\"}\n // The status is spelled out as text right next to it, so the icon is\n // decorative. Overriding `AlertIcon`'s generic \"Status …\" label with\n // `undefined` is what makes `Icon` hide it from the accessibility tree.\n aria-label={undefined}\n />\n );\n\n const status = translate.format(\n result.isValid\n ? \"password.requirements.rule.fulfilled\"\n : \"password.requirements.rule.unfulfilled\",\n );\n\n let [translationKey, translationValues] = generateValidationTranslation(\n result,\n true,\n );\n\n if (unspecifiedRules) {\n translationKey = `${translationKey}.unspecified`;\n translationValues = {};\n }\n\n return (\n <li\n className={styles.validationResultEntry}\n data-rule={result.ruleType}\n data-rule-valid={result.isValid}\n >\n {icon}\n <Text>\n <Aria.VisuallyHidden elementType=\"span\">{`${status}: `}</Aria.VisuallyHidden>\n {translate.format(translationKey, translationValues)}\n </Text>\n </li>\n );\n};\n\nexport default ValidationResultEntry;\n"],"mappings":";;;;;;;;;;;;AAgBA,IAAa,yBAAoC,UAAU;CACzD,MAAM,EAAE,QAAQ,mBAAmB,UAAU;CAC7C,MAAM,YAAY,4BAChB,kBACA,uBACF;CAEA,MAAM,OACJ,oBAAC,WAAD;EACE,QAAQ,OAAO,UAAU,YAAY;EAIrC,cAAY,KAAA;CACb,CAAA;CAGH,MAAM,SAAS,UAAU,OACvB,OAAO,UACH,yCACA,wCACN;CAEA,IAAI,CAAC,gBAAgB,qBAAqB,8BACxC,QACA,IACF;CAEA,IAAI,kBAAkB;EACpB,iBAAiB,GAAG,eAAe;EACnC,oBAAoB,CAAC;CACvB;CAEA,OACE,qBAAC,MAAD;EACE,WAAW,qCAAO;EAClB,aAAW,OAAO;EAClB,mBAAiB,OAAO;EAH1B,UAAA,CAKG,MACD,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,KAAK,gBAAN;GAAqB,aAAY;GAAQ,UAAA,GAAG,OAAO;EAAyB,CAAA,GAC3E,UAAU,OAAO,gBAAgB,iBAAiB,CAC/C,EAAA,CAAA,CACJ;;AAER"}
@@ -0,0 +1,52 @@
1
+ "use client"
2
+ /* */
3
+ import { useLocalizedStringFormatter } from "../../TranslationProvider/useLocalizedStringFormatter.mjs";
4
+ import __locale_default from "../../../../../../_virtual/_28.locale.mjs";
5
+ import { useEffect, useRef } from "react";
6
+ import { announce } from "@react-aria/live-announcer";
7
+ //#region src/components/PasswordCreationField/lib/ariaAnnounce.ts
8
+ /**
9
+ * Announces the current validation message — which rule the password misses, or
10
+ * that it is secure.
11
+ *
12
+ * The same text is wired to the field via `aria-describedby`, but a description
13
+ * is only read when the field receives focus. The field keeps focus while the
14
+ * password is typed, so without a live region a screen reader reports the field
15
+ * as invalid and never says why.
16
+ *
17
+ * @internal
18
+ */
19
+ var useAriaAnnounceValidationState = (text, isAnnounceable) => {
20
+ const lastAnnouncedText = useRef(void 0);
21
+ useEffect(() => {
22
+ if (!isAnnounceable || !text) {
23
+ lastAnnouncedText.current = void 0;
24
+ return;
25
+ }
26
+ if (text === lastAnnouncedText.current) return;
27
+ lastAnnouncedText.current = text;
28
+ announce(text, "polite");
29
+ }, [text, isAnnounceable]);
30
+ };
31
+ /**
32
+ * Announces that the password became visible or hidden. The toggle button keeps
33
+ * focus, and a screen reader does not report that the input switched between
34
+ * masked characters and plain text on its own.
35
+ *
36
+ * @internal
37
+ */
38
+ var useAriaAnnouncePasswordVisibility = (isRevealed) => {
39
+ const formatter = useLocalizedStringFormatter(__locale_default, "PasswordCreationField");
40
+ const isInitialRender = useRef(true);
41
+ useEffect(() => {
42
+ if (isInitialRender.current) {
43
+ isInitialRender.current = false;
44
+ return;
45
+ }
46
+ announce(formatter.format(isRevealed ? "password.announce.revealed" : "password.announce.hidden"), "polite");
47
+ }, [isRevealed]);
48
+ };
49
+ //#endregion
50
+ export { useAriaAnnouncePasswordVisibility, useAriaAnnounceValidationState };
51
+
52
+ //# sourceMappingURL=ariaAnnounce.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ariaAnnounce.mjs","names":[],"sources":["../../../../../../../../src/components/PasswordCreationField/lib/ariaAnnounce.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { announce } from \"@react-aria/live-announcer\";\nimport { useLocalizedStringFormatter } from \"@/components/TranslationProvider/useLocalizedStringFormatter\";\nimport locales from \"./../locales/*.locale.json\";\n\n/**\n * Announces the current validation message — which rule the password misses, or\n * that it is secure.\n *\n * The same text is wired to the field via `aria-describedby`, but a description\n * is only read when the field receives focus. The field keeps focus while the\n * password is typed, so without a live region a screen reader reports the field\n * as invalid and never says why.\n *\n * @internal\n */\nexport const useAriaAnnounceValidationState = (\n text: string | undefined,\n isAnnounceable: boolean,\n): void => {\n const lastAnnouncedText = useRef<string>(undefined);\n\n useEffect(() => {\n if (!isAnnounceable || !text) {\n // Announce the text again when it comes back after an empty field or a\n // pending validation.\n lastAnnouncedText.current = undefined;\n return;\n }\n\n if (text === lastAnnouncedText.current) {\n return;\n }\n\n lastAnnouncedText.current = text;\n announce(text, \"polite\");\n }, [text, isAnnounceable]);\n};\n\n/**\n * Announces that the password became visible or hidden. The toggle button keeps\n * focus, and a screen reader does not report that the input switched between\n * masked characters and plain text on its own.\n *\n * @internal\n */\nexport const useAriaAnnouncePasswordVisibility = (\n isRevealed: boolean,\n): void => {\n const formatter = useLocalizedStringFormatter(\n locales,\n \"PasswordCreationField\",\n );\n const isInitialRender = useRef(true);\n\n useEffect(() => {\n if (isInitialRender.current) {\n isInitialRender.current = false;\n return;\n }\n\n announce(\n formatter.format(\n isRevealed ? \"password.announce.revealed\" : \"password.announce.hidden\",\n ),\n \"polite\",\n );\n }, [isRevealed]);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgBA,IAAa,kCACX,MACA,mBACS;CACT,MAAM,oBAAoB,OAAe,KAAA,CAAS;CAElD,gBAAgB;EACd,IAAI,CAAC,kBAAkB,CAAC,MAAM;GAG5B,kBAAkB,UAAU,KAAA;GAC5B;EACF;EAEA,IAAI,SAAS,kBAAkB,SAC7B;EAGF,kBAAkB,UAAU;EAC5B,SAAS,MAAM,QAAQ;CACzB,GAAG,CAAC,MAAM,cAAc,CAAC;AAC3B;;;;;;;;AASA,IAAa,qCACX,eACS;CACT,MAAM,YAAY,4BAChB,kBACA,uBACF;CACA,MAAM,kBAAkB,OAAO,IAAI;CAEnC,gBAAgB;EACd,IAAI,gBAAgB,SAAS;GAC3B,gBAAgB,UAAU;GAC1B;EACF;EAEA,SACE,UAAU,OACR,aAAa,+BAA+B,0BAC9C,GACA,QACF;CACF,GAAG,CAAC,UAAU,CAAC;AACjB"}
@@ -14,6 +14,7 @@ var generateTranslationString = (rule, shortVersion = false) => {
14
14
  else finalTranslationString = translateString;
15
15
  return shortVersion ? `${finalTranslationString}.short` : finalTranslationString;
16
16
  };
17
+ /** @internal */
17
18
  var generateValidationTranslation = (r, shotVersion = false) => {
18
19
  const translationKey = generateTranslationString(r, shotVersion);
19
20
  if (r.ruleType === RuleType.char && r.chars) return [translationKey, {
@@ -1 +1 @@
1
- {"version":3,"file":"generateValidationTranslation.mjs","names":[],"sources":["../../../../../../../../src/components/PasswordCreationField/lib/generateValidationTranslation.ts"],"sourcesContent":["import {\n RuleType,\n type RuleValidationResult,\n} from \"@/integrations/@mittwald/password-tools-js\";\n\nconst generateTranslationString = (\n rule: Partial<RuleValidationResult> & { translationKey?: string },\n shortVersion = false,\n): string => {\n if (rule.translationKey) {\n return `validation.${rule.translationKey}`;\n }\n const translateString = `validation.${rule.ruleType ?? \"general\"}`;\n let finalTranslationString: string;\n\n if (\n (\"min\" in rule && rule.min !== undefined) ||\n (\"max\" in rule && rule.max !== undefined)\n ) {\n const breakingBoundaryProperty = rule.failingBoundary\n ? rule.failingBoundary\n : rule.min\n ? \"min\"\n : \"max\";\n\n if (rule.identifier) {\n finalTranslationString = `${translateString}.${rule.identifier}.${breakingBoundaryProperty}`;\n } else {\n finalTranslationString = `${translateString}.${breakingBoundaryProperty}`;\n }\n } else if (rule.identifier) {\n finalTranslationString = `${translateString}.${rule.identifier}`;\n } else {\n finalTranslationString = translateString;\n }\n\n return shortVersion\n ? `${finalTranslationString}.short`\n : finalTranslationString;\n};\n\nexport const generateValidationTranslation = (\n r: Partial<RuleValidationResult>,\n shotVersion = false,\n): [string, Record<string, string | number | boolean> | undefined] => {\n const translationKey = generateTranslationString(r, shotVersion);\n\n if (r.ruleType === RuleType.char && r.chars) {\n return [\n translationKey,\n { ...r, chars: r.chars.map((c) => c.char).join(\"\") },\n ];\n }\n\n return [\n translationKey,\n r as unknown as Record<string, string | number | boolean> | undefined,\n ];\n};\n\nexport default generateValidationTranslation;\n"],"mappings":";;;;AAKA,IAAM,6BACJ,MACA,eAAe,UACJ;CACX,IAAI,KAAK,gBACP,OAAO,cAAc,KAAK;CAE5B,MAAM,kBAAkB,cAAc,KAAK,YAAY;CACvD,IAAI;CAEJ,IACG,SAAS,QAAQ,KAAK,QAAQ,KAAA,KAC9B,SAAS,QAAQ,KAAK,QAAQ,KAAA,GAC/B;EACA,MAAM,2BAA2B,KAAK,kBAClC,KAAK,kBACL,KAAK,MACH,QACA;EAEN,IAAI,KAAK,YACP,yBAAyB,GAAG,gBAAgB,GAAG,KAAK,WAAW,GAAG;OAElE,yBAAyB,GAAG,gBAAgB,GAAG;CAEnD,OAAO,IAAI,KAAK,YACd,yBAAyB,GAAG,gBAAgB,GAAG,KAAK;MAEpD,yBAAyB;CAG3B,OAAO,eACH,GAAG,uBAAuB,UAC1B;AACN;AAEA,IAAa,iCACX,GACA,cAAc,UACsD;CACpE,MAAM,iBAAiB,0BAA0B,GAAG,WAAW;CAE/D,IAAI,EAAE,aAAa,SAAS,QAAQ,EAAE,OACpC,OAAO,CACL,gBACA;EAAE,GAAG;EAAG,OAAO,EAAE,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE;CAAE,CACrD;CAGF,OAAO,CACL,gBACA,CACF;AACF"}
1
+ {"version":3,"file":"generateValidationTranslation.mjs","names":[],"sources":["../../../../../../../../src/components/PasswordCreationField/lib/generateValidationTranslation.ts"],"sourcesContent":["import {\n RuleType,\n type RuleValidationResult,\n} from \"@/integrations/@mittwald/password-tools-js\";\n\nconst generateTranslationString = (\n rule: Partial<RuleValidationResult> & { translationKey?: string },\n shortVersion = false,\n): string => {\n if (rule.translationKey) {\n return `validation.${rule.translationKey}`;\n }\n const translateString = `validation.${rule.ruleType ?? \"general\"}`;\n let finalTranslationString: string;\n\n if (\n (\"min\" in rule && rule.min !== undefined) ||\n (\"max\" in rule && rule.max !== undefined)\n ) {\n const breakingBoundaryProperty = rule.failingBoundary\n ? rule.failingBoundary\n : rule.min\n ? \"min\"\n : \"max\";\n\n if (rule.identifier) {\n finalTranslationString = `${translateString}.${rule.identifier}.${breakingBoundaryProperty}`;\n } else {\n finalTranslationString = `${translateString}.${breakingBoundaryProperty}`;\n }\n } else if (rule.identifier) {\n finalTranslationString = `${translateString}.${rule.identifier}`;\n } else {\n finalTranslationString = translateString;\n }\n\n return shortVersion\n ? `${finalTranslationString}.short`\n : finalTranslationString;\n};\n\n/** @internal */\nexport const generateValidationTranslation = (\n r: Partial<RuleValidationResult>,\n shotVersion = false,\n): [string, Record<string, string | number | boolean> | undefined] => {\n const translationKey = generateTranslationString(r, shotVersion);\n\n if (r.ruleType === RuleType.char && r.chars) {\n return [\n translationKey,\n { ...r, chars: r.chars.map((c) => c.char).join(\"\") },\n ];\n }\n\n return [\n translationKey,\n r as unknown as Record<string, string | number | boolean> | undefined,\n ];\n};\n\nexport default generateValidationTranslation;\n"],"mappings":";;;;AAKA,IAAM,6BACJ,MACA,eAAe,UACJ;CACX,IAAI,KAAK,gBACP,OAAO,cAAc,KAAK;CAE5B,MAAM,kBAAkB,cAAc,KAAK,YAAY;CACvD,IAAI;CAEJ,IACG,SAAS,QAAQ,KAAK,QAAQ,KAAA,KAC9B,SAAS,QAAQ,KAAK,QAAQ,KAAA,GAC/B;EACA,MAAM,2BAA2B,KAAK,kBAClC,KAAK,kBACL,KAAK,MACH,QACA;EAEN,IAAI,KAAK,YACP,yBAAyB,GAAG,gBAAgB,GAAG,KAAK,WAAW,GAAG;OAElE,yBAAyB,GAAG,gBAAgB,GAAG;CAEnD,OAAO,IAAI,KAAK,YACd,yBAAyB,GAAG,gBAAgB,GAAG,KAAK;MAEpD,yBAAyB;CAG3B,OAAO,eACH,GAAG,uBAAuB,UAC1B;AACN;;AAGA,IAAa,iCACX,GACA,cAAc,UACsD;CACpE,MAAM,iBAAiB,0BAA0B,GAAG,WAAW;CAE/D,IAAI,EAAE,aAAa,SAAS,QAAQ,EAAE,OACpC,OAAO,CACL,gBACA;EAAE,GAAG;EAAG,OAAO,EAAE,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE;CAAE,CACrD;CAGF,OAAO,CACL,gBACA,CACF;AACF"}
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
  /* */
3
3
  //#region src/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.ts
4
+ /** @internal */
4
5
  var getStatusFromPolicyValidationResult = (isValid, result) => {
5
6
  if (isValid === true) {
6
7
  if (result.complexity && result.complexity.actual === result.complexity.min && result.complexity.min !== 4) return "warning";
@@ -1 +1 @@
1
- {"version":3,"file":"getStatusFromPolicyValidationResult.mjs","names":[],"sources":["../../../../../../../../src/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.ts"],"sourcesContent":["import type { ResolvedPolicyValidationResult } from \"@/components/PasswordCreationField/PasswordCreationField\";\nimport type { ComplexityStatus } from \"@/components/PasswordCreationField/components/ComplexityIndicator/ComplexityIndicator\";\n\nexport const getStatusFromPolicyValidationResult = (\n isValid: ResolvedPolicyValidationResult[\"isValid\"],\n result: ResolvedPolicyValidationResult,\n): ComplexityStatus => {\n if (isValid === true) {\n if (\n result.complexity &&\n result.complexity.actual === result.complexity.min &&\n result.complexity.min !== 4\n ) {\n return \"warning\";\n }\n\n return \"success\";\n } else if (isValid === \"indeterminate\") {\n return \"indeterminate\";\n }\n\n return \"danger\";\n};\n\nexport default getStatusFromPolicyValidationResult;\n"],"mappings":";;;AAGA,IAAa,uCACX,SACA,WACqB;CACrB,IAAI,YAAY,MAAM;EACpB,IACE,OAAO,cACP,OAAO,WAAW,WAAW,OAAO,WAAW,OAC/C,OAAO,WAAW,QAAQ,GAE1B,OAAO;EAGT,OAAO;CACT,OAAO,IAAI,YAAY,iBACrB,OAAO;CAGT,OAAO;AACT"}
1
+ {"version":3,"file":"getStatusFromPolicyValidationResult.mjs","names":[],"sources":["../../../../../../../../src/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.ts"],"sourcesContent":["import type { ResolvedPolicyValidationResult } from \"@/components/PasswordCreationField/PasswordCreationField\";\nimport type { ComplexityStatus } from \"@/components/PasswordCreationField/components/ComplexityIndicator/ComplexityIndicator\";\n\n/** @internal */\nexport const getStatusFromPolicyValidationResult = (\n isValid: ResolvedPolicyValidationResult[\"isValid\"],\n result: ResolvedPolicyValidationResult,\n): ComplexityStatus => {\n if (isValid === true) {\n if (\n result.complexity &&\n result.complexity.actual === result.complexity.min &&\n result.complexity.min !== 4\n ) {\n return \"warning\";\n }\n\n return \"success\";\n } else if (isValid === \"indeterminate\") {\n return \"indeterminate\";\n }\n\n return \"danger\";\n};\n\nexport default getStatusFromPolicyValidationResult;\n"],"mappings":";;;;AAIA,IAAa,uCACX,SACA,WACqB;CACrB,IAAI,YAAY,MAAM;EACpB,IACE,OAAO,cACP,OAAO,WAAW,WAAW,OAAO,WAAW,OAC/C,OAAO,WAAW,QAAQ,GAE1B,OAAO;EAGT,OAAO;CACT,OAAO,IAAI,YAAY,iBACrB,OAAO;CAGT,OAAO;AACT"}
@@ -3,6 +3,7 @@
3
3
  import { startTransition, useEffect, useRef } from "react";
4
4
  import { useDebouncedCallback } from "use-debounce";
5
5
  //#region src/components/PasswordCreationField/lib/usePolicyValidationResult.ts
6
+ /** @internal */
6
7
  var usePolicyValidationResult = (validationPolicy, password, onValidationStart, onValidationResult) => {
7
8
  const cache = useRef(null);
8
9
  const validatePassword = (pwd) => {
@@ -1 +1 @@
1
- {"version":3,"file":"usePolicyValidationResult.mjs","names":[],"sources":["../../../../../../../../src/components/PasswordCreationField/lib/usePolicyValidationResult.ts"],"sourcesContent":["import { startTransition, useEffect, useRef } from \"react\";\nimport type { Policy } from \"@/integrations/@mittwald/password-tools-js\";\nimport type { ResolvedPolicyValidationResult } from \"@/components/PasswordCreationField/PasswordCreationField\";\nimport { useDebouncedCallback } from \"use-debounce\";\n\nexport const usePolicyValidationResult = (\n validationPolicy: Policy,\n password: string,\n onValidationStart?: () => void,\n onValidationResult?: (data: {\n password: string;\n isValid: boolean;\n results: ResolvedPolicyValidationResult;\n }) => void,\n) => {\n const cache = useRef<{\n password: string;\n results: ResolvedPolicyValidationResult;\n } | null>(null);\n\n const validatePassword = (pwd: string) => {\n onValidationStart?.();\n\n startTransition(async () => {\n if (cache.current?.password === pwd) {\n onValidationResult?.({\n password: cache.current.password,\n isValid: Boolean(cache.current.results.isValid),\n results: cache.current.results,\n });\n return;\n }\n\n const validationResult = await validationPolicy.validate(pwd);\n cache.current = { password: pwd, results: validationResult };\n onValidationResult?.({\n password: pwd,\n isValid: validationResult.isValid,\n results: validationResult,\n });\n });\n };\n\n const validatePasswordDebounce = useDebouncedCallback(validatePassword, 350, {\n trailing: true,\n });\n\n useEffect(() => {\n if (password) {\n validatePasswordDebounce(password);\n } else {\n validatePassword(password);\n }\n }, [password, validationPolicy]);\n};\n"],"mappings":";;;;;AAKA,IAAa,6BACX,kBACA,UACA,mBACA,uBAKG;CACH,MAAM,QAAQ,OAGJ,IAAI;CAEd,MAAM,oBAAoB,QAAgB;EACxC,oBAAoB;EAEpB,gBAAgB,YAAY;GAC1B,IAAI,MAAM,SAAS,aAAa,KAAK;IACnC,qBAAqB;KACnB,UAAU,MAAM,QAAQ;KACxB,SAAS,QAAQ,MAAM,QAAQ,QAAQ,OAAO;KAC9C,SAAS,MAAM,QAAQ;IACzB,CAAC;IACD;GACF;GAEA,MAAM,mBAAmB,MAAM,iBAAiB,SAAS,GAAG;GAC5D,MAAM,UAAU;IAAE,UAAU;IAAK,SAAS;GAAiB;GAC3D,qBAAqB;IACnB,UAAU;IACV,SAAS,iBAAiB;IAC1B,SAAS;GACX,CAAC;EACH,CAAC;CACH;CAEA,MAAM,2BAA2B,qBAAqB,kBAAkB,KAAK,EAC3E,UAAU,KACZ,CAAC;CAED,gBAAgB;EACd,IAAI,UACF,yBAAyB,QAAQ;OAEjC,iBAAiB,QAAQ;CAE7B,GAAG,CAAC,UAAU,gBAAgB,CAAC;AACjC"}
1
+ {"version":3,"file":"usePolicyValidationResult.mjs","names":[],"sources":["../../../../../../../../src/components/PasswordCreationField/lib/usePolicyValidationResult.ts"],"sourcesContent":["import { startTransition, useEffect, useRef } from \"react\";\nimport type { Policy } from \"@/integrations/@mittwald/password-tools-js\";\nimport type { ResolvedPolicyValidationResult } from \"@/components/PasswordCreationField/PasswordCreationField\";\nimport { useDebouncedCallback } from \"use-debounce\";\n\n/** @internal */\nexport const usePolicyValidationResult = (\n validationPolicy: Policy,\n password: string,\n onValidationStart?: () => void,\n onValidationResult?: (data: {\n password: string;\n isValid: boolean;\n results: ResolvedPolicyValidationResult;\n }) => void,\n) => {\n const cache = useRef<{\n password: string;\n results: ResolvedPolicyValidationResult;\n } | null>(null);\n\n const validatePassword = (pwd: string) => {\n onValidationStart?.();\n\n startTransition(async () => {\n if (cache.current?.password === pwd) {\n onValidationResult?.({\n password: cache.current.password,\n isValid: Boolean(cache.current.results.isValid),\n results: cache.current.results,\n });\n return;\n }\n\n const validationResult = await validationPolicy.validate(pwd);\n cache.current = { password: pwd, results: validationResult };\n onValidationResult?.({\n password: pwd,\n isValid: validationResult.isValid,\n results: validationResult,\n });\n });\n };\n\n const validatePasswordDebounce = useDebouncedCallback(validatePassword, 350, {\n trailing: true,\n });\n\n useEffect(() => {\n if (password) {\n validatePasswordDebounce(password);\n } else {\n validatePassword(password);\n }\n }, [password, validationPolicy]);\n};\n"],"mappings":";;;;;;AAMA,IAAa,6BACX,kBACA,UACA,mBACA,uBAKG;CACH,MAAM,QAAQ,OAGJ,IAAI;CAEd,MAAM,oBAAoB,QAAgB;EACxC,oBAAoB;EAEpB,gBAAgB,YAAY;GAC1B,IAAI,MAAM,SAAS,aAAa,KAAK;IACnC,qBAAqB;KACnB,UAAU,MAAM,QAAQ;KACxB,SAAS,QAAQ,MAAM,QAAQ,QAAQ,OAAO;KAC9C,SAAS,MAAM,QAAQ;IACzB,CAAC;IACD;GACF;GAEA,MAAM,mBAAmB,MAAM,iBAAiB,SAAS,GAAG;GAC5D,MAAM,UAAU;IAAE,UAAU;IAAK,SAAS;GAAiB;GAC3D,qBAAqB;IACnB,UAAU;IACV,SAAS,iBAAiB;IAC1B,SAAS;GACX,CAAC;EACH,CAAC;CACH;CAEA,MAAM,2BAA2B,qBAAqB,kBAAkB,KAAK,EAC3E,UAAU,KACZ,CAAC;CAED,gBAAgB;EACd,IAAI,UACF,yBAAyB,QAAQ;OAEjC,iBAAiB,QAAQ;CAE7B,GAAG,CAAC,UAAU,gBAAgB,CAAC;AACjC"}
@@ -4,14 +4,16 @@
4
4
  var popover = "flow--popover";
5
5
  var content = "flow--popover--content";
6
6
  var tip = "flow--popover--tip";
7
+ var tipIcon = "flow--popover--tip-icon";
7
8
  var Popover_module_default = {
8
9
  "non-modal": "flow--popover--non-modal",
9
10
  popover,
10
11
  content,
11
12
  tip,
13
+ tipIcon,
12
14
  "popover-slide": "flow--popover--popover-slide"
13
15
  };
14
16
  //#endregion
15
- export { content, Popover_module_default as default, popover, tip };
17
+ export { content, Popover_module_default as default, popover, tip, tipIcon };
16
18
 
17
19
  //# sourceMappingURL=Popover.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Popover.module.mjs","names":[],"sources":["../../../../../../../src/components/Popover/Popover.module.scss"],"sourcesContent":["@use \"@/styles/mixins/scrollbar\";\n\n/* Variants */\n\n/*\n * A non-modal popover stays in the document and scrolls with the page, so the\n * height react-aria caps it at — the room left in the viewport when it was last\n * measured — is not a limit that applies to it. Anchored below the fold that\n * room is zero, and the hint renders as a two-pixel sliver.\n *\n * `useOverlayPosition` writes the value straight onto the element\n * (`overlay.style.maxHeight = …`) rather than returning it among its props, so\n * no React style can remove it and only `!important` reaches it.\n */\n.non-modal {\n max-height: none !important;\n}\n\n.popover {\n background-color: var(--popover--background-color);\n box-shadow: var(--popover--box-shadow);\n border-radius: var(--popover--corner-radius);\n display: flex;\n flex-direction: column;\n\n --size-px--s--negative: calc(var(--size-px--s) * -1);\n\n min-width: var(--popover--min-width);\n border-width: var(--popover--border-width);\n border-style: var(--popover--border-style);\n border-color: var(--popover--border-color);\n max-width: calc(100% - 2 * var(--popover--viewport-padding));\n\n :where(.content) {\n @include scrollbar.scrollbar;\n\n overflow-y: auto;\n\n &:focus-visible {\n outline: none;\n }\n }\n\n .tip {\n width: var(--popover--tip-width);\n\n svg {\n display: block;\n fill: var(--popover--background-color);\n stroke: var(--popover--border-color);\n stroke-width: var(--popover--border-width);\n }\n }\n\n &[data-placement=\"top\"] {\n --origin: translateY(var(--size-px--s));\n\n &:has(.tip) {\n margin-bottom: var(--popover--tip-height);\n }\n }\n\n &[data-placement=\"bottom\"] {\n --origin: translateY(var(--size-px--s--negative));\n\n &:has(.tip) {\n margin-top: var(--popover--tip-height);\n }\n\n .tip svg {\n transform: rotate(180deg);\n }\n }\n\n &[data-placement=\"right\"] {\n --origin: translateX(var(--size-px--s--negative));\n\n &:has(.tip) {\n margin-inline-start: var(--popover--tip-height);\n }\n\n .tip svg {\n transform: rotate(90deg);\n }\n }\n\n &[data-placement=\"left\"] {\n --origin: translateX(var(--size-px--s));\n\n &:has(.tip) {\n margin-inline-end: var(--popover--tip-height);\n }\n\n .tip svg {\n transform: rotate(-90deg);\n }\n }\n\n &[data-entering] {\n animation: popover-slide var(--transition--duration--default);\n }\n\n &[data-exiting] {\n animation: popover-slide var(--transition--duration--default) reverse\n ease-in;\n pointer-events: none;\n }\n\n &:has(:global(.flow--options):empty) {\n display: none;\n }\n}\n\n@keyframes popover-slide {\n from {\n transform: var(--origin);\n opacity: 0;\n }\n\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"Popover.module.mjs","names":[],"sources":["../../../../../../../src/components/Popover/Popover.module.scss"],"sourcesContent":["@use \"@/styles/mixins/scrollbar\";\n\n/* Variants */\n\n/*\n * A non-modal popover stays in the document and scrolls with the page, so the\n * height react-aria caps it at — the room left in the viewport when it was last\n * measured — is not a limit that applies to it. Anchored below the fold that\n * room is zero, and the hint renders as a two-pixel sliver.\n *\n * `useOverlayPosition` writes the value straight onto the element\n * (`overlay.style.maxHeight = …`) rather than returning it among its props, so\n * no React style can remove it and only `!important` reaches it.\n */\n.non-modal {\n max-height: none !important;\n\n /*\n * The lowest level that still puts the popover in front of the page. Claiming\n * none at all left it in the `z-index: auto` group, where paint order is tree\n * order — so anything positioned that came later in the document painted over\n * it, and a `position: relative` written as a containing block for something\n * inside it was enough (a `TabNavigation` in a `LayoutCard` below the anchor,\n * #3232). Nothing an overlay can read tells that apart from a deliberate\n * claim, so it has to outrank the whole group.\n *\n * The cost is app chrome that claims level 1 itself: the popover comes later\n * in the document than a header does, so it now wins that tie. Chrome that has\n * to stay above a coach mark claims 2 — the docs site's own header does.\n */\n z-index: 1;\n}\n\n.popover {\n background-color: var(--popover--background-color);\n box-shadow: var(--popover--box-shadow);\n border-radius: var(--popover--corner-radius);\n display: flex;\n flex-direction: column;\n\n --size-px--s--negative: calc(var(--size-px--s) * -1);\n\n min-width: var(--popover--min-width);\n border-width: var(--popover--border-width);\n border-style: var(--popover--border-style);\n border-color: var(--popover--border-color);\n max-width: calc(100% - 2 * var(--popover--viewport-padding));\n\n :where(.content) {\n @include scrollbar.scrollbar;\n\n overflow-y: auto;\n\n &:focus-visible {\n outline: none;\n }\n }\n\n .tip {\n width: var(--popover--tip-width);\n\n .tipIcon {\n display: block;\n fill: var(--popover--background-color);\n stroke: var(--popover--border-color);\n stroke-width: var(--popover--border-width);\n }\n }\n\n &[data-placement=\"top\"] {\n --origin: translateY(var(--size-px--s));\n\n &:has(.tip) {\n margin-bottom: var(--popover--tip-height);\n }\n }\n\n &[data-placement=\"bottom\"] {\n --origin: translateY(var(--size-px--s--negative));\n\n &:has(.tip) {\n margin-top: var(--popover--tip-height);\n }\n\n .tip .tipIcon {\n transform: rotate(180deg);\n }\n }\n\n &[data-placement=\"right\"] {\n --origin: translateX(var(--size-px--s--negative));\n\n &:has(.tip) {\n margin-inline-start: var(--popover--tip-height);\n }\n\n .tip .tipIcon {\n transform: rotate(90deg);\n }\n }\n\n &[data-placement=\"left\"] {\n --origin: translateX(var(--size-px--s));\n\n &:has(.tip) {\n margin-inline-end: var(--popover--tip-height);\n }\n\n .tip .tipIcon {\n transform: rotate(-90deg);\n }\n }\n\n &[data-entering] {\n animation: popover-slide var(--transition--duration--default);\n }\n\n &[data-exiting] {\n animation: popover-slide var(--transition--duration--default) reverse\n ease-in;\n pointer-events: none;\n }\n\n &:has(:global(.flow--options):empty) {\n display: none;\n }\n}\n\n@keyframes popover-slide {\n from {\n transform: var(--origin);\n opacity: 0;\n }\n\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n"],"mappings":""}