@simplybusiness/mobius 10.3.1 → 10.3.3

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 (185) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/components/AddressLookup/AddressLookup.js +888 -5314
  3. package/dist/cjs/components/AddressLookup/AddressLookup.js.map +4 -4
  4. package/dist/cjs/components/AddressLookup/index.js +952 -5381
  5. package/dist/cjs/components/AddressLookup/index.js.map +4 -4
  6. package/dist/cjs/components/Alert/Alert.js +1 -1
  7. package/dist/cjs/components/Alert/Alert.js.map +2 -2
  8. package/dist/cjs/components/Alert/index.js +1 -1
  9. package/dist/cjs/components/Alert/index.js.map +2 -2
  10. package/dist/cjs/components/Checkbox/Checkbox.js +245 -5625
  11. package/dist/cjs/components/Checkbox/Checkbox.js.map +4 -4
  12. package/dist/cjs/components/Checkbox/CheckboxGroup.js +371 -5614
  13. package/dist/cjs/components/Checkbox/CheckboxGroup.js.map +4 -4
  14. package/dist/cjs/components/Checkbox/index.js +377 -5619
  15. package/dist/cjs/components/Checkbox/index.js.map +4 -4
  16. package/dist/cjs/components/Combobox/Combobox.js +829 -5344
  17. package/dist/cjs/components/Combobox/Combobox.js.map +4 -4
  18. package/dist/cjs/components/Combobox/index.js +813 -5327
  19. package/dist/cjs/components/Combobox/index.js.map +4 -4
  20. package/dist/cjs/components/DateField/DateField.js +379 -5505
  21. package/dist/cjs/components/DateField/DateField.js.map +4 -4
  22. package/dist/cjs/components/DateField/index.js +383 -5508
  23. package/dist/cjs/components/DateField/index.js.map +4 -4
  24. package/dist/cjs/components/ErrorMessage/ErrorMessage.js +120 -5643
  25. package/dist/cjs/components/ErrorMessage/ErrorMessage.js.map +4 -4
  26. package/dist/cjs/components/ErrorMessage/index.js +121 -5642
  27. package/dist/cjs/components/ErrorMessage/index.js.map +4 -4
  28. package/dist/cjs/components/ExpandableText/ExpandableText.js +511 -5604
  29. package/dist/cjs/components/ExpandableText/ExpandableText.js.map +4 -4
  30. package/dist/cjs/components/ExpandableText/index.js +512 -5604
  31. package/dist/cjs/components/ExpandableText/index.js.map +4 -4
  32. package/dist/cjs/components/MaskedField/MaskedField.js +394 -5511
  33. package/dist/cjs/components/MaskedField/MaskedField.js.map +4 -4
  34. package/dist/cjs/components/MaskedField/index.js +294 -5119
  35. package/dist/cjs/components/MaskedField/index.js.map +4 -4
  36. package/dist/cjs/components/NumberField/NumberField.js +357 -5523
  37. package/dist/cjs/components/NumberField/NumberField.js.map +4 -4
  38. package/dist/cjs/components/NumberField/index.js +358 -5523
  39. package/dist/cjs/components/NumberField/index.js.map +4 -4
  40. package/dist/cjs/components/PasswordField/PasswordField.js +330 -5511
  41. package/dist/cjs/components/PasswordField/PasswordField.js.map +4 -4
  42. package/dist/cjs/components/PasswordField/ShowHideButton.js +32 -5655
  43. package/dist/cjs/components/PasswordField/ShowHideButton.js.map +4 -4
  44. package/dist/cjs/components/PasswordField/index.js +332 -5512
  45. package/dist/cjs/components/PasswordField/index.js.map +4 -4
  46. package/dist/cjs/components/Radio/Radio.js +242 -5632
  47. package/dist/cjs/components/Radio/Radio.js.map +4 -4
  48. package/dist/cjs/components/Radio/RadioGroup.js +274 -5619
  49. package/dist/cjs/components/Radio/RadioGroup.js.map +4 -4
  50. package/dist/cjs/components/Radio/index.js +404 -5628
  51. package/dist/cjs/components/Radio/index.js.map +4 -4
  52. package/dist/cjs/components/Select/Select.js +233 -5588
  53. package/dist/cjs/components/Select/Select.js.map +4 -4
  54. package/dist/cjs/components/Select/index.js +234 -5588
  55. package/dist/cjs/components/Select/index.js.map +4 -4
  56. package/dist/cjs/components/TextArea/TextArea.js +229 -5531
  57. package/dist/cjs/components/TextArea/TextArea.js.map +4 -4
  58. package/dist/cjs/components/TextArea/index.js +230 -5531
  59. package/dist/cjs/components/TextArea/index.js.map +4 -4
  60. package/dist/cjs/components/TextField/TextField.js +265 -5522
  61. package/dist/cjs/components/TextField/TextField.js.map +4 -4
  62. package/dist/cjs/components/TextField/index.js +266 -5522
  63. package/dist/cjs/components/TextField/index.js.map +4 -4
  64. package/dist/cjs/components/TextOrHTML/TextOrHTML.js +1 -1
  65. package/dist/cjs/components/TextOrHTML/TextOrHTML.js.map +2 -2
  66. package/dist/cjs/components/TextOrHTML/index.js +1 -1
  67. package/dist/cjs/components/TextOrHTML/index.js.map +2 -2
  68. package/dist/cjs/components/index.js +4273 -5291
  69. package/dist/cjs/components/index.js.map +4 -4
  70. package/dist/cjs/index.js +4304 -5334
  71. package/dist/cjs/index.js.map +4 -4
  72. package/dist/cjs/meta.json +5692 -51487
  73. package/dist/esm/chunk-3O5DIIGS.js +1 -0
  74. package/dist/esm/chunk-3O5DIIGS.js.map +7 -0
  75. package/dist/esm/chunk-3PRSHEVX.js +1 -0
  76. package/dist/esm/chunk-3PRSHEVX.js.map +7 -0
  77. package/dist/esm/chunk-4NBLO5WK.js +54 -0
  78. package/dist/esm/chunk-4NBLO5WK.js.map +7 -0
  79. package/dist/esm/chunk-4WVJNNBK.js +157 -0
  80. package/dist/esm/chunk-4WVJNNBK.js.map +7 -0
  81. package/dist/esm/chunk-5L4G4VLM.js +1 -0
  82. package/dist/esm/chunk-5L4G4VLM.js.map +7 -0
  83. package/dist/esm/chunk-5OFXPT4J.js +135 -0
  84. package/dist/esm/chunk-5OFXPT4J.js.map +7 -0
  85. package/dist/esm/chunk-6O77SOOB.js +1 -0
  86. package/dist/esm/chunk-6O77SOOB.js.map +7 -0
  87. package/dist/esm/chunk-AFU7BFCD.js +151 -0
  88. package/dist/esm/chunk-AFU7BFCD.js.map +7 -0
  89. package/dist/esm/chunk-BGR2OTTR.js +1 -0
  90. package/dist/esm/chunk-BGR2OTTR.js.map +7 -0
  91. package/dist/esm/chunk-BIGO5EVC.js +1 -0
  92. package/dist/esm/chunk-BIGO5EVC.js.map +7 -0
  93. package/dist/esm/chunk-CUOVI2HT.js +1 -0
  94. package/dist/esm/chunk-CUOVI2HT.js.map +7 -0
  95. package/dist/esm/{chunk-HPUPB75I.js → chunk-DYTHXKMX.js} +2 -2
  96. package/dist/esm/chunk-DYTHXKMX.js.map +7 -0
  97. package/dist/esm/chunk-F4RQKLF7.js +1 -0
  98. package/dist/esm/chunk-F4RQKLF7.js.map +7 -0
  99. package/dist/esm/chunk-FKTDL7KO.js +355 -0
  100. package/dist/esm/chunk-FKTDL7KO.js.map +7 -0
  101. package/dist/esm/{chunk-NRU3WNV7.js → chunk-JNAQ76CR.js} +2 -2
  102. package/dist/esm/chunk-KQZ3MNK5.js +100 -0
  103. package/dist/esm/chunk-KQZ3MNK5.js.map +7 -0
  104. package/dist/esm/chunk-M2NDSQR5.js +106 -0
  105. package/dist/esm/chunk-M2NDSQR5.js.map +7 -0
  106. package/dist/esm/chunk-N4WQ6522.js +125 -0
  107. package/dist/esm/chunk-N4WQ6522.js.map +7 -0
  108. package/dist/esm/{chunk-CNOF66SV.js → chunk-NGNVAFBJ.js} +4 -4
  109. package/dist/esm/chunk-NOQ27VLY.js +1 -0
  110. package/dist/esm/chunk-NOQ27VLY.js.map +7 -0
  111. package/dist/esm/chunk-ONDOONBM.js +101 -0
  112. package/dist/esm/chunk-ONDOONBM.js.map +7 -0
  113. package/dist/esm/chunk-P34DI6BE.js +1 -0
  114. package/dist/esm/chunk-P34DI6BE.js.map +7 -0
  115. package/dist/esm/chunk-P5VEI574.js +97 -0
  116. package/dist/esm/chunk-P5VEI574.js.map +7 -0
  117. package/dist/esm/chunk-QPIA6BGW.js +64 -0
  118. package/dist/esm/chunk-QPIA6BGW.js.map +7 -0
  119. package/dist/esm/chunk-SZEFLEDA.js +1 -0
  120. package/dist/esm/chunk-SZEFLEDA.js.map +7 -0
  121. package/dist/esm/chunk-TXB4BOHB.js +1 -0
  122. package/dist/esm/chunk-TXB4BOHB.js.map +7 -0
  123. package/dist/esm/chunk-UIIXVY6K.js +123 -0
  124. package/dist/esm/chunk-UIIXVY6K.js.map +7 -0
  125. package/dist/esm/chunk-UQVAEWY2.js +44 -0
  126. package/dist/esm/chunk-UQVAEWY2.js.map +7 -0
  127. package/dist/esm/chunk-WC3D5GNN.js +29 -0
  128. package/dist/esm/chunk-WC3D5GNN.js.map +7 -0
  129. package/dist/esm/chunk-WNRO77YH.js +1 -0
  130. package/dist/esm/chunk-WNRO77YH.js.map +7 -0
  131. package/dist/esm/chunk-X6EPYQKX.js +96 -0
  132. package/dist/esm/chunk-X6EPYQKX.js.map +7 -0
  133. package/dist/esm/chunk-ZN5TRIVZ.js +41 -0
  134. package/dist/esm/chunk-ZN5TRIVZ.js.map +7 -0
  135. package/dist/esm/components/AddressLookup/AddressLookup.js +10 -107
  136. package/dist/esm/components/AddressLookup/index.js +11 -104
  137. package/dist/esm/components/Alert/Alert.js +2 -2
  138. package/dist/esm/components/Alert/index.js +2 -2
  139. package/dist/esm/components/Checkbox/Checkbox.js +4 -117
  140. package/dist/esm/components/Checkbox/CheckboxGroup.js +5 -115
  141. package/dist/esm/components/Checkbox/index.js +8 -115
  142. package/dist/esm/components/Combobox/Combobox.js +7 -108
  143. package/dist/esm/components/Combobox/index.js +9 -108
  144. package/dist/esm/components/DateField/DateField.js +7 -114
  145. package/dist/esm/components/DateField/index.js +8 -114
  146. package/dist/esm/components/ErrorMessage/ErrorMessage.js +2 -147
  147. package/dist/esm/components/ErrorMessage/index.js +3 -147
  148. package/dist/esm/components/ExpandableText/ExpandableText.js +2 -109
  149. package/dist/esm/components/ExpandableText/index.js +3 -109
  150. package/dist/esm/components/MaskedField/MaskedField.js +6 -115
  151. package/dist/esm/components/MaskedField/MaskedField.js.map +1 -1
  152. package/dist/esm/components/MaskedField/index.js +7 -115
  153. package/dist/esm/components/NumberField/NumberField.js +7 -115
  154. package/dist/esm/components/NumberField/index.js +8 -115
  155. package/dist/esm/components/PasswordField/PasswordField.js +8 -115
  156. package/dist/esm/components/PasswordField/ShowHideButton.js +1 -149
  157. package/dist/esm/components/PasswordField/index.js +9 -115
  158. package/dist/esm/components/Radio/Radio.js +4 -145
  159. package/dist/esm/components/Radio/RadioGroup.js +4 -115
  160. package/dist/esm/components/Radio/index.js +8 -116
  161. package/dist/esm/components/Select/Select.js +4 -115
  162. package/dist/esm/components/Select/index.js +5 -115
  163. package/dist/esm/components/Slider/Slider.js +2 -2
  164. package/dist/esm/components/Slider/index.js +2 -2
  165. package/dist/esm/components/TextArea/TextArea.js +4 -113
  166. package/dist/esm/components/TextArea/index.js +5 -113
  167. package/dist/esm/components/TextField/TextField.js +5 -115
  168. package/dist/esm/components/TextField/index.js +6 -115
  169. package/dist/esm/components/TextOrHTML/TextOrHTML.js +1 -1
  170. package/dist/esm/components/TextOrHTML/index.js +1 -1
  171. package/dist/esm/components/index.js +83 -46
  172. package/dist/esm/index.js +83 -41
  173. package/dist/esm/meta.json +6905 -19124
  174. package/dist/tsconfig.build.tsbuildinfo +1 -1
  175. package/package.json +1 -1
  176. package/src/components/ErrorMessage/ErrorMessage.tsx +1 -1
  177. package/src/components/ExpandableText/ExpandableText.tsx +3 -1
  178. package/src/components/PasswordField/ShowHideButton.tsx +1 -1
  179. package/src/components/TextOrHTML/TextOrHTML.test.tsx +18 -0
  180. package/src/components/TextOrHTML/TextOrHTML.tsx +2 -1
  181. package/dist/esm/chunk-HPUPB75I.js.map +0 -7
  182. package/dist/esm/chunk-M7LTJZQU.js +0 -1606
  183. package/dist/esm/chunk-M7LTJZQU.js.map +0 -7
  184. /package/dist/esm/{chunk-NRU3WNV7.js.map → chunk-JNAQ76CR.js.map} +0 -0
  185. /package/dist/esm/{chunk-CNOF66SV.js.map → chunk-NGNVAFBJ.js.map} +0 -0
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-CUOVI2HT.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -8,7 +8,7 @@ import classNames from "classnames/dedupe";
8
8
  import { useMemo } from "react";
9
9
  import { jsx } from "react/jsx-runtime";
10
10
  var isBlockHTML = (text) => /^\s*<[a-z]/i.test(text);
11
- var containsHTML = (text) => /<[a-z/]/i.test(text);
11
+ var containsHTML = (text) => /<[a-z/]/i.test(text) || /&(?:#\d+|#x[\da-f]+|[a-z]\w*);/i.test(text);
12
12
  var buildTextClasses = (textProps, htmlClassName) => {
13
13
  const { variant, elementType, spacing, className } = textProps;
14
14
  const variantType = variant || getElementType(variant, elementType);
@@ -64,4 +64,4 @@ TextOrHTML.displayName = "TextOrHTML";
64
64
  export {
65
65
  TextOrHTML
66
66
  };
67
- //# sourceMappingURL=chunk-HPUPB75I.js.map
67
+ //# sourceMappingURL=chunk-DYTHXKMX.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/components/TextOrHTML/TextOrHTML.tsx"],
4
+ "sourcesContent": ["import classNames from \"classnames/dedupe\";\nimport type { RefAttributes } from \"react\";\nimport { useMemo } from \"react\";\nimport type { TextElementType, TextProps } from \"../Text/Text\";\nimport { Text, getElementType } from \"../Text/Text\";\n\nexport type HTMLElementType = \"span\" | \"div\";\n\nconst isBlockHTML = (text: string) => /^\\s*<[a-z]/i.test(text);\nconst containsHTML = (text: string) =>\n /<[a-z/]/i.test(text) || /&(?:#\\d+|#x[\\da-f]+|[a-z]\\w*);/i.test(text); // tag or entity\n\nconst buildTextClasses = (\n textProps: Omit<TextProps, \"children\">,\n htmlClassName?: string,\n) => {\n const { variant, elementType, spacing, className } = textProps;\n const variantType = variant || getElementType(variant, elementType);\n return classNames(\n \"mobius\",\n \"mobius-text\",\n { [`--is-${variantType}`]: variantType },\n { [`--has-line-height-${spacing}`]: spacing },\n className,\n htmlClassName,\n );\n};\n\nexport interface TextOrHTMLProps\n extends Omit<TextProps, \"children\">, RefAttributes<TextElementType> {\n /** HTML string to be rendered with dangerouslySetInnerHTML */\n text: string;\n /** Custom class name for the dangerous HTML element */\n htmlClassName?: string;\n /** HTML element type for the dangerous HTML element */\n htmlElementType?: HTMLElementType;\n /** If true, wraps the dangerous HTML element inside a Text component */\n textWrapper?: boolean;\n /** If true, auto-detects whether text is HTML or plain text to determine wrapping and element type */\n autoDetect?: boolean;\n}\n\nconst TextOrHTML = ({\n ref,\n text,\n htmlClassName,\n htmlElementType,\n textWrapper = false,\n autoDetect = false,\n ...textProps\n}: TextOrHTMLProps) => {\n const textIsBlockHTML = autoDetect && isBlockHTML(text);\n\n // Memoize the dangerouslySetInnerHTML object to prevent unnecessary re-renders\n // See: https://github.com/facebook/react/issues/31660\n const dangerousHTML = useMemo(() => ({ __html: text }), [text]);\n\n // Non-block text with autoDetect: render directly on a Text-equivalent element,\n // avoiding unnecessary <span> nesting inside <p>.\n if (autoDetect && !textIsBlockHTML) {\n const { variant, spacing, elementType, className, ...domProps } = textProps;\n const Element = getElementType(variant, elementType);\n const classes = buildTextClasses(textProps, htmlClassName);\n\n return containsHTML(text) ? (\n <Element\n ref={ref}\n {...domProps}\n className={classes}\n dangerouslySetInnerHTML={dangerousHTML}\n />\n ) : (\n <Element ref={ref} {...domProps} className={classes}>\n {text}\n </Element>\n );\n }\n\n const DangerousComponent =\n htmlElementType ?? (textIsBlockHTML ? \"div\" : \"span\");\n const dangerousElement = (\n <DangerousComponent\n className={htmlClassName}\n dangerouslySetInnerHTML={dangerousHTML}\n />\n );\n\n if (textWrapper) {\n return (\n <Text ref={ref} {...textProps}>\n {dangerousElement}\n </Text>\n );\n }\n\n return dangerousElement;\n};\n\nTextOrHTML.displayName = \"TextOrHTML\";\nexport { TextOrHTML };\n"],
5
+ "mappings": ";;;;;;AAAA,OAAO,gBAAgB;AAEvB,SAAS,eAAe;AA+DlB;AAzDN,IAAM,cAAc,CAAC,SAAiB,cAAc,KAAK,IAAI;AAC7D,IAAM,eAAe,CAAC,SACpB,WAAW,KAAK,IAAI,KAAK,kCAAkC,KAAK,IAAI;AAEtE,IAAM,mBAAmB,CACvB,WACA,kBACG;AACH,QAAM,EAAE,SAAS,aAAa,SAAS,UAAU,IAAI;AACrD,QAAM,cAAc,WAAW,eAAe,SAAS,WAAW;AAClE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,CAAC,QAAQ,WAAW,EAAE,GAAG,YAAY;AAAA,IACvC,EAAE,CAAC,qBAAqB,OAAO,EAAE,GAAG,QAAQ;AAAA,IAC5C;AAAA,IACA;AAAA,EACF;AACF;AAgBA,IAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb,GAAG;AACL,MAAuB;AACrB,QAAM,kBAAkB,cAAc,YAAY,IAAI;AAItD,QAAM,gBAAgB,QAAQ,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC,IAAI,CAAC;AAI9D,MAAI,cAAc,CAAC,iBAAiB;AAClC,UAAM,EAAE,SAAS,SAAS,aAAa,WAAW,GAAG,SAAS,IAAI;AAClE,UAAM,UAAU,eAAe,SAAS,WAAW;AACnD,UAAM,UAAU,iBAAiB,WAAW,aAAa;AAEzD,WAAO,aAAa,IAAI,IACtB;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACC,GAAG;AAAA,QACJ,WAAW;AAAA,QACX,yBAAyB;AAAA;AAAA,IAC3B,IAEA,oBAAC,WAAQ,KAAW,GAAG,UAAU,WAAW,SACzC,gBACH;AAAA,EAEJ;AAEA,QAAM,qBACJ,oBAAoB,kBAAkB,QAAQ;AAChD,QAAM,mBACJ;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,MACX,yBAAyB;AAAA;AAAA,EAC3B;AAGF,MAAI,aAAa;AACf,WACE,oBAAC,QAAK,KAAW,GAAG,WACjB,4BACH;AAAA,EAEJ;AAEA,SAAO;AACT;AAEA,WAAW,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-F4RQKLF7.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,355 @@
1
+ import {
2
+ Listbox
3
+ } from "./chunk-PQSPISME.js";
4
+ import {
5
+ useComboboxHighlight
6
+ } from "./chunk-XZUQV54A.js";
7
+ import {
8
+ useComboboxOptions
9
+ } from "./chunk-GU5OMKTM.js";
10
+ import {
11
+ getOptionLabel,
12
+ getOptionValue,
13
+ isOptionGroup
14
+ } from "./chunk-2JLZNSLY.js";
15
+ import {
16
+ TextField
17
+ } from "./chunk-UIIXVY6K.js";
18
+ import {
19
+ VisuallyHidden
20
+ } from "./chunk-PRZRE6A5.js";
21
+ import {
22
+ useBreakpoint
23
+ } from "./chunk-7FQ7SXK4.js";
24
+
25
+ // src/components/Combobox/Combobox.tsx
26
+ import { useOnUnmount } from "@simplybusiness/mobius-hooks";
27
+ import classNames from "classnames/dedupe";
28
+ import { useEffect, useId, useRef, useState } from "react";
29
+ import "@simplybusiness/mobius/src/components/Combobox/Combobox.css";
30
+ import { jsx, jsxs } from "react/jsx-runtime";
31
+ var ComboboxInner = ({
32
+ ref,
33
+ ...props
34
+ }) => {
35
+ const {
36
+ id,
37
+ defaultValue,
38
+ value,
39
+ options,
40
+ asyncOptions,
41
+ delay,
42
+ minSearchLength,
43
+ onSelected,
44
+ className,
45
+ placeholder,
46
+ icon,
47
+ onBlur,
48
+ onFocus,
49
+ onChange,
50
+ // onSearched, // unused prop, consider removing
51
+ optionComponent,
52
+ optionTestIdPrefix,
53
+ errorMessage,
54
+ ...otherProps
55
+ } = props;
56
+ const skipNextDebounceRef = useRef(false);
57
+ const fallbackRef = useRef(null);
58
+ const [inputValue, setInputValue] = useState(defaultValue || "");
59
+ const [isOpen, setIsOpen] = useState(false);
60
+ const [isChanging, setIsChanging] = useState(false);
61
+ const { filteredOptions, updateFilteredOptions, isLoading, error } = useComboboxOptions({
62
+ options,
63
+ asyncOptions,
64
+ inputValue,
65
+ delay,
66
+ minSearchLength,
67
+ skipNextDebounceRef
68
+ });
69
+ const [validationError, setValidationError] = useState(
70
+ error?.message || errorMessage
71
+ );
72
+ const {
73
+ highlightedIndex,
74
+ highlightedGroupIndex,
75
+ highlightNextOption,
76
+ highlightPreviousOption,
77
+ highlightFirstOption,
78
+ highlightLastOption,
79
+ clearHighlight
80
+ } = useComboboxHighlight(filteredOptions);
81
+ const inputRef = ref || fallbackRef;
82
+ const listboxId = useId();
83
+ const statusId = useId();
84
+ const blurTimeoutRef = useRef(null);
85
+ const userInteractedRef = useRef(false);
86
+ const justSelectedRef = useRef(false);
87
+ const { down } = useBreakpoint();
88
+ const isMobile = down("md");
89
+ useEffect(() => {
90
+ setValidationError(error?.message || errorMessage);
91
+ }, [error, errorMessage]);
92
+ const getEmptyValue = () => {
93
+ const firstOption = filteredOptions ? isOptionGroup(filteredOptions) ? filteredOptions[0]?.options[0] : filteredOptions[0] : options ? isOptionGroup(options) ? options[0]?.options[0] : options[0] : void 0;
94
+ if (typeof firstOption === "string") {
95
+ return "";
96
+ }
97
+ return { label: "", value: "" };
98
+ };
99
+ const handleFocus = (e) => {
100
+ onFocus?.(e);
101
+ if (!filteredOptions || filteredOptions.length === 0) return;
102
+ if (blurTimeoutRef.current) {
103
+ clearTimeout(blurTimeoutRef.current);
104
+ blurTimeoutRef.current = null;
105
+ }
106
+ const isNaturalFocus = userInteractedRef.current || e.relatedTarget !== null;
107
+ if (userInteractedRef.current) {
108
+ userInteractedRef.current = false;
109
+ }
110
+ if (justSelectedRef.current && !isNaturalFocus) {
111
+ return;
112
+ }
113
+ if (isNaturalFocus) {
114
+ setIsOpen(true);
115
+ justSelectedRef.current = false;
116
+ }
117
+ };
118
+ useEffect(() => {
119
+ if (!inputRef || typeof inputRef === "function") return;
120
+ const inputElement = inputRef.current;
121
+ if (!inputElement) return;
122
+ const handleMouseDown = () => {
123
+ userInteractedRef.current = true;
124
+ };
125
+ inputElement.addEventListener("mousedown", handleMouseDown);
126
+ return () => {
127
+ inputElement.removeEventListener("mousedown", handleMouseDown);
128
+ };
129
+ }, [inputRef]);
130
+ useOnUnmount(() => {
131
+ if (blurTimeoutRef.current) {
132
+ clearTimeout(blurTimeoutRef.current);
133
+ }
134
+ });
135
+ const handleInputChange = (e) => {
136
+ const newValue = e.target.value;
137
+ setInputValue(newValue);
138
+ setValidationError(void 0);
139
+ justSelectedRef.current = false;
140
+ setIsChanging(true);
141
+ if (!asyncOptions) {
142
+ setIsOpen(true);
143
+ }
144
+ clearHighlight();
145
+ onChange?.(e);
146
+ };
147
+ const handleOptionSelect = (option) => {
148
+ const val = getOptionValue(option);
149
+ if (!val && val !== "") return;
150
+ if (typeof option === "object" && "callback" in option && option.callback && typeof option.callback === "function") {
151
+ justSelectedRef.current = true;
152
+ setTimeout(() => {
153
+ if (inputRef && typeof inputRef !== "function" && inputRef.current) {
154
+ inputRef.current.focus();
155
+ }
156
+ }, 0);
157
+ const callbackPromise = option.callback();
158
+ updateFilteredOptions(callbackPromise);
159
+ callbackPromise.then(() => {
160
+ setIsOpen(true);
161
+ setIsChanging(true);
162
+ }).catch(() => {
163
+ });
164
+ return;
165
+ }
166
+ skipNextDebounceRef.current = true;
167
+ justSelectedRef.current = true;
168
+ setIsChanging(false);
169
+ setValidationError(void 0);
170
+ setIsOpen(false);
171
+ setInputValue(val);
172
+ onSelected?.(option);
173
+ };
174
+ const getFirstOption = () => {
175
+ if (!filteredOptions) return void 0;
176
+ if (isOptionGroup(filteredOptions)) {
177
+ return filteredOptions[0]?.options[0];
178
+ }
179
+ return filteredOptions[0];
180
+ };
181
+ const getHighlightedOption = () => {
182
+ if (!filteredOptions) return void 0;
183
+ if (highlightedIndex === -1) return void 0;
184
+ if (isOptionGroup(filteredOptions)) {
185
+ const group = filteredOptions[highlightedGroupIndex];
186
+ return group?.options[highlightedIndex];
187
+ }
188
+ return filteredOptions[highlightedIndex];
189
+ };
190
+ const getHighlightedOptionId = () => {
191
+ const option = getHighlightedOption();
192
+ if (!option) return void 0;
193
+ if (isOptionGroup(filteredOptions)) {
194
+ return `${listboxId}-option-${highlightedGroupIndex}-${highlightedIndex}`;
195
+ }
196
+ return `${listboxId}-option-${highlightedIndex}`;
197
+ };
198
+ const handleBlur = (e) => {
199
+ if (!justSelectedRef.current) {
200
+ const typedText = inputValue.trim();
201
+ const typedTextLower = typedText.toLowerCase();
202
+ const highlightedOption = getHighlightedOption();
203
+ const label = getOptionLabel(highlightedOption);
204
+ if (typedTextLower === label?.toLowerCase()) {
205
+ setTimeout(() => {
206
+ handleOptionSelect(highlightedOption);
207
+ }, 0);
208
+ } else if (typedText === "") {
209
+ setTimeout(() => {
210
+ handleOptionSelect(getEmptyValue());
211
+ }, 0);
212
+ } else {
213
+ setValidationError(
214
+ errorMessage || "Please select an option from the list"
215
+ );
216
+ setTimeout(() => {
217
+ setInputValue("");
218
+ }, 0);
219
+ }
220
+ }
221
+ blurTimeoutRef.current = setTimeout(() => {
222
+ onBlur?.(e);
223
+ setIsOpen(false);
224
+ setIsChanging(false);
225
+ }, 150);
226
+ };
227
+ const handleKeyDown = (e) => {
228
+ switch (e.key) {
229
+ case "ArrowDown":
230
+ e.preventDefault();
231
+ justSelectedRef.current = false;
232
+ setIsOpen(true);
233
+ highlightNextOption();
234
+ break;
235
+ case "ArrowUp":
236
+ e.preventDefault();
237
+ justSelectedRef.current = false;
238
+ setIsOpen(true);
239
+ highlightPreviousOption();
240
+ break;
241
+ case "Home":
242
+ e.preventDefault();
243
+ justSelectedRef.current = false;
244
+ setIsOpen(true);
245
+ highlightFirstOption();
246
+ break;
247
+ case "End":
248
+ e.preventDefault();
249
+ justSelectedRef.current = false;
250
+ setIsOpen(true);
251
+ highlightLastOption();
252
+ break;
253
+ case "Enter":
254
+ e.preventDefault();
255
+ if (isOpen) {
256
+ const selectedOption = getHighlightedOption() || getFirstOption();
257
+ if (selectedOption) {
258
+ handleOptionSelect(selectedOption);
259
+ }
260
+ }
261
+ break;
262
+ case "Escape":
263
+ e.preventDefault();
264
+ setInputValue("");
265
+ setIsOpen(false);
266
+ clearHighlight();
267
+ break;
268
+ default:
269
+ }
270
+ };
271
+ useEffect(() => {
272
+ if (value) {
273
+ setInputValue(value);
274
+ }
275
+ }, [value]);
276
+ useEffect(() => {
277
+ if (asyncOptions && isChanging) {
278
+ setIsOpen(!!filteredOptions && filteredOptions.length > 0);
279
+ }
280
+ }, [filteredOptions, asyncOptions, isChanging]);
281
+ const classes = classNames(
282
+ "mobius mobius-combobox",
283
+ {
284
+ "mobius-combobox--is-expanded": isOpen,
285
+ "mobius-combobox--is-loading": isLoading,
286
+ "mobius-combobox--is-mobile": isMobile
287
+ },
288
+ className
289
+ );
290
+ const getStatusMessage = () => {
291
+ if (isLoading) return "Loading options";
292
+ if (!filteredOptions || filteredOptions.length === 0) {
293
+ return isChanging ? "No options found" : "";
294
+ }
295
+ const count = isOptionGroup(filteredOptions) ? filteredOptions.reduce((sum, group) => sum + group.options.length, 0) : filteredOptions.length;
296
+ return isOpen && isChanging ? `${count} option${count === 1 ? "" : "s"} available` : "";
297
+ };
298
+ return /* @__PURE__ */ jsxs("div", { id, "data-testid": "mobius-combobox__wrapper", className: classes, children: [
299
+ /* @__PURE__ */ jsx(
300
+ VisuallyHidden,
301
+ {
302
+ role: "status",
303
+ "aria-live": "polite",
304
+ id: statusId,
305
+ elementType: "div",
306
+ className: "mobius-combobox__status",
307
+ children: getStatusMessage()
308
+ }
309
+ ),
310
+ /* @__PURE__ */ jsx(
311
+ TextField,
312
+ {
313
+ ...otherProps,
314
+ className: "mobius-combobox__input",
315
+ role: "combobox",
316
+ value: inputValue,
317
+ placeholder,
318
+ onFocus: handleFocus,
319
+ onBlur: handleBlur,
320
+ onKeyDown: handleKeyDown,
321
+ onChange: handleInputChange,
322
+ autoComplete: "off",
323
+ "aria-describedby": isLoading ? statusId : void 0,
324
+ "aria-autocomplete": "list",
325
+ "aria-haspopup": "listbox",
326
+ "aria-controls": isOpen ? listboxId : void 0,
327
+ "aria-expanded": isOpen,
328
+ "aria-activedescendant": highlightedIndex === -1 ? void 0 : getHighlightedOptionId(),
329
+ prefixInside: icon,
330
+ ref: inputRef,
331
+ errorMessage: errorMessage || validationError || error?.message
332
+ }
333
+ ),
334
+ /* @__PURE__ */ jsx(
335
+ Listbox,
336
+ {
337
+ id: listboxId,
338
+ isOpen,
339
+ isLoading,
340
+ options: filteredOptions,
341
+ highlightedIndex,
342
+ highlightedGroupIndex,
343
+ onOptionSelect: handleOptionSelect,
344
+ optionComponent,
345
+ optionTestIdPrefix
346
+ }
347
+ )
348
+ ] });
349
+ };
350
+ var Combobox = ComboboxInner;
351
+
352
+ export {
353
+ Combobox
354
+ };
355
+ //# sourceMappingURL=chunk-FKTDL7KO.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/components/Combobox/Combobox.tsx"],
4
+ "sourcesContent": ["import { useOnUnmount } from \"@simplybusiness/mobius-hooks\";\nimport classNames from \"classnames/dedupe\";\nimport type React from \"react\";\nimport type { FocusEvent } from \"react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { useBreakpoint } from \"../../hooks\";\nimport { TextField } from \"../TextField\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport { Listbox } from \"./Listbox\"; // Import Listbox component\nimport type { ComboboxOption, ComboboxProps, ComboboxRef } from \"./types\";\nimport { useComboboxHighlight } from \"./useComboboxHighlight\";\nimport { useComboboxOptions } from \"./useComboboxOptions\";\nimport { getOptionLabel, getOptionValue, isOptionGroup } from \"./utils\";\nimport \"./Combobox.css\";\n\nconst ComboboxInner = <T extends ComboboxOption>({\n ref,\n ...props\n}: ComboboxProps<T>) => {\n const {\n id,\n defaultValue,\n value,\n options,\n asyncOptions,\n delay,\n minSearchLength,\n onSelected,\n className,\n placeholder,\n icon,\n onBlur,\n onFocus,\n onChange,\n // onSearched, // unused prop, consider removing\n optionComponent,\n optionTestIdPrefix,\n errorMessage,\n ...otherProps\n } = props;\n // Avoid re-fetching after selecting an option\n const skipNextDebounceRef = useRef(false);\n const fallbackRef = useRef<HTMLInputElement>(null);\n const [inputValue, setInputValue] = useState(defaultValue || \"\");\n const [isOpen, setIsOpen] = useState(false);\n const [isChanging, setIsChanging] = useState(false);\n const { filteredOptions, updateFilteredOptions, isLoading, error } =\n useComboboxOptions({\n options,\n asyncOptions,\n inputValue,\n delay,\n minSearchLength,\n skipNextDebounceRef,\n });\n const [validationError, setValidationError] = useState(\n error?.message || errorMessage,\n );\n const {\n highlightedIndex,\n highlightedGroupIndex,\n highlightNextOption,\n highlightPreviousOption,\n highlightFirstOption,\n highlightLastOption,\n clearHighlight,\n } = useComboboxHighlight(filteredOptions);\n\n const inputRef = ref || fallbackRef;\n const listboxId = useId();\n const statusId = useId();\n const blurTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const userInteractedRef = useRef(false);\n const justSelectedRef = useRef(false);\n const { down } = useBreakpoint();\n const isMobile = down(\"md\");\n\n useEffect(() => {\n setValidationError(error?.message || errorMessage);\n }, [error, errorMessage]);\n\n // Helper to create properly-typed empty value based on option type\n const getEmptyValue = (): T => {\n // Check first available option to determine if we're using string or object options\n const firstOption = filteredOptions\n ? isOptionGroup(filteredOptions)\n ? filteredOptions[0]?.options[0]\n : filteredOptions[0]\n : options\n ? isOptionGroup(options)\n ? options[0]?.options[0]\n : options[0]\n : undefined;\n\n // If options are strings, return empty string\n if (typeof firstOption === \"string\") {\n return \"\" as T;\n }\n\n // If options are objects, return empty object with same shape\n return { label: \"\", value: \"\" } as T;\n };\n\n const handleFocus = (e: FocusEvent) => {\n onFocus?.(e);\n if (!filteredOptions || filteredOptions.length === 0) return;\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n blurTimeoutRef.current = null;\n }\n\n // Check if this is natural focus (user click/Tab) or programmatic focus\n const isNaturalFocus =\n userInteractedRef.current || e.relatedTarget !== null;\n if (userInteractedRef.current) {\n userInteractedRef.current = false;\n }\n\n // Block opening only if programmatic focus right after selection\n if (justSelectedRef.current && !isNaturalFocus) {\n return;\n }\n\n // Open dropdown for natural focus\n if (isNaturalFocus) {\n setIsOpen(true);\n justSelectedRef.current = false;\n }\n };\n\n useEffect(() => {\n if (!inputRef || typeof inputRef === \"function\") return;\n const inputElement = inputRef.current;\n if (!inputElement) return;\n\n const handleMouseDown = () => {\n // Track that user clicked/interacted with input\n userInteractedRef.current = true;\n };\n\n inputElement.addEventListener(\"mousedown\", handleMouseDown);\n return () => {\n inputElement.removeEventListener(\"mousedown\", handleMouseDown);\n };\n }, [inputRef]);\n\n useOnUnmount(() => {\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n }\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n setValidationError(undefined);\n justSelectedRef.current = false;\n setIsChanging(true);\n // Only open immediately for sync options; async options controlled by useEffect\n if (!asyncOptions) {\n setIsOpen(true);\n }\n clearHighlight();\n onChange?.(e);\n };\n\n const handleOptionSelect = (option: T) => {\n const val = getOptionValue(option);\n\n // Allow empty values to pass through\n if (!val && val !== \"\") return;\n\n if (\n typeof option === \"object\" &&\n \"callback\" in option &&\n option.callback &&\n typeof option.callback === \"function\"\n ) {\n justSelectedRef.current = true;\n setTimeout(() => {\n if (inputRef && typeof inputRef !== \"function\" && inputRef.current) {\n inputRef.current.focus();\n }\n }, 0);\n const callbackPromise = option.callback();\n updateFilteredOptions(callbackPromise);\n callbackPromise\n .then(() => {\n setIsOpen(true);\n setIsChanging(true);\n })\n .catch(() => {\n // error handled inside updateFilteredOptions via setError\n });\n return;\n }\n\n // Prevent re-fetching options after selecting an option\n skipNextDebounceRef.current = true;\n justSelectedRef.current = true;\n\n setIsChanging(false);\n setValidationError(undefined);\n setIsOpen(false);\n setInputValue(val);\n onSelected?.(option);\n };\n\n const getFirstOption = () => {\n if (!filteredOptions) return undefined;\n if (isOptionGroup(filteredOptions)) {\n return filteredOptions[0]?.options[0];\n }\n\n return filteredOptions[0];\n };\n\n const getHighlightedOption = () => {\n if (!filteredOptions) return undefined;\n if (highlightedIndex === -1) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n const group = filteredOptions[highlightedGroupIndex];\n return group?.options[highlightedIndex];\n }\n\n return filteredOptions[highlightedIndex];\n };\n\n const getHighlightedOptionId = () => {\n const option = getHighlightedOption();\n if (!option) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n return `${listboxId}-option-${highlightedGroupIndex}-${highlightedIndex}`;\n }\n\n return `${listboxId}-option-${highlightedIndex}`;\n };\n\n const handleBlur = (e: FocusEvent<Element, Element>) => {\n // Force selection if user has matched an entry by typing (not already selected)\n // Defer this to allow natural focus flow to complete first\n if (!justSelectedRef.current) {\n const typedText = inputValue.trim();\n const typedTextLower = typedText.toLowerCase();\n const highlightedOption = getHighlightedOption();\n const label = getOptionLabel(highlightedOption);\n\n if (typedTextLower === label?.toLowerCase()) {\n // Exact match with an option\n setTimeout(() => {\n handleOptionSelect(highlightedOption as T);\n }, 0);\n } else if (typedText === \"\") {\n // Allow empty values\n setTimeout(() => {\n handleOptionSelect(getEmptyValue());\n }, 0);\n } else {\n // Invalid value (not in options and not empty)\n setValidationError(\n errorMessage || \"Please select an option from the list\",\n );\n setTimeout(() => {\n setInputValue(\"\");\n }, 0);\n }\n }\n\n blurTimeoutRef.current = setTimeout(() => {\n onBlur?.(e);\n setIsOpen(false);\n setIsChanging(false);\n }, 150);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n justSelectedRef.current = false;\n setIsOpen(true);\n highlightNextOption();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n justSelectedRef.current = false;\n setIsOpen(true);\n highlightPreviousOption();\n break;\n case \"Home\":\n e.preventDefault();\n justSelectedRef.current = false;\n setIsOpen(true);\n highlightFirstOption();\n break;\n case \"End\":\n e.preventDefault();\n justSelectedRef.current = false;\n setIsOpen(true);\n highlightLastOption();\n break;\n case \"Enter\":\n e.preventDefault();\n if (isOpen) {\n const selectedOption = getHighlightedOption() || getFirstOption();\n if (selectedOption) {\n handleOptionSelect(selectedOption);\n }\n }\n break;\n case \"Escape\":\n e.preventDefault();\n setInputValue(\"\");\n setIsOpen(false);\n clearHighlight();\n break;\n default:\n // Do nothing\n }\n };\n\n useEffect(() => {\n if (value) {\n setInputValue(value);\n }\n }, [value]);\n\n // Open and close the combobox based on async filtered options\n useEffect(() => {\n if (asyncOptions && isChanging) {\n setIsOpen(!!filteredOptions && filteredOptions.length > 0);\n }\n }, [filteredOptions, asyncOptions, isChanging]);\n\n const classes = classNames(\n \"mobius mobius-combobox\",\n {\n \"mobius-combobox--is-expanded\": isOpen,\n \"mobius-combobox--is-loading\": isLoading,\n \"mobius-combobox--is-mobile\": isMobile,\n },\n className,\n );\n\n const getStatusMessage = () => {\n if (isLoading) return \"Loading options\";\n if (!filteredOptions || filteredOptions.length === 0) {\n return isChanging ? \"No options found\" : \"\";\n }\n const count = isOptionGroup(filteredOptions)\n ? filteredOptions.reduce((sum, group) => sum + group.options.length, 0)\n : filteredOptions.length;\n return isOpen && isChanging\n ? `${count} option${count === 1 ? \"\" : \"s\"} available`\n : \"\";\n };\n\n return (\n <div id={id} data-testid=\"mobius-combobox__wrapper\" className={classes}>\n <VisuallyHidden\n role=\"status\"\n aria-live=\"polite\"\n id={statusId}\n elementType=\"div\"\n className=\"mobius-combobox__status\"\n >\n {getStatusMessage()}\n </VisuallyHidden>\n <TextField\n {...otherProps}\n className=\"mobius-combobox__input\"\n role=\"combobox\"\n value={inputValue}\n placeholder={placeholder}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n onChange={handleInputChange}\n autoComplete=\"off\"\n aria-describedby={isLoading ? statusId : undefined}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n aria-controls={isOpen ? listboxId : undefined}\n aria-expanded={isOpen}\n aria-activedescendant={\n highlightedIndex === -1 ? undefined : getHighlightedOptionId()\n }\n prefixInside={icon}\n ref={inputRef}\n errorMessage={errorMessage || validationError || error?.message}\n />\n <Listbox\n id={listboxId}\n isOpen={isOpen}\n isLoading={isLoading}\n options={filteredOptions}\n highlightedIndex={highlightedIndex}\n highlightedGroupIndex={highlightedGroupIndex}\n onOptionSelect={handleOptionSelect}\n optionComponent={optionComponent}\n optionTestIdPrefix={optionTestIdPrefix}\n />\n </div>\n );\n};\n\nexport const Combobox = ComboboxInner as <T extends ComboboxOption>(\n props: ComboboxProps<T> & { ref?: ComboboxRef },\n) => React.JSX.Element;\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-TKIP5Q5H.js";
4
4
  import {
5
5
  TextOrHTML
6
- } from "./chunk-HPUPB75I.js";
6
+ } from "./chunk-DYTHXKMX.js";
7
7
 
8
8
  // src/components/Alert/Alert.tsx
9
9
  import { circleInfo, circleTick, error, warning } from "@simplybusiness/icons";
@@ -68,4 +68,4 @@ Alert.displayName = "Alert";
68
68
  export {
69
69
  Alert
70
70
  };
71
- //# sourceMappingURL=chunk-NRU3WNV7.js.map
71
+ //# sourceMappingURL=chunk-JNAQ76CR.js.map
@@ -0,0 +1,100 @@
1
+ import {
2
+ convertToDateFormat,
3
+ isValidDate
4
+ } from "./chunk-CI5GCPUG.js";
5
+ import {
6
+ TextField
7
+ } from "./chunk-UIIXVY6K.js";
8
+ import {
9
+ mergeRefs
10
+ } from "./chunk-QGGILW3D.js";
11
+
12
+ // src/components/DateField/DateField.tsx
13
+ import classNames from "classnames/dedupe";
14
+ import { useEffect, useRef, useState } from "react";
15
+ import { jsx } from "react/jsx-runtime";
16
+ var MIN_MAX_ERROR = '"min" value should not be greater than "max" value.';
17
+ var DateField = ({ ref, ...props }) => {
18
+ const {
19
+ min,
20
+ max,
21
+ format,
22
+ className,
23
+ errorMessage,
24
+ defaultValue,
25
+ value,
26
+ ...otherProps
27
+ } = props;
28
+ const [error, setError] = useState(errorMessage);
29
+ const [isInvalid, setIsInvalid] = useState(void 0);
30
+ const localRef = useRef(null);
31
+ const classes = classNames("mobius-date-field", className);
32
+ const formattedMin = min ? convertToDateFormat(min, format) : void 0;
33
+ const formattedMax = max ? convertToDateFormat(max, format) : void 0;
34
+ const formattedDefaultValue = defaultValue ? convertToDateFormat(defaultValue, format) : void 0;
35
+ const formattedValue = value ? convertToDateFormat(value, format) : void 0;
36
+ const setInvalidState = (error2) => {
37
+ setError(error2);
38
+ setIsInvalid(true);
39
+ };
40
+ const setValidState = () => {
41
+ setError(props.errorMessage);
42
+ setIsInvalid(false);
43
+ };
44
+ useEffect(() => {
45
+ if (!isValidDate(min, format)) {
46
+ setInvalidState(`Invalid min date: ${min}`);
47
+ return;
48
+ }
49
+ if (!isValidDate(max, format)) {
50
+ setInvalidState(`Invalid max date: ${max}`);
51
+ return;
52
+ }
53
+ if (min && max) {
54
+ const minDate = new Date(min);
55
+ const maxDate = new Date(max);
56
+ if (minDate > maxDate) {
57
+ setInvalidState(MIN_MAX_ERROR);
58
+ } else {
59
+ setValidState();
60
+ }
61
+ } else {
62
+ setValidState();
63
+ }
64
+ }, [min, max, format]);
65
+ const validate = () => {
66
+ const isValidInput = localRef.current?.checkValidity();
67
+ if (!isValidInput) {
68
+ setInvalidState("Invalid date input");
69
+ } else {
70
+ setValidState();
71
+ }
72
+ };
73
+ const handleBlur = (event) => {
74
+ validate();
75
+ otherProps.onBlur?.(event);
76
+ };
77
+ return /* @__PURE__ */ jsx(
78
+ TextField,
79
+ {
80
+ ref: mergeRefs([localRef, ref]),
81
+ className: classes,
82
+ type: "date",
83
+ min: formattedMin,
84
+ max: formattedMax,
85
+ errorMessage: errorMessage ?? error,
86
+ isInvalid,
87
+ defaultValue: formattedDefaultValue,
88
+ value: formattedValue,
89
+ onBlur: handleBlur,
90
+ ...otherProps
91
+ }
92
+ );
93
+ };
94
+ DateField.displayName = "DateField";
95
+
96
+ export {
97
+ MIN_MAX_ERROR,
98
+ DateField
99
+ };
100
+ //# sourceMappingURL=chunk-KQZ3MNK5.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/components/DateField/DateField.tsx"],
4
+ "sourcesContent": ["\"use client\";\n\nimport classNames from \"classnames/dedupe\";\nimport type { FocusEvent, RefAttributes } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { mergeRefs } from \"../../utils/mergeRefs\";\nimport {\n TextField,\n type TextFieldElementType,\n type TextFieldProps,\n} from \"../TextField\";\nimport { convertToDateFormat, isValidDate } from \"./validation\";\n\nexport interface DateFieldProps\n extends Omit<TextFieldProps, \"type\">, RefAttributes<TextFieldElementType> {\n /** The earliest date allowed for the input. */\n min?: string;\n /** The latest date allowed for the input. */\n max?: string;\n /** Date format to use. */\n format?: string;\n}\n\nexport const MIN_MAX_ERROR =\n '\"min\" value should not be greater than \"max\" value.';\n\nexport const DateField = ({ ref, ...props }: DateFieldProps) => {\n const {\n min,\n max,\n format,\n className,\n errorMessage,\n defaultValue,\n value,\n ...otherProps\n } = props;\n const [error, setError] = useState<string | undefined>(errorMessage);\n const [isInvalid, setIsInvalid] = useState<boolean | undefined>(undefined);\n const localRef = useRef<TextFieldElementType>(null);\n const classes = classNames(\"mobius-date-field\", className);\n\n // If a custom format is provided, convert the min, max,\n // and defaultValue dates to that format\n const formattedMin = min ? convertToDateFormat(min, format) : undefined;\n const formattedMax = max ? convertToDateFormat(max, format) : undefined;\n const formattedDefaultValue = defaultValue\n ? convertToDateFormat(defaultValue, format)\n : undefined;\n const formattedValue = value ? convertToDateFormat(value, format) : undefined;\n\n const setInvalidState = (error?: string) => {\n setError(error);\n setIsInvalid(true);\n };\n\n const setValidState = () => {\n setError(props.errorMessage);\n setIsInvalid(false);\n };\n\n // Validate min and max values\n useEffect(() => {\n if (!isValidDate(min, format)) {\n setInvalidState(`Invalid min date: ${min}`);\n return;\n }\n if (!isValidDate(max, format)) {\n setInvalidState(`Invalid max date: ${max}`);\n return;\n }\n\n if (min && max) {\n const minDate = new Date(min);\n const maxDate = new Date(max);\n if (minDate > maxDate) {\n setInvalidState(MIN_MAX_ERROR);\n } else {\n setValidState();\n }\n } else {\n setValidState();\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [min, max, format]);\n\n const validate = () => {\n // If 'min' or 'max' values are provided, checkValidity() will\n // validate the date and return a boolean\n const isValidInput = localRef.current?.checkValidity();\n\n if (!isValidInput) {\n setInvalidState(\"Invalid date input\");\n } else {\n setValidState();\n }\n };\n\n // User has interacted with the component and navigated away\n const handleBlur = (event: FocusEvent<Element>) => {\n validate();\n otherProps.onBlur?.(event);\n };\n\n return (\n <TextField\n ref={mergeRefs([localRef, ref])}\n className={classes}\n type=\"date\"\n min={formattedMin}\n max={formattedMax}\n errorMessage={errorMessage ?? error}\n isInvalid={isInvalid}\n defaultValue={formattedDefaultValue}\n value={formattedValue}\n onBlur={handleBlur}\n {...otherProps}\n />\n );\n};\n\nDateField.displayName = \"DateField\";\n"],
5
+ "mappings": ";;;;;;;;;;;;AAEA,OAAO,gBAAgB;AAEvB,SAAS,WAAW,QAAQ,gBAAgB;AAqGxC;AAlFG,IAAM,gBACX;AAEK,IAAM,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,MAAsB;AAC9D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA6B,YAAY;AACnE,QAAM,CAAC,WAAW,YAAY,IAAI,SAA8B,MAAS;AACzE,QAAM,WAAW,OAA6B,IAAI;AAClD,QAAM,UAAU,WAAW,qBAAqB,SAAS;AAIzD,QAAM,eAAe,MAAM,oBAAoB,KAAK,MAAM,IAAI;AAC9D,QAAM,eAAe,MAAM,oBAAoB,KAAK,MAAM,IAAI;AAC9D,QAAM,wBAAwB,eAC1B,oBAAoB,cAAc,MAAM,IACxC;AACJ,QAAM,iBAAiB,QAAQ,oBAAoB,OAAO,MAAM,IAAI;AAEpE,QAAM,kBAAkB,CAACA,WAAmB;AAC1C,aAASA,MAAK;AACd,iBAAa,IAAI;AAAA,EACnB;AAEA,QAAM,gBAAgB,MAAM;AAC1B,aAAS,MAAM,YAAY;AAC3B,iBAAa,KAAK;AAAA,EACpB;AAGA,YAAU,MAAM;AACd,QAAI,CAAC,YAAY,KAAK,MAAM,GAAG;AAC7B,sBAAgB,qBAAqB,GAAG,EAAE;AAC1C;AAAA,IACF;AACA,QAAI,CAAC,YAAY,KAAK,MAAM,GAAG;AAC7B,sBAAgB,qBAAqB,GAAG,EAAE;AAC1C;AAAA,IACF;AAEA,QAAI,OAAO,KAAK;AACd,YAAM,UAAU,IAAI,KAAK,GAAG;AAC5B,YAAM,UAAU,IAAI,KAAK,GAAG;AAC5B,UAAI,UAAU,SAAS;AACrB,wBAAgB,aAAa;AAAA,MAC/B,OAAO;AACL,sBAAc;AAAA,MAChB;AAAA,IACF,OAAO;AACL,oBAAc;AAAA,IAChB;AAAA,EAEF,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC;AAErB,QAAM,WAAW,MAAM;AAGrB,UAAM,eAAe,SAAS,SAAS,cAAc;AAErD,QAAI,CAAC,cAAc;AACjB,sBAAgB,oBAAoB;AAAA,IACtC,OAAO;AACL,oBAAc;AAAA,IAChB;AAAA,EACF;AAGA,QAAM,aAAa,CAAC,UAA+B;AACjD,aAAS;AACT,eAAW,SAAS,KAAK;AAAA,EAC3B;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,UAAU,CAAC,UAAU,GAAG,CAAC;AAAA,MAC9B,WAAW;AAAA,MACX,MAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,cAAc,gBAAgB;AAAA,MAC9B;AAAA,MACA,cAAc;AAAA,MACd,OAAO;AAAA,MACP,QAAQ;AAAA,MACP,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,UAAU,cAAc;",
6
+ "names": ["error"]
7
+ }