@mittwald/flow-react-components 1.3.0-next.9 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +42 -1
- package/USAGE.md +14 -6
- package/dist/assets/component-index.json +1 -1
- package/dist/assets/doc-properties.json +1 -1
- package/dist/css/all-layered.css +1 -1
- package/dist/css/all.css +1 -1
- package/dist/js/_virtual/_28.locale.mjs +10 -0
- package/dist/js/default.mjs +1 -2
- package/dist/js/flr-universal.mjs +1 -2
- package/dist/js/packages/components/src/components/Accordion/Accordion.mjs +26 -15
- package/dist/js/packages/components/src/components/Accordion/Accordion.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Accordion/Accordion.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Action/models/ActionModel.mjs +8 -25
- package/dist/js/packages/components/src/components/Action/models/ActionModel.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Avatar/Avatar.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Button/Button.module.mjs +1 -0
- package/dist/js/packages/components/src/components/Button/Button.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Calendar/Calendar.module.mjs +3 -1
- package/dist/js/packages/components/src/components/Calendar/Calendar.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Calendar/components/CalendarHeader/CalendarHeader.mjs +19 -15
- package/dist/js/packages/components/src/components/Calendar/components/CalendarHeader/CalendarHeader.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CartesianChart/CartesianChart.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CartesianChart/components/Area/Area.mjs +4 -1
- package/dist/js/packages/components/src/components/CartesianChart/components/Area/Area.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CartesianChart/components/Bar/Bar.mjs +4 -1
- package/dist/js/packages/components/src/components/CartesianChart/components/Bar/Bar.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CartesianChart/components/Line/Line.mjs +4 -1
- package/dist/js/packages/components/src/components/CartesianChart/components/Line/Line.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CartesianChart/hooks/useChartAnimation.mjs +27 -0
- package/dist/js/packages/components/src/components/CartesianChart/hooks/useChartAnimation.mjs.map +1 -0
- package/dist/js/packages/components/src/components/Chat/Chat.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Checkbox/Checkbox.module.mjs +2 -1
- package/dist/js/packages/components/src/components/Checkbox/Checkbox.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CheckboxGroup/CheckboxGroup.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CodeBlock/CodeBlock.mjs +31 -5
- package/dist/js/packages/components/src/components/CodeBlock/CodeBlock.mjs.map +1 -1
- package/dist/js/packages/components/src/components/CodeBlock/CodeBlock.module.mjs +6 -4
- package/dist/js/packages/components/src/components/CodeBlock/CodeBlock.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Combine/Combine.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/ComboBox/ComboBox.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/ContextMenu/ContextMenu.mjs +2 -2
- package/dist/js/packages/components/src/components/ContextMenu/ContextMenu.mjs.map +1 -1
- package/dist/js/packages/components/src/components/ContextMenu/ContextMenu.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/ContextMenu/components/ContextMenuSection/ContextMenuSection.mjs +2 -2
- package/dist/js/packages/components/src/components/ContextMenu/components/ContextMenuSection/ContextMenuSection.mjs.map +1 -1
- package/dist/js/packages/components/src/components/ContextMenu/lib.mjs +3 -7
- package/dist/js/packages/components/src/components/ContextMenu/lib.mjs.map +1 -1
- package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.mjs +14 -11
- package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.mjs.map +1 -1
- package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.module.mjs +4 -2
- package/dist/js/packages/components/src/components/DateRangePicker/components/DateRangeInput/DateRangeInput.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/DonutChart/DonutChart.module.mjs +6 -3
- package/dist/js/packages/components/src/components/DonutChart/DonutChart.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/DonutChart/components/DonutChartFill.mjs +13 -2
- package/dist/js/packages/components/src/components/DonutChart/components/DonutChartFill.mjs.map +1 -1
- package/dist/js/packages/components/src/components/FileDropZone/FileDropZone.mjs +2 -2
- package/dist/js/packages/components/src/components/FileDropZone/FileDropZone.mjs.map +1 -1
- package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.mjs +9 -6
- package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.mjs.map +1 -1
- package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.module.mjs +5 -1
- package/dist/js/packages/components/src/components/HeaderNavigation/HeaderNavigation.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Heading/Heading.mjs +1 -1
- package/dist/js/packages/components/src/components/Heading/Heading.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Heading/Heading.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/LayoutCard/LayoutCard.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/LightBox/LightBox.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/LightBox/components/LightBoxGallery/Controls.mjs +1 -1
- package/dist/js/packages/components/src/components/LightBox/components/LightBoxGallery/Controls.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Link/Link.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/List.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/Header/components/SearchField/SearchField.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/Items/Items.mjs +3 -6
- package/dist/js/packages/components/src/components/List/components/Items/Items.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/Items/components/Item/Item.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/ListItemView/ListItemView.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/components/Table/Table.mjs +6 -2
- package/dist/js/packages/components/src/components/List/components/Table/Table.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/{components/Items/hooks → hooks}/useInfiniteScrollTrigger.mjs +11 -12
- package/dist/js/packages/components/src/components/List/hooks/useInfiniteScrollTrigger.mjs.map +1 -0
- package/dist/js/packages/components/src/components/Markdown/Markdown.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/MarkdownEditor/MarkdownEditor.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/MessageThread/MessageThread.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Modal/Modal.mjs +4 -6
- package/dist/js/packages/components/src/components/Modal/Modal.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Modal/Modal.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Navigation/Navigation.module.mjs +3 -1
- package/dist/js/packages/components/src/components/Navigation/Navigation.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Navigation/components/LinkListTunnelExit/LinkListTunnelExit.mjs +5 -1
- package/dist/js/packages/components/src/components/Navigation/components/LinkListTunnelExit/LinkListTunnelExit.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Navigation/components/NavigationGroup/NavigationGroup.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/NumberField/NumberField.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Overlay/Overlay.module.mjs +3 -1
- package/dist/js/packages/components/src/components/Overlay/Overlay.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Overlay/components/OverlayContent.mjs +47 -9
- package/dist/js/packages/components/src/components/Overlay/components/OverlayContent.mjs.map +1 -1
- package/dist/js/packages/components/src/components/OverlayTrigger/OverlayTrigger.mjs +0 -1
- package/dist/js/packages/components/src/components/OverlayTrigger/OverlayTrigger.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/PasswordCreationField.mjs +8 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/PasswordCreationField.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.mjs +2 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.mjs +13 -7
- package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.module.mjs +9 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.module.mjs.map +1 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry.mjs +12 -4
- package/dist/js/packages/components/src/components/PasswordCreationField/components/ValidationResultEntry/ValidationResultEntry.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/ariaAnnounce.mjs +52 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/ariaAnnounce.mjs.map +1 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/generateValidationTranslation.mjs +1 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/generateValidationTranslation.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.mjs +1 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.mjs.map +1 -1
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/usePolicyValidationResult.mjs +1 -0
- package/dist/js/packages/components/src/components/PasswordCreationField/lib/usePolicyValidationResult.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Popover/Popover.module.mjs +3 -1
- package/dist/js/packages/components/src/components/Popover/Popover.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Popover/components/NonModalPopoverContent/NonModalPopoverContent.mjs +2 -6
- package/dist/js/packages/components/src/components/Popover/components/NonModalPopoverContent/NonModalPopoverContent.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Popover/components/PopoverContent/PopoverContent.mjs +2 -6
- package/dist/js/packages/components/src/components/Popover/components/PopoverContent/PopoverContent.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Popover/components/PopoverTip/PopoverTip.mjs +17 -0
- package/dist/js/packages/components/src/components/Popover/components/PopoverTip/PopoverTip.mjs.map +1 -0
- package/dist/js/packages/components/src/components/ProgressBar/ProgressBar.module.mjs +1 -0
- package/dist/js/packages/components/src/components/ProgressBar/ProgressBar.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/RadioGroup/components/Radio/Radio.module.mjs +2 -1
- package/dist/js/packages/components/src/components/RadioGroup/components/Radio/Radio.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/SearchField/SearchField.module.mjs +3 -3
- package/dist/js/packages/components/src/components/SearchField/SearchField.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Section/Section.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Section/components/SectionHeader/SectionHeader.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Slider/Slider.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Switch/Switch.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.mjs +2 -1
- package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.mjs.map +1 -1
- package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.module.mjs +5 -1
- package/dist/js/packages/components/src/components/TabNavigation/TabNavigation.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Table/Table.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Tabs/components/TabTitle/TabTitle.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Text/Text.module.mjs.map +1 -1
- package/dist/js/packages/components/src/components/TextArea/TextArea.mjs +14 -13
- package/dist/js/packages/components/src/components/TextArea/TextArea.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Tooltip/Tooltip.mjs +1 -0
- package/dist/js/packages/components/src/components/Tooltip/Tooltip.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Tooltip/Tooltip.module.mjs +3 -1
- package/dist/js/packages/components/src/components/Tooltip/Tooltip.module.mjs.map +1 -1
- package/dist/js/packages/components/src/lib/controller/overlay/OverlayContextProvider.mjs +3 -6
- package/dist/js/packages/components/src/lib/controller/overlay/OverlayContextProvider.mjs.map +1 -1
- package/dist/js/packages/components/src/lib/controller/overlay/OverlayController.mjs.map +1 -1
- package/dist/js/packages/components/src/lib/controller/overlay/context.mjs +1 -1
- package/dist/js/packages/components/src/lib/controller/overlay/context.mjs.map +1 -1
- package/dist/js/packages/components/src/lib/controller/overlay/useOverlayController.mjs +1 -1
- package/dist/js/packages/components/src/lib/controller/overlay/useOverlayController.mjs.map +1 -1
- package/dist/js/packages/components/src/lib/files/acceptedFiles.mjs +34 -0
- package/dist/js/packages/components/src/lib/files/acceptedFiles.mjs.map +1 -0
- package/dist/js/packages/components/src/lib/hooks/dom/useKeepBrowserExtensionsInteractive.mjs +1 -1
- package/dist/js/packages/components/src/lib/hooks/dom/useKeepBrowserExtensionsInteractive.mjs.map +1 -1
- package/dist/js/packages/design-tokens/dist/json-runtime/all-dark.mjs +12 -0
- package/dist/js/packages/design-tokens/dist/json-runtime/all-dark.mjs.map +1 -1
- package/dist/js/packages/design-tokens/dist/json-runtime/all-light.mjs +12 -0
- package/dist/js/packages/design-tokens/dist/json-runtime/all-light.mjs.map +1 -1
- package/dist/types/components/Action/models/ActionModel.d.ts +4 -12
- package/dist/types/components/Action/types.d.ts +2 -9
- package/dist/types/components/CartesianChart/hooks/useChartAnimation.d.ts +1 -0
- package/dist/types/components/ContextMenu/lib.d.ts +1 -5
- package/dist/types/components/Icon/components/icons/index.d.ts +0 -1
- package/dist/types/components/List/hooks/useInfiniteScrollTrigger.d.ts +17 -0
- package/dist/types/components/PasswordCreationField/components/PasswordGenerateButton/PasswordGenerateButton.d.ts +0 -7
- package/dist/types/components/PasswordCreationField/components/ValidationResultButton/ValidationResultButton.d.ts +0 -9
- package/dist/types/components/PasswordCreationField/lib/ariaAnnounce.d.ts +1 -0
- package/dist/types/components/PasswordCreationField/lib/generateValidationTranslation.d.ts +0 -2
- package/dist/types/components/PasswordCreationField/lib/getStatusFromPolicyValidationResult.d.ts +0 -3
- package/dist/types/components/PasswordCreationField/lib/usePolicyValidationResult.d.ts +1 -7
- package/dist/types/components/Popover/components/PopoverTip/PopoverTip.d.ts +1 -2
- package/dist/types/lib/controller/overlay/OverlayContextProvider.d.ts +0 -9
- package/dist/types/lib/controller/overlay/OverlayController.d.ts +1 -11
- package/dist/types/lib/controller/overlay/context.d.ts +2 -19
- package/dist/types/lib/files/acceptedFiles.d.ts +1 -0
- package/dist/types/lib/hooks/dom/useKeepBrowserExtensionsInteractive.d.ts +1 -0
- package/dist/types/lib/theming/hooks/useDesignTokens.d.ts +14 -0
- package/package.json +10 -10
- package/dist/js/packages/components/src/components/Icon/components/icons/IconAIHosting.mjs +0 -19
- package/dist/js/packages/components/src/components/Icon/components/icons/IconAIHosting.mjs.map +0 -1
- package/dist/js/packages/components/src/components/List/components/Items/hooks/useInfiniteScrollTrigger.mjs.map +0 -1
- package/dist/types/components/Icon/components/icons/IconAIHosting.d.ts +0 -4
- package/dist/types/components/List/components/Items/hooks/useInfiniteScrollTrigger.d.ts +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.module.mjs","names":[],"sources":["../../../../../../../src/components/Modal/Modal.module.scss"],"sourcesContent":["@use \"@/styles/mixins/scrollbar\";\n\n/* Caps the dialog width per size token (`size-s` / `size-m` / `size-l`). */\n@mixin size($size) {\n &.size-#{$size} {\n [role=\"dialog\"] {\n width: inherit;\n max-width: var(--modal--size--#{$size});\n }\n }\n}\n\n/* ===========================================================================\n Base — shared by Modal (centered / bottom sheet) and OffCanvas (side panel).\n =========================================================================== */\n.modal,\n.offCanvas {\n /* Fixed full-viewport layer + centering wrapper. Sized to the visual\n viewport so it tracks the on-screen keyboard (Overlay sets the var). */\n > div {\n position: fixed;\n height: var(--visual-viewport-height);\n display: flex;\n flex-direction: column;\n width: 100%;\n justify-content: center;\n align-items: center;\n pointer-events: none;\n\n > div {\n display: flex;\n justify-content: center;\n width: inherit;\n max-height: var(--visual-viewport-height);\n padding: var(--size-px--xl);\n }\n }\n\n /* Close button: hidden when an action group is present, unless forced. */\n &:has(.actionGroup) {\n .closeButton:not(.alwaysVisible) {\n display: none;\n }\n }\n\n .closeButton.alwaysHidden {\n display: none;\n }\n\n /* The dialog card itself — a flex column. It is also the scroll container on\n mobile, and the sticky header/footer stack against its content only. */\n [role=\"dialog\"] {\n display: flex;\n flex-direction: column;\n isolation: isolate;\n max-height: 100%;\n overflow: hidden;\n outline: none;\n pointer-events: all;\n background-color: var(--modal--background-color);\n border: var(--modal--border-width) var(--modal--border-style)\n var(--modal--border-color);\n\n > form {\n display: contents;\n }\n }\n\n /* Header */\n header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .header {\n display: flex;\n align-items: center;\n gap: var(--heading--icon-to-text-spacing);\n padding-top: var(--modal--header-padding-top);\n padding-inline: var(--modal--padding);\n\n /* Flatten the heading's text and content spans into the header row, so\n badges stay behind the title instead of wrapping onto their own line. */\n > span {\n display: contents;\n }\n\n .headerTitle {\n min-width: 0;\n }\n\n .closeButton {\n order: 1;\n margin-inline-start: auto;\n }\n }\n\n /* Content — the scrollable body */\n .content,\n .columnLayout {\n @include scrollbar.scrollbar;\n\n flex-grow: 1;\n min-height: var(--modal--content-min-height);\n padding: var(--modal--padding);\n overflow: hidden auto;\n background-color: var(--modal--background-color);\n }\n\n .columnLayout .accentBox {\n position: sticky;\n top: 0;\n height: fit-content;\n }\n\n /* Action group — the footer */\n .actionGroup {\n padding: var(--modal--padding);\n background-color: var(--modal--footer-background-color);\n }\n\n /* ActionGroup's \"sorted\" layout sets flex-grow:1 on itself; the modal footer\n must keep its natural height (otherwise it fills the full-height off-canvas\n panel). Scope via the dialog so flex-grow:0 wins the equal-specificity /\n source-order battle — without raising the specificity of the padding above,\n which the mobile rules override. */\n [role=\"dialog\"] .actionGroup {\n flex-grow: 0;\n }\n}\n\n/* ===========================================================================\n Modal — desktop (centered dialog, zoom in/out).\n =========================================================================== */\n@media (width > 768px) {\n .modal {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n border-radius: var(--modal--corner-radius);\n }\n\n > div > div {\n &[data-entering] {\n animation: modal-zoom var(--transition--duration--slow)\n cubic-bezier(0.1, 0.8, 0.3, 1.2);\n }\n\n &[data-exiting] {\n animation: modal-zoom var(--transition--duration--default)\n cubic-bezier(0.1, 0.8, 0.3, 1.2) reverse;\n }\n }\n }\n}\n\n/* ===========================================================================\n OffCanvas — side panel (all widths), full height, slides in from the side.\n =========================================================================== */\n.offCanvas {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n height: 100%;\n border-radius: 0;\n }\n\n > div {\n padding-inline-start: var(--size-px--m);\n\n > div {\n justify-content: flex-end;\n width: inherit;\n height: inherit;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-right var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-right\n var(--transition--duration--default) reverse;\n }\n }\n }\n\n &.left {\n > div {\n padding-inline: 0 var(--size-px--m);\n\n > div {\n justify-content: flex-start;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-left var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-left\n var(--transition--duration--default) reverse;\n }\n }\n }\n }\n}\n\n/* ===========================================================================\n Modal — mobile (bottom sheet), issue #2423.\n\n One scroll container: the dialog scrolls while the header sticks to the top\n and the footer to the bottom. While the virtual keyboard is open\n (.keyboardVisible) both un-stick so header + content + footer flow normally\n and the content gets the full available space.\n =========================================================================== */\n@media (max-width: 768px) {\n .modal {\n /* Bottom-anchored sheet: rounded top corners + slide-up animation. */\n > div > div {\n margin-top: auto;\n width: 100%;\n padding: unset;\n overflow: hidden;\n border-start-start-radius: var(--modal--corner-radius);\n border-start-end-radius: var(--modal--corner-radius);\n pointer-events: all;\n\n &[data-entering] {\n animation: modal-slide-up var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-up var(--transition--duration--default) reverse;\n }\n }\n\n /* The dialog is the single scroll container. Capped just below the visual\n viewport so a full-height sheet keeps a small gap from the top edge. */\n [role=\"dialog\"] {\n @include scrollbar.scrollbar;\n\n width: inherit;\n max-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n overflow: hidden auto;\n }\n\n [role=\"dialog\"] > form {\n width: inherit;\n }\n\n /* flex: 0 0 auto keeps natural heights; without flex-shrink:0 the flex\n column compresses the content box and the (overflow:visible) fields\n spill over the footer. */\n .header,\n .content,\n .columnLayout,\n .actionGroup {\n flex: 0 0 auto;\n }\n\n .content,\n .columnLayout {\n min-height: 0;\n overflow: visible;\n }\n\n /* Docked header + footer (sticky within the single scroll container). */\n .header {\n position: sticky;\n top: 0;\n z-index: 1;\n background-color: var(--modal--background-color);\n }\n\n .actionGroup {\n position: sticky;\n bottom: 0;\n z-index: 1;\n }\n }\n\n /* Keyboard open: un-stick both so everything flows for maximum typing space.\n The dialog fills the available height and the footer uses margin-top:auto,\n so with SHORT content the footer still sits at the bottom (not mid-air) and\n with tall content it scrolls away with everything else. Never hidden. */\n .modal.keyboardVisible {\n [role=\"dialog\"] {\n min-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n }\n\n .header,\n .actionGroup {\n position: static;\n }\n\n .actionGroup {\n margin-top: auto;\n }\n }\n\n /* Shared mobile spacing + full-width stacked actions (Modal + OffCanvas). */\n .modal,\n .offCanvas {\n .header {\n padding-inline: var(--modal--padding-mobile);\n }\n\n .content,\n .columnLayout,\n .actionGroup {\n padding: var(--modal--padding-mobile);\n }\n\n .actionGroup {\n flex-direction: column;\n\n button {\n width: 100%;\n }\n }\n }\n}\n\n/* ===========================================================================\n Animations\n =========================================================================== */\n@keyframes modal-zoom {\n /* Start with scale(1) to let ResizeObservers take the correct width on mount. */\n 0% {\n opacity: 0;\n transform: scale(1);\n }\n\n 0.1% {\n opacity: 1;\n transform: scale(0.8);\n }\n\n 100% {\n transform: scale(1);\n }\n}\n\n@keyframes modal-slide-up {\n from {\n translate: 0 30%;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-right {\n from {\n translate: 30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-left {\n from {\n translate: -30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Modal.module.mjs","names":[],"sources":["../../../../../../../src/components/Modal/Modal.module.scss"],"sourcesContent":["@use \"@/styles/mixins/scrollbar\";\n\n/* Caps the dialog width per size token (`size-s` / `size-m` / `size-l`). */\n@mixin size($size) {\n &.size-#{$size} {\n [role=\"dialog\"] {\n width: inherit;\n max-width: var(--modal--size--#{$size});\n }\n }\n}\n\n/* ===========================================================================\n Base — shared by Modal (centered / bottom sheet) and OffCanvas (side panel).\n =========================================================================== */\n.modal,\n.offCanvas {\n /* Overlay.module.scss lays the viewport out; the dialog inside it is the only\n pointer target. */\n > :global(.flow--overlay--viewport) {\n pointer-events: none;\n\n /* react-aria's own modal wrapper — it gets no className from Flow, so\n its default `react-aria-Modal` class is the handle. */\n > :global(.react-aria-Modal) {\n display: flex;\n justify-content: center;\n width: inherit;\n max-height: var(--visual-viewport-height);\n padding: var(--size-px--xl);\n }\n }\n\n /* Close button: hidden when an action group is present, unless forced. */\n &:has(.actionGroup) {\n .closeButton:not(.alwaysVisible) {\n display: none;\n }\n }\n\n .closeButton.alwaysHidden {\n display: none;\n }\n\n /* The dialog card itself — a flex column. It is also the scroll container on\n mobile, and the sticky header/footer stack against its content only. */\n [role=\"dialog\"] {\n display: flex;\n flex-direction: column;\n isolation: isolate;\n max-height: 100%;\n overflow: hidden;\n outline: none;\n pointer-events: all;\n background-color: var(--modal--background-color);\n border: var(--modal--border-width) var(--modal--border-style)\n var(--modal--border-color);\n\n // A consumer `Form` (or raw `<form>`) wrapping the modal content. `Form`\n // is a plain FC, so the props context cannot give it a class.\n // stylelint-disable-next-line selector-max-type -- consumer form, no class\n > form {\n display: contents;\n }\n }\n\n /* Header */\n // A `Header`/`SectionHeader` — or a raw `<header>` — the consumer places in\n // the modal; both must match.\n // stylelint-disable-next-line selector-max-type -- consumer-placed header\n header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .header {\n display: flex;\n align-items: center;\n gap: var(--heading--icon-to-text-spacing);\n padding-top: var(--modal--header-padding-top);\n padding-inline: var(--modal--padding);\n\n /* Flatten the heading's text and content spans into the header row, so\n badges stay behind the title instead of wrapping onto their own line.\n Heading's spans do carry classes, but `.flow--heading--heading-content`\n already sets `display: none` on `:empty` at the same specificity a class\n selector here would reach — source order would then decide. */\n // stylelint-disable-next-line selector-max-type -- see above\n > span {\n display: contents;\n }\n\n .headerTitle {\n min-width: 0;\n }\n\n .closeButton {\n order: 1;\n margin-inline-start: auto;\n }\n }\n\n /* Content — the scrollable body */\n .content,\n .columnLayout {\n @include scrollbar.scrollbar;\n\n flex-grow: 1;\n min-height: var(--modal--content-min-height);\n padding: var(--modal--padding);\n overflow: hidden auto;\n background-color: var(--modal--background-color);\n }\n\n .columnLayout .accentBox {\n position: sticky;\n top: 0;\n height: fit-content;\n }\n\n /* Action group — the footer */\n .actionGroup {\n padding: var(--modal--padding);\n background-color: var(--modal--footer-background-color);\n }\n\n /* ActionGroup's \"sorted\" layout sets flex-grow:1 on itself; the modal footer\n must keep its natural height (otherwise it fills the full-height off-canvas\n panel). Scope via the dialog so flex-grow:0 wins the equal-specificity /\n source-order battle — without raising the specificity of the padding above,\n which the mobile rules override. */\n [role=\"dialog\"] .actionGroup {\n flex-grow: 0;\n }\n}\n\n/* ===========================================================================\n Modal — desktop (centered dialog, zoom in/out).\n =========================================================================== */\n@media (width > 768px) {\n .modal {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n border-radius: var(--modal--corner-radius);\n }\n\n > :global(.flow--overlay--viewport) > :global(.react-aria-Modal) {\n &[data-entering] {\n animation: modal-zoom var(--transition--duration--slow)\n cubic-bezier(0.1, 0.8, 0.3, 1.2);\n }\n\n &[data-exiting] {\n animation: modal-zoom var(--transition--duration--default)\n cubic-bezier(0.1, 0.8, 0.3, 1.2) reverse;\n }\n }\n }\n}\n\n/* ===========================================================================\n OffCanvas — side panel (all widths), full height, slides in from the side.\n =========================================================================== */\n.offCanvas {\n @include size(s);\n @include size(m);\n @include size(l);\n\n [role=\"dialog\"] {\n height: 100%;\n border-radius: 0;\n }\n\n > :global(.flow--overlay--viewport) {\n padding-inline-start: var(--size-px--m);\n\n > :global(.react-aria-Modal) {\n justify-content: flex-end;\n width: inherit;\n height: inherit;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-right var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-right\n var(--transition--duration--default) reverse;\n }\n }\n }\n\n &.left {\n > :global(.flow--overlay--viewport) {\n padding-inline: 0 var(--size-px--m);\n\n > :global(.react-aria-Modal) {\n justify-content: flex-start;\n padding: unset;\n\n &[data-entering] {\n animation: modal-slide-in-from-left var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-in-from-left\n var(--transition--duration--default) reverse;\n }\n }\n }\n }\n}\n\n/* ===========================================================================\n Modal — mobile (bottom sheet), issue #2423.\n\n One scroll container: the dialog scrolls while the header sticks to the top\n and the footer to the bottom. While the virtual keyboard is open\n (.keyboardVisible) both un-stick so header + content + footer flow normally\n and the content gets the full available space.\n =========================================================================== */\n@media (max-width: 768px) {\n .modal {\n /* Bottom-anchored sheet: rounded top corners + slide-up animation. */\n > :global(.flow--overlay--viewport) > :global(.react-aria-Modal) {\n margin-top: auto;\n width: 100%;\n padding: unset;\n overflow: hidden;\n border-start-start-radius: var(--modal--corner-radius);\n border-start-end-radius: var(--modal--corner-radius);\n pointer-events: all;\n\n &[data-entering] {\n animation: modal-slide-up var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: modal-slide-up var(--transition--duration--default) reverse;\n }\n }\n\n /* The dialog is the single scroll container. Capped just below the visual\n viewport so a full-height sheet keeps a small gap from the top edge. */\n [role=\"dialog\"] {\n @include scrollbar.scrollbar;\n\n width: inherit;\n max-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n overflow: hidden auto;\n }\n\n // stylelint-disable-next-line selector-max-type -- consumer form, see above\n [role=\"dialog\"] > form {\n width: inherit;\n }\n\n /* flex: 0 0 auto keeps natural heights; without flex-shrink:0 the flex\n column compresses the content box and the (overflow:visible) fields\n spill over the footer. */\n .header,\n .content,\n .columnLayout,\n .actionGroup {\n flex: 0 0 auto;\n }\n\n .content,\n .columnLayout {\n min-height: 0;\n overflow: visible;\n }\n\n /* Docked header + footer (sticky within the single scroll container). */\n .header {\n position: sticky;\n top: 0;\n z-index: 1;\n background-color: var(--modal--background-color);\n }\n\n .actionGroup {\n position: sticky;\n bottom: 0;\n z-index: 1;\n }\n }\n\n /* Keyboard open: un-stick both so everything flows for maximum typing space.\n The dialog fills the available height and the footer uses margin-top:auto,\n so with SHORT content the footer still sits at the bottom (not mid-air) and\n with tall content it scrolls away with everything else. Never hidden. */\n .modal.keyboardVisible {\n [role=\"dialog\"] {\n min-height: calc(\n var(--visual-viewport-height) - var(--modal--tray-top-spacing)\n );\n }\n\n .header,\n .actionGroup {\n position: static;\n }\n\n .actionGroup {\n margin-top: auto;\n }\n }\n\n /* Shared mobile spacing + full-width stacked actions (Modal + OffCanvas). */\n .modal,\n .offCanvas {\n .header {\n padding-inline: var(--modal--padding-mobile);\n }\n\n .content,\n .columnLayout,\n .actionGroup {\n padding: var(--modal--padding-mobile);\n }\n\n .actionGroup {\n flex-direction: column;\n\n // The footer's buttons are consumer `Button`/`Action` children; a raw\n // `<button>` matches today too.\n // stylelint-disable-next-line selector-max-type -- consumer children\n button {\n width: 100%;\n }\n }\n }\n}\n\n/* ===========================================================================\n Animations\n =========================================================================== */\n@keyframes modal-zoom {\n /* Start with scale(1) to let ResizeObservers take the correct width on mount. */\n 0% {\n opacity: 0;\n transform: scale(1);\n }\n\n 0.1% {\n opacity: 1;\n transform: scale(0.8);\n }\n\n 100% {\n transform: scale(1);\n }\n}\n\n@keyframes modal-slide-up {\n from {\n translate: 0 30%;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-right {\n from {\n translate: 30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n\n@keyframes modal-slide-in-from-left {\n from {\n translate: -30% 0;\n }\n\n to {\n translate: 0 0;\n }\n}\n"],"mappings":""}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/* */
|
|
3
3
|
//#region src/components/Navigation/Navigation.module.scss
|
|
4
4
|
var navigation = "flow--navigation";
|
|
5
|
+
var linkList = "flow--navigation--link-list";
|
|
5
6
|
var item = "flow--navigation--item";
|
|
6
7
|
var icon = "flow--navigation--icon";
|
|
7
8
|
var checkMark = "flow--navigation--check-mark";
|
|
@@ -11,6 +12,7 @@ var avatar = "flow--navigation--avatar";
|
|
|
11
12
|
var badge = "flow--navigation--badge";
|
|
12
13
|
var Navigation_module_default = {
|
|
13
14
|
navigation,
|
|
15
|
+
linkList,
|
|
14
16
|
item,
|
|
15
17
|
"switch": "flow--navigation--switch",
|
|
16
18
|
icon,
|
|
@@ -21,6 +23,6 @@ var Navigation_module_default = {
|
|
|
21
23
|
badge
|
|
22
24
|
};
|
|
23
25
|
//#endregion
|
|
24
|
-
export { ariaDisabled, avatar, badge, checkMark, controlIcon, Navigation_module_default as default, icon, item, navigation };
|
|
26
|
+
export { ariaDisabled, avatar, badge, checkMark, controlIcon, Navigation_module_default as default, icon, item, linkList, navigation };
|
|
25
27
|
|
|
26
28
|
//# sourceMappingURL=Navigation.module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Navigation.module.mjs","names":[],"sources":["../../../../../../../src/components/Navigation/Navigation.module.scss"],"sourcesContent":["@use \"@/styles/mixins/menuItem\";\n@use \"@/styles/mixins/ellipsis\";\n\n.navigation {\n display: flex;\n flex-direction: column;\n min-width: 0;\n\n
|
|
1
|
+
{"version":3,"file":"Navigation.module.mjs","names":[],"sources":["../../../../../../../src/components/Navigation/Navigation.module.scss"],"sourcesContent":["@use \"@/styles/mixins/menuItem\";\n@use \"@/styles/mixins/ellipsis\";\n\n.navigation {\n display: flex;\n flex-direction: column;\n min-width: 0;\n\n .linkList {\n display: flex;\n flex-direction: column;\n row-gap: var(--menu--item-to-item-spacing);\n\n &:empty {\n display: none;\n }\n }\n\n .item {\n @include menuItem.menuItem();\n\n > .icon {\n order: 0;\n flex-shrink: 0;\n }\n\n > :not(.icon) {\n order: 1;\n }\n\n > :not(.icon, .badge) {\n @include ellipsis.ellipsis;\n\n min-width: 0;\n }\n\n .badge {\n margin-inline-start: auto;\n flex-shrink: 0;\n }\n }\n\n // A consumer-placed `Separator`, which is a plain FC — Navigation's props\n // context cannot inject a class into it.\n // stylelint-disable-next-line selector-max-type -- consumer separator\n hr {\n margin-bottom: var(--menu--group-to-group-spacing);\n }\n}\n"],"mappings":""}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
/* */
|
|
3
3
|
import { UiComponentTunnelExit } from "../../../UiComponentTunnel/UiComponentTunnelExit.mjs";
|
|
4
|
+
import Navigation_module_default from "../../Navigation.module.mjs";
|
|
4
5
|
import { Children } from "react";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/Navigation/components/LinkListTunnelExit/LinkListTunnelExit.tsx
|
|
@@ -8,7 +9,10 @@ var LinkListTunnelExit = (props) => {
|
|
|
8
9
|
return /* @__PURE__ */ jsx(UiComponentTunnelExit, {
|
|
9
10
|
...props,
|
|
10
11
|
children: (children) => {
|
|
11
|
-
if (Children.count(children) >= 1) return /* @__PURE__ */ jsx("ul", {
|
|
12
|
+
if (Children.count(children) >= 1) return /* @__PURE__ */ jsx("ul", {
|
|
13
|
+
className: Navigation_module_default.linkList,
|
|
14
|
+
children
|
|
15
|
+
});
|
|
12
16
|
return null;
|
|
13
17
|
}
|
|
14
18
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LinkListTunnelExit.mjs","names":[],"sources":["../../../../../../../../../src/components/Navigation/components/LinkListTunnelExit/LinkListTunnelExit.tsx"],"sourcesContent":["import { Children } from \"react\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\n\nexport const LinkListTunnelExit: typeof UiComponentTunnelExit = (props) => {\n return (\n <UiComponentTunnelExit\n {...props}\n children={(children) => {\n if (Children.count(children) >= 1) {\n return <ul>{children}</ul>;\n }\n\n return null;\n }}\n />\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"LinkListTunnelExit.mjs","names":[],"sources":["../../../../../../../../../src/components/Navigation/components/LinkListTunnelExit/LinkListTunnelExit.tsx"],"sourcesContent":["import { Children } from \"react\";\nimport { UiComponentTunnelExit } from \"@/components/UiComponentTunnel/UiComponentTunnelExit\";\nimport styles from \"../../Navigation.module.scss\";\n\nexport const LinkListTunnelExit: typeof UiComponentTunnelExit = (props) => {\n return (\n <UiComponentTunnelExit\n {...props}\n children={(children) => {\n if (Children.count(children) >= 1) {\n return <ul className={styles.linkList}>{children}</ul>;\n }\n\n return null;\n }}\n />\n );\n};\n"],"mappings":";;;;;;;AAIA,IAAa,sBAAoD,UAAU;CACzE,OACE,oBAAC,uBAAD;EACE,GAAI;EACJ,WAAW,aAAa;GACtB,IAAI,SAAS,MAAM,QAAQ,KAAK,GAC9B,OAAO,oBAAC,MAAD;IAAI,WAAW,0BAAO;IAAW;GAAa,CAAA;GAGvD,OAAO;EACT;CACD,CAAA;AAEL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationGroup.module.mjs","names":[],"sources":["../../../../../../../../../src/components/Navigation/components/NavigationGroup/NavigationGroup.module.scss"],"sourcesContent":[".navigationGroup {\n &:not(.collapsable)
|
|
1
|
+
{"version":3,"file":"NavigationGroup.module.mjs","names":[],"sources":["../../../../../../../../../src/components/Navigation/components/NavigationGroup/NavigationGroup.module.scss"],"sourcesContent":[".navigationGroup {\n &:not(.collapsable) :global(.flow--navigation--link-list) {\n padding-inline: var(--accordion--padding-x);\n padding-block: var(--accordion--padding-y);\n }\n\n &:not(:last-child) {\n margin-bottom: var(--menu--group-to-group-spacing);\n }\n\n .label {\n display: block;\n padding-block: var(--accordion--padding-y);\n padding-inline: var(--accordion--padding-x);\n }\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberField.module.mjs","names":[],"sources":["../../../../../../../src/components/NumberField/NumberField.module.scss"],"sourcesContent":["@use \"@/styles/mixins/formControl\";\n\n.group {\n order: 2;\n display: grid;\n grid-template-columns: 1fr max-content;\n grid-template-areas:\n \"input increment\"\n \"input decrement\";\n\n .input {\n grid-area: input;\n\n @include formControl.formControl();\n }\n\n .decrementButton {\n grid-area: decrement;\n }\n\n .incrementButton {\n grid-area: increment;\n }\n\n .decrementButton,\n .incrementButton {\n border-width: var(--form-control--border-width);\n border-color: var(--form-control--border-color--default);\n border-style: solid;\n }\n\n &[aria-invalid] {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--invalid);\n }\n }\n\n &[data-focus-within] {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--focused);\n }\n }\n\n &[aria-disabled=\"true\"] {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--disabled);\n }\n }\n\n &:has(input:read-only):not([aria-disabled=\"true\"]) {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--default);\n }\n }\n}\n\n@media (pointer: fine) {\n .group {\n .input {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .incrementButton,\n .decrementButton {\n border-inline-start: none;\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n padding-block: 0;\n\n .coarsePointerIcon {\n display: none;\n }\n }\n\n .decrementButton {\n border-start-end-radius: 0;\n }\n\n .incrementButton {\n border-block-end: none;\n border-end-end-radius: 0;\n }\n }\n}\n\n@media (pointer: coarse) {\n .group {\n grid-template-columns: auto 1fr auto;\n grid-template-areas: \"decrement input increment\";\n max-width: 100%;\n\n .input {\n border-radius: 0;\n min-width: 0;\n }\n\n .decrementButton,\n .incrementButton {\n padding-inline: var(--button--padding-icon-only);\n\n .coarsePointerIcon {\n width: var(--icon--size--m);\n height: var(--icon--size--m);\n }\n\n svg:not(.coarsePointerIcon) {\n display: none;\n }\n }\n\n .decrementButton {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n border-inline-end: none;\n }\n\n .incrementButton {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n border-inline-start: none;\n }\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"NumberField.module.mjs","names":[],"sources":["../../../../../../../src/components/NumberField/NumberField.module.scss"],"sourcesContent":["@use \"@/styles/mixins/formControl\";\n\n.group {\n order: 2;\n display: grid;\n grid-template-columns: 1fr max-content;\n grid-template-areas:\n \"input increment\"\n \"input decrement\";\n\n .input {\n grid-area: input;\n\n @include formControl.formControl();\n }\n\n .decrementButton {\n grid-area: decrement;\n }\n\n .incrementButton {\n grid-area: increment;\n }\n\n .decrementButton,\n .incrementButton {\n border-width: var(--form-control--border-width);\n border-color: var(--form-control--border-color--default);\n border-style: solid;\n }\n\n &[aria-invalid] {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--invalid);\n }\n }\n\n &[data-focus-within] {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--focused);\n }\n }\n\n &[aria-disabled=\"true\"] {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--disabled);\n }\n }\n\n &:has(.input:read-only):not([aria-disabled=\"true\"]) {\n .decrementButton,\n .incrementButton {\n border-color: var(--form-control--border-color--default);\n }\n }\n}\n\n@media (pointer: fine) {\n .group {\n .input {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .incrementButton,\n .decrementButton {\n border-inline-start: none;\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n padding-block: 0;\n\n .coarsePointerIcon {\n display: none;\n }\n }\n\n .decrementButton {\n border-start-end-radius: 0;\n }\n\n .incrementButton {\n border-block-end: none;\n border-end-end-radius: 0;\n }\n }\n}\n\n@media (pointer: coarse) {\n .group {\n grid-template-columns: auto 1fr auto;\n grid-template-areas: \"decrement input increment\";\n max-width: 100%;\n\n .input {\n border-radius: 0;\n min-width: 0;\n }\n\n .decrementButton,\n .incrementButton {\n padding-inline: var(--button--padding-icon-only);\n\n .coarsePointerIcon {\n width: var(--icon--size--m);\n height: var(--icon--size--m);\n }\n\n // Hides every icon the stepper buttons render except the coarse-pointer\n // one — Button's own pending/success/failure icons included, and those\n // share no class with the chevrons.\n // stylelint-disable-next-line selector-max-type -- see above\n svg:not(.coarsePointerIcon) {\n display: none;\n }\n }\n\n .decrementButton {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n border-inline-end: none;\n }\n\n .incrementButton {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n border-inline-start: none;\n }\n }\n}\n"],"mappings":""}
|
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
/* */
|
|
3
3
|
//#region src/components/Overlay/Overlay.module.scss
|
|
4
4
|
var overlay = "flow--overlay";
|
|
5
|
+
var viewport = "flow--overlay--viewport";
|
|
5
6
|
var suspense = "flow--overlay--suspense";
|
|
6
7
|
var Overlay_module_default = {
|
|
7
8
|
overlay,
|
|
8
9
|
"overlay-fade": "flow--overlay--overlay-fade",
|
|
10
|
+
viewport,
|
|
9
11
|
suspense
|
|
10
12
|
};
|
|
11
13
|
//#endregion
|
|
12
|
-
export { Overlay_module_default as default, overlay, suspense };
|
|
14
|
+
export { Overlay_module_default as default, overlay, suspense, viewport };
|
|
13
15
|
|
|
14
16
|
//# sourceMappingURL=Overlay.module.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Overlay.module.mjs","names":[],"sources":["../../../../../../../src/components/Overlay/Overlay.module.scss"],"sourcesContent":["/* Full-page backdrop behind every overlay (Modal, LightBox, …).\n\n Sized to `--visual-viewport-height`, the property the Modal wrapper uses too,\n so the whole overlay stack tracks the on-screen keyboard as one. react-aria\n publishes it on this element and updates it with the visual viewport.\n\n Not `--page-height` (the full document height): this element is `fixed`, so\n anything past the viewport never shows, while `backdrop-filter` blurs the\n whole layer every frame. Chromium drops an oversized one for single frames\n while scrolling, flickering backdrop and modal alike. #2338 switched\n `absolute` → `fixed` and added the blur, but kept the `--page-height` that\n `absolute` had needed. */\n.overlay {\n position: fixed;\n top: 0;\n inset-inline-start: 0;\n width: 100%;\n height: var(--visual-viewport-height);\n isolation: isolate;\n background: var(--overlay--backdrop-color);\n backdrop-filter: blur(var(--blur));\n\n &[data-entering] {\n animation: overlay-fade var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: overlay-fade var(--transition--duration--default) reverse ease-in;\n }\n}\n\n/* Fallback shown while suspended overlay content loads. */\n.suspense {\n padding: var(--size-px--xl);\n}\n\n@keyframes overlay-fade {\n 0% {\n opacity: 0;\n }\n\n 50% {\n opacity: 1;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n@media (prefers-contrast: more), (prefers-reduced-transparency) {\n .overlay {\n backdrop-filter: none;\n background-color: var(--dark--color--600);\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Overlay.module.mjs","names":[],"sources":["../../../../../../../src/components/Overlay/Overlay.module.scss"],"sourcesContent":["/* Full-page backdrop behind every overlay (Modal, LightBox, …).\n\n Sized to `--visual-viewport-height`, the property the Modal wrapper uses too,\n so the whole overlay stack tracks the on-screen keyboard as one. react-aria\n publishes it on this element and updates it with the visual viewport.\n\n Not `--page-height` (the full document height): this element is `fixed`, so\n anything past the viewport never shows, while `backdrop-filter` blurs the\n whole layer every frame. Chromium drops an oversized one for single frames\n while scrolling, flickering backdrop and modal alike. #2338 switched\n `absolute` → `fixed` and added the blur, but kept the `--page-height` that\n `absolute` had needed. */\n.overlay {\n position: fixed;\n top: 0;\n inset-inline-start: 0;\n width: 100%;\n height: var(--visual-viewport-height);\n isolation: isolate;\n background: var(--overlay--backdrop-color);\n backdrop-filter: blur(var(--blur));\n\n &[data-entering] {\n animation: overlay-fade var(--transition--duration--slow);\n }\n\n &[data-exiting] {\n animation: overlay-fade var(--transition--duration--default) reverse ease-in;\n }\n}\n\n/* Centering viewport between the backdrop and react-aria's modal wrapper.\n Sized to the visual viewport so it tracks the on-screen keyboard. Modal,\n OffCanvas and LightBox refine it (padding, pointer-events) — they used to\n repeat this whole block behind a `> div` selector each. */\n.viewport {\n position: fixed;\n height: var(--visual-viewport-height);\n display: flex;\n flex-direction: column;\n width: 100%;\n justify-content: center;\n align-items: center;\n}\n\n/* Fallback shown while suspended overlay content loads. */\n.suspense {\n padding: var(--size-px--xl);\n}\n\n@keyframes overlay-fade {\n 0% {\n opacity: 0;\n }\n\n 50% {\n opacity: 1;\n }\n\n 100% {\n opacity: 1;\n }\n}\n\n@media (prefers-contrast: more), (prefers-reduced-transparency) {\n .overlay {\n backdrop-filter: none;\n background-color: var(--dark--color--600);\n }\n}\n"],"mappings":""}
|
|
@@ -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
|
|
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, {
|
|
62
|
-
|
|
63
|
-
children: /* @__PURE__ */ jsx(Aria.
|
|
64
|
-
|
|
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
|
};
|
package/dist/js/packages/components/src/components/Overlay/components/OverlayContent.mjs.map
CHANGED
|
@@ -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 {
|
|
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"}
|
|
@@ -31,7 +31,6 @@ var OverlayTrigger = (props) => {
|
|
|
31
31
|
return /* @__PURE__ */ jsx(OverlayContextProvider, {
|
|
32
32
|
type: overlayType,
|
|
33
33
|
controller: overlayController,
|
|
34
|
-
isOverlayContent: false,
|
|
35
34
|
children: /* @__PURE__ */ jsx(PropsContextProvider, {
|
|
36
35
|
props: propsContext,
|
|
37
36
|
dependencies: [isPending, overlayController],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverlayTrigger.mjs","names":[],"sources":["../../../../../../../src/components/OverlayTrigger/OverlayTrigger.tsx"],"sourcesContent":["import type { ComponentType, FC, PropsWithChildren, ReactNode } from \"react\";\nimport { OverlayController } from \"@/lib/controller\";\nimport type { PropsContext } from \"@/lib/propsContext\";\nimport { PropsContextProvider } from \"@/lib/propsContext\";\nimport { useComponentPropsContext } from \"@/lib/propsContext/propsContext\";\nimport type { FlowComponentName } from \"@/components/propTypes\";\nimport OverlayContextProvider from \"@/lib/controller/overlay/OverlayContextProvider\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { useIsPendingWithWait } from \"@/components/Action/hooks/useIsPendingWithWait\";\n\ntype AriaComponentType = ComponentType<{\n isOpen?: boolean;\n children: ReactNode;\n}>;\n\nexport interface OverlayTriggerProps\n extends FlowComponentProps, PropsWithChildren {\n /** Whether the overlay should be open initially. */\n isDefaultOpen?: boolean;\n /** A controller to control the state of the overlay. */\n controller?: OverlayController;\n}\n\ninterface Props extends OverlayTriggerProps {\n overlayType: FlowComponentName;\n component: AriaComponentType;\n}\n\nexport const OverlayTrigger: FC<Props> = (props) => {\n const {\n overlayType,\n isDefaultOpen = false,\n component: AriaOverlayTrigger,\n children,\n controller: controllerFromProps,\n } = props;\n const buttonPropsContext = useComponentPropsContext(\"Button\");\n\n const newOverlayController = OverlayController.useNew({ isDefaultOpen });\n const overlayController = controllerFromProps ?? newOverlayController;\n const isOpen = overlayController.useIsOpen();\n const isContentSuspended = overlayController.useIsContentSuspended();\n const isPending = useIsPendingWithWait(isContentSuspended);\n\n const propsContext: PropsContext = {\n Button: {\n onPress: overlayController.open,\n isPending,\n isDisabled: Boolean(buttonPropsContext?.isDisabled) || isContentSuspended,\n /*\n * The button is the react-aria trigger. react-aria hands the press\n * handling, the trigger ref and `aria-haspopup`/`aria-expanded`/\n * `aria-controls` down through a `PressResponder`, which only reaches its\n * own subtree — so a surrounding props context must not tunnel the button\n * out of the trigger. Such a context has to tunnel the trigger as a whole\n * instead (`overlayTriggersTunneledTo`).\n */\n tunnel: null,\n },\n /*\n * The aria trigger this renders is the host-side identity of every trigger\n * that is not `@flr-generate` (see `overlayTriggersTunneledTo`), so a\n * tunneling props context targets it as well. Here it is already inside the\n * trigger the author wrote — tunneling it again would send the same subtree\n * into the exit twice.\n */\n DialogTrigger: {\n tunnel: null,\n },\n /*\n * The overlay is already placed — this trigger owns it. A surrounding\n * props context that moves or wraps the bare overlay, so that it can be\n * written without a trigger (`Badge`), must not reach this one: it would\n * pull the overlay out of its trigger and add a second trigger button\n * around it.\n */\n [overlayType]: {\n tunnel: null,\n wrapWith: null,\n },\n };\n\n return (\n <OverlayContextProvider
|
|
1
|
+
{"version":3,"file":"OverlayTrigger.mjs","names":[],"sources":["../../../../../../../src/components/OverlayTrigger/OverlayTrigger.tsx"],"sourcesContent":["import type { ComponentType, FC, PropsWithChildren, ReactNode } from \"react\";\nimport { OverlayController } from \"@/lib/controller\";\nimport type { PropsContext } from \"@/lib/propsContext\";\nimport { PropsContextProvider } from \"@/lib/propsContext\";\nimport { useComponentPropsContext } from \"@/lib/propsContext/propsContext\";\nimport type { FlowComponentName } from \"@/components/propTypes\";\nimport OverlayContextProvider from \"@/lib/controller/overlay/OverlayContextProvider\";\nimport type { FlowComponentProps } from \"@/lib/componentFactory/flowComponent\";\nimport { useIsPendingWithWait } from \"@/components/Action/hooks/useIsPendingWithWait\";\n\ntype AriaComponentType = ComponentType<{\n isOpen?: boolean;\n children: ReactNode;\n}>;\n\nexport interface OverlayTriggerProps\n extends FlowComponentProps, PropsWithChildren {\n /** Whether the overlay should be open initially. */\n isDefaultOpen?: boolean;\n /** A controller to control the state of the overlay. */\n controller?: OverlayController;\n}\n\ninterface Props extends OverlayTriggerProps {\n overlayType: FlowComponentName;\n component: AriaComponentType;\n}\n\nexport const OverlayTrigger: FC<Props> = (props) => {\n const {\n overlayType,\n isDefaultOpen = false,\n component: AriaOverlayTrigger,\n children,\n controller: controllerFromProps,\n } = props;\n const buttonPropsContext = useComponentPropsContext(\"Button\");\n\n const newOverlayController = OverlayController.useNew({ isDefaultOpen });\n const overlayController = controllerFromProps ?? newOverlayController;\n const isOpen = overlayController.useIsOpen();\n const isContentSuspended = overlayController.useIsContentSuspended();\n const isPending = useIsPendingWithWait(isContentSuspended);\n\n const propsContext: PropsContext = {\n Button: {\n onPress: overlayController.open,\n isPending,\n isDisabled: Boolean(buttonPropsContext?.isDisabled) || isContentSuspended,\n /*\n * The button is the react-aria trigger. react-aria hands the press\n * handling, the trigger ref and `aria-haspopup`/`aria-expanded`/\n * `aria-controls` down through a `PressResponder`, which only reaches its\n * own subtree — so a surrounding props context must not tunnel the button\n * out of the trigger. Such a context has to tunnel the trigger as a whole\n * instead (`overlayTriggersTunneledTo`).\n */\n tunnel: null,\n },\n /*\n * The aria trigger this renders is the host-side identity of every trigger\n * that is not `@flr-generate` (see `overlayTriggersTunneledTo`), so a\n * tunneling props context targets it as well. Here it is already inside the\n * trigger the author wrote — tunneling it again would send the same subtree\n * into the exit twice.\n */\n DialogTrigger: {\n tunnel: null,\n },\n /*\n * The overlay is already placed — this trigger owns it. A surrounding\n * props context that moves or wraps the bare overlay, so that it can be\n * written without a trigger (`Badge`), must not reach this one: it would\n * pull the overlay out of its trigger and add a second trigger button\n * around it.\n */\n [overlayType]: {\n tunnel: null,\n wrapWith: null,\n },\n };\n\n return (\n <OverlayContextProvider type={overlayType} controller={overlayController}>\n <PropsContextProvider\n props={propsContext}\n dependencies={[isPending, overlayController]}\n >\n <AriaOverlayTrigger isOpen={isOpen}>{children}</AriaOverlayTrigger>\n </PropsContextProvider>\n </OverlayContextProvider>\n );\n};\n\nexport default OverlayTrigger;\n"],"mappings":";;;;;;;;;AA4BA,IAAa,kBAA6B,UAAU;CAClD,MAAM,EACJ,aACA,gBAAgB,OAChB,WAAW,oBACX,UACA,YAAY,wBACV;CACJ,MAAM,qBAAqB,yBAAyB,QAAQ;CAE5D,MAAM,uBAAuB,kBAAkB,OAAO,EAAE,cAAc,CAAC;CACvE,MAAM,oBAAoB,uBAAuB;CACjD,MAAM,SAAS,kBAAkB,UAAU;CAC3C,MAAM,qBAAqB,kBAAkB,sBAAsB;CACnE,MAAM,YAAY,qBAAqB,kBAAkB;CAEzD,MAAM,eAA6B;EACjC,QAAQ;GACN,SAAS,kBAAkB;GAC3B;GACA,YAAY,QAAQ,oBAAoB,UAAU,KAAK;GASvD,QAAQ;EACV;EAQA,eAAe,EACb,QAAQ,KACV;GAQC,cAAc;GACb,QAAQ;GACR,UAAU;EACZ;CACF;CAEA,OACE,oBAAC,wBAAD;EAAwB,MAAM;EAAa,YAAY;EACrD,UAAA,oBAAC,sBAAD;GACE,OAAO;GACP,cAAc,CAAC,WAAW,iBAAiB;GAE3C,UAAA,oBAAC,oBAAD;IAA4B;IAAS;GAA6B,CAAA;EAC9C,CAAA;CACA,CAAA;AAE5B"}
|
package/dist/js/packages/components/src/components/PasswordCreationField/PasswordCreationField.mjs
CHANGED
|
@@ -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":"
|
|
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 "
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
|
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, {
|
|
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(
|
|
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,
|
|
26
|
-
|
|
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
|
|
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"}
|