@seekalfred/primitives 0.5.1 → 0.6.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 (215) hide show
  1. package/README.md +664 -9
  2. package/dist/accordion/accordion.js +10 -4
  3. package/dist/accordion/accordion.js.map +1 -1
  4. package/dist/action-button/action-button.d.ts +50 -0
  5. package/dist/action-button/action-button.js +159 -0
  6. package/dist/action-button/action-button.js.map +1 -0
  7. package/dist/action-button/index.d.ts +2 -0
  8. package/dist/action-button/index.js +2 -0
  9. package/dist/alert/alert.d.ts +15 -1
  10. package/dist/alert/alert.js +116 -20
  11. package/dist/alert/alert.js.map +1 -1
  12. package/dist/avatar/avatar.d.ts +6 -0
  13. package/dist/avatar/avatar.js +47 -16
  14. package/dist/avatar/avatar.js.map +1 -1
  15. package/dist/badge/badge.d.ts +11 -4
  16. package/dist/badge/badge.js +35 -5
  17. package/dist/badge/badge.js.map +1 -1
  18. package/dist/breadcrumb/breadcrumb.d.ts +0 -27
  19. package/dist/breadcrumb/breadcrumb.js +20 -6
  20. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  21. package/dist/button/button.d.ts +35 -22
  22. package/dist/button/button.js +177 -34
  23. package/dist/button/button.js.map +1 -1
  24. package/dist/button-group/button-group.d.ts +76 -0
  25. package/dist/button-group/button-group.js +403 -0
  26. package/dist/button-group/button-group.js.map +1 -0
  27. package/dist/button-group/index.d.ts +2 -0
  28. package/dist/button-group/index.js +2 -0
  29. package/dist/calendar/calendar.d.ts +1 -1
  30. package/dist/calendar/calendar.js +257 -30
  31. package/dist/calendar/calendar.js.map +1 -1
  32. package/dist/card/card.d.ts +9 -2
  33. package/dist/card/card.js +15 -4
  34. package/dist/card/card.js.map +1 -1
  35. package/dist/checkbox/checkbox.d.ts +0 -7
  36. package/dist/checkbox/checkbox.js +24 -27
  37. package/dist/checkbox/checkbox.js.map +1 -1
  38. package/dist/confirm-morph/confirm-morph.d.ts +85 -0
  39. package/dist/confirm-morph/confirm-morph.js +652 -0
  40. package/dist/confirm-morph/confirm-morph.js.map +1 -0
  41. package/dist/confirm-morph/index.d.ts +2 -0
  42. package/dist/confirm-morph/index.js +2 -0
  43. package/dist/copy-button/copy-button.d.ts +32 -0
  44. package/dist/copy-button/copy-button.js +134 -0
  45. package/dist/copy-button/copy-button.js.map +1 -0
  46. package/dist/copy-button/index.d.ts +2 -0
  47. package/dist/copy-button/index.js +2 -0
  48. package/dist/date-picker/date-picker.d.ts +51 -0
  49. package/dist/date-picker/date-picker.js +241 -0
  50. package/dist/date-picker/date-picker.js.map +1 -0
  51. package/dist/date-picker/date-range-picker.d.ts +78 -0
  52. package/dist/date-picker/date-range-picker.js +1015 -0
  53. package/dist/date-picker/date-range-picker.js.map +1 -0
  54. package/dist/date-picker/index.d.ts +4 -0
  55. package/dist/date-picker/index.js +4 -0
  56. package/dist/date-picker/picker-parts.d.ts +46 -0
  57. package/dist/date-picker/picker-parts.js +135 -0
  58. package/dist/date-picker/picker-parts.js.map +1 -0
  59. package/dist/dialog/dialog.d.ts +10 -14
  60. package/dist/dialog/dialog.js +39 -11
  61. package/dist/dialog/dialog.js.map +1 -1
  62. package/dist/empty-state/empty-state.d.ts +44 -0
  63. package/dist/empty-state/empty-state.js +83 -0
  64. package/dist/empty-state/empty-state.js.map +1 -0
  65. package/dist/empty-state/index.d.ts +2 -0
  66. package/dist/empty-state/index.js +2 -0
  67. package/dist/expanding-button-group/expanding-button-group.d.ts +54 -0
  68. package/dist/expanding-button-group/expanding-button-group.js +350 -0
  69. package/dist/expanding-button-group/expanding-button-group.js.map +1 -0
  70. package/dist/expanding-button-group/index.d.ts +2 -0
  71. package/dist/expanding-button-group/index.js +2 -0
  72. package/dist/file-drop-zone/file-drop-zone.d.ts +71 -0
  73. package/dist/file-drop-zone/file-drop-zone.js +116 -0
  74. package/dist/file-drop-zone/file-drop-zone.js.map +1 -0
  75. package/dist/file-drop-zone/index.d.ts +2 -0
  76. package/dist/file-drop-zone/index.js +2 -0
  77. package/dist/filter-bar/filter-bar.d.ts +121 -0
  78. package/dist/filter-bar/filter-bar.js +300 -0
  79. package/dist/filter-bar/filter-bar.js.map +1 -0
  80. package/dist/filter-bar/index.d.ts +2 -0
  81. package/dist/filter-bar/index.js +2 -0
  82. package/dist/floating-button-group/floating-button-group.d.ts +61 -0
  83. package/dist/floating-button-group/floating-button-group.js +340 -0
  84. package/dist/floating-button-group/floating-button-group.js.map +1 -0
  85. package/dist/floating-button-group/index.d.ts +2 -0
  86. package/dist/floating-button-group/index.js +2 -0
  87. package/dist/icon-morph/icon-morph.d.ts +55 -0
  88. package/dist/icon-morph/icon-morph.js +331 -0
  89. package/dist/icon-morph/icon-morph.js.map +1 -0
  90. package/dist/icon-morph/index.d.ts +2 -0
  91. package/dist/icon-morph/index.js +2 -0
  92. package/dist/index.d.ts +24 -2
  93. package/dist/index.js +29 -7
  94. package/dist/input/input.d.ts +22 -0
  95. package/dist/input/input.js +36 -12
  96. package/dist/input/input.js.map +1 -1
  97. package/dist/lib/count-to.js +113 -0
  98. package/dist/lib/count-to.js.map +1 -0
  99. package/dist/lib/eye-morph.js +62 -0
  100. package/dist/lib/eye-morph.js.map +1 -0
  101. package/dist/lib/field-message.js +336 -0
  102. package/dist/lib/field-message.js.map +1 -0
  103. package/dist/lib/field-shell.js +81 -0
  104. package/dist/lib/field-shell.js.map +1 -0
  105. package/dist/lib/field-surface.js +4 -2
  106. package/dist/lib/field-surface.js.map +1 -1
  107. package/dist/lib/flip.js +80 -0
  108. package/dist/lib/flip.js.map +1 -0
  109. package/dist/lib/height-follow.js +59 -0
  110. package/dist/lib/height-follow.js.map +1 -0
  111. package/dist/lib/icon-swap.js +47 -0
  112. package/dist/lib/icon-swap.js.map +1 -0
  113. package/dist/lib/leaving-list.js +57 -0
  114. package/dist/lib/leaving-list.js.map +1 -0
  115. package/dist/lib/merge-refs.js +3 -3
  116. package/dist/lib/merge-refs.js.map +1 -1
  117. package/dist/lib/morph-text.js +128 -0
  118. package/dist/lib/morph-text.js.map +1 -0
  119. package/dist/lib/motion.js +162 -0
  120. package/dist/lib/motion.js.map +1 -0
  121. package/dist/lib/pausable-timeout.js +67 -0
  122. package/dist/lib/pausable-timeout.js.map +1 -0
  123. package/dist/lib/rolling-value.js +31 -0
  124. package/dist/lib/rolling-value.js.map +1 -0
  125. package/dist/lib/spring-progress.js +114 -0
  126. package/dist/lib/spring-progress.js.map +1 -0
  127. package/dist/lib/svg-id.js +10 -0
  128. package/dist/lib/svg-id.js.map +1 -0
  129. package/dist/lib/swap-text.js +77 -0
  130. package/dist/lib/swap-text.js.map +1 -0
  131. package/dist/lib/use-copy-feedback.js +36 -0
  132. package/dist/lib/use-copy-feedback.js.map +1 -0
  133. package/dist/page-header/index.d.ts +2 -0
  134. package/dist/page-header/index.js +2 -0
  135. package/dist/page-header/page-header.d.ts +75 -0
  136. package/dist/page-header/page-header.js +83 -0
  137. package/dist/page-header/page-header.js.map +1 -0
  138. package/dist/password-field/index.d.ts +2 -0
  139. package/dist/password-field/index.js +2 -0
  140. package/dist/password-field/password-field.d.ts +37 -0
  141. package/dist/password-field/password-field.js +101 -0
  142. package/dist/password-field/password-field.js.map +1 -0
  143. package/dist/password-strength/index.d.ts +2 -0
  144. package/dist/password-strength/index.js +2 -0
  145. package/dist/password-strength/password-strength.d.ts +127 -0
  146. package/dist/password-strength/password-strength.js +455 -0
  147. package/dist/password-strength/password-strength.js.map +1 -0
  148. package/dist/popover/popover.d.ts +8 -0
  149. package/dist/popover/popover.js +3 -3
  150. package/dist/popover/popover.js.map +1 -1
  151. package/dist/progress/progress.js +46 -5
  152. package/dist/progress/progress.js.map +1 -1
  153. package/dist/radio-group/radio-group.js +7 -6
  154. package/dist/radio-group/radio-group.js.map +1 -1
  155. package/dist/scroll-area/scroll-area.d.ts +10 -0
  156. package/dist/scroll-area/scroll-area.js +25 -3
  157. package/dist/scroll-area/scroll-area.js.map +1 -1
  158. package/dist/search-field/index.d.ts +2 -0
  159. package/dist/search-field/index.js +2 -0
  160. package/dist/search-field/search-field.d.ts +26 -0
  161. package/dist/search-field/search-field.js +155 -0
  162. package/dist/search-field/search-field.js.map +1 -0
  163. package/dist/select/select.js +79 -21
  164. package/dist/select/select.js.map +1 -1
  165. package/dist/sheet/sheet.js +2 -1
  166. package/dist/sheet/sheet.js.map +1 -1
  167. package/dist/side-nav/index.d.ts +2 -0
  168. package/dist/side-nav/index.js +2 -0
  169. package/dist/side-nav/side-nav.d.ts +72 -0
  170. package/dist/side-nav/side-nav.js +142 -0
  171. package/dist/side-nav/side-nav.js.map +1 -0
  172. package/dist/skeleton/skeleton.d.ts +8 -2
  173. package/dist/skeleton/skeleton.js +6 -2
  174. package/dist/skeleton/skeleton.js.map +1 -1
  175. package/dist/slider/slider.js +61 -7
  176. package/dist/slider/slider.js.map +1 -1
  177. package/dist/split-button/index.d.ts +2 -0
  178. package/dist/split-button/index.js +2 -0
  179. package/dist/split-button/split-button.d.ts +46 -0
  180. package/dist/split-button/split-button.js +122 -0
  181. package/dist/split-button/split-button.js.map +1 -0
  182. package/dist/stat-card/index.d.ts +2 -0
  183. package/dist/stat-card/index.js +2 -0
  184. package/dist/stat-card/stat-card.d.ts +81 -0
  185. package/dist/stat-card/stat-card.js +152 -0
  186. package/dist/stat-card/stat-card.js.map +1 -0
  187. package/dist/styles.css +1 -1
  188. package/dist/switch/switch.js +9 -8
  189. package/dist/switch/switch.js.map +1 -1
  190. package/dist/table/index.d.ts +2 -2
  191. package/dist/table/index.js +2 -2
  192. package/dist/table/table.d.ts +36 -5
  193. package/dist/table/table.js +112 -11
  194. package/dist/table/table.js.map +1 -1
  195. package/dist/tabs/tabs.js +142 -6
  196. package/dist/tabs/tabs.js.map +1 -1
  197. package/dist/textarea/textarea.d.ts +23 -0
  198. package/dist/textarea/textarea.js +24 -2
  199. package/dist/textarea/textarea.js.map +1 -1
  200. package/dist/theme-switch/index.d.ts +2 -0
  201. package/dist/theme-switch/index.js +2 -0
  202. package/dist/theme-switch/theme-switch.d.ts +37 -0
  203. package/dist/theme-switch/theme-switch.js +123 -0
  204. package/dist/theme-switch/theme-switch.js.map +1 -0
  205. package/dist/time-picker/index.d.ts +2 -0
  206. package/dist/time-picker/index.js +2 -0
  207. package/dist/time-picker/time-picker.d.ts +54 -0
  208. package/dist/time-picker/time-picker.js +201 -0
  209. package/dist/time-picker/time-picker.js.map +1 -0
  210. package/dist/toast/toast.js +58 -38
  211. package/dist/toast/toast.js.map +1 -1
  212. package/dist/tooltip/tooltip.d.ts +1 -1
  213. package/dist/tooltip/tooltip.js +63 -14
  214. package/dist/tooltip/tooltip.js.map +1 -1
  215. package/package.json +83 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-field.js","names":["AriaGroup","AriaInput","AriaButton"],"sources":["../../src/password-field/password-field.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useState } from \"react\";\nimport {\n Button as AriaButton,\n Group as AriaGroup,\n Input as AriaInput,\n type InputProps as AriaInputProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { EyeMorph } from \"../lib/eye-morph.js\";\nimport { FieldShell, useFieldWiring } from \"../lib/field-shell.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\n\n/** The toggle's accessible names. Both have English defaults; pass the ones you translate. */\nexport interface PasswordFieldMessages {\n showPassword: string;\n hidePassword: string;\n}\n\nexport const defaultPasswordFieldMessages: PasswordFieldMessages = {\n showPassword: \"Show password\",\n hidePassword: \"Hide password\",\n};\n\n/**\n * The field's box. A React Aria `Group` rather than the input, because the\n * toggle sits inside the same border: it carries the shared field surface (the\n * 160ms border transition, the darker border on hover and focus, the muted\n * disabled look, the invalid border) and the input inside is borderless.\n *\n * `data-focus-within` darkens the border too. A Group reports focus-visible\n * from the input modality, so a mouse click into the input, or onto the toggle,\n * would otherwise leave the border pale; a plain Input is different because\n * React Aria treats a text input as always keyboard-focused.\n */\nconst shellClass = cn(\n fieldSurfaceVariants(),\n \"flex items-center h-(--density-control-h-md) ps-(--density-control-pad-md) field-toggle-shell [&:where([data-focus-within])]:border-foreground\",\n);\n\n/** The input inside the box: no border, no fill, no outline of its own. */\nconst inputClass =\n \"min-w-0 flex-1 border-0 bg-transparent p-0 font-sans text-base text-foreground outline-none placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed motion-safe:data-[reveal=shown]:animate-field-reveal-shown motion-safe:data-[reveal=hidden]:animate-field-reveal-hidden\";\n\nconst toggleClass =\n \"field-toggle-button grid size-7.5 flex-none cursor-pointer place-items-center rounded-md border-0 bg-transparent p-0 text-muted-foreground outline-none data-[hovered]:bg-muted data-[hovered]:text-foreground aria-pressed:text-foreground data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) data-[disabled]:cursor-not-allowed\";\n\nexport interface PasswordFieldProps extends Omit<\n AriaInputProps,\n \"className\" | \"style\" | \"size\" | \"type\"\n> {\n /** Extra classes for the input. Merged last, so they win conflicts with ours. */\n className?: string;\n /** The visible label, tied to the input with `htmlFor`. */\n label: string;\n /**\n * Helper text under the field, named by `aria-describedby`. A string, because\n * the copy is animated word by word: it opens on a spring when it appears\n * after mount and changed words rise in. Nothing animates on first render.\n */\n description?: string;\n /** Translated or reworded toggle names. */\n messages?: Partial<PasswordFieldMessages>;\n}\n\n/**\n * A password input with a show and hide toggle. Switching resolves the text in\n * place from a soft blur, and the eye is one outline that a slash draws across.\n * Native input props apply to the input; `disabled` also disables the toggle,\n * and `aria-invalid` gives the box its invalid border.\n *\n * The toggle's name changes with the state and it is also `aria-pressed`, so a\n * screen reader may announce both (\"Hide password, pressed\").\n *\n * @example\n * <PasswordField label=\"Password\" description=\"At least eight characters.\" />\n */\nexport const PasswordField = forwardRef<HTMLInputElement, PasswordFieldProps>(\n function PasswordField({ label, description, messages: messagesProp, className, ...props }, ref) {\n const messages = { ...defaultPasswordFieldMessages, ...messagesProp };\n const [visible, setVisible] = useState(false);\n // data-reveal only appears after the first toggle, so the animation runs on each change but never on mount.\n const [toggled, setToggled] = useState(false);\n const field = useFieldWiring({\n enabled: true,\n id: props.id,\n describedBy: props[\"aria-describedby\"],\n invalid: props[\"aria-invalid\"],\n description,\n error: undefined,\n });\n const disabled = Boolean(props.disabled);\n const ariaInvalid = props[\"aria-invalid\"];\n const invalid = ariaInvalid !== undefined && ariaInvalid !== false && ariaInvalid !== \"false\";\n\n const control = (\n <AriaGroup\n role=\"presentation\"\n isDisabled={disabled}\n isInvalid={invalid}\n data-slot=\"password-field-shell\"\n className={shellClass}\n >\n <AriaInput\n ref={ref}\n {...props}\n {...field.controlProps}\n type={visible ? \"text\" : \"password\"}\n data-slot=\"password-field\"\n data-reveal={toggled ? (visible ? \"shown\" : \"hidden\") : undefined}\n className={cn(inputClass, className)}\n />\n <AriaButton\n type=\"button\"\n isDisabled={disabled}\n aria-label={visible ? messages.hidePassword : messages.showPassword}\n aria-pressed={visible}\n onPress={() => {\n setVisible((current) => !current);\n setToggled(true);\n }}\n className={toggleClass}\n >\n <EyeMorph slashed={visible} />\n </AriaButton>\n </AriaGroup>\n );\n\n if (!field.active) return control;\n return (\n <FieldShell\n label={label}\n controlId={field.controlId}\n description={description}\n hintId={field.hintId}\n error={undefined}\n errorId={undefined}\n wordExit=\"sharp\"\n numbers={false}\n >\n {control}\n </FieldShell>\n );\n },\n);\n"],"mappings":";;;;;;;;;AAqBA,MAAa,+BAAsD;CACjE,cAAc;CACd,cAAc;AAChB;;;;;;;;;;;;AAaA,MAAM,aAAa,GACjB,qBAAqB,GACrB,gJACF;;AAGA,MAAM,aACJ;AAEF,MAAM,cACJ;;;;;;;;;;;;;AAgCF,MAAa,gBAAgB,WAC3B,SAAS,cAAc,EAAE,OAAO,aAAa,UAAU,cAAc,WAAW,GAAG,SAAS,KAAK;CAC/F,MAAM,WAAW;EAAE,GAAG;EAA8B,GAAG;CAAa;CACpE,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,QAAQ,eAAe;EAC3B,SAAS;EACT,IAAI,MAAM;EACV,aAAa,MAAM;EACnB,SAAS,MAAM;EACf;EACA,OAAO,KAAA;CACT,CAAC;CACD,MAAM,WAAW,QAAQ,MAAM,QAAQ;CACvC,MAAM,cAAc,MAAM;CAG1B,MAAM,UACJ,qBAACA,OAAD;EACE,MAAK;EACL,YAAY;EACZ,WANY,gBAAgB,KAAA,KAAa,gBAAgB,SAAS,gBAAgB;EAOlF,aAAU;EACV,WAAW;EALb,UAAA,CAOE,oBAACC,OAAD;GACO;GACL,GAAI;GACJ,GAAI,MAAM;GACV,MAAM,UAAU,SAAS;GACzB,aAAU;GACV,eAAa,UAAW,UAAU,UAAU,WAAY,KAAA;GACxD,WAAW,GAAG,YAAY,SAAS;EACpC,CAAA,GACD,oBAACC,QAAD;GACE,MAAK;GACL,YAAY;GACZ,cAAY,UAAU,SAAS,eAAe,SAAS;GACvD,gBAAc;GACd,eAAe;IACb,YAAY,YAAY,CAAC,OAAO;IAChC,WAAW,IAAI;GACjB;GACA,WAAW;GAEX,UAAA,oBAAC,UAAD,EAAU,SAAS,QAAU,CAAA;EACnB,CAAA,CACH;;CAGb,IAAI,CAAC,MAAM,QAAQ,OAAO;CAC1B,OACE,oBAAC,YAAD;EACS;EACP,WAAW,MAAM;EACJ;EACb,QAAQ,MAAM;EACd,OAAO,KAAA;EACP,SAAS,KAAA;EACT,UAAS;EACT,SAAS;EAER,UAAA;CACS,CAAA;AAEhB,CACF"}
@@ -0,0 +1,2 @@
1
+ import { EstimateStrengthOptions, PasswordRule, PasswordRuleLabels, PasswordRuleOptions, PasswordStrength, PasswordStrengthLevels, PasswordStrengthMessages, PasswordStrengthMessagesInput, PasswordStrengthProps, PasswordStrengthResult, countCodePoints, createPasswordRules, defaultPasswordRules, defaultPasswordStrengthMessages, estimateStrength } from "./password-strength.js";
2
+ export { type EstimateStrengthOptions, type PasswordRule, type PasswordRuleLabels, type PasswordRuleOptions, PasswordStrength, type PasswordStrengthLevels, type PasswordStrengthMessages, type PasswordStrengthMessagesInput, type PasswordStrengthProps, type PasswordStrengthResult, countCodePoints, createPasswordRules, defaultPasswordRules, defaultPasswordStrengthMessages, estimateStrength };
@@ -0,0 +1,2 @@
1
+ import { PasswordStrength, countCodePoints, createPasswordRules, defaultPasswordRules, defaultPasswordStrengthMessages, estimateStrength } from "./password-strength.js";
2
+ export { PasswordStrength, countCodePoints, createPasswordRules, defaultPasswordRules, defaultPasswordStrengthMessages, estimateStrength };
@@ -0,0 +1,127 @@
1
+ import { InputProps } from "react-aria-components";
2
+ //#region src/password-strength/password-strength.d.ts
3
+ export interface PasswordRule {
4
+ id: string;
5
+ label: string;
6
+ test: (password: string) => boolean;
7
+ /** Characters still missing. Shown as a rolling count beside the rule while it is unmet. */
8
+ remaining?: (password: string) => number;
9
+ }
10
+ export interface PasswordStrengthResult {
11
+ level: 0 | 1 | 2 | 3 | 4;
12
+ label: string;
13
+ met: string[];
14
+ /** True while the password is shorter than the short length, so the meter holds at its first step. */
15
+ short?: boolean;
16
+ }
17
+ /** The strength words, one per meter step. Override them to translate the meter. */
18
+ export interface PasswordStrengthLevels {
19
+ tooShort: string;
20
+ weak: string;
21
+ fair: string;
22
+ good: string;
23
+ strong: string;
24
+ }
25
+ /** Every user-facing string in PasswordStrength. All have English defaults; pass only the ones you change. */
26
+ export interface PasswordStrengthMessages {
27
+ levels: PasswordStrengthLevels;
28
+ /** Name of the show password toggle. It is a pressed toggle (aria-pressed), so the name stays the same in both states. */
29
+ showPassword: string;
30
+ /** Accessible name of the strength meter. */
31
+ meterLabel: string;
32
+ /** Meter value text while the field is empty. */
33
+ noPassword: string;
34
+ /** Accessible name of the requirements list. */
35
+ rulesLabel: string;
36
+ /** Read after a rule's label for screen readers, including any leading punctuation. */
37
+ ruleMet: string;
38
+ ruleNotMet: string;
39
+ /** Words around the rolling count beside an unmet rule, as in "4 more". */
40
+ remainingPrefix: string;
41
+ remainingSuffix: string;
42
+ /** The polite live-region summary read after each change. */
43
+ summary: (result: {
44
+ label: string;
45
+ met: number;
46
+ total: number;
47
+ }) => string;
48
+ }
49
+ export declare const defaultPasswordStrengthMessages: PasswordStrengthMessages;
50
+ /** Partial messages, with partial strength words, for the `messages` prop. */
51
+ export type PasswordStrengthMessagesInput = Partial<Omit<PasswordStrengthMessages, "levels">> & {
52
+ levels?: Partial<PasswordStrengthLevels>;
53
+ };
54
+ /**
55
+ * A new-password field that shows how strong the password is while it is typed:
56
+ * four segments fill and change tone, each rule checks off with a drawn tick, and
57
+ * the strength word swaps in place. Use it when creating or changing a password;
58
+ * use `PasswordField` for sign in. Scoring happens on the device, nothing is sent
59
+ * anywhere.
60
+ *
61
+ * Native input props apply to the input (`className` too); `disabled` also
62
+ * disables the toggle, and `aria-invalid` gives the box its invalid border.
63
+ *
64
+ * @example
65
+ * <PasswordStrength label="New password" onValueChange={(value, strength) => save(value, strength.level)} />
66
+ */
67
+ export interface PasswordStrengthProps extends Omit<InputProps, "className" | "style" | "size" | "type" | "value" | "defaultValue" | "children"> {
68
+ /** Extra classes for the input. Merged last, so they win conflicts with ours. */
69
+ className?: string;
70
+ /** The visible label, tied to the input with `htmlFor`. */
71
+ label: string;
72
+ value?: string;
73
+ defaultValue?: string;
74
+ onValueChange?: (value: string, strength: PasswordStrengthResult) => void;
75
+ /** Rules to check. Strength is the share of rules met, spread over four steps. */
76
+ rules?: PasswordRule[];
77
+ /**
78
+ * Error copy tied to the field. It is an alert, opens on a spring and swaps as a
79
+ * whole when it changes; the field shakes once each time a new error appears.
80
+ */
81
+ error?: string;
82
+ revealed?: boolean;
83
+ onRevealedChange?: (revealed: boolean) => void;
84
+ /** Translated or reworded copy. Every string has an English default; see PasswordStrengthMessages. */
85
+ messages?: PasswordStrengthMessagesInput;
86
+ /** Below this many characters the meter holds at its first step and reads "Too short". Defaults to 8. */
87
+ shortLength?: number;
88
+ /** How characters are counted for shortLength. Defaults to code points, so an emoji counts once; pass `password => password.length` to count UTF-16 units like a server might. */
89
+ countCharacters?: (password: string) => number;
90
+ }
91
+ /** Counts code points, so an emoji or other astral character counts as one. */
92
+ export declare const countCodePoints: (password: string) => number;
93
+ /** Labels for the built-in rules. `length` receives the minimum length. */
94
+ export interface PasswordRuleLabels {
95
+ length: (minLength: number) => string;
96
+ case: string;
97
+ number: string;
98
+ symbol: string;
99
+ }
100
+ export interface PasswordRuleOptions {
101
+ /** Characters the length rule asks for. Defaults to 12. */
102
+ minLength?: number;
103
+ /** Translated or reworded rule labels. */
104
+ labels?: Partial<PasswordRuleLabels>;
105
+ /** How the length rule counts characters. Defaults to code points. */
106
+ countCharacters?: (password: string) => number;
107
+ }
108
+ /** The built-in rules (length, mixed case, number, symbol) with your own minimum length, labels and character count. */
109
+ export declare function createPasswordRules({ minLength, labels, countCharacters }?: PasswordRuleOptions): PasswordRule[];
110
+ export declare const defaultPasswordRules: PasswordRule[];
111
+ export interface EstimateStrengthOptions {
112
+ /** Below this many characters a password stays at the first step, however varied it is. Defaults to 8. */
113
+ shortLength?: number;
114
+ /** Strength words for the result's label. */
115
+ levels?: Partial<PasswordStrengthLevels>;
116
+ /** How characters are counted for shortLength. Defaults to code points. */
117
+ countCharacters?: (password: string) => number;
118
+ }
119
+ /** Scores a password on the device: one step per share of rules met, capped at the first step while it is shorter than the short length (8 by default). */
120
+ export declare function estimateStrength(password: string, rules?: PasswordRule[], { shortLength, levels, countCharacters }?: EstimateStrengthOptions): PasswordStrengthResult;
121
+ /**
122
+ * A new-password input with a strength meter and a checklist (see
123
+ * {@link PasswordStrengthProps}).
124
+ */
125
+ export declare const PasswordStrength: import("react").ForwardRefExoticComponent<PasswordStrengthProps & import("react").RefAttributes<HTMLInputElement>>;
126
+ //#endregion
127
+ //# sourceMappingURL=password-strength.d.ts.map
@@ -0,0 +1,455 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect, useLeavingItems } from "../lib/motion.js";
4
+ import { SwapText } from "../lib/swap-text.js";
5
+ import { fieldSurfaceVariants } from "../lib/field-surface.js";
6
+ import { Label as Label$1 } from "../label/label.js";
7
+ import { FieldMessage } from "../lib/field-message.js";
8
+ import { useFieldWiring } from "../lib/field-shell.js";
9
+ import { EyeMorph } from "../lib/eye-morph.js";
10
+ import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
11
+ import { Button, Group, Input } from "react-aria-components";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ //#region src/password-strength/password-strength.tsx
14
+ const defaultPasswordStrengthMessages = {
15
+ levels: {
16
+ tooShort: "Too short",
17
+ weak: "Weak",
18
+ fair: "Fair",
19
+ good: "Good",
20
+ strong: "Strong"
21
+ },
22
+ showPassword: "Show password",
23
+ meterLabel: "Password strength",
24
+ noPassword: "No password yet",
25
+ rulesLabel: "Password requirements",
26
+ ruleMet: ", met",
27
+ ruleNotMet: ", not met",
28
+ remainingPrefix: "",
29
+ remainingSuffix: "more",
30
+ summary: ({ label, met, total }) => `Strength: ${label}. ${met} of ${total} requirements met.`
31
+ };
32
+ /** Counts code points, so an emoji or other astral character counts as one. */
33
+ const countCodePoints = (password) => Array.from(password).length;
34
+ const defaultRuleLabels = {
35
+ length: (minLength) => `At least ${minLength} characters`,
36
+ case: "Upper and lowercase letters",
37
+ number: "At least one number",
38
+ symbol: "At least one symbol"
39
+ };
40
+ /** The built-in rules (length, mixed case, number, symbol) with your own minimum length, labels and character count. */
41
+ function createPasswordRules({ minLength = 12, labels, countCharacters = countCodePoints } = {}) {
42
+ const copy = {
43
+ ...defaultRuleLabels,
44
+ ...labels
45
+ };
46
+ return [
47
+ {
48
+ id: "length",
49
+ label: copy.length(minLength),
50
+ test: (password) => countCharacters(password) >= minLength,
51
+ remaining: (password) => Math.max(0, minLength - countCharacters(password))
52
+ },
53
+ {
54
+ id: "case",
55
+ label: copy.case,
56
+ test: (password) => /\p{Ll}/u.test(password) && /\p{Lu}/u.test(password)
57
+ },
58
+ {
59
+ id: "number",
60
+ label: copy.number,
61
+ test: (password) => /\p{N}/u.test(password)
62
+ },
63
+ {
64
+ id: "symbol",
65
+ label: copy.symbol,
66
+ test: (password) => /[^\p{L}\p{N}\s]/u.test(password)
67
+ }
68
+ ];
69
+ }
70
+ const defaultPasswordRules = createPasswordRules();
71
+ /** Scores a password on the device: one step per share of rules met, capped at the first step while it is shorter than the short length (8 by default). */
72
+ function estimateStrength(password, rules = defaultPasswordRules, { shortLength = 8, levels, countCharacters = countCodePoints } = {}) {
73
+ if (!password) return {
74
+ level: 0,
75
+ label: "",
76
+ met: []
77
+ };
78
+ const words = {
79
+ ...defaultPasswordStrengthMessages.levels,
80
+ ...levels
81
+ };
82
+ const met = rules.filter((rule) => rule.test(password)).map((rule) => rule.id);
83
+ if (countCharacters(password) < shortLength) return {
84
+ level: 1,
85
+ label: words.tooShort,
86
+ met,
87
+ short: true
88
+ };
89
+ const level = Math.min(4, Math.max(1, Math.round(met.length / Math.max(rules.length, 1) * 4)));
90
+ return {
91
+ level,
92
+ label: [
93
+ words.weak,
94
+ words.fair,
95
+ words.good,
96
+ words.strong
97
+ ][level - 1] ?? words.weak,
98
+ met
99
+ };
100
+ }
101
+ /**
102
+ * The field's box: a React Aria `Group` with the shared field surface (see
103
+ * PasswordField for why a Group and for `data-focus-within`). Hover darkens the
104
+ * border only to `muted-foreground`, as the reference does here, and a new error
105
+ * shakes the box (two animation names, so each restart plays).
106
+ */
107
+ const shellClass = cn(fieldSurfaceVariants(), "flex items-center gap-1 h-(--density-control-h-md) ps-(--density-control-pad-md) field-toggle-shell [--field-toggle-size:calc(var(--spacing)*8.5)] [&:where([data-focus-within])]:border-foreground data-[hovered]:not-data-[disabled]:not-data-[focus-within]:not-data-[invalid]:border-muted-foreground motion-safe:data-[shake=a]:animate-field-shake-a motion-safe:data-[shake=b]:animate-field-shake-b");
108
+ const inputClass = "min-w-0 flex-1 border-0 bg-transparent p-0 font-sans text-base text-foreground outline-none placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground data-[disabled]:cursor-not-allowed motion-safe:data-[reveal=shown]:animate-field-reveal-shown motion-safe:data-[reveal=hidden]:animate-field-reveal-hidden";
109
+ const toggleClass = "field-toggle-button grid size-(--field-toggle-size) flex-none cursor-pointer place-items-center rounded-md border-0 bg-transparent p-0 text-muted-foreground [--press-scale:.94] [-webkit-tap-highlight-color:transparent] outline-none data-[hovered]:bg-muted data-[hovered]:text-foreground aria-pressed:text-foreground data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) data-[disabled]:cursor-not-allowed";
110
+ const toPlaces = (value, seq, previous) => {
111
+ const digits = String(value).split("");
112
+ return digits.map((digit, index) => {
113
+ const key = digits.length - 1 - index;
114
+ return {
115
+ key,
116
+ digit,
117
+ seq: previous.get(key)?.seq ?? seq
118
+ };
119
+ });
120
+ };
121
+ function LeavingDigit({ id, rect, onDone, children }) {
122
+ const remove = useCallback(() => onDone(id), [onDone, id]);
123
+ useExitBackstop(true, remove);
124
+ return /* @__PURE__ */ jsx("span", {
125
+ "aria-hidden": "true",
126
+ onAnimationEnd: (event) => {
127
+ if (event.target === event.currentTarget) remove();
128
+ },
129
+ style: rect,
130
+ className: "absolute motion-safe:animate-field-count-out motion-reduce:animate-fade-out",
131
+ children
132
+ });
133
+ }
134
+ function PlaceColumn({ place, direction, leaving, onDone }) {
135
+ const digitRef = useRef(null);
136
+ const digits = useLeavingItems(place.digit, place.digit, () => digitRef.current);
137
+ const done = useCallback(() => onDone(place.key), [onDone, place.key]);
138
+ useExitBackstop(leaving, done, 900);
139
+ useIsomorphicLayoutEffect(() => {
140
+ if (leaving && prefersReducedMotion()) done();
141
+ }, [leaving, done]);
142
+ return /* @__PURE__ */ jsxs("span", {
143
+ style: { "--roll": direction },
144
+ onAnimationEnd: (event) => {
145
+ if (leaving && event.target === event.currentTarget && event.animationName === "field-column-shrink") done();
146
+ },
147
+ className: cn("field-number-column overflow-x-clip", leaving ? "motion-safe:animate-field-column-out" : place.seq > 0 && "motion-safe:animate-field-column-in"),
148
+ children: [digits.leaving.map((item) => /* @__PURE__ */ jsx(LeavingDigit, {
149
+ id: item.id,
150
+ rect: item.rect,
151
+ onDone: digits.remove,
152
+ children: item.value
153
+ }, item.id)), /* @__PURE__ */ jsx("span", {
154
+ ref: digitRef,
155
+ className: digits.changed ? "motion-safe:animate-field-count-in motion-reduce:animate-field-swap-fade-in" : void 0,
156
+ children: place.digit
157
+ }, place.digit)]
158
+ });
159
+ }
160
+ /** The count beside an unmet rule. It rolls digit by digit: arriving from below when the count grew, from above when it fell, and a gained or lost digit opens or closes its column. */
161
+ function RollingCount({ value }) {
162
+ const [state, setState] = useState(() => ({
163
+ value,
164
+ direction: 1,
165
+ seq: 0,
166
+ places: toPlaces(value, 0, /* @__PURE__ */ new Map()),
167
+ gone: []
168
+ }));
169
+ let { places, gone, direction } = state;
170
+ if (state.value !== value) {
171
+ const seq = state.seq + 1;
172
+ direction = value > state.value ? 1 : -1;
173
+ places = toPlaces(value, seq, new Map(state.places.map((place) => [place.key, place])));
174
+ const kept = new Set(places.map((place) => place.key));
175
+ gone = [...state.gone.filter((place) => !kept.has(place.key)), ...state.places.filter((place) => !kept.has(place.key))];
176
+ setState({
177
+ value,
178
+ direction,
179
+ seq,
180
+ places,
181
+ gone
182
+ });
183
+ }
184
+ const finish = useCallback((key) => setState((current) => ({
185
+ ...current,
186
+ gone: current.gone.filter((place) => place.key !== key)
187
+ })), []);
188
+ const all = [...places.map((place) => ({
189
+ place,
190
+ leaving: false
191
+ })), ...gone.map((place) => ({
192
+ place,
193
+ leaving: true
194
+ }))].sort((a, b) => b.place.key - a.place.key);
195
+ return /* @__PURE__ */ jsx("span", {
196
+ className: "inline-flex tabular-nums",
197
+ children: all.map(({ place, leaving }) => /* @__PURE__ */ jsx(PlaceColumn, {
198
+ place,
199
+ direction,
200
+ leaving,
201
+ onDone: finish
202
+ }, place.key))
203
+ });
204
+ }
205
+ /** One rule's tick: the tint grows under it while it draws from its short stroke; unchecking retracts it faster than it drew. */
206
+ function RuleMark() {
207
+ return /* @__PURE__ */ jsxs("span", {
208
+ "aria-hidden": "true",
209
+ className: "field-rule-mark",
210
+ children: [/* @__PURE__ */ jsx("span", { className: "field-rule-fill" }), /* @__PURE__ */ jsx("svg", {
211
+ className: "field-rule-glyph",
212
+ viewBox: "0 0 16 16",
213
+ fill: "none",
214
+ stroke: "currentColor",
215
+ strokeWidth: 1.75,
216
+ strokeLinecap: "round",
217
+ strokeLinejoin: "round",
218
+ children: /* @__PURE__ */ jsx("path", {
219
+ d: "M4.75 8.25 7 10.5l4.25-4.75",
220
+ pathLength: 1,
221
+ className: "field-rule-tick"
222
+ })
223
+ })]
224
+ });
225
+ }
226
+ /**
227
+ * A new-password input with a strength meter and a checklist (see
228
+ * {@link PasswordStrengthProps}).
229
+ */
230
+ const PasswordStrength = forwardRef(function PasswordStrength({ label, value: valueProp, defaultValue = "", onValueChange, onChange, rules = defaultPasswordRules, error, revealed: revealedProp, onRevealedChange, messages: messagesProp, shortLength, countCharacters, className, ...props }, ref) {
231
+ const messages = {
232
+ ...defaultPasswordStrengthMessages,
233
+ ...messagesProp,
234
+ levels: {
235
+ ...defaultPasswordStrengthMessages.levels,
236
+ ...messagesProp?.levels
237
+ }
238
+ };
239
+ const options = {
240
+ ...shortLength === void 0 ? {} : { shortLength },
241
+ levels: messages.levels,
242
+ ...countCharacters === void 0 ? {} : { countCharacters }
243
+ };
244
+ const field = useFieldWiring({
245
+ enabled: true,
246
+ id: props.id,
247
+ describedBy: props["aria-describedby"],
248
+ invalid: props["aria-invalid"],
249
+ description: void 0,
250
+ error
251
+ });
252
+ const rulesId = `${field.controlId}-rules`;
253
+ const [internal, setInternal] = useState(defaultValue);
254
+ const value = valueProp ?? internal;
255
+ const [revealedInternal, setRevealedInternal] = useState(false);
256
+ const revealed = revealedProp ?? revealedInternal;
257
+ const strength = estimateStrength(value, rules, options);
258
+ const met = new Set(strength.met);
259
+ const rank = strength.short ? .5 : strength.level;
260
+ const metKey = strength.met.join(" ");
261
+ const [track, setTrack] = useState({
262
+ rank,
263
+ level: strength.level,
264
+ previousLevel: strength.level,
265
+ direction: 1,
266
+ metKey,
267
+ previousMet: metKey
268
+ });
269
+ if (track.rank !== rank || track.metKey !== metKey) setTrack({
270
+ rank,
271
+ level: strength.level,
272
+ previousLevel: track.rank !== rank ? track.level : track.previousLevel,
273
+ direction: track.rank === rank ? track.direction : rank > track.rank ? 1 : -1,
274
+ metKey,
275
+ previousMet: track.metKey !== metKey ? track.metKey : track.previousMet
276
+ });
277
+ const previousMet = new Set(track.previousMet.split(" ").filter(Boolean));
278
+ const changed = rules.filter((rule) => met.has(rule.id) !== previousMet.has(rule.id)).map((rule) => rule.id);
279
+ const [reveal, setReveal] = useState({
280
+ revealed,
281
+ changed: false
282
+ });
283
+ if (reveal.revealed !== revealed) setReveal({
284
+ revealed,
285
+ changed: true
286
+ });
287
+ const lastError = useRef(error);
288
+ const [shake, setShake] = useState(void 0);
289
+ useEffect(() => {
290
+ const previous = lastError.current;
291
+ lastError.current = error;
292
+ if (!error || error === previous) return;
293
+ setShake((current) => current === "a" ? "b" : "a");
294
+ }, [error]);
295
+ const summary = strength.level ? messages.summary({
296
+ label: strength.label,
297
+ met: strength.met.length,
298
+ total: rules.length
299
+ }) : "";
300
+ const disabled = Boolean(props.disabled);
301
+ const ariaInvalid = props["aria-invalid"];
302
+ const invalid = Boolean(error) || ariaInvalid !== void 0 && ariaInvalid !== false && ariaInvalid !== "false";
303
+ const describedBy = [field.controlProps["aria-describedby"] ?? props["aria-describedby"], rulesId].filter(Boolean).join(" ");
304
+ function toggleReveal() {
305
+ if (revealedProp === void 0) setRevealedInternal(!revealed);
306
+ onRevealedChange?.(!revealed);
307
+ }
308
+ return /* @__PURE__ */ jsxs("div", {
309
+ "data-slot": "password-strength",
310
+ "data-level": strength.level,
311
+ className: "grid min-w-0 font-sans [--tone:var(--color-border)] data-[level=1]:[--tone:var(--color-destructive-strong)] data-[level=2]:[--tone:var(--color-warning-strong)] data-[level=3]:[--tone:var(--color-success-strong)] data-[level=4]:[--tone:var(--color-success-strong)]",
312
+ children: [
313
+ /* @__PURE__ */ jsx(Label$1, {
314
+ htmlFor: field.controlId,
315
+ className: "mb-1.5",
316
+ children: label
317
+ }),
318
+ /* @__PURE__ */ jsxs(Group, {
319
+ role: "presentation",
320
+ isDisabled: disabled,
321
+ isInvalid: invalid,
322
+ "data-shake": shake,
323
+ "data-slot": "password-strength-shell",
324
+ className: shellClass,
325
+ children: [/* @__PURE__ */ jsx(Input, {
326
+ autoComplete: "new-password",
327
+ autoCapitalize: "off",
328
+ autoCorrect: "off",
329
+ spellCheck: false,
330
+ ...props,
331
+ ref,
332
+ ...field.controlProps,
333
+ type: revealed ? "text" : "password",
334
+ value,
335
+ onChange: (event) => {
336
+ const next = event.target.value;
337
+ if (valueProp === void 0) setInternal(next);
338
+ onChange?.(event);
339
+ onValueChange?.(next, estimateStrength(next, rules, options));
340
+ },
341
+ "aria-describedby": describedBy,
342
+ "data-slot": "password-strength-input",
343
+ "data-reveal": reveal.changed ? revealed ? "shown" : "hidden" : void 0,
344
+ className: cn(inputClass, className)
345
+ }), /* @__PURE__ */ jsx(Button, {
346
+ type: "button",
347
+ isDisabled: disabled,
348
+ "aria-label": messages.showPassword,
349
+ "aria-pressed": revealed,
350
+ "aria-controls": field.controlId,
351
+ onPress: toggleReveal,
352
+ className: toggleClass,
353
+ children: /* @__PURE__ */ jsx(EyeMorph, {
354
+ slashed: revealed,
355
+ slashPath: "M3 3l18 18"
356
+ })
357
+ })]
358
+ }),
359
+ /* @__PURE__ */ jsx(FieldMessage, {
360
+ id: field.errorId,
361
+ text: error,
362
+ tone: "error",
363
+ copy: "swap"
364
+ }),
365
+ /* @__PURE__ */ jsxs("div", {
366
+ className: "mt-3 flex items-center gap-3",
367
+ children: [/* @__PURE__ */ jsx("div", {
368
+ role: "meter",
369
+ "aria-label": messages.meterLabel,
370
+ "aria-valuemin": 0,
371
+ "aria-valuemax": 4,
372
+ "aria-valuenow": strength.level,
373
+ "aria-valuetext": strength.level ? strength.label : messages.noPassword,
374
+ className: "grid min-w-0 flex-1 grid-cols-4 gap-1.25",
375
+ children: [
376
+ 0,
377
+ 1,
378
+ 2,
379
+ 3
380
+ ].map((index) => {
381
+ const on = index < strength.level;
382
+ const wave = on ? index - track.previousLevel : track.previousLevel - 1 - index;
383
+ const delay = Math.max(0, wave) * (on ? .05 : .03);
384
+ return /* @__PURE__ */ jsx("span", {
385
+ className: "relative isolate h-1 overflow-hidden rounded-full bg-muted",
386
+ children: /* @__PURE__ */ jsx("span", {
387
+ "data-on": on || void 0,
388
+ style: { "--wave-delay": `${delay}s` },
389
+ className: "field-meter-fill absolute inset-0 rounded-[inherit]"
390
+ })
391
+ }, index);
392
+ })
393
+ }), /* @__PURE__ */ jsx("span", {
394
+ "aria-hidden": "true",
395
+ className: "field-strength-word text-sm font-medium text-foreground",
396
+ children: /* @__PURE__ */ jsx(SwapText, {
397
+ swapKey: strength.level ? strength.label : "",
398
+ dir: track.direction,
399
+ children: strength.label
400
+ })
401
+ })]
402
+ }),
403
+ /* @__PURE__ */ jsx("ul", {
404
+ id: rulesId,
405
+ "aria-label": messages.rulesLabel,
406
+ className: "mt-4 grid list-none gap-1.5 p-0",
407
+ children: rules.map((rule) => {
408
+ const ok = met.has(rule.id);
409
+ const remaining = value && !ok ? rule.remaining?.(value) ?? 0 : 0;
410
+ const order = changed.indexOf(rule.id);
411
+ return /* @__PURE__ */ jsxs("li", {
412
+ "data-met": ok || void 0,
413
+ style: { "--delay": `${order > 0 ? order * .05 : 0}s` },
414
+ className: "flex min-h-5 items-center gap-2.5 text-sm leading-(--leading-normal) text-muted-foreground data-[met]:text-foreground motion-safe:transition-colors motion-safe:duration-(--duration-standard) motion-safe:ease-settle",
415
+ children: [
416
+ /* @__PURE__ */ jsx(RuleMark, {}),
417
+ /* @__PURE__ */ jsxs("span", {
418
+ className: "min-w-0",
419
+ children: [rule.label, /* @__PURE__ */ jsx("span", {
420
+ className: "sr-only",
421
+ children: ok ? messages.ruleMet : messages.ruleNotMet
422
+ })]
423
+ }),
424
+ /* @__PURE__ */ jsx("span", {
425
+ "aria-hidden": "true",
426
+ className: "relative ms-auto ps-2 text-xs whitespace-nowrap text-muted-foreground",
427
+ children: /* @__PURE__ */ jsx(SwapText, {
428
+ swapKey: remaining > 0 ? "remaining" : "",
429
+ dir: 1,
430
+ children: /* @__PURE__ */ jsxs("span", {
431
+ className: "field-count-text",
432
+ children: [
433
+ messages.remainingPrefix ? `${messages.remainingPrefix} ` : null,
434
+ /* @__PURE__ */ jsx(RollingCount, { value: remaining }),
435
+ messages.remainingSuffix ? ` ${messages.remainingSuffix}` : null
436
+ ]
437
+ })
438
+ })
439
+ })
440
+ ]
441
+ }, rule.id);
442
+ })
443
+ }),
444
+ /* @__PURE__ */ jsx("span", {
445
+ className: "sr-only",
446
+ role: "status",
447
+ children: summary
448
+ })
449
+ ]
450
+ });
451
+ });
452
+ //#endregion
453
+ export { PasswordStrength, countCodePoints, createPasswordRules, defaultPasswordRules, defaultPasswordStrengthMessages, estimateStrength };
454
+
455
+ //# sourceMappingURL=password-strength.js.map