@beautinique/frontend-components 1.0.6 → 1.0.8

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 (140) hide show
  1. package/README.md +184 -20
  2. package/dist/auth/AuthBottomInstructions.d.cts +42 -0
  3. package/dist/auth/AuthBottomInstructions.d.cts.map +1 -0
  4. package/dist/auth/AuthBottomInstructions.d.ts +42 -0
  5. package/dist/auth/AuthBottomInstructions.d.ts.map +1 -0
  6. package/dist/auth/index.cjs +76 -0
  7. package/dist/auth/index.cjs.map +1 -0
  8. package/dist/auth/index.d.cts +2 -0
  9. package/dist/auth/index.d.cts.map +1 -0
  10. package/dist/auth/index.d.ts +2 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +74 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/index.cjs +14 -0
  15. package/dist/index.d.cts +2 -0
  16. package/dist/index.d.cts.map +1 -1
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/layout/ScrollToTop.d.cts +36 -0
  21. package/dist/layout/ScrollToTop.d.cts.map +1 -0
  22. package/dist/layout/ScrollToTop.d.ts +36 -0
  23. package/dist/layout/ScrollToTop.d.ts.map +1 -0
  24. package/dist/layout/index.cjs +33 -0
  25. package/dist/layout/index.cjs.map +1 -0
  26. package/dist/layout/index.d.cts +2 -0
  27. package/dist/layout/index.d.cts.map +1 -0
  28. package/dist/layout/index.d.ts +2 -0
  29. package/dist/layout/index.d.ts.map +1 -0
  30. package/dist/layout/index.js +31 -0
  31. package/dist/layout/index.js.map +1 -0
  32. package/dist/lib/content.d.cts +7 -0
  33. package/dist/lib/content.d.cts.map +1 -0
  34. package/dist/lib/content.d.ts +7 -0
  35. package/dist/lib/content.d.ts.map +1 -0
  36. package/dist/lib/countdown.d.cts +13 -0
  37. package/dist/lib/countdown.d.cts.map +1 -0
  38. package/dist/lib/countdown.d.ts +13 -0
  39. package/dist/lib/countdown.d.ts.map +1 -0
  40. package/dist/lib/stepper.d.cts +9 -0
  41. package/dist/lib/stepper.d.cts.map +1 -0
  42. package/dist/lib/stepper.d.ts +9 -0
  43. package/dist/lib/stepper.d.ts.map +1 -0
  44. package/dist/linear-gradient/LinearGradient.d.cts +13 -0
  45. package/dist/linear-gradient/LinearGradient.d.cts.map +1 -0
  46. package/dist/linear-gradient/LinearGradient.d.ts +13 -0
  47. package/dist/linear-gradient/LinearGradient.d.ts.map +1 -0
  48. package/dist/linear-gradient/index.cjs +25 -0
  49. package/dist/linear-gradient/index.cjs.map +1 -0
  50. package/dist/linear-gradient/index.d.cts +2 -0
  51. package/dist/linear-gradient/index.d.cts.map +1 -0
  52. package/dist/linear-gradient/index.d.ts +2 -0
  53. package/dist/linear-gradient/index.d.ts.map +1 -0
  54. package/dist/linear-gradient/index.js +23 -0
  55. package/dist/linear-gradient/index.js.map +1 -0
  56. package/dist/resend/Resend.d.cts +37 -0
  57. package/dist/resend/Resend.d.cts.map +1 -0
  58. package/dist/resend/Resend.d.ts +37 -0
  59. package/dist/resend/Resend.d.ts.map +1 -0
  60. package/dist/resend/index.cjs +138 -0
  61. package/dist/resend/index.cjs.map +1 -0
  62. package/dist/resend/index.d.cts +2 -0
  63. package/dist/resend/index.d.cts.map +1 -0
  64. package/dist/resend/index.d.ts +2 -0
  65. package/dist/resend/index.d.ts.map +1 -0
  66. package/dist/resend/index.js +136 -0
  67. package/dist/resend/index.js.map +1 -0
  68. package/dist/stepper/Stepper.d.cts +35 -0
  69. package/dist/stepper/Stepper.d.cts.map +1 -0
  70. package/dist/stepper/Stepper.d.ts +35 -0
  71. package/dist/stepper/Stepper.d.ts.map +1 -0
  72. package/dist/stepper/index.cjs +149 -0
  73. package/dist/stepper/index.cjs.map +1 -0
  74. package/dist/stepper/index.d.cts +2 -0
  75. package/dist/stepper/index.d.cts.map +1 -0
  76. package/dist/stepper/index.d.ts +2 -0
  77. package/dist/stepper/index.d.ts.map +1 -0
  78. package/dist/stepper/index.js +147 -0
  79. package/dist/stepper/index.js.map +1 -0
  80. package/dist/stepper/stepper-icons.d.cts +10 -0
  81. package/dist/stepper/stepper-icons.d.cts.map +1 -0
  82. package/dist/stepper/stepper-icons.d.ts +10 -0
  83. package/dist/stepper/stepper-icons.d.ts.map +1 -0
  84. package/dist/theme/Theme.d.cts +23 -0
  85. package/dist/theme/Theme.d.cts.map +1 -0
  86. package/dist/theme/Theme.d.ts +23 -0
  87. package/dist/theme/Theme.d.ts.map +1 -0
  88. package/dist/theme/index.cjs +76 -0
  89. package/dist/theme/index.cjs.map +1 -0
  90. package/dist/theme/index.d.cts +2 -0
  91. package/dist/theme/index.d.cts.map +1 -0
  92. package/dist/theme/index.d.ts +2 -0
  93. package/dist/theme/index.d.ts.map +1 -0
  94. package/dist/theme/index.js +74 -0
  95. package/dist/theme/index.js.map +1 -0
  96. package/dist/theme/theme-icons.d.cts +21 -0
  97. package/dist/theme/theme-icons.d.cts.map +1 -0
  98. package/dist/theme/theme-icons.d.ts +21 -0
  99. package/dist/theme/theme-icons.d.ts.map +1 -0
  100. package/dist/toast/index.cjs +0 -142
  101. package/dist/toast/index.cjs.map +1 -1
  102. package/dist/toast/index.d.cts +0 -1
  103. package/dist/toast/index.d.cts.map +1 -1
  104. package/dist/toast/index.d.ts +0 -1
  105. package/dist/toast/index.d.ts.map +1 -1
  106. package/dist/toast/index.js +1 -142
  107. package/dist/toast/index.js.map +1 -1
  108. package/dist/toast/types.d.cts +4 -37
  109. package/dist/toast/types.d.cts.map +1 -1
  110. package/dist/toast/types.d.ts +4 -37
  111. package/dist/toast/types.d.ts.map +1 -1
  112. package/dist/toast/uploads.d.cts +1 -31
  113. package/dist/toast/uploads.d.cts.map +1 -1
  114. package/dist/toast/uploads.d.ts +1 -31
  115. package/dist/toast/uploads.d.ts.map +1 -1
  116. package/dist/ui/Badge.d.cts +12 -0
  117. package/dist/ui/Badge.d.cts.map +1 -0
  118. package/dist/ui/Badge.d.ts +12 -0
  119. package/dist/ui/Badge.d.ts.map +1 -0
  120. package/dist/ui/Divider.d.cts +12 -0
  121. package/dist/ui/Divider.d.cts.map +1 -0
  122. package/dist/ui/Divider.d.ts +12 -0
  123. package/dist/ui/Divider.d.ts.map +1 -0
  124. package/dist/ui/GradientText.d.cts +30 -0
  125. package/dist/ui/GradientText.d.cts.map +1 -0
  126. package/dist/ui/GradientText.d.ts +30 -0
  127. package/dist/ui/GradientText.d.ts.map +1 -0
  128. package/dist/ui/index.cjs +37 -3
  129. package/dist/ui/index.cjs.map +1 -1
  130. package/dist/ui/index.d.cts +3 -0
  131. package/dist/ui/index.d.cts.map +1 -1
  132. package/dist/ui/index.d.ts +3 -0
  133. package/dist/ui/index.d.ts.map +1 -1
  134. package/dist/ui/index.js +35 -4
  135. package/dist/ui/index.js.map +1 -1
  136. package/package.json +44 -8
  137. package/dist/toast/toaster.d.cts +0 -80
  138. package/dist/toast/toaster.d.cts.map +0 -1
  139. package/dist/toast/toaster.d.ts +0 -80
  140. package/dist/toast/toaster.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/resend/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { type IResend, Resend } from './Resend.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resend/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,136 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
+
7
+ // src/lib/countdown.ts
8
+ var toSeconds = /* @__PURE__ */ __name((seconds) => Number.isFinite(seconds) ? Math.max(Math.ceil(seconds), 0) : 0, "toSeconds");
9
+ var getSecondsLeft = /* @__PURE__ */ __name((endsAt, now) => Math.max(Math.ceil((endsAt - now) / 1e3), 0), "getSecondsLeft");
10
+ var isPromiseLike = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && "then" in value && typeof value.then === "function", "isPromiseLike");
11
+ var BASE_CSS = "text-fill-transparent w-fit";
12
+ var FORCED_COLORS_CSS = "forced-colors:bg-none forced-colors:[-webkit-text-fill-color:currentColor]";
13
+ var GRADIENT_CSS = {
14
+ accent: "bg-accent-duo",
15
+ silver: "bg-silver-duo"
16
+ };
17
+ var GradientText = /* @__PURE__ */ __name(({
18
+ as,
19
+ type,
20
+ text,
21
+ children,
22
+ className = "",
23
+ ...rest
24
+ }) => {
25
+ const Component = as ?? "span";
26
+ return /* @__PURE__ */ jsx(
27
+ Component,
28
+ {
29
+ ...rest,
30
+ className: `${BASE_CSS} ${GRADIENT_CSS[type]} ${FORCED_COLORS_CSS} ${className}`,
31
+ children: children ?? text
32
+ }
33
+ );
34
+ }, "GradientText");
35
+ var defaultFormatSeconds = /* @__PURE__ */ __name((seconds) => `${String(seconds)}s`, "defaultFormatSeconds");
36
+ var BUTTON_CSS = "cursor-pointer rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-crayola-c disabled:cursor-wait disabled:opacity-60";
37
+ var Resend = /* @__PURE__ */ __name(({
38
+ label,
39
+ count,
40
+ onResend,
41
+ resendLabel = "Resend",
42
+ countdownLabel = "Resend in",
43
+ sendingLabel = "Sending\u2026",
44
+ readyLabel = "You can resend now",
45
+ formatSeconds = defaultFormatSeconds,
46
+ className = "",
47
+ ...pProps
48
+ }) => {
49
+ const [wait, setWait] = useState(() => ({ seconds: toSeconds(count) }));
50
+ const [secondsLeft, setSecondsLeft] = useState(wait.seconds);
51
+ const [isReady, setIsReady] = useState(false);
52
+ const [isSending, setIsSending] = useState(false);
53
+ const isSendingRef = useRef(false);
54
+ const countRef = useRef(count);
55
+ useEffect(() => {
56
+ countRef.current = count;
57
+ }, [count]);
58
+ useEffect(() => {
59
+ if (wait.seconds <= 0) return;
60
+ const endsAt = Date.now() + wait.seconds * 1e3;
61
+ let shown = wait.seconds;
62
+ const update = /* @__PURE__ */ __name(() => {
63
+ const left = getSecondsLeft(endsAt, Date.now());
64
+ if (left !== shown) {
65
+ shown = left;
66
+ setSecondsLeft(left);
67
+ }
68
+ if (left > 0) return;
69
+ window.clearInterval(timer);
70
+ setIsReady(true);
71
+ }, "update");
72
+ const timer = window.setInterval(update, 250);
73
+ document.addEventListener("visibilitychange", update);
74
+ return () => {
75
+ window.clearInterval(timer);
76
+ document.removeEventListener("visibilitychange", update);
77
+ };
78
+ }, [wait]);
79
+ const startWait = /* @__PURE__ */ __name(() => {
80
+ const seconds = toSeconds(countRef.current);
81
+ setIsReady(false);
82
+ setSecondsLeft(seconds);
83
+ setWait({ seconds });
84
+ }, "startWait");
85
+ const resend = /* @__PURE__ */ __name(async () => {
86
+ if (isSendingRef.current) return;
87
+ const result = onResend();
88
+ if (!isPromiseLike(result)) {
89
+ startWait();
90
+ return;
91
+ }
92
+ isSendingRef.current = true;
93
+ setIsSending(true);
94
+ try {
95
+ await result;
96
+ startWait();
97
+ } finally {
98
+ isSendingRef.current = false;
99
+ setIsSending(false);
100
+ }
101
+ }, "resend");
102
+ return /* @__PURE__ */ jsxs("p", { ...pProps, className: `space-x-2 ${className}`, children: [
103
+ /* @__PURE__ */ jsx(GradientText, { text: label, type: "silver", className: "text-xs sm:text-sm" }),
104
+ secondsLeft > 0 ? /* @__PURE__ */ jsxs("span", { role: "timer", className: "text-muted text-xs sm:text-sm", children: [
105
+ /* @__PURE__ */ jsx(GradientText, { text: countdownLabel, type: "silver" }),
106
+ " ",
107
+ /* @__PURE__ */ jsx(GradientText, { text: formatSeconds(secondsLeft), type: "accent", className: "font-semibold" })
108
+ ] }) : /* @__PURE__ */ jsx(
109
+ "button",
110
+ {
111
+ type: "button",
112
+ disabled: isSending,
113
+ "aria-busy": isSending || void 0,
114
+ onClick: () => {
115
+ resend().catch((error) => {
116
+ reportError(error);
117
+ });
118
+ },
119
+ className: BUTTON_CSS,
120
+ children: /* @__PURE__ */ jsx(
121
+ GradientText,
122
+ {
123
+ type: "accent",
124
+ text: isSending ? sendingLabel : resendLabel,
125
+ className: "text-xs font-semibold sm:text-sm"
126
+ }
127
+ )
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsx("span", { role: "status", className: "sr-only", children: isReady ? readyLabel : "" })
131
+ ] });
132
+ }, "Resend");
133
+
134
+ export { Resend };
135
+ //# sourceMappingURL=index.js.map
136
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/countdown.ts","../../src/ui/GradientText.tsx","../../src/resend/Resend.tsx"],"names":["jsx"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * A number of seconds to wait as a whole number that is never negative. A value that is not a number\n * of seconds (`NaN`, `Infinity`, a negative one) is no wait at all: a countdown that cannot end would\n * lock the button for good.\n */\nexport const toSeconds = (seconds: number) =>\n Number.isFinite(seconds) ? Math.max(Math.ceil(seconds), 0) : 0;\n\n/**\n * The whole seconds left until `endsAt` (a time in ms, as `Date.now()` gives), counted up: 0.2 s left\n * is shown as 1, so the countdown shows 0 only once it is over.\n */\nexport const getSecondsLeft = (endsAt: number, now: number) =>\n Math.max(Math.ceil((endsAt - now) / 1000), 0);\n\nexport const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function';\n","import type { ComponentPropsWithRef, ElementType, ReactNode } from 'react';\n\n/** `accent`: the blue gradient of the brand. `silver`: the quiet grey one. */\nexport type TGradientTextType = 'accent' | 'silver';\n\n/** The text is given as `text`, or as `children` (anything React can show), not both. */\ntype TGradientTextContent =\n { text: string; children?: never } | { children: ReactNode; text?: never };\n\ninterface IGradientTextOwnProps<TAs extends ElementType> {\n /**\n * What renders the text: an element (`'a'`, `'p'`, ...) or a component, e.g. the `Link` of the\n * router, which then gets its own props (`to`) next to these.\n * @default 'span'\n */\n as?: TAs;\n type: TGradientTextType;\n className?: string;\n}\n\nexport type IGradientText<TAs extends ElementType = 'span'> = IGradientTextOwnProps<TAs> &\n TGradientTextContent &\n Omit<ComponentPropsWithRef<TAs>, 'as' | 'type' | 'className' | 'text' | 'children'>;\n\nconst BASE_CSS = 'text-fill-transparent w-fit';\n// In forced-colors mode (Windows high contrast) the browser draws no background, so a text that is\n// only a transparent fill over a gradient would vanish: there it takes the colour of the system.\nconst FORCED_COLORS_CSS =\n 'forced-colors:bg-none forced-colors:[-webkit-text-fill-color:currentColor]';\n\nconst GRADIENT_CSS: Record<TGradientTextType, string> = {\n accent: 'bg-accent-duo',\n silver: 'bg-silver-duo',\n};\n\n/**\n * Text filled with a gradient. A link is a text that is rendered by a link component:\n * `<GradientText as={Link} to=\"/terms\" type=\"accent\" text=\"Terms\" />`; the router is the app's, not\n * this package's.\n */\nexport const GradientText = <TAs extends ElementType = 'span'>({\n as,\n type,\n text,\n children,\n className = '',\n ...rest\n}: IGradientText<TAs>) => {\n const Component: ElementType = as ?? 'span';\n\n return (\n <Component\n {...rest}\n className={`${BASE_CSS} ${GRADIENT_CSS[type]} ${FORCED_COLORS_CSS} ${className}`}\n >\n {children ?? text}\n </Component>\n );\n};\n","import { type ComponentProps, useEffect, useRef, useState } from 'react';\n\nimport { getSecondsLeft, isPromiseLike, toSeconds } from '../lib/countdown.js';\nimport { GradientText } from '../ui/GradientText.js';\n\nexport interface IResend extends Omit<ComponentProps<'p'>, 'children'> {\n /** What is said before the countdown or the button, e.g. `\"Didn't get the code?\"`. */\n label: string;\n /**\n * How many seconds to wait before it can be asked again. The wait starts when the component is\n * shown (the thing was just sent) and again after each resend, with the `count` of that moment, so\n * an app can stop making people wait after a few tries by passing 0. A change of `count` in the\n * middle of a wait does not change that wait; to start over (a new email address), give the\n * component a new `key`.\n */\n count: number;\n /**\n * Asks again. When it returns a promise, the button waits for it (it cannot be pressed twice, and\n * says it is sending) and the wait starts when it resolves; if it rejects there is no wait, the\n * button can be pressed again and the error is reported to the window like any uncaught error.\n * Handle the errors you can recover from (a toast) inside it.\n */\n onResend: () => unknown;\n /** @default 'Resend' */\n resendLabel?: string;\n /** What comes before the seconds while waiting. @default 'Resend in' */\n countdownLabel?: string;\n /** The button while `onResend` is waiting for its promise. @default 'Sending…' */\n sendingLabel?: string;\n /** Read out by assistive technology when the wait ends. @default 'You can resend now' */\n readyLabel?: string;\n /** How the seconds are written. @default (seconds) => `${seconds}s` */\n formatSeconds?: (seconds: number) => string;\n}\n\nconst defaultFormatSeconds = (seconds: number) => `${String(seconds)}s`;\n\n// No `outline-hidden` here: in Tailwind v4 it sets `--tw-outline-style: none`, which the\n// `focus-visible:outline-2` below uses for its style, so the ring would never be drawn. A button only\n// shows the browser's own ring for the keyboard anyway (`:focus-visible`), never for a click.\nconst BUTTON_CSS =\n 'cursor-pointer rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-crayola-c disabled:cursor-wait disabled:opacity-60';\n\n/**\n * \"Didn't get the code? Resend in 30s\" and, once the wait is over, a button to ask again. The wait is\n * measured against the clock, not counted tick by tick, so a tab that was in the background or a\n * laptop that slept does not leave people waiting longer than the time that really went by.\n */\nexport const Resend = ({\n label,\n count,\n onResend,\n resendLabel = 'Resend',\n countdownLabel = 'Resend in',\n sendingLabel = 'Sending…',\n readyLabel = 'You can resend now',\n formatSeconds = defaultFormatSeconds,\n className = '',\n ...pProps\n}: IResend) => {\n // What is rendered first is the same on the server and in the browser: the wait starts when the\n // component is on the page (the effect below), never from the time the render happened.\n const [wait, setWait] = useState(() => ({ seconds: toSeconds(count) }));\n const [secondsLeft, setSecondsLeft] = useState(wait.seconds);\n const [isReady, setIsReady] = useState(false);\n const [isSending, setIsSending] = useState(false);\n const isSendingRef = useRef(false);\n const countRef = useRef(count);\n\n useEffect(() => {\n countRef.current = count;\n }, [count]);\n\n useEffect(() => {\n if (wait.seconds <= 0) return;\n\n const endsAt = Date.now() + wait.seconds * 1000;\n let shown = wait.seconds;\n\n const update = () => {\n const left = getSecondsLeft(endsAt, Date.now());\n\n // Several ticks fall in every second; the state is only set (and React only asked to render,\n // which it sometimes does even for a value that is the same) when the second changes.\n if (left !== shown) {\n shown = left;\n setSecondsLeft(left);\n }\n if (left > 0) return;\n\n window.clearInterval(timer);\n setIsReady(true);\n };\n\n // Several times a second, not once: a tick that comes a little late still shows the right second.\n const timer = window.setInterval(update, 250);\n // A background tab runs timers rarely; come back to the right number as soon as it is seen.\n document.addEventListener('visibilitychange', update);\n\n return () => {\n window.clearInterval(timer);\n document.removeEventListener('visibilitychange', update);\n };\n }, [wait]);\n\n const startWait = () => {\n const seconds = toSeconds(countRef.current);\n\n setIsReady(false);\n setSecondsLeft(seconds);\n setWait({ seconds });\n };\n\n const resend = async () => {\n if (isSendingRef.current) return;\n\n const result = onResend();\n\n // `onResend` that does not return a promise (most) starts the wait at once, as the click.\n if (!isPromiseLike(result)) {\n startWait();\n\n return;\n }\n\n isSendingRef.current = true;\n setIsSending(true);\n\n try {\n await result;\n startWait();\n } finally {\n isSendingRef.current = false;\n setIsSending(false);\n }\n };\n\n return (\n <p {...pProps} className={`space-x-2 ${className}`}>\n <GradientText text={label} type=\"silver\" className=\"text-xs sm:text-sm\" />\n\n {secondsLeft > 0 ? (\n <span role=\"timer\" className=\"text-muted text-xs sm:text-sm\">\n <GradientText text={countdownLabel} type=\"silver\" />{' '}\n <GradientText text={formatSeconds(secondsLeft)} type=\"accent\" className=\"font-semibold\" />\n </span>\n ) : (\n <button\n type=\"button\"\n disabled={isSending}\n aria-busy={isSending || undefined}\n onClick={() => {\n // `reportError` is what the browser does for an error nobody caught: it is reported to\n // `window` (and to the error tracker of the app) instead of vanishing in a dropped promise.\n resend().catch((error: unknown) => {\n reportError(error);\n });\n }}\n className={BUTTON_CSS}\n >\n <GradientText\n type=\"accent\"\n text={isSending ? sendingLabel : resendLabel}\n className=\"text-xs font-semibold sm:text-sm\"\n />\n </button>\n )}\n\n {/* Said once, when the wait ends: a person using a screen reader cannot see the button appear. */}\n <span role=\"status\" className=\"sr-only\">\n {isReady ? readyLabel : ''}\n </span>\n </p>\n );\n};\n"]}
@@ -0,0 +1,35 @@
1
+ import type { ComponentProps } from 'react';
2
+ export interface IStepperStep {
3
+ title: string;
4
+ description?: string;
5
+ /**
6
+ * An Iconify name (`'solar:user-linear'`) shown in the circle while the step is not completed; the
7
+ * step's number if left out. Iconify downloads it the first time it is shown and draws it in the
8
+ * browser, not on a server: to have it without a network, bundle it in the app (`addCollection`).
9
+ */
10
+ icon?: string;
11
+ /** Cannot be clicked. Only matters when `onStepClick` is given. */
12
+ disabled?: boolean;
13
+ }
14
+ export interface IStepper extends ComponentProps<'div'> {
15
+ steps: readonly IStepperStep[];
16
+ /** The index of the current step: the ones before it are completed, the ones after it are upcoming. */
17
+ activeStep: number;
18
+ /** Makes the steps buttons. It gets the index of the step that was clicked. */
19
+ onStepClick?: (step: number) => void;
20
+ /** Names the list of steps for assistive technology, e.g. `"Checkout progress"`. */
21
+ label?: string;
22
+ /**
23
+ * What assistive technology reads before the title of a completed step, which otherwise only shows
24
+ * a tick.
25
+ * @default 'Completed'
26
+ */
27
+ completedLabel?: string;
28
+ }
29
+ /**
30
+ * A row of steps with the current one marked, for a checkout, a sign-up or any other wizard. The
31
+ * steps are an ordered list; the current one has `aria-current="step"`. With `onStepClick` the steps
32
+ * are buttons, and the content in `children` is shown under them.
33
+ */
34
+ export declare const Stepper: ({ steps, activeStep, onStepClick, label, completedLabel, className, children, ...divProps }: IStepper) => import("react").JSX.Element;
35
+ //# sourceMappingURL=Stepper.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stepper.d.cts","sourceRoot":"","sources":["../../src/stepper/Stepper.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,OAAO,CAAC;AAM3D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,QAAS,SAAQ,cAAc,CAAC,KAAK,CAAC;IACrD,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAuCD;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAAI,6FASrB,QAAQ,gCA8FV,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { ComponentProps } from 'react';
2
+ export interface IStepperStep {
3
+ title: string;
4
+ description?: string;
5
+ /**
6
+ * An Iconify name (`'solar:user-linear'`) shown in the circle while the step is not completed; the
7
+ * step's number if left out. Iconify downloads it the first time it is shown and draws it in the
8
+ * browser, not on a server: to have it without a network, bundle it in the app (`addCollection`).
9
+ */
10
+ icon?: string;
11
+ /** Cannot be clicked. Only matters when `onStepClick` is given. */
12
+ disabled?: boolean;
13
+ }
14
+ export interface IStepper extends ComponentProps<'div'> {
15
+ steps: readonly IStepperStep[];
16
+ /** The index of the current step: the ones before it are completed, the ones after it are upcoming. */
17
+ activeStep: number;
18
+ /** Makes the steps buttons. It gets the index of the step that was clicked. */
19
+ onStepClick?: (step: number) => void;
20
+ /** Names the list of steps for assistive technology, e.g. `"Checkout progress"`. */
21
+ label?: string;
22
+ /**
23
+ * What assistive technology reads before the title of a completed step, which otherwise only shows
24
+ * a tick.
25
+ * @default 'Completed'
26
+ */
27
+ completedLabel?: string;
28
+ }
29
+ /**
30
+ * A row of steps with the current one marked, for a checkout, a sign-up or any other wizard. The
31
+ * steps are an ordered list; the current one has `aria-current="step"`. With `onStepClick` the steps
32
+ * are buttons, and the content in `children` is shown under them.
33
+ */
34
+ export declare const Stepper: ({ steps, activeStep, onStepClick, label, completedLabel, className, children, ...divProps }: IStepper) => import("react").JSX.Element;
35
+ //# sourceMappingURL=Stepper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stepper.d.ts","sourceRoot":"","sources":["../../src/stepper/Stepper.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,OAAO,CAAC;AAM3D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,QAAS,SAAQ,cAAc,CAAC,KAAK,CAAC;IACrD,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/B,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAuCD;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAAI,6FASrB,QAAQ,gCA8FV,CAAC"}
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ var react = require('@iconify/react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+
9
+ // src/lib/content.ts
10
+ var hasContent = /* @__PURE__ */ __name((content) => content !== null && content !== void 0 && content !== false && content !== true && content !== "", "hasContent");
11
+
12
+ // src/lib/stepper.ts
13
+ var getCurrentStep = /* @__PURE__ */ __name((activeStep, count) => Number.isNaN(activeStep) ? 0 : Math.min(Math.max(Math.trunc(activeStep), 0), Math.max(count - 1, 0)), "getCurrentStep");
14
+ var getStepState = /* @__PURE__ */ __name((index, currentStep) => {
15
+ if (index < currentStep) return "completed";
16
+ return index === currentStep ? "active" : "upcoming";
17
+ }, "getStepState");
18
+ var STEPPER_ICON_SETS = [
19
+ {
20
+ prefix: "solar",
21
+ width: 24,
22
+ height: 24,
23
+ icons: {
24
+ "check-read-linear": {
25
+ body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M4 12.9L7.14286 16.5L15 7.5"/><path d="M20 7.5625L11.4283 16.5625L11 16"/></g>'
26
+ }
27
+ }
28
+ }
29
+ ];
30
+ var registered = false;
31
+ var registerStepperIcons = /* @__PURE__ */ __name(() => {
32
+ if (registered) return;
33
+ for (const iconSet of STEPPER_ICON_SETS) react.addCollection(iconSet);
34
+ registered = true;
35
+ }, "registerStepperIcons");
36
+ STEPPER_ICON_SETS.flatMap(
37
+ ({ prefix, icons }) => Object.keys(icons).map((name) => `${prefix}:${name}`)
38
+ );
39
+ var ITEM_CSS = "group relative flex min-w-0 items-start gap-3 text-left md:flex-col md:items-center md:gap-3 md:px-2";
40
+ var CLICKABLE_CSS = "cursor-pointer rounded-lg focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-blue-crayola-c disabled:cursor-not-allowed disabled:opacity-50";
41
+ var CIRCLE_CSS = "relative z-1 flex size-10 shrink-0 items-center justify-center rounded-full border text-sm font-semibold transition-all duration-300 motion-reduce:transition-none md:size-11";
42
+ var CIRCLE_STATE_CSS = {
43
+ completed: "border-blue-crayola-c bg-accent-duo text-white shadow-[0_8px_24px_rgba(var(--blue-crayola-c-rgb),0.28)] forced-colors:border-[Highlight] forced-colors:bg-[Highlight] forced-colors:bg-none forced-colors:text-[HighlightText]",
44
+ active: "border-blue-crayola-c bg-primary-invert text-primary shadow-[0_0_0_5px_rgba(var(--blue-crayola-c-rgb),0.12)] forced-colors:border-[Highlight] forced-colors:border-4",
45
+ upcoming: "border-primary/20 bg-primary-invert text-primary/60 forced-colors:border-[GrayText] forced-colors:text-[GrayText]"
46
+ };
47
+ var TITLE_STATE_CSS = {
48
+ completed: "text-primary/80",
49
+ active: "text-primary",
50
+ upcoming: "text-primary/45 forced-colors:text-[GrayText]"
51
+ };
52
+ var DESCRIPTION_STATE_CSS = {
53
+ completed: "text-primary/35",
54
+ active: "text-secondary",
55
+ upcoming: "text-primary/35 forced-colors:text-[GrayText]"
56
+ };
57
+ var CONNECTOR_BASE_CSS = "absolute top-10 left-5 h-4 w-0.5 -translate-x-1/2 md:top-5 md:left-[calc(50%+1.375rem)] md:h-0.5 md:w-[calc(100%-2.75rem)] md:translate-x-0";
58
+ var Stepper = /* @__PURE__ */ __name(({
59
+ steps,
60
+ activeStep,
61
+ onStepClick,
62
+ label,
63
+ completedLabel = "Completed",
64
+ className = "",
65
+ children,
66
+ ...divProps
67
+ }) => {
68
+ registerStepperIcons();
69
+ const currentStep = getCurrentStep(activeStep, steps.length);
70
+ return /* @__PURE__ */ jsxRuntime.jsxs(
71
+ "div",
72
+ {
73
+ ...divProps,
74
+ className: `flex w-full flex-col gap-6 p-4 shadow-sm sm:p-5 lg:p-6 ${className}`,
75
+ children: [
76
+ steps.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
77
+ "ol",
78
+ {
79
+ "aria-label": label,
80
+ style: { "--steps-count": steps.length },
81
+ className: "m-0 flex list-none flex-col gap-4 p-0 md:grid md:grid-cols-[repeat(var(--steps-count),minmax(0,1fr))] md:gap-0",
82
+ children: steps.map((step, index) => {
83
+ const state = getStepState(index, currentStep);
84
+ const hasNextStep = index < steps.length - 1;
85
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
86
+ hasNextStep && /* @__PURE__ */ jsxRuntime.jsx(
87
+ "span",
88
+ {
89
+ "aria-hidden": "true",
90
+ className: `${CONNECTOR_BASE_CSS} ${state === "completed" ? "bg-accent-duo forced-colors:bg-[Highlight] forced-colors:bg-none" : "bg-primary/20 forced-colors:bg-[GrayText]"}`
91
+ }
92
+ ),
93
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: `${CIRCLE_CSS} ${CIRCLE_STATE_CSS[state]}`, children: state === "completed" ? /* @__PURE__ */ jsxRuntime.jsx(react.Icon, { icon: "solar:check-read-linear", className: "size-5" }) : step.icon ? /* @__PURE__ */ jsxRuntime.jsx(react.Icon, { icon: step.icon, className: "size-5" }) : index + 1 }),
94
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-1 pt-0.5 md:items-center md:pt-0 md:text-center", children: [
95
+ /* @__PURE__ */ jsxRuntime.jsxs(
96
+ "span",
97
+ {
98
+ className: `line-clamp-1 text-sm font-semibold md:text-[13px] lg:text-sm ${TITLE_STATE_CSS[state]}`,
99
+ children: [
100
+ state === "completed" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: `${completedLabel}: ` }),
101
+ step.title
102
+ ]
103
+ }
104
+ ),
105
+ hasContent(step.description) && /* @__PURE__ */ jsxRuntime.jsx(
106
+ "span",
107
+ {
108
+ className: `line-clamp-1 text-xs leading-4 md:line-clamp-2 ${DESCRIPTION_STATE_CSS[state]}`,
109
+ children: step.description
110
+ }
111
+ )
112
+ ] })
113
+ ] });
114
+ return (
115
+ // The steps are a fixed order that is never sorted or filtered, and two can have the
116
+ // same title: the place is what tells them apart.
117
+ /* @__PURE__ */ jsxRuntime.jsx(
118
+ "li",
119
+ {
120
+ "data-state": state,
121
+ "aria-current": state === "active" ? "step" : void 0,
122
+ children: onStepClick ? /* @__PURE__ */ jsxRuntime.jsx(
123
+ "button",
124
+ {
125
+ type: "button",
126
+ disabled: step.disabled,
127
+ onClick: () => {
128
+ onStepClick(index);
129
+ },
130
+ className: `${ITEM_CSS} ${CLICKABLE_CSS} w-full`,
131
+ children: content
132
+ }
133
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${ITEM_CSS} cursor-default`, children: content })
134
+ },
135
+ index
136
+ )
137
+ );
138
+ })
139
+ }
140
+ ),
141
+ hasContent(children) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-primary/10 border-t pt-5", children })
142
+ ]
143
+ }
144
+ );
145
+ }, "Stepper");
146
+
147
+ exports.Stepper = Stepper;
148
+ //# sourceMappingURL=index.cjs.map
149
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/content.ts","../../src/lib/stepper.ts","../../src/stepper/stepper-icons.ts","../../src/stepper/Stepper.tsx"],"names":["addCollection","jsxs","jsx","Fragment","Icon"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ReactNode } from 'react';\n\n/**\n * Whether React would show something for `content`. `null`, `undefined`, booleans and an empty string\n * show nothing; `0` does show (the text \"0\"), which is why this is not a plain truthiness check.\n */\nexport const hasContent = (content: ReactNode) =>\n content !== null &&\n content !== undefined &&\n content !== false &&\n content !== true &&\n content !== '';\n","export type TStepState = 'completed' | 'active' | 'upcoming';\n\n/**\n * The step that is the current one: `activeStep` kept inside the steps. A number past the ends (or\n * with a fraction) is put on the nearest step, and `NaN` is the first, so a wrong value never makes\n * every step look completed or none look current.\n */\nexport const getCurrentStep = (activeStep: number, count: number) =>\n Number.isNaN(activeStep)\n ? 0\n : Math.min(Math.max(Math.trunc(activeStep), 0), Math.max(count - 1, 0));\n\nexport const getStepState = (index: number, currentStep: number): TStepState => {\n if (index < currentStep) return 'completed';\n\n return index === currentStep ? 'active' : 'upcoming';\n};\n","import { addCollection, type IconifyJSON } from '@iconify/react';\n\n/*\n * The tick of a completed step. Iconify downloads an icon from its API the first time it is shown,\n * which cannot work offline and leaves an empty circle until it arrives, so the icon is bundled here\n * and registered with Iconify (the same way as the toast icons, see `toast-icons.ts`).\n *\n * An icon a step asks for with `icon: 'solar:...'` is the app's and is downloaded as usual; add it to\n * the app with `addCollection` when it has to show without a network.\n *\n * The data is what `https://api.iconify.design/<prefix>.json?icons=<name>` answers.\n */\nconst STEPPER_ICON_SETS: IconifyJSON[] = [\n {\n prefix: 'solar',\n width: 24,\n height: 24,\n icons: {\n 'check-read-linear': {\n body: '<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\"><path d=\"M4 12.9L7.14286 16.5L15 7.5\"/><path d=\"M20 7.5625L11.4283 16.5625L11 16\"/></g>',\n },\n },\n },\n];\n\nlet registered = false;\n\n/**\n * Registers the icons above with Iconify. `Stepper` calls it when it renders, not when this file is\n * imported: an app that never renders a stepper never runs it, and nothing depends on the bundler\n * keeping an import that is only there for its effect (see `toast-icons.ts`). Calling it again is\n * harmless.\n */\nexport const registerStepperIcons = () => {\n if (registered) return;\n\n for (const iconSet of STEPPER_ICON_SETS) addCollection(iconSet);\n registered = true;\n};\n\n/** Every icon name registered above, e.g. `solar:check-read-linear`. */\nexport const STEPPER_ICON_NAMES = STEPPER_ICON_SETS.flatMap(({ prefix, icons }) =>\n Object.keys(icons).map((name) => `${prefix}:${name}`),\n);\n","import { Icon } from '@iconify/react';\nimport type { ComponentProps, CSSProperties } from 'react';\n\nimport { hasContent } from '../lib/content.js';\nimport { getCurrentStep, getStepState, type TStepState } from '../lib/stepper.js';\nimport { registerStepperIcons } from './stepper-icons.js';\n\nexport interface IStepperStep {\n title: string;\n description?: string;\n /**\n * An Iconify name (`'solar:user-linear'`) shown in the circle while the step is not completed; the\n * step's number if left out. Iconify downloads it the first time it is shown and draws it in the\n * browser, not on a server: to have it without a network, bundle it in the app (`addCollection`).\n */\n icon?: string;\n /** Cannot be clicked. Only matters when `onStepClick` is given. */\n disabled?: boolean;\n}\n\nexport interface IStepper extends ComponentProps<'div'> {\n steps: readonly IStepperStep[];\n /** The index of the current step: the ones before it are completed, the ones after it are upcoming. */\n activeStep: number;\n /** Makes the steps buttons. It gets the index of the step that was clicked. */\n onStepClick?: (step: number) => void;\n /** Names the list of steps for assistive technology, e.g. `\"Checkout progress\"`. */\n label?: string;\n /**\n * What assistive technology reads before the title of a completed step, which otherwise only shows\n * a tick.\n * @default 'Completed'\n */\n completedLabel?: string;\n}\n\nconst ITEM_CSS =\n 'group relative flex min-w-0 items-start gap-3 text-left md:flex-col md:items-center md:gap-3 md:px-2';\n// No `outline-hidden`: in Tailwind v4 it sets `--tw-outline-style: none`, which the\n// `focus-visible:outline-2` below uses for its style, so the ring would never be drawn. A button only\n// shows the browser's own ring for the keyboard anyway (`:focus-visible`), never for a click.\nconst CLICKABLE_CSS =\n 'cursor-pointer rounded-lg focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-blue-crayola-c disabled:cursor-not-allowed disabled:opacity-50';\n\nconst CIRCLE_CSS =\n 'relative z-1 flex size-10 shrink-0 items-center justify-center rounded-full border text-sm font-semibold transition-all duration-300 motion-reduce:transition-none md:size-11';\n\n// `Record`s: a new state is a type error here until it has a look. Every state also looks different\n// in forced-colors mode (Windows high contrast), where backgrounds and shadows are not drawn.\nconst CIRCLE_STATE_CSS: Record<TStepState, string> = {\n completed:\n 'border-blue-crayola-c bg-accent-duo text-white shadow-[0_8px_24px_rgba(var(--blue-crayola-c-rgb),0.28)] forced-colors:border-[Highlight] forced-colors:bg-[Highlight] forced-colors:bg-none forced-colors:text-[HighlightText]',\n active:\n 'border-blue-crayola-c bg-primary-invert text-primary shadow-[0_0_0_5px_rgba(var(--blue-crayola-c-rgb),0.12)] forced-colors:border-[Highlight] forced-colors:border-4',\n upcoming:\n 'border-primary/20 bg-primary-invert text-primary/60 forced-colors:border-[GrayText] forced-colors:text-[GrayText]',\n};\n\nconst TITLE_STATE_CSS: Record<TStepState, string> = {\n completed: 'text-primary/80',\n active: 'text-primary',\n upcoming: 'text-primary/45 forced-colors:text-[GrayText]',\n};\n\nconst DESCRIPTION_STATE_CSS: Record<TStepState, string> = {\n completed: 'text-primary/35',\n active: 'text-secondary',\n upcoming: 'text-primary/35 forced-colors:text-[GrayText]',\n};\n\nconst CONNECTOR_BASE_CSS =\n 'absolute top-10 left-5 h-4 w-0.5 -translate-x-1/2 md:top-5 md:left-[calc(50%+1.375rem)] md:h-0.5 md:w-[calc(100%-2.75rem)] md:translate-x-0';\n\n/**\n * A row of steps with the current one marked, for a checkout, a sign-up or any other wizard. The\n * steps are an ordered list; the current one has `aria-current=\"step\"`. With `onStepClick` the steps\n * are buttons, and the content in `children` is shown under them.\n */\nexport const Stepper = ({\n steps,\n activeStep,\n onStepClick,\n label,\n completedLabel = 'Completed',\n className = '',\n children,\n ...divProps\n}: IStepper) => {\n registerStepperIcons();\n\n const currentStep = getCurrentStep(activeStep, steps.length);\n\n return (\n <div\n {...divProps}\n className={`flex w-full flex-col gap-6 p-4 shadow-sm sm:p-5 lg:p-6 ${className}`}\n >\n {steps.length > 0 && (\n <ol\n aria-label={label}\n style={{ '--steps-count': steps.length } as CSSProperties}\n className=\"m-0 flex list-none flex-col gap-4 p-0 md:grid md:grid-cols-[repeat(var(--steps-count),minmax(0,1fr))] md:gap-0\"\n >\n {steps.map((step, index) => {\n const state = getStepState(index, currentStep);\n const hasNextStep = index < steps.length - 1;\n\n const content = (\n <>\n {hasNextStep && (\n <span\n aria-hidden=\"true\"\n className={`${CONNECTOR_BASE_CSS} ${\n state === 'completed'\n ? 'bg-accent-duo forced-colors:bg-[Highlight] forced-colors:bg-none'\n : 'bg-primary/20 forced-colors:bg-[GrayText]'\n }`}\n />\n )}\n\n <span aria-hidden=\"true\" className={`${CIRCLE_CSS} ${CIRCLE_STATE_CSS[state]}`}>\n {state === 'completed' ? (\n <Icon icon=\"solar:check-read-linear\" className=\"size-5\" />\n ) : step.icon ? (\n <Icon icon={step.icon} className=\"size-5\" />\n ) : (\n index + 1\n )}\n </span>\n\n <span className=\"flex min-w-0 flex-1 flex-col gap-1 pt-0.5 md:items-center md:pt-0 md:text-center\">\n <span\n className={`line-clamp-1 text-sm font-semibold md:text-[13px] lg:text-sm ${TITLE_STATE_CSS[state]}`}\n >\n {state === 'completed' && (\n <span className=\"sr-only\">{`${completedLabel}: `}</span>\n )}\n {step.title}\n </span>\n {hasContent(step.description) && (\n <span\n className={`line-clamp-1 text-xs leading-4 md:line-clamp-2 ${DESCRIPTION_STATE_CSS[state]}`}\n >\n {step.description}\n </span>\n )}\n </span>\n </>\n );\n\n return (\n // The steps are a fixed order that is never sorted or filtered, and two can have the\n // same title: the place is what tells them apart.\n <li\n key={index}\n data-state={state}\n aria-current={state === 'active' ? 'step' : undefined}\n >\n {onStepClick ? (\n <button\n type=\"button\"\n disabled={step.disabled}\n onClick={() => {\n onStepClick(index);\n }}\n className={`${ITEM_CSS} ${CLICKABLE_CSS} w-full`}\n >\n {content}\n </button>\n ) : (\n <div className={`${ITEM_CSS} cursor-default`}>{content}</div>\n )}\n </li>\n );\n })}\n </ol>\n )}\n\n {hasContent(children) && <div className=\"border-primary/10 border-t pt-5\">{children}</div>}\n </div>\n );\n};\n"]}
@@ -0,0 +1,2 @@
1
+ export { type IStepper, type IStepperStep, Stepper } from './Stepper.js';
2
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/stepper/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { type IStepper, type IStepperStep, Stepper } from './Stepper.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stepper/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,147 @@
1
+ import { Icon, addCollection } from '@iconify/react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
+
7
+ // src/lib/content.ts
8
+ var hasContent = /* @__PURE__ */ __name((content) => content !== null && content !== void 0 && content !== false && content !== true && content !== "", "hasContent");
9
+
10
+ // src/lib/stepper.ts
11
+ var getCurrentStep = /* @__PURE__ */ __name((activeStep, count) => Number.isNaN(activeStep) ? 0 : Math.min(Math.max(Math.trunc(activeStep), 0), Math.max(count - 1, 0)), "getCurrentStep");
12
+ var getStepState = /* @__PURE__ */ __name((index, currentStep) => {
13
+ if (index < currentStep) return "completed";
14
+ return index === currentStep ? "active" : "upcoming";
15
+ }, "getStepState");
16
+ var STEPPER_ICON_SETS = [
17
+ {
18
+ prefix: "solar",
19
+ width: 24,
20
+ height: 24,
21
+ icons: {
22
+ "check-read-linear": {
23
+ body: '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"><path d="M4 12.9L7.14286 16.5L15 7.5"/><path d="M20 7.5625L11.4283 16.5625L11 16"/></g>'
24
+ }
25
+ }
26
+ }
27
+ ];
28
+ var registered = false;
29
+ var registerStepperIcons = /* @__PURE__ */ __name(() => {
30
+ if (registered) return;
31
+ for (const iconSet of STEPPER_ICON_SETS) addCollection(iconSet);
32
+ registered = true;
33
+ }, "registerStepperIcons");
34
+ STEPPER_ICON_SETS.flatMap(
35
+ ({ prefix, icons }) => Object.keys(icons).map((name) => `${prefix}:${name}`)
36
+ );
37
+ var ITEM_CSS = "group relative flex min-w-0 items-start gap-3 text-left md:flex-col md:items-center md:gap-3 md:px-2";
38
+ var CLICKABLE_CSS = "cursor-pointer rounded-lg focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-blue-crayola-c disabled:cursor-not-allowed disabled:opacity-50";
39
+ var CIRCLE_CSS = "relative z-1 flex size-10 shrink-0 items-center justify-center rounded-full border text-sm font-semibold transition-all duration-300 motion-reduce:transition-none md:size-11";
40
+ var CIRCLE_STATE_CSS = {
41
+ completed: "border-blue-crayola-c bg-accent-duo text-white shadow-[0_8px_24px_rgba(var(--blue-crayola-c-rgb),0.28)] forced-colors:border-[Highlight] forced-colors:bg-[Highlight] forced-colors:bg-none forced-colors:text-[HighlightText]",
42
+ active: "border-blue-crayola-c bg-primary-invert text-primary shadow-[0_0_0_5px_rgba(var(--blue-crayola-c-rgb),0.12)] forced-colors:border-[Highlight] forced-colors:border-4",
43
+ upcoming: "border-primary/20 bg-primary-invert text-primary/60 forced-colors:border-[GrayText] forced-colors:text-[GrayText]"
44
+ };
45
+ var TITLE_STATE_CSS = {
46
+ completed: "text-primary/80",
47
+ active: "text-primary",
48
+ upcoming: "text-primary/45 forced-colors:text-[GrayText]"
49
+ };
50
+ var DESCRIPTION_STATE_CSS = {
51
+ completed: "text-primary/35",
52
+ active: "text-secondary",
53
+ upcoming: "text-primary/35 forced-colors:text-[GrayText]"
54
+ };
55
+ var CONNECTOR_BASE_CSS = "absolute top-10 left-5 h-4 w-0.5 -translate-x-1/2 md:top-5 md:left-[calc(50%+1.375rem)] md:h-0.5 md:w-[calc(100%-2.75rem)] md:translate-x-0";
56
+ var Stepper = /* @__PURE__ */ __name(({
57
+ steps,
58
+ activeStep,
59
+ onStepClick,
60
+ label,
61
+ completedLabel = "Completed",
62
+ className = "",
63
+ children,
64
+ ...divProps
65
+ }) => {
66
+ registerStepperIcons();
67
+ const currentStep = getCurrentStep(activeStep, steps.length);
68
+ return /* @__PURE__ */ jsxs(
69
+ "div",
70
+ {
71
+ ...divProps,
72
+ className: `flex w-full flex-col gap-6 p-4 shadow-sm sm:p-5 lg:p-6 ${className}`,
73
+ children: [
74
+ steps.length > 0 && /* @__PURE__ */ jsx(
75
+ "ol",
76
+ {
77
+ "aria-label": label,
78
+ style: { "--steps-count": steps.length },
79
+ className: "m-0 flex list-none flex-col gap-4 p-0 md:grid md:grid-cols-[repeat(var(--steps-count),minmax(0,1fr))] md:gap-0",
80
+ children: steps.map((step, index) => {
81
+ const state = getStepState(index, currentStep);
82
+ const hasNextStep = index < steps.length - 1;
83
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
84
+ hasNextStep && /* @__PURE__ */ jsx(
85
+ "span",
86
+ {
87
+ "aria-hidden": "true",
88
+ className: `${CONNECTOR_BASE_CSS} ${state === "completed" ? "bg-accent-duo forced-colors:bg-[Highlight] forced-colors:bg-none" : "bg-primary/20 forced-colors:bg-[GrayText]"}`
89
+ }
90
+ ),
91
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: `${CIRCLE_CSS} ${CIRCLE_STATE_CSS[state]}`, children: state === "completed" ? /* @__PURE__ */ jsx(Icon, { icon: "solar:check-read-linear", className: "size-5" }) : step.icon ? /* @__PURE__ */ jsx(Icon, { icon: step.icon, className: "size-5" }) : index + 1 }),
92
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-1 pt-0.5 md:items-center md:pt-0 md:text-center", children: [
93
+ /* @__PURE__ */ jsxs(
94
+ "span",
95
+ {
96
+ className: `line-clamp-1 text-sm font-semibold md:text-[13px] lg:text-sm ${TITLE_STATE_CSS[state]}`,
97
+ children: [
98
+ state === "completed" && /* @__PURE__ */ jsx("span", { className: "sr-only", children: `${completedLabel}: ` }),
99
+ step.title
100
+ ]
101
+ }
102
+ ),
103
+ hasContent(step.description) && /* @__PURE__ */ jsx(
104
+ "span",
105
+ {
106
+ className: `line-clamp-1 text-xs leading-4 md:line-clamp-2 ${DESCRIPTION_STATE_CSS[state]}`,
107
+ children: step.description
108
+ }
109
+ )
110
+ ] })
111
+ ] });
112
+ return (
113
+ // The steps are a fixed order that is never sorted or filtered, and two can have the
114
+ // same title: the place is what tells them apart.
115
+ /* @__PURE__ */ jsx(
116
+ "li",
117
+ {
118
+ "data-state": state,
119
+ "aria-current": state === "active" ? "step" : void 0,
120
+ children: onStepClick ? /* @__PURE__ */ jsx(
121
+ "button",
122
+ {
123
+ type: "button",
124
+ disabled: step.disabled,
125
+ onClick: () => {
126
+ onStepClick(index);
127
+ },
128
+ className: `${ITEM_CSS} ${CLICKABLE_CSS} w-full`,
129
+ children: content
130
+ }
131
+ ) : /* @__PURE__ */ jsx("div", { className: `${ITEM_CSS} cursor-default`, children: content })
132
+ },
133
+ index
134
+ )
135
+ );
136
+ })
137
+ }
138
+ ),
139
+ hasContent(children) && /* @__PURE__ */ jsx("div", { className: "border-primary/10 border-t pt-5", children })
140
+ ]
141
+ }
142
+ );
143
+ }, "Stepper");
144
+
145
+ export { Stepper };
146
+ //# sourceMappingURL=index.js.map
147
+ //# sourceMappingURL=index.js.map