@alto-avios/alto-ui 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/assets/Accordion.css +1 -0
  2. package/dist/assets/AviosBadge.css +1 -0
  3. package/dist/assets/Button.css +1 -1
  4. package/dist/assets/CalloutBanner.css +1 -0
  5. package/dist/assets/ErrorSummary.css +1 -0
  6. package/dist/assets/FieldHeader.css +1 -1
  7. package/dist/assets/FieldLabel.css +1 -1
  8. package/dist/assets/Menu.css +1 -0
  9. package/dist/assets/PhoneNumberField.css +1 -1
  10. package/dist/assets/SelectCard.css +1 -0
  11. package/dist/assets/Tag.css +1 -1
  12. package/dist/assets/flex.css +1 -1
  13. package/dist/assets/position.css +1 -0
  14. package/dist/components/Accordion/Accordion.d.ts +35 -0
  15. package/dist/components/Accordion/Accordion.js +140 -0
  16. package/dist/components/Accordion/Accordion.js.map +1 -0
  17. package/dist/components/Accordion/index.d.ts +1 -0
  18. package/dist/components/Accordion/index.js +5 -0
  19. package/dist/components/Accordion/index.js.map +1 -0
  20. package/dist/components/AviosBadge/AviosBadge.d.ts +34 -0
  21. package/dist/components/AviosBadge/AviosBadge.js +58 -0
  22. package/dist/components/AviosBadge/AviosBadge.js.map +1 -0
  23. package/dist/components/AviosBadge/index.d.ts +1 -0
  24. package/dist/components/AviosBadge/index.js +5 -0
  25. package/dist/components/AviosBadge/index.js.map +1 -0
  26. package/dist/components/Badge/Badge.d.ts +7 -2
  27. package/dist/components/Badge/Badge.js +2 -1
  28. package/dist/components/Badge/Badge.js.map +1 -1
  29. package/dist/components/Box/Box.d.ts +4 -4
  30. package/dist/components/Box/Box.js +6 -3
  31. package/dist/components/Box/Box.js.map +1 -1
  32. package/dist/components/Button/Button.js +31 -31
  33. package/dist/components/CalloutBanner/CalloutBanner.d.ts +21 -0
  34. package/dist/components/CalloutBanner/CalloutBanner.js +96 -0
  35. package/dist/components/CalloutBanner/CalloutBanner.js.map +1 -0
  36. package/dist/components/CalloutBanner/index.d.ts +1 -0
  37. package/dist/components/CalloutBanner/index.js +5 -0
  38. package/dist/components/CalloutBanner/index.js.map +1 -0
  39. package/dist/components/Checkbox/index.d.ts +1 -0
  40. package/dist/components/CreditCardNumberField/CreditCardNumberField.d.ts +2 -2
  41. package/dist/components/CreditCardNumberField/CreditCardNumberField.js +4 -3
  42. package/dist/components/CreditCardNumberField/CreditCardNumberField.js.map +1 -1
  43. package/dist/components/DateField/DateField.d.ts +22 -3
  44. package/dist/components/DateField/DateField.js +856 -2
  45. package/dist/components/DateField/DateField.js.map +1 -1
  46. package/dist/components/ErrorSummary/ErrorSummary.d.ts +7 -0
  47. package/dist/components/ErrorSummary/ErrorSummary.js +44 -0
  48. package/dist/components/ErrorSummary/ErrorSummary.js.map +1 -0
  49. package/dist/components/ErrorSummary/index.d.ts +1 -0
  50. package/dist/components/ErrorSummary/index.js +5 -0
  51. package/dist/components/ErrorSummary/index.js.map +1 -0
  52. package/dist/components/FieldHeader/FieldHeader.js +6 -6
  53. package/dist/components/FieldLabel/FieldLabel.js +1 -1
  54. package/dist/components/Heading/Heading.d.ts +1 -1
  55. package/dist/components/Icon/Icon.d.ts +1 -0
  56. package/dist/components/Image/Image.d.ts +1 -1
  57. package/dist/components/Menu/Menu.d.ts +75 -0
  58. package/dist/components/Menu/Menu.js +356 -0
  59. package/dist/components/Menu/Menu.js.map +1 -0
  60. package/dist/components/Menu/index.d.ts +1 -0
  61. package/dist/components/Menu/index.js +5 -0
  62. package/dist/components/Menu/index.js.map +1 -0
  63. package/dist/components/Paragraph/Paragraph.d.ts +1 -1
  64. package/dist/components/PhoneNumberField/PhoneNumberField.d.ts +1 -1
  65. package/dist/components/PhoneNumberField/PhoneNumberField.js +13 -8
  66. package/dist/components/PhoneNumberField/PhoneNumberField.js.map +1 -1
  67. package/dist/components/Popover/Popover.d.ts +36 -4
  68. package/dist/components/Popover/Popover.js +21 -24
  69. package/dist/components/Popover/Popover.js.map +1 -1
  70. package/dist/components/Radio/index.d.ts +1 -0
  71. package/dist/components/SelectCard/SelectCard.d.ts +51 -0
  72. package/dist/components/SelectCard/SelectCard.js +85 -0
  73. package/dist/components/SelectCard/SelectCard.js.map +1 -0
  74. package/dist/components/SelectCard/index.d.ts +1 -0
  75. package/dist/components/SelectCard/index.js +5 -0
  76. package/dist/components/SelectCard/index.js.map +1 -0
  77. package/dist/components/SubHeading/SubHeading.d.ts +1 -1
  78. package/dist/components/Tag/Tag.js +4 -4
  79. package/dist/components/index.d.ts +6 -0
  80. package/dist/components/index.js +16 -4
  81. package/dist/components/index.js.map +1 -1
  82. package/dist/index.js +16 -4
  83. package/dist/index.js.map +1 -1
  84. package/dist/utils/border/border.d.ts +2 -2
  85. package/dist/utils/flex/flex.d.ts +10 -5
  86. package/dist/utils/flex/flex.js +383 -51
  87. package/dist/utils/flex/flex.js.map +1 -1
  88. package/dist/utils/flex/flex.test.d.ts +1 -0
  89. package/dist/utils/focus/focusStyles.d.ts +1 -1
  90. package/dist/utils/foregroundColour/foregroundColor.d.ts +1 -1
  91. package/dist/utils/position/position.d.ts +8 -0
  92. package/dist/utils/position/position.js +57 -0
  93. package/dist/utils/position/position.js.map +1 -0
  94. package/dist/utils/position/position.test.d.ts +1 -0
  95. package/dist/utils/stories/iconPropsArgTypes.js +49 -49
  96. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DateField.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"DateField.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,7 @@
1
+ import { CalloutBannerProps } from '../CalloutBanner/CalloutBanner';
2
+ import { default as React } from 'react';
3
+ export interface ErrorSummaryProps extends Pick<CalloutBannerProps, 'title' | 'description'> {
4
+ children: React.ReactElement[] | React.ReactElement;
5
+ }
6
+ export declare const ErrorSummary: ({ title, description, children, }: ErrorSummaryProps) => import("react/jsx-runtime").JSX.Element | null;
7
+ export default ErrorSummary;
@@ -0,0 +1,44 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { CalloutBanner } from "../CalloutBanner/CalloutBanner.js";
3
+ import React, { useState } from "react";
4
+ import Link from "../Link/Link.js";
5
+ import { Box } from "../Box/Box.js";
6
+ import '../../assets/ErrorSummary.css';const styles = {
7
+ "errorSummary-list": "_errorSummary-list_yt1ru_1",
8
+ "errorSummary-list__item": "_errorSummary-list__item_yt1ru_24"
9
+ };
10
+ const MAX_ERRORS_SHOW = 4;
11
+ const ErrorSummary = ({
12
+ title,
13
+ description,
14
+ children
15
+ }) => {
16
+ const [showAllErrors, setShowAllErrors] = useState(false);
17
+ if (!children || Array.isArray(children) && children.length === 0) {
18
+ return null;
19
+ }
20
+ const errorsToRender = showAllErrors ? children : Array.isArray(children) ? children.slice(0, MAX_ERRORS_SHOW) : [children];
21
+ const hasMoreErrors = Array.isArray(children) && children.length > MAX_ERRORS_SHOW;
22
+ return /* @__PURE__ */ jsx(CalloutBanner, { title, description, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
23
+ /* @__PURE__ */ jsx("div", { className: styles["errorSummary-list"], children: React.Children.map(errorsToRender, (child, index) => {
24
+ if (React.isValidElement(child)) {
25
+ return React.cloneElement(child, {
26
+ key: `error-${index}`,
27
+ iconEndProps: false
28
+ });
29
+ }
30
+ return child;
31
+ }) }),
32
+ hasMoreErrors && !showAllErrors && /* @__PURE__ */ jsx(Link, { onPress: () => setShowAllErrors(true), underline: true, size: "sm", iconEndProps: {
33
+ iconName: "chevron-down"
34
+ }, children: "Show more" }),
35
+ showAllErrors && hasMoreErrors && /* @__PURE__ */ jsx(Link, { onPress: () => setShowAllErrors(false), underline: true, size: "sm", iconEndProps: {
36
+ iconName: "chevron-up"
37
+ }, children: "Show less" })
38
+ ] }) });
39
+ };
40
+ export {
41
+ ErrorSummary,
42
+ ErrorSummary as default
43
+ };
44
+ //# sourceMappingURL=ErrorSummary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorSummary.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1 @@
1
+ export { default } from './ErrorSummary';
@@ -0,0 +1,5 @@
1
+ import { ErrorSummary } from "./ErrorSummary.js";
2
+ export {
3
+ ErrorSummary as default
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -2,10 +2,10 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import FieldDescription from "../FieldDescription/FieldDescription.js";
3
3
  import { FieldError } from "../FieldError/FieldError.js";
4
4
  import { FieldLabel } from "../FieldLabel/FieldLabel.js";
5
- import '../../assets/FieldHeader.css';const fieldHeader = "_fieldHeader_3rjlj_1";
5
+ import '../../assets/FieldHeader.css';const fieldHeader = "_fieldHeader_jjofg_1";
6
6
  const styles = {
7
7
  fieldHeader,
8
- "label-container": "_label-container_3rjlj_12"
8
+ "label-container": "_label-container_jjofg_13"
9
9
  };
10
10
  const FieldHeader = ({
11
11
  label,
@@ -16,10 +16,10 @@ const FieldHeader = ({
16
16
  labelFor
17
17
  }) => {
18
18
  return /* @__PURE__ */ jsxs("div", { className: styles.fieldHeader, "data-invalid": isInvalid ? true : void 0, children: [
19
- /* @__PURE__ */ jsxs("div", { className: styles["label-container"], children: [
20
- label && /* @__PURE__ */ jsx(FieldLabel, { required: isRequired ? true : void 0, htmlFor: labelFor, children: label }),
21
- !isRequired && /* @__PURE__ */ jsx("span", { children: "Optional" })
22
- ] }),
19
+ /* @__PURE__ */ jsx("div", { className: styles["label-container"], children: label && /* @__PURE__ */ jsxs(FieldLabel, { required: isRequired ? true : void 0, htmlFor: labelFor, children: [
20
+ label,
21
+ !isRequired && ` (Optional)`
22
+ ] }) }),
23
23
  description && /* @__PURE__ */ jsx(FieldDescription, { children: description }),
24
24
  /* @__PURE__ */ jsx(FieldError, { children: errorMessage })
25
25
  ] });
@@ -1,6 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Label } from "react-aria-components";
3
- import '../../assets/FieldLabel.css';const fieldLabel = "_fieldLabel_1jqml_1";
3
+ import '../../assets/FieldLabel.css';const fieldLabel = "_fieldLabel_vou7c_1";
4
4
  const styles = {
5
5
  fieldLabel
6
6
  };
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
3
3
  import { ForegroundVariants } from '../../utils/foregroundColour/foregroundColor';
4
4
  declare const heading: (props?: ({
5
5
  size?: "lg" | "sm" | "xs" | "xl" | "md" | "xxs" | null | undefined;
6
- textAlign?: "end" | "start" | "center" | null | undefined;
6
+ textAlign?: "center" | "end" | "start" | null | undefined;
7
7
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
8
8
  type HeadingVariants = VariantProps<typeof heading>;
9
9
  type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'span' | 'legend';
@@ -13,6 +13,7 @@ export interface IconProps {
13
13
  iconName: string;
14
14
  /**
15
15
  * The prefix of the icon to load.
16
+ * See https://docs.fontawesome.com/web/add-icons/how-to
16
17
  * @default 'fas'
17
18
  */
18
19
  iconPrefix?: 'fas' | 'far' | 'fal' | 'fab' | 'fad' | 'fak';
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
3
3
  import { BorderVariants } from '../../utils/border/border';
4
4
  import { BackgroundVariants } from '../../utils/backgroundColor/backgroundColor';
5
5
  declare const imageVariants: (props?: ({
6
- fit?: "fill" | "none" | "contain" | "cover" | "scale-down" | "scaleDown" | null | undefined;
6
+ fit?: "none" | "fill" | "contain" | "cover" | "scale-down" | "scaleDown" | null | undefined;
7
7
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
8
8
  export interface ImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'className' | 'style'>, Pick<BorderVariants, 'borderRadius'>, BackgroundVariants, VariantProps<typeof imageVariants> {
9
9
  /** Image url that will be used as a fallback in case `src` prop is not set or image cannot be loaded */
@@ -0,0 +1,75 @@
1
+ import { default as React } from 'react';
2
+ import { ButtonProps } from '../Button/Button';
3
+ type PlacementType = 'top' | 'top start' | 'top end' | 'right' | 'right top' | 'right bottom' | 'bottom' | 'bottom start' | 'bottom end' | 'left' | 'left top' | 'left bottom';
4
+ export interface MenuProps {
5
+ /**
6
+ * The label text for the menu button
7
+ */
8
+ label: string;
9
+ /**
10
+ * Accessible label for the menu (for screen readers)
11
+ * Defaults to the button label if not provided
12
+ * @default props.label
13
+ */
14
+ 'aria-label'?: string;
15
+ /**
16
+ * Whether the menu is disabled
17
+ * @default false
18
+ */
19
+ isDisabled?: boolean;
20
+ /**
21
+ * The placement of the menu relative to the button
22
+ * @default 'bottom start'
23
+ */
24
+ placement?: PlacementType;
25
+ /**
26
+ * The button emphasis
27
+ * @default 'tertiary'
28
+ */
29
+ buttonEmphasis?: ButtonProps['emphasis'];
30
+ /**
31
+ * The button style variant
32
+ * @default 'neutral'
33
+ */
34
+ buttonStyleVariant?: ButtonProps['styleVariant'];
35
+ /**
36
+ * The button style variant when menu is open
37
+ * @default buttonStyleVariant value
38
+ */
39
+ buttonOpenVariant?: ButtonProps['styleVariant'];
40
+ /**
41
+ * ID of an element to position the menu against instead of the button
42
+ * When provided, the menu will be positioned relative to this element
43
+ */
44
+ alignToElementId?: string;
45
+ /**
46
+ * Whether the menu should flip to fit in the viewport
47
+ * @default false
48
+ */
49
+ shouldFlip?: boolean;
50
+ /**
51
+ * Whether the menu should open on hover and focus
52
+ * @default false
53
+ */
54
+ openOnHoverAndFocus?: boolean;
55
+ /**
56
+ * Whether to allow tabbing out of the menu
57
+ * @default true
58
+ */
59
+ allowTabOut?: boolean;
60
+ /**
61
+ * The content to display inside the menu (as JSX children)
62
+ * Can be custom HTML content
63
+ */
64
+ children: React.ReactNode;
65
+ /**
66
+ * Icon props for the start of the button
67
+ */
68
+ iconStartProps?: ButtonProps['iconStartProps'];
69
+ /**
70
+ * Icon props for the end of the button
71
+ */
72
+ iconEndProps?: ButtonProps['iconEndProps'];
73
+ }
74
+ export declare const Menu: ({ label, "aria-label": ariaLabel, isDisabled, placement, buttonEmphasis, buttonStyleVariant, buttonOpenVariant, alignToElementId, shouldFlip, openOnHoverAndFocus, allowTabOut, children, iconStartProps, iconEndProps, }: MenuProps) => import("react/jsx-runtime").JSX.Element;
75
+ export default Menu;
@@ -0,0 +1,356 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useId, useState, useRef, useEffect } from "react";
3
+ import { Button } from "../Button/Button.js";
4
+ import { Popover } from "../Popover/Popover.js";
5
+ import '../../assets/Menu.css';const menuContainer = "_menuContainer_qw7ps_1";
6
+ const menuContent = "_menuContent_qw7ps_6";
7
+ const styles = {
8
+ menuContainer,
9
+ menuContent
10
+ };
11
+ const menuRegistry = {
12
+ activeMenuId: null,
13
+ setActiveMenu: (id) => {
14
+ menuRegistry.activeMenuId = id;
15
+ },
16
+ getActiveMenu: () => menuRegistry.activeMenuId
17
+ };
18
+ const Menu = ({
19
+ label,
20
+ "aria-label": ariaLabel,
21
+ isDisabled = false,
22
+ placement = "bottom start",
23
+ buttonEmphasis = "tertiary",
24
+ buttonStyleVariant = "neutral",
25
+ buttonOpenVariant = buttonStyleVariant,
26
+ alignToElementId,
27
+ shouldFlip = false,
28
+ openOnHoverAndFocus = false,
29
+ allowTabOut = true,
30
+ children,
31
+ iconStartProps,
32
+ iconEndProps
33
+ }) => {
34
+ const uniqueId = useId();
35
+ const menuId = `menu-${uniqueId}`;
36
+ const [isOpen, setIsOpen] = useState(false);
37
+ const [preventFocusOpen, setPreventFocusOpen] = useState(false);
38
+ const [openedViaKeyboard, setOpenedViaKeyboard] = useState(false);
39
+ const buttonRef = useRef(null);
40
+ const containerRef = useRef(null);
41
+ const menuRef = useRef(null);
42
+ const currentStyleVariant = isOpen ? buttonOpenVariant : buttonStyleVariant;
43
+ const getAlignmentTarget = () => {
44
+ if (alignToElementId) {
45
+ const targetElement = document.getElementById(alignToElementId);
46
+ if (targetElement) {
47
+ return {
48
+ current: targetElement
49
+ };
50
+ }
51
+ }
52
+ return buttonRef;
53
+ };
54
+ const handleKeyDown = (e) => {
55
+ if (e.key === "Escape" && isOpen) {
56
+ e.preventDefault();
57
+ menuRegistry.setActiveMenu(null);
58
+ setIsOpen(false);
59
+ setPreventFocusOpen(true);
60
+ setTimeout(() => {
61
+ setPreventFocusOpen(false);
62
+ }, 300);
63
+ } else if (e.key === "ArrowDown" && !isOpen) {
64
+ e.preventDefault();
65
+ if (menuRegistry.getActiveMenu() && menuRegistry.getActiveMenu() !== menuId) {
66
+ document.dispatchEvent(new CustomEvent("menu:closeAll", {
67
+ detail: {
68
+ exceptMenuId: menuId
69
+ }
70
+ }));
71
+ }
72
+ menuRegistry.setActiveMenu(menuId);
73
+ setIsOpen(true);
74
+ setOpenedViaKeyboard(true);
75
+ }
76
+ };
77
+ const closeMenu = () => {
78
+ menuRegistry.setActiveMenu(null);
79
+ setIsOpen(false);
80
+ setPreventFocusOpen(true);
81
+ setTimeout(() => {
82
+ setPreventFocusOpen(false);
83
+ }, 300);
84
+ };
85
+ const toggleMenu = () => {
86
+ const isKeyboardFocused = document.activeElement === buttonRef.current;
87
+ setOpenedViaKeyboard(isKeyboardFocused);
88
+ if (isDisabled) return;
89
+ if (!isOpen) {
90
+ if (menuRegistry.getActiveMenu() && menuRegistry.getActiveMenu() !== menuId) {
91
+ document.dispatchEvent(new CustomEvent("menu:closeAll", {
92
+ detail: {
93
+ exceptMenuId: menuId
94
+ }
95
+ }));
96
+ }
97
+ menuRegistry.setActiveMenu(menuId);
98
+ setIsOpen(true);
99
+ } else {
100
+ menuRegistry.setActiveMenu(null);
101
+ setIsOpen(false);
102
+ }
103
+ };
104
+ useEffect(() => {
105
+ const handleCloseAll = (e) => {
106
+ const {
107
+ exceptMenuId
108
+ } = e.detail || {};
109
+ if (exceptMenuId && exceptMenuId === menuId) {
110
+ return;
111
+ }
112
+ if (isOpen) {
113
+ setIsOpen(false);
114
+ }
115
+ };
116
+ document.addEventListener("menu:closeAll", handleCloseAll);
117
+ return () => {
118
+ document.removeEventListener("menu:closeAll", handleCloseAll);
119
+ };
120
+ }, [menuId, isOpen]);
121
+ useEffect(() => {
122
+ const isActive = menuRegistry.getActiveMenu() === menuId;
123
+ if (isActive !== isOpen) {
124
+ setIsOpen(isActive);
125
+ }
126
+ }, [menuId, isOpen]);
127
+ const findNextFocusableElement = (startElement) => {
128
+ const focusableElements = Array.from(document.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'));
129
+ const startIndex = focusableElements.indexOf(startElement);
130
+ return startIndex !== -1 && startIndex < focusableElements.length - 1 ? focusableElements[startIndex + 1] : focusableElements[0];
131
+ };
132
+ const handleMenuKeyDown = (e) => {
133
+ if (e.key === "Escape") {
134
+ e.preventDefault();
135
+ closeMenu();
136
+ if (buttonRef.current) {
137
+ buttonRef.current.focus();
138
+ }
139
+ return;
140
+ }
141
+ if (e.key === "Tab" && allowTabOut) {
142
+ e.preventDefault();
143
+ const triggerElement = buttonRef.current;
144
+ if (triggerElement) {
145
+ const nextElement = findNextFocusableElement(triggerElement);
146
+ closeMenu();
147
+ setTimeout(() => {
148
+ if (nextElement) {
149
+ nextElement.focus();
150
+ }
151
+ }, 10);
152
+ } else {
153
+ closeMenu();
154
+ }
155
+ return;
156
+ }
157
+ if (!menuRef.current) return;
158
+ const focusableElements = Array.from(menuRef.current.querySelectorAll('[role="menuitem"], a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'));
159
+ if (focusableElements.length === 0) return;
160
+ const focusedElement = document.activeElement;
161
+ const currentIndex = focusableElements.indexOf(focusedElement);
162
+ if (e.key === "ArrowDown" && currentIndex < focusableElements.length - 1) {
163
+ e.preventDefault();
164
+ focusableElements[currentIndex + 1].focus();
165
+ } else if (e.key === "ArrowUp" && currentIndex > 0) {
166
+ e.preventDefault();
167
+ focusableElements[currentIndex - 1].focus();
168
+ } else if (e.key === "Home") {
169
+ e.preventDefault();
170
+ focusableElements[0].focus();
171
+ } else if (e.key === "End") {
172
+ e.preventDefault();
173
+ focusableElements[focusableElements.length - 1].focus();
174
+ }
175
+ };
176
+ useEffect(() => {
177
+ if (isOpen && openedViaKeyboard && menuRef.current) {
178
+ const focusableElements = menuRef.current.querySelectorAll('[role="menuitem"], a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])');
179
+ if (focusableElements.length > 0) {
180
+ setTimeout(() => {
181
+ focusableElements[0].focus();
182
+ }, 50);
183
+ }
184
+ }
185
+ }, [isOpen, openedViaKeyboard]);
186
+ useEffect(() => {
187
+ if (!openOnHoverAndFocus || isDisabled) return;
188
+ let hoverTimeout = null;
189
+ const handleMouseEnter = () => {
190
+ if (hoverTimeout) {
191
+ clearTimeout(hoverTimeout);
192
+ }
193
+ hoverTimeout = setTimeout(() => {
194
+ if (menuRegistry.getActiveMenu() && menuRegistry.getActiveMenu() !== menuId) {
195
+ document.dispatchEvent(new CustomEvent("menu:closeAll", {
196
+ detail: {
197
+ exceptMenuId: menuId
198
+ }
199
+ }));
200
+ }
201
+ setOpenedViaKeyboard(false);
202
+ menuRegistry.setActiveMenu(menuId);
203
+ setIsOpen(true);
204
+ }, 300);
205
+ };
206
+ const handleMouseLeave = () => {
207
+ if (hoverTimeout) {
208
+ clearTimeout(hoverTimeout);
209
+ }
210
+ hoverTimeout = setTimeout(() => {
211
+ if (menuRegistry.getActiveMenu() === menuId) {
212
+ menuRegistry.setActiveMenu(null);
213
+ }
214
+ setIsOpen(false);
215
+ }, 300);
216
+ };
217
+ const buttonElement = buttonRef.current;
218
+ if (buttonElement) {
219
+ buttonElement.addEventListener("mouseenter", handleMouseEnter);
220
+ buttonElement.addEventListener("mouseleave", handleMouseLeave);
221
+ }
222
+ return () => {
223
+ if (buttonElement) {
224
+ buttonElement.removeEventListener("mouseenter", handleMouseEnter);
225
+ buttonElement.removeEventListener("mouseleave", handleMouseLeave);
226
+ }
227
+ if (hoverTimeout) {
228
+ clearTimeout(hoverTimeout);
229
+ }
230
+ };
231
+ }, [openOnHoverAndFocus, isDisabled, menuId]);
232
+ useEffect(() => {
233
+ if (!openOnHoverAndFocus || isDisabled) return;
234
+ const buttonElement = buttonRef.current;
235
+ if (!buttonElement) return;
236
+ let focusTimeout = null;
237
+ let isMenuOpening = false;
238
+ const handleFocus = () => {
239
+ if (preventFocusOpen) return;
240
+ if (focusTimeout) {
241
+ clearTimeout(focusTimeout);
242
+ }
243
+ isMenuOpening = true;
244
+ focusTimeout = setTimeout(() => {
245
+ if (menuRegistry.getActiveMenu() && menuRegistry.getActiveMenu() !== menuId) {
246
+ document.dispatchEvent(new CustomEvent("menu:closeAll", {
247
+ detail: {
248
+ exceptMenuId: menuId
249
+ }
250
+ }));
251
+ }
252
+ const activeElement = document.activeElement;
253
+ const wasFocusedByKeyboard = activeElement === buttonElement && document.activeElement !== document.body;
254
+ setOpenedViaKeyboard(wasFocusedByKeyboard);
255
+ menuRegistry.setActiveMenu(menuId);
256
+ setIsOpen(true);
257
+ setTimeout(() => {
258
+ isMenuOpening = false;
259
+ }, 500);
260
+ }, 300);
261
+ };
262
+ const handleBlur = (e) => {
263
+ if (isMenuOpening) return;
264
+ if (focusTimeout) {
265
+ clearTimeout(focusTimeout);
266
+ }
267
+ const containerElement = containerRef.current;
268
+ const relatedTarget = e.relatedTarget;
269
+ if (containerElement && !containerElement.contains(relatedTarget)) {
270
+ focusTimeout = setTimeout(() => {
271
+ if (menuRegistry.getActiveMenu() === menuId) {
272
+ menuRegistry.setActiveMenu(null);
273
+ }
274
+ setIsOpen(false);
275
+ }, 200);
276
+ }
277
+ };
278
+ buttonElement.addEventListener("focus", handleFocus);
279
+ buttonElement.addEventListener("blur", handleBlur);
280
+ return () => {
281
+ buttonElement.removeEventListener("focus", handleFocus);
282
+ buttonElement.removeEventListener("blur", handleBlur);
283
+ if (focusTimeout) {
284
+ clearTimeout(focusTimeout);
285
+ }
286
+ };
287
+ }, [openOnHoverAndFocus, isDisabled, preventFocusOpen, menuId]);
288
+ useEffect(() => {
289
+ if (!isOpen) return;
290
+ const handleOutsideClick = (e) => {
291
+ const container = containerRef.current;
292
+ if (container && !container.contains(e.target)) {
293
+ if (menuRegistry.getActiveMenu() === menuId) {
294
+ menuRegistry.setActiveMenu(null);
295
+ }
296
+ setIsOpen(false);
297
+ }
298
+ };
299
+ document.addEventListener("mousedown", handleOutsideClick);
300
+ return () => {
301
+ document.removeEventListener("mousedown", handleOutsideClick);
302
+ };
303
+ }, [isOpen, menuId]);
304
+ useEffect(() => {
305
+ if (!isOpen) {
306
+ setOpenedViaKeyboard(false);
307
+ }
308
+ }, [isOpen]);
309
+ const menuTriggerState = {
310
+ isOpen,
311
+ open: () => {
312
+ if (menuRegistry.getActiveMenu() && menuRegistry.getActiveMenu() !== menuId) {
313
+ document.dispatchEvent(new CustomEvent("menu:closeAll", {
314
+ detail: {
315
+ exceptMenuId: menuId
316
+ }
317
+ }));
318
+ }
319
+ menuRegistry.setActiveMenu(menuId);
320
+ setIsOpen(true);
321
+ },
322
+ close: closeMenu,
323
+ toggle: () => toggleMenu(),
324
+ setOpen: (open) => {
325
+ if (open) {
326
+ if (menuRegistry.getActiveMenu() && menuRegistry.getActiveMenu() !== menuId) {
327
+ document.dispatchEvent(new CustomEvent("menu:closeAll", {
328
+ detail: {
329
+ exceptMenuId: menuId
330
+ }
331
+ }));
332
+ }
333
+ menuRegistry.setActiveMenu(menuId);
334
+ } else {
335
+ if (menuRegistry.getActiveMenu() === menuId) {
336
+ menuRegistry.setActiveMenu(null);
337
+ }
338
+ }
339
+ setIsOpen(open);
340
+ }
341
+ };
342
+ return /* @__PURE__ */ jsxs("div", { className: styles.menuContainer, ref: containerRef, children: [
343
+ /* @__PURE__ */ jsx(Button, { ref: buttonRef, emphasis: buttonEmphasis, styleVariant: currentStyleVariant, isDisabled, onPress: () => {
344
+ setPreventFocusOpen(false);
345
+ const isKeyboardFocused = document.activeElement === buttonRef.current;
346
+ setOpenedViaKeyboard(isKeyboardFocused);
347
+ toggleMenu();
348
+ }, onKeyDown: handleKeyDown, "aria-haspopup": "true", "aria-expanded": isOpen, iconStartProps, iconEndProps, children: label }),
349
+ isOpen && /* @__PURE__ */ jsx(Popover, { state: menuTriggerState, triggerRef: getAlignmentTarget(), hasArrow: false, placement, shouldFlip, isNonModal: true, allowTabOut, autoFocus: openedViaKeyboard, children: /* @__PURE__ */ jsx("div", { className: styles.menuContent, ref: menuRef, onKeyDown: handleMenuKeyDown, role: "menu", "aria-label": ariaLabel || label, tabIndex: -1, children }) })
350
+ ] });
351
+ };
352
+ export {
353
+ Menu,
354
+ Menu as default
355
+ };
356
+ //# sourceMappingURL=Menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Menu.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1 @@
1
+ export { default } from './Menu';
@@ -0,0 +1,5 @@
1
+ import { Menu } from "./Menu.js";
2
+ export {
3
+ Menu as default
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -3,7 +3,7 @@ import { VariantProps } from 'class-variance-authority';
3
3
  import { ForegroundVariants } from '../../utils/foregroundColour/foregroundColor';
4
4
  declare const paragraph: (props?: ({
5
5
  size?: "lg" | "sm" | "xs" | "md" | null | undefined;
6
- textAlign?: "end" | "start" | "center" | null | undefined;
6
+ textAlign?: "center" | "end" | "start" | null | undefined;
7
7
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
8
8
  type ParagraphVariants = VariantProps<typeof paragraph>;
9
9
  type ParagraphSize = 'xs' | 'sm' | 'md' | 'lg';
@@ -49,5 +49,5 @@ export interface PhoneNumberFieldProps extends FieldProps {
49
49
  */
50
50
  placeholder?: string;
51
51
  }
52
- export declare const PhoneNumberField: ({ defaultCountry, hasCountrySelect, options, onChange, countryCode: countryCodeControlled, value: valueControlled, countryCodes, isFormatted, id: providedId, countrySelectLabel, ...props }: PhoneNumberFieldProps) => import("react/jsx-runtime").JSX.Element;
52
+ export declare const PhoneNumberField: ({ defaultCountry, hasCountrySelect, options, onChange, countryCode: countryCodeControlled, value: valueControlled, countryCodes, isFormatted, id: providedId, countrySelectLabel, label, ...props }: PhoneNumberFieldProps) => import("react/jsx-runtime").JSX.Element;
53
53
  export default PhoneNumberField;
@@ -4,10 +4,10 @@ import { TextField, Input } from "react-aria-components";
4
4
  import { u as usePatternFormat, N as NumberFormatBase } from "../../react-number-format.es-DMLgWFZX.js";
5
5
  import { DEFAULT_PATTERN, getCountryOptions, getCountryFlagEmoji, countryPhoneMap, getPhoneNumberValue } from "../../utils/phoneNumber/phoneNumber.js";
6
6
  import { Field } from "../_base/Field/Field.js";
7
- import '../../assets/PhoneNumberField.css';const phoneNumberField = "_phoneNumberField_1owea_1";
8
- const selectHandlerValue = "_selectHandlerValue_1owea_35";
9
- const selectHandlerIcon = "_selectHandlerIcon_1owea_66";
10
- const selectWrapper = "_selectWrapper_1owea_78";
7
+ import '../../assets/PhoneNumberField.css';const phoneNumberField = "_phoneNumberField_1udln_1";
8
+ const selectHandlerValue = "_selectHandlerValue_1udln_34";
9
+ const selectHandlerIcon = "_selectHandlerIcon_1udln_65";
10
+ const selectWrapper = "_selectWrapper_1udln_77";
11
11
  const styles = {
12
12
  phoneNumberField,
13
13
  selectHandlerValue,
@@ -25,6 +25,7 @@ const PhoneNumberField = ({
25
25
  isFormatted = true,
26
26
  id: providedId,
27
27
  countrySelectLabel = "Select a country",
28
+ label = "Phone number",
28
29
  ...props
29
30
  }) => {
30
31
  const inputRef = useRef(null);
@@ -73,15 +74,19 @@ const PhoneNumberField = ({
73
74
  };
74
75
  const countryOptions = getCountryOptions(options, countryCodes);
75
76
  const defaultValue = countryOptions.find((opt) => opt.value === defaultCountry);
76
- return /* @__PURE__ */ jsx(Field, { as: TextField, ...props, children: /* @__PURE__ */ jsxs("div", { className: styles.phoneNumberField, children: [
77
- /* @__PURE__ */ jsx(NumberFormatBase, { type: "tel", format: handleFormat, autoComplete: "tel", customInput: Input, getInputRef: inputRef, "aria-label": (props == null ? void 0 : props.label) || "Phone number", ...inputProps, onValueChange: (e) => handleInputChange(e.value), value: valueDefinitive, placeholder: props == null ? void 0 : props.placeholder }),
77
+ return /* @__PURE__ */ jsx(Field, { as: TextField, label, labelFor: id, ...props, children: /* @__PURE__ */ jsxs("div", { className: styles.phoneNumberField, children: [
78
78
  hasCountrySelect && /* @__PURE__ */ jsxs("div", { className: styles.selectWrapper, children: [
79
- /* @__PURE__ */ jsx("select", { className: styles.selectInput, defaultValue: defaultValue == null ? void 0 : defaultValue.value, value: countryCodeDefinitive, onChange: (e) => handleSelectCountry(e.target.value), "aria-label": countrySelectLabel, id: `${id}-country-select`, disabled: props == null ? void 0 : props.isDisabled, "aria-disabled": props == null ? void 0 : props.isDisabled, children: countryOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value)) }),
79
+ /* @__PURE__ */ jsx("select", { className: styles.selectInput, ...countryCodeControlled ? {
80
+ value: countryCodeDefinitive
81
+ } : {
82
+ defaultValue: defaultValue == null ? void 0 : defaultValue.value
83
+ }, onChange: (e) => handleSelectCountry(e.target.value), "aria-label": countrySelectLabel, id: `${id}-country-select`, disabled: props == null ? void 0 : props.isDisabled, "aria-disabled": props == null ? void 0 : props.isDisabled, children: countryOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value)) }),
80
84
  /* @__PURE__ */ jsxs("div", { className: styles.selectHandlerValue, "aria-hidden": "true", id: `${id}-country-display`, children: [
81
85
  getCountryFlagEmoji(countryCodeDefinitive) + " " + countryCodeDefinitive,
82
86
  /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "16", fill: "none", viewBox: "0 0 13 16", className: styles.selectHandlerIcon, "aria-hidden": "true", role: "presentation", children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "m6.926 2.664 3.75 3c.305.281.375.75.117 1.055-.28.328-.75.375-1.054.117L6.457 4.21l-3.28 2.625a.747.747 0 0 1-1.056-.117.75.75 0 0 1 .118-1.055l3.75-3c.257-.21.656-.21.937 0m3.75 8.672-3.75 3a.71.71 0 0 1-.937 0l-3.75-3c-.328-.258-.399-.727-.118-1.055a.77.77 0 0 1 1.055-.117l3.281 2.625 3.282-2.625c.304-.258.797-.187 1.054.117a.77.77 0 0 1-.117 1.055" }) })
83
87
  ] })
84
- ] })
88
+ ] }),
89
+ /* @__PURE__ */ jsx(NumberFormatBase, { id, type: "tel", format: handleFormat, autoComplete: "tel", customInput: Input, getInputRef: inputRef, ...inputProps, onValueChange: (e) => handleInputChange(e.value), value: valueDefinitive, placeholder: props == null ? void 0 : props.placeholder })
85
90
  ] }) });
86
91
  };
87
92
  export {