@beautinique/frontend-components 1.0.7 → 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 (120) hide show
  1. package/README.md +180 -14
  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/ui/Badge.d.cts +12 -0
  101. package/dist/ui/Badge.d.cts.map +1 -0
  102. package/dist/ui/Badge.d.ts +12 -0
  103. package/dist/ui/Badge.d.ts.map +1 -0
  104. package/dist/ui/Divider.d.cts +12 -0
  105. package/dist/ui/Divider.d.cts.map +1 -0
  106. package/dist/ui/Divider.d.ts +12 -0
  107. package/dist/ui/Divider.d.ts.map +1 -0
  108. package/dist/ui/GradientText.d.cts +30 -0
  109. package/dist/ui/GradientText.d.cts.map +1 -0
  110. package/dist/ui/GradientText.d.ts +30 -0
  111. package/dist/ui/GradientText.d.ts.map +1 -0
  112. package/dist/ui/index.cjs +37 -3
  113. package/dist/ui/index.cjs.map +1 -1
  114. package/dist/ui/index.d.cts +3 -0
  115. package/dist/ui/index.d.cts.map +1 -1
  116. package/dist/ui/index.d.ts +3 -0
  117. package/dist/ui/index.d.ts.map +1 -1
  118. package/dist/ui/index.js +35 -4
  119. package/dist/ui/index.js.map +1 -1
  120. package/package.json +42 -6
@@ -0,0 +1,36 @@
1
+ export interface IScrollToTop {
2
+ /**
3
+ * The current path of the page, from the router (`useLocation().pathname`). The scroll happens
4
+ * when it changes. The query and the hash are not part of it: a search that only changes `?page=2`
5
+ * does not throw people back to the top.
6
+ */
7
+ pathname: string;
8
+ /**
9
+ * The `id` of the element that scrolls: the one that holds the page. Nothing happens when the page
10
+ * has no element with this id.
11
+ * @default 'main'
12
+ */
13
+ id?: string;
14
+ /**
15
+ * `'smooth'` is turned into `'auto'` for a person who asked their system for less motion.
16
+ * @default 'auto'
17
+ */
18
+ behavior?: ScrollBehavior;
19
+ /** @default 'start' */
20
+ block?: ScrollLogicalPosition;
21
+ /** @default 'nearest' */
22
+ inline?: ScrollLogicalPosition;
23
+ /**
24
+ * Leaves the page where it is when the component first shows, and scrolls only on the changes of
25
+ * `pathname` after that. Needed when the browser puts the page back where it was on a reload.
26
+ * @default false
27
+ */
28
+ skipInitial?: boolean;
29
+ }
30
+ /**
31
+ * Scrolls an element of the page (`#main` unless told another) to the top each time the path
32
+ * changes, as a router does not do it for a single page app. It draws nothing; put it once, inside
33
+ * the layout that has the element.
34
+ */
35
+ export declare const ScrollToTop: ({ pathname, id, behavior, block, inline, skipInitial, }: IScrollToTop) => null;
36
+ //# sourceMappingURL=ScrollToTop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollToTop.d.ts","sourceRoot":"","sources":["../../src/layout/ScrollToTop.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,uBAAuB;IACvB,KAAK,CAAC,EAAE,qBAAqB,CAAC;IAC9B,yBAAyB;IACzB,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,yDAOzB,YAAY,SAsBd,CAAC"}
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var ScrollToTop = /* @__PURE__ */ __name(({
8
+ pathname,
9
+ id = "main",
10
+ behavior = "auto",
11
+ block = "start",
12
+ inline = "nearest",
13
+ skipInitial = false
14
+ }) => {
15
+ const scrolledPath = react.useRef(skipInitial ? pathname : void 0);
16
+ react.useEffect(() => {
17
+ if (scrolledPath.current === pathname) return;
18
+ scrolledPath.current = pathname;
19
+ const element = document.getElementById(id);
20
+ if (!element) return;
21
+ const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
22
+ element.scrollIntoView({
23
+ behavior: behavior === "smooth" && reduceMotion ? "auto" : behavior,
24
+ block,
25
+ inline
26
+ });
27
+ }, [pathname, id, behavior, block, inline]);
28
+ return null;
29
+ }, "ScrollToTop");
30
+
31
+ exports.ScrollToTop = ScrollToTop;
32
+ //# sourceMappingURL=index.cjs.map
33
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/layout/ScrollToTop.tsx"],"names":["useRef","useEffect"],"mappings":";;;;;;AAqCO,IAAM,8BAAc,MAAA,CAAA,CAAC;AAAA,EAC1B,QAAA;AAAA,EACA,EAAA,GAAK,MAAA;AAAA,EACL,QAAA,GAAW,MAAA;AAAA,EACX,KAAA,GAAQ,OAAA;AAAA,EACR,MAAA,GAAS,SAAA;AAAA,EACT,WAAA,GAAc;AAChB,CAAA,KAAoB;AAGlB,EAAA,MAAM,YAAA,GAAeA,YAAA,CAAO,WAAA,GAAc,QAAA,GAAW,MAAS,CAAA;AAE9D,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,YAAA,CAAa,YAAY,QAAA,EAAU;AACvC,IAAA,YAAA,CAAa,OAAA,GAAU,QAAA;AAEvB,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,EAAE,CAAA;AAC1C,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,UAAA,CAAW,kCAAkC,CAAA,CAAE,OAAA;AAE3E,IAAA,OAAA,CAAQ,cAAA,CAAe;AAAA,MACrB,QAAA,EAAU,QAAA,KAAa,QAAA,IAAY,YAAA,GAAe,MAAA,GAAS,QAAA;AAAA,MAC3D,KAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH,GAAG,CAAC,QAAA,EAAU,IAAI,QAAA,EAAU,KAAA,EAAO,MAAM,CAAC,CAAA;AAE1C,EAAA,OAAO,IAAA;AACT,CAAA,EA7B2B,aAAA","file":"index.cjs","sourcesContent":["import { useEffect, useRef } from 'react';\n\nexport interface IScrollToTop {\n /**\n * The current path of the page, from the router (`useLocation().pathname`). The scroll happens\n * when it changes. The query and the hash are not part of it: a search that only changes `?page=2`\n * does not throw people back to the top.\n */\n pathname: string;\n /**\n * The `id` of the element that scrolls: the one that holds the page. Nothing happens when the page\n * has no element with this id.\n * @default 'main'\n */\n id?: string;\n /**\n * `'smooth'` is turned into `'auto'` for a person who asked their system for less motion.\n * @default 'auto'\n */\n behavior?: ScrollBehavior;\n /** @default 'start' */\n block?: ScrollLogicalPosition;\n /** @default 'nearest' */\n inline?: ScrollLogicalPosition;\n /**\n * Leaves the page where it is when the component first shows, and scrolls only on the changes of\n * `pathname` after that. Needed when the browser puts the page back where it was on a reload.\n * @default false\n */\n skipInitial?: boolean;\n}\n\n/**\n * Scrolls an element of the page (`#main` unless told another) to the top each time the path\n * changes, as a router does not do it for a single page app. It draws nothing; put it once, inside\n * the layout that has the element.\n */\nexport const ScrollToTop = ({\n pathname,\n id = 'main',\n behavior = 'auto',\n block = 'start',\n inline = 'nearest',\n skipInitial = false,\n}: IScrollToTop) => {\n // The path that was scrolled for last. A change of `id`, `behavior`, `block` or `inline` alone is\n // not a new page, so it does not scroll. With `skipInitial` the first path counts as scrolled already.\n const scrolledPath = useRef(skipInitial ? pathname : undefined);\n\n useEffect(() => {\n if (scrolledPath.current === pathname) return;\n scrolledPath.current = pathname;\n\n const element = document.getElementById(id);\n if (!element) return;\n\n const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n element.scrollIntoView({\n behavior: behavior === 'smooth' && reduceMotion ? 'auto' : behavior,\n block,\n inline,\n });\n }, [pathname, id, behavior, block, inline]);\n\n return null;\n};\n"]}
@@ -0,0 +1,2 @@
1
+ export { type IScrollToTop, ScrollToTop } from './ScrollToTop.js';
2
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { type IScrollToTop, ScrollToTop } from './ScrollToTop.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { useRef, useEffect } from 'react';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+ var ScrollToTop = /* @__PURE__ */ __name(({
6
+ pathname,
7
+ id = "main",
8
+ behavior = "auto",
9
+ block = "start",
10
+ inline = "nearest",
11
+ skipInitial = false
12
+ }) => {
13
+ const scrolledPath = useRef(skipInitial ? pathname : void 0);
14
+ useEffect(() => {
15
+ if (scrolledPath.current === pathname) return;
16
+ scrolledPath.current = pathname;
17
+ const element = document.getElementById(id);
18
+ if (!element) return;
19
+ const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
20
+ element.scrollIntoView({
21
+ behavior: behavior === "smooth" && reduceMotion ? "auto" : behavior,
22
+ block,
23
+ inline
24
+ });
25
+ }, [pathname, id, behavior, block, inline]);
26
+ return null;
27
+ }, "ScrollToTop");
28
+
29
+ export { ScrollToTop };
30
+ //# sourceMappingURL=index.js.map
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/layout/ScrollToTop.tsx"],"names":[],"mappings":";;;;AAqCO,IAAM,8BAAc,MAAA,CAAA,CAAC;AAAA,EAC1B,QAAA;AAAA,EACA,EAAA,GAAK,MAAA;AAAA,EACL,QAAA,GAAW,MAAA;AAAA,EACX,KAAA,GAAQ,OAAA;AAAA,EACR,MAAA,GAAS,SAAA;AAAA,EACT,WAAA,GAAc;AAChB,CAAA,KAAoB;AAGlB,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,WAAA,GAAc,QAAA,GAAW,MAAS,CAAA;AAE9D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,YAAA,CAAa,YAAY,QAAA,EAAU;AACvC,IAAA,YAAA,CAAa,OAAA,GAAU,QAAA;AAEvB,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,EAAE,CAAA;AAC1C,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,UAAA,CAAW,kCAAkC,CAAA,CAAE,OAAA;AAE3E,IAAA,OAAA,CAAQ,cAAA,CAAe;AAAA,MACrB,QAAA,EAAU,QAAA,KAAa,QAAA,IAAY,YAAA,GAAe,MAAA,GAAS,QAAA;AAAA,MAC3D,KAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH,GAAG,CAAC,QAAA,EAAU,IAAI,QAAA,EAAU,KAAA,EAAO,MAAM,CAAC,CAAA;AAE1C,EAAA,OAAO,IAAA;AACT,CAAA,EA7B2B,aAAA","file":"index.js","sourcesContent":["import { useEffect, useRef } from 'react';\n\nexport interface IScrollToTop {\n /**\n * The current path of the page, from the router (`useLocation().pathname`). The scroll happens\n * when it changes. The query and the hash are not part of it: a search that only changes `?page=2`\n * does not throw people back to the top.\n */\n pathname: string;\n /**\n * The `id` of the element that scrolls: the one that holds the page. Nothing happens when the page\n * has no element with this id.\n * @default 'main'\n */\n id?: string;\n /**\n * `'smooth'` is turned into `'auto'` for a person who asked their system for less motion.\n * @default 'auto'\n */\n behavior?: ScrollBehavior;\n /** @default 'start' */\n block?: ScrollLogicalPosition;\n /** @default 'nearest' */\n inline?: ScrollLogicalPosition;\n /**\n * Leaves the page where it is when the component first shows, and scrolls only on the changes of\n * `pathname` after that. Needed when the browser puts the page back where it was on a reload.\n * @default false\n */\n skipInitial?: boolean;\n}\n\n/**\n * Scrolls an element of the page (`#main` unless told another) to the top each time the path\n * changes, as a router does not do it for a single page app. It draws nothing; put it once, inside\n * the layout that has the element.\n */\nexport const ScrollToTop = ({\n pathname,\n id = 'main',\n behavior = 'auto',\n block = 'start',\n inline = 'nearest',\n skipInitial = false,\n}: IScrollToTop) => {\n // The path that was scrolled for last. A change of `id`, `behavior`, `block` or `inline` alone is\n // not a new page, so it does not scroll. With `skipInitial` the first path counts as scrolled already.\n const scrolledPath = useRef(skipInitial ? pathname : undefined);\n\n useEffect(() => {\n if (scrolledPath.current === pathname) return;\n scrolledPath.current = pathname;\n\n const element = document.getElementById(id);\n if (!element) return;\n\n const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n element.scrollIntoView({\n behavior: behavior === 'smooth' && reduceMotion ? 'auto' : behavior,\n block,\n inline,\n });\n }, [pathname, id, behavior, block, inline]);\n\n return null;\n};\n"]}
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * Whether React would show something for `content`. `null`, `undefined`, booleans and an empty string
4
+ * show nothing; `0` does show (the text "0"), which is why this is not a plain truthiness check.
5
+ */
6
+ export declare const hasContent: (content: ReactNode) => boolean;
7
+ //# sourceMappingURL=content.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.cts","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,SAAS,YAK7B,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * Whether React would show something for `content`. `null`, `undefined`, booleans and an empty string
4
+ * show nothing; `0` does show (the text "0"), which is why this is not a plain truthiness check.
5
+ */
6
+ export declare const hasContent: (content: ReactNode) => boolean;
7
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/lib/content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,SAAS,YAK7B,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A number of seconds to wait as a whole number that is never negative. A value that is not a number
3
+ * of seconds (`NaN`, `Infinity`, a negative one) is no wait at all: a countdown that cannot end would
4
+ * lock the button for good.
5
+ */
6
+ export declare const toSeconds: (seconds: number) => number;
7
+ /**
8
+ * The whole seconds left until `endsAt` (a time in ms, as `Date.now()` gives), counted up: 0.2 s left
9
+ * is shown as 1, so the countdown shows 0 only once it is over.
10
+ */
11
+ export declare const getSecondsLeft: (endsAt: number, now: number) => number;
12
+ export declare const isPromiseLike: (value: unknown) => value is PromiseLike<unknown>;
13
+ //# sourceMappingURL=countdown.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"countdown.d.cts","sourceRoot":"","sources":["../../src/lib/countdown.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,WACuB,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,EAAE,KAAK,MAAM,WACX,CAAC;AAEhD,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAAW,CAAC,OAAO,CAIzC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A number of seconds to wait as a whole number that is never negative. A value that is not a number
3
+ * of seconds (`NaN`, `Infinity`, a negative one) is no wait at all: a countdown that cannot end would
4
+ * lock the button for good.
5
+ */
6
+ export declare const toSeconds: (seconds: number) => number;
7
+ /**
8
+ * The whole seconds left until `endsAt` (a time in ms, as `Date.now()` gives), counted up: 0.2 s left
9
+ * is shown as 1, so the countdown shows 0 only once it is over.
10
+ */
11
+ export declare const getSecondsLeft: (endsAt: number, now: number) => number;
12
+ export declare const isPromiseLike: (value: unknown) => value is PromiseLike<unknown>;
13
+ //# sourceMappingURL=countdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"countdown.d.ts","sourceRoot":"","sources":["../../src/lib/countdown.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,WACuB,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,EAAE,KAAK,MAAM,WACX,CAAC;AAEhD,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAAW,CAAC,OAAO,CAIzC,CAAC"}
@@ -0,0 +1,9 @@
1
+ export type TStepState = 'completed' | 'active' | 'upcoming';
2
+ /**
3
+ * The step that is the current one: `activeStep` kept inside the steps. A number past the ends (or
4
+ * with a fraction) is put on the nearest step, and `NaN` is the first, so a wrong value never makes
5
+ * every step look completed or none look current.
6
+ */
7
+ export declare const getCurrentStep: (activeStep: number, count: number) => number;
8
+ export declare const getStepState: (index: number, currentStep: number) => TStepState;
9
+ //# sourceMappingURL=stepper.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepper.d.cts","sourceRoot":"","sources":["../../src/lib/stepper.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,YAAY,MAAM,EAAE,OAAO,MAAM,WAGW,CAAC;AAE5E,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,aAAa,MAAM,KAAG,UAIjE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export type TStepState = 'completed' | 'active' | 'upcoming';
2
+ /**
3
+ * The step that is the current one: `activeStep` kept inside the steps. A number past the ends (or
4
+ * with a fraction) is put on the nearest step, and `NaN` is the first, so a wrong value never makes
5
+ * every step look completed or none look current.
6
+ */
7
+ export declare const getCurrentStep: (activeStep: number, count: number) => number;
8
+ export declare const getStepState: (index: number, currentStep: number) => TStepState;
9
+ //# sourceMappingURL=stepper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stepper.d.ts","sourceRoot":"","sources":["../../src/lib/stepper.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,YAAY,MAAM,EAAE,OAAO,MAAM,WAGW,CAAC;AAE5E,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,aAAa,MAAM,KAAG,UAIjE,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ComponentProps } from 'react';
2
+ /** The edge of its container the gradient sits on. It is solid there and fades out going inwards. */
3
+ export type TGradientPosition = 'top' | 'bottom' | 'left' | 'right';
4
+ export interface ILinearGradient extends Omit<ComponentProps<'div'>, 'children'> {
5
+ position: TGradientPosition;
6
+ }
7
+ /**
8
+ * A fade, in the background colour, over one edge of a scrollable area: it tells that there is more
9
+ * to scroll without a hard cut. Put it inside a container that is `relative`. It is only decoration:
10
+ * it does not catch the pointer and assistive technology skips it.
11
+ */
12
+ export declare const LinearGradient: ({ position, className, ...divProps }: ILinearGradient) => import("react").JSX.Element;
13
+ //# sourceMappingURL=LinearGradient.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinearGradient.d.cts","sourceRoot":"","sources":["../../src/linear-gradient/LinearGradient.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,qGAAqG;AACrG,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC9E,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAWD;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,sCAA2C,eAAe,gCAOxF,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ComponentProps } from 'react';
2
+ /** The edge of its container the gradient sits on. It is solid there and fades out going inwards. */
3
+ export type TGradientPosition = 'top' | 'bottom' | 'left' | 'right';
4
+ export interface ILinearGradient extends Omit<ComponentProps<'div'>, 'children'> {
5
+ position: TGradientPosition;
6
+ }
7
+ /**
8
+ * A fade, in the background colour, over one edge of a scrollable area: it tells that there is more
9
+ * to scroll without a hard cut. Put it inside a container that is `relative`. It is only decoration:
10
+ * it does not catch the pointer and assistive technology skips it.
11
+ */
12
+ export declare const LinearGradient: ({ position, className, ...divProps }: ILinearGradient) => import("react").JSX.Element;
13
+ //# sourceMappingURL=LinearGradient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinearGradient.d.ts","sourceRoot":"","sources":["../../src/linear-gradient/LinearGradient.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,qGAAqG;AACrG,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC9E,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAWD;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,sCAA2C,eAAe,gCAOxF,CAAC"}
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var BASE_CSS = "from-primary-invert pointer-events-none absolute z-1 to-transparent";
8
+ var POSITION_CSS = {
9
+ top: "inset-x-0 top-0 h-10 md:h-20 bg-linear-to-b",
10
+ bottom: "inset-x-0 bottom-0 h-10 md:h-20 bg-linear-to-t",
11
+ left: "inset-y-0 left-0 w-10 md:w-20 bg-linear-to-r",
12
+ right: "inset-y-0 right-0 w-10 md:w-20 bg-linear-to-l"
13
+ };
14
+ var LinearGradient = /* @__PURE__ */ __name(({ position, className = "", ...divProps }) => /* @__PURE__ */ jsxRuntime.jsx(
15
+ "div",
16
+ {
17
+ "aria-hidden": "true",
18
+ ...divProps,
19
+ className: `${BASE_CSS} ${Object.hasOwn(POSITION_CSS, position) ? POSITION_CSS[position] : ""} ${className}`
20
+ }
21
+ ), "LinearGradient");
22
+
23
+ exports.LinearGradient = LinearGradient;
24
+ //# sourceMappingURL=index.cjs.map
25
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/linear-gradient/LinearGradient.tsx"],"names":["jsx"],"mappings":";;;;;;AASA,IAAM,QAAA,GAAW,qEAAA;AAEjB,IAAM,YAAA,GAAkD;AAAA,EACtD,GAAA,EAAK,6CAAA;AAAA,EACL,MAAA,EAAQ,gDAAA;AAAA,EACR,IAAA,EAAM,8CAAA;AAAA,EACN,KAAA,EAAO;AACT,CAAA;AAOO,IAAM,cAAA,2BAAkB,EAAE,QAAA,EAAU,YAAY,EAAA,EAAI,GAAG,UAAS,qBACrEA,cAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,aAAA,EAAY,MAAA;AAAA,IACX,GAAG,QAAA;AAAA,IAEJ,SAAA,EAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,OAAO,MAAA,CAAO,YAAA,EAAc,QAAQ,CAAA,GAAI,YAAA,CAAa,QAAQ,CAAA,GAAI,EAAE,IAAI,SAAS,CAAA;AAAA;AAC5G,CAAA,EAN4B,gBAAA","file":"index.cjs","sourcesContent":["import type { ComponentProps } from 'react';\n\n/** The edge of its container the gradient sits on. It is solid there and fades out going inwards. */\nexport type TGradientPosition = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface ILinearGradient extends Omit<ComponentProps<'div'>, 'children'> {\n position: TGradientPosition;\n}\n\nconst BASE_CSS = 'from-primary-invert pointer-events-none absolute z-1 to-transparent';\n\nconst POSITION_CSS: Record<TGradientPosition, string> = {\n top: 'inset-x-0 top-0 h-10 md:h-20 bg-linear-to-b',\n bottom: 'inset-x-0 bottom-0 h-10 md:h-20 bg-linear-to-t',\n left: 'inset-y-0 left-0 w-10 md:w-20 bg-linear-to-r',\n right: 'inset-y-0 right-0 w-10 md:w-20 bg-linear-to-l',\n};\n\n/**\n * A fade, in the background colour, over one edge of a scrollable area: it tells that there is more\n * to scroll without a hard cut. Put it inside a container that is `relative`. It is only decoration:\n * it does not catch the pointer and assistive technology skips it.\n */\nexport const LinearGradient = ({ position, className = '', ...divProps }: ILinearGradient) => (\n <div\n aria-hidden=\"true\"\n {...divProps}\n // a position that is not one (code that is not type checked) draws nothing, not \"undefined\"\n className={`${BASE_CSS} ${Object.hasOwn(POSITION_CSS, position) ? POSITION_CSS[position] : ''} ${className}`}\n />\n);\n"]}
@@ -0,0 +1,2 @@
1
+ export { type ILinearGradient, LinearGradient, type TGradientPosition } from './LinearGradient.js';
2
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/linear-gradient/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { type ILinearGradient, LinearGradient, type TGradientPosition } from './LinearGradient.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/linear-gradient/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+ var BASE_CSS = "from-primary-invert pointer-events-none absolute z-1 to-transparent";
6
+ var POSITION_CSS = {
7
+ top: "inset-x-0 top-0 h-10 md:h-20 bg-linear-to-b",
8
+ bottom: "inset-x-0 bottom-0 h-10 md:h-20 bg-linear-to-t",
9
+ left: "inset-y-0 left-0 w-10 md:w-20 bg-linear-to-r",
10
+ right: "inset-y-0 right-0 w-10 md:w-20 bg-linear-to-l"
11
+ };
12
+ var LinearGradient = /* @__PURE__ */ __name(({ position, className = "", ...divProps }) => /* @__PURE__ */ jsx(
13
+ "div",
14
+ {
15
+ "aria-hidden": "true",
16
+ ...divProps,
17
+ className: `${BASE_CSS} ${Object.hasOwn(POSITION_CSS, position) ? POSITION_CSS[position] : ""} ${className}`
18
+ }
19
+ ), "LinearGradient");
20
+
21
+ export { LinearGradient };
22
+ //# sourceMappingURL=index.js.map
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/linear-gradient/LinearGradient.tsx"],"names":[],"mappings":";;;;AASA,IAAM,QAAA,GAAW,qEAAA;AAEjB,IAAM,YAAA,GAAkD;AAAA,EACtD,GAAA,EAAK,6CAAA;AAAA,EACL,MAAA,EAAQ,gDAAA;AAAA,EACR,IAAA,EAAM,8CAAA;AAAA,EACN,KAAA,EAAO;AACT,CAAA;AAOO,IAAM,cAAA,2BAAkB,EAAE,QAAA,EAAU,YAAY,EAAA,EAAI,GAAG,UAAS,qBACrE,GAAA;AAAA,EAAC,KAAA;AAAA,EAAA;AAAA,IACC,aAAA,EAAY,MAAA;AAAA,IACX,GAAG,QAAA;AAAA,IAEJ,SAAA,EAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,OAAO,MAAA,CAAO,YAAA,EAAc,QAAQ,CAAA,GAAI,YAAA,CAAa,QAAQ,CAAA,GAAI,EAAE,IAAI,SAAS,CAAA;AAAA;AAC5G,CAAA,EAN4B,gBAAA","file":"index.js","sourcesContent":["import type { ComponentProps } from 'react';\n\n/** The edge of its container the gradient sits on. It is solid there and fades out going inwards. */\nexport type TGradientPosition = 'top' | 'bottom' | 'left' | 'right';\n\nexport interface ILinearGradient extends Omit<ComponentProps<'div'>, 'children'> {\n position: TGradientPosition;\n}\n\nconst BASE_CSS = 'from-primary-invert pointer-events-none absolute z-1 to-transparent';\n\nconst POSITION_CSS: Record<TGradientPosition, string> = {\n top: 'inset-x-0 top-0 h-10 md:h-20 bg-linear-to-b',\n bottom: 'inset-x-0 bottom-0 h-10 md:h-20 bg-linear-to-t',\n left: 'inset-y-0 left-0 w-10 md:w-20 bg-linear-to-r',\n right: 'inset-y-0 right-0 w-10 md:w-20 bg-linear-to-l',\n};\n\n/**\n * A fade, in the background colour, over one edge of a scrollable area: it tells that there is more\n * to scroll without a hard cut. Put it inside a container that is `relative`. It is only decoration:\n * it does not catch the pointer and assistive technology skips it.\n */\nexport const LinearGradient = ({ position, className = '', ...divProps }: ILinearGradient) => (\n <div\n aria-hidden=\"true\"\n {...divProps}\n // a position that is not one (code that is not type checked) draws nothing, not \"undefined\"\n className={`${BASE_CSS} ${Object.hasOwn(POSITION_CSS, position) ? POSITION_CSS[position] : ''} ${className}`}\n />\n);\n"]}
@@ -0,0 +1,37 @@
1
+ import { type ComponentProps } from 'react';
2
+ export interface IResend extends Omit<ComponentProps<'p'>, 'children'> {
3
+ /** What is said before the countdown or the button, e.g. `"Didn't get the code?"`. */
4
+ label: string;
5
+ /**
6
+ * How many seconds to wait before it can be asked again. The wait starts when the component is
7
+ * shown (the thing was just sent) and again after each resend, with the `count` of that moment, so
8
+ * an app can stop making people wait after a few tries by passing 0. A change of `count` in the
9
+ * middle of a wait does not change that wait; to start over (a new email address), give the
10
+ * component a new `key`.
11
+ */
12
+ count: number;
13
+ /**
14
+ * Asks again. When it returns a promise, the button waits for it (it cannot be pressed twice, and
15
+ * says it is sending) and the wait starts when it resolves; if it rejects there is no wait, the
16
+ * button can be pressed again and the error is reported to the window like any uncaught error.
17
+ * Handle the errors you can recover from (a toast) inside it.
18
+ */
19
+ onResend: () => unknown;
20
+ /** @default 'Resend' */
21
+ resendLabel?: string;
22
+ /** What comes before the seconds while waiting. @default 'Resend in' */
23
+ countdownLabel?: string;
24
+ /** The button while `onResend` is waiting for its promise. @default 'Sending…' */
25
+ sendingLabel?: string;
26
+ /** Read out by assistive technology when the wait ends. @default 'You can resend now' */
27
+ readyLabel?: string;
28
+ /** How the seconds are written. @default (seconds) => `${seconds}s` */
29
+ formatSeconds?: (seconds: number) => string;
30
+ }
31
+ /**
32
+ * "Didn't get the code? Resend in 30s" and, once the wait is over, a button to ask again. The wait is
33
+ * measured against the clock, not counted tick by tick, so a tab that was in the background or a
34
+ * laptop that slept does not leave people waiting longer than the time that really went by.
35
+ */
36
+ export declare const Resend: ({ label, count, onResend, resendLabel, countdownLabel, sendingLabel, readyLabel, formatSeconds, className, ...pProps }: IResend) => import("react").JSX.Element;
37
+ //# sourceMappingURL=Resend.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Resend.d.cts","sourceRoot":"","sources":["../../src/resend/Resend.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA+B,MAAM,OAAO,CAAC;AAKzE,MAAM,WAAW,OAAQ,SAAQ,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IACpE,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;AAUD;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,wHAWpB,OAAO,gCAmHT,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type ComponentProps } from 'react';
2
+ export interface IResend extends Omit<ComponentProps<'p'>, 'children'> {
3
+ /** What is said before the countdown or the button, e.g. `"Didn't get the code?"`. */
4
+ label: string;
5
+ /**
6
+ * How many seconds to wait before it can be asked again. The wait starts when the component is
7
+ * shown (the thing was just sent) and again after each resend, with the `count` of that moment, so
8
+ * an app can stop making people wait after a few tries by passing 0. A change of `count` in the
9
+ * middle of a wait does not change that wait; to start over (a new email address), give the
10
+ * component a new `key`.
11
+ */
12
+ count: number;
13
+ /**
14
+ * Asks again. When it returns a promise, the button waits for it (it cannot be pressed twice, and
15
+ * says it is sending) and the wait starts when it resolves; if it rejects there is no wait, the
16
+ * button can be pressed again and the error is reported to the window like any uncaught error.
17
+ * Handle the errors you can recover from (a toast) inside it.
18
+ */
19
+ onResend: () => unknown;
20
+ /** @default 'Resend' */
21
+ resendLabel?: string;
22
+ /** What comes before the seconds while waiting. @default 'Resend in' */
23
+ countdownLabel?: string;
24
+ /** The button while `onResend` is waiting for its promise. @default 'Sending…' */
25
+ sendingLabel?: string;
26
+ /** Read out by assistive technology when the wait ends. @default 'You can resend now' */
27
+ readyLabel?: string;
28
+ /** How the seconds are written. @default (seconds) => `${seconds}s` */
29
+ formatSeconds?: (seconds: number) => string;
30
+ }
31
+ /**
32
+ * "Didn't get the code? Resend in 30s" and, once the wait is over, a button to ask again. The wait is
33
+ * measured against the clock, not counted tick by tick, so a tab that was in the background or a
34
+ * laptop that slept does not leave people waiting longer than the time that really went by.
35
+ */
36
+ export declare const Resend: ({ label, count, onResend, resendLabel, countdownLabel, sendingLabel, readyLabel, formatSeconds, className, ...pProps }: IResend) => import("react").JSX.Element;
37
+ //# sourceMappingURL=Resend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Resend.d.ts","sourceRoot":"","sources":["../../src/resend/Resend.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA+B,MAAM,OAAO,CAAC;AAKzE,MAAM,WAAW,OAAQ,SAAQ,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IACpE,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;AAUD;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,wHAWpB,OAAO,gCAmHT,CAAC"}
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+
3
+ var react = require('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/countdown.ts
10
+ var toSeconds = /* @__PURE__ */ __name((seconds) => Number.isFinite(seconds) ? Math.max(Math.ceil(seconds), 0) : 0, "toSeconds");
11
+ var getSecondsLeft = /* @__PURE__ */ __name((endsAt, now) => Math.max(Math.ceil((endsAt - now) / 1e3), 0), "getSecondsLeft");
12
+ var isPromiseLike = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && "then" in value && typeof value.then === "function", "isPromiseLike");
13
+ var BASE_CSS = "text-fill-transparent w-fit";
14
+ var FORCED_COLORS_CSS = "forced-colors:bg-none forced-colors:[-webkit-text-fill-color:currentColor]";
15
+ var GRADIENT_CSS = {
16
+ accent: "bg-accent-duo",
17
+ silver: "bg-silver-duo"
18
+ };
19
+ var GradientText = /* @__PURE__ */ __name(({
20
+ as,
21
+ type,
22
+ text,
23
+ children,
24
+ className = "",
25
+ ...rest
26
+ }) => {
27
+ const Component = as ?? "span";
28
+ return /* @__PURE__ */ jsxRuntime.jsx(
29
+ Component,
30
+ {
31
+ ...rest,
32
+ className: `${BASE_CSS} ${GRADIENT_CSS[type]} ${FORCED_COLORS_CSS} ${className}`,
33
+ children: children ?? text
34
+ }
35
+ );
36
+ }, "GradientText");
37
+ var defaultFormatSeconds = /* @__PURE__ */ __name((seconds) => `${String(seconds)}s`, "defaultFormatSeconds");
38
+ 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";
39
+ var Resend = /* @__PURE__ */ __name(({
40
+ label,
41
+ count,
42
+ onResend,
43
+ resendLabel = "Resend",
44
+ countdownLabel = "Resend in",
45
+ sendingLabel = "Sending\u2026",
46
+ readyLabel = "You can resend now",
47
+ formatSeconds = defaultFormatSeconds,
48
+ className = "",
49
+ ...pProps
50
+ }) => {
51
+ const [wait, setWait] = react.useState(() => ({ seconds: toSeconds(count) }));
52
+ const [secondsLeft, setSecondsLeft] = react.useState(wait.seconds);
53
+ const [isReady, setIsReady] = react.useState(false);
54
+ const [isSending, setIsSending] = react.useState(false);
55
+ const isSendingRef = react.useRef(false);
56
+ const countRef = react.useRef(count);
57
+ react.useEffect(() => {
58
+ countRef.current = count;
59
+ }, [count]);
60
+ react.useEffect(() => {
61
+ if (wait.seconds <= 0) return;
62
+ const endsAt = Date.now() + wait.seconds * 1e3;
63
+ let shown = wait.seconds;
64
+ const update = /* @__PURE__ */ __name(() => {
65
+ const left = getSecondsLeft(endsAt, Date.now());
66
+ if (left !== shown) {
67
+ shown = left;
68
+ setSecondsLeft(left);
69
+ }
70
+ if (left > 0) return;
71
+ window.clearInterval(timer);
72
+ setIsReady(true);
73
+ }, "update");
74
+ const timer = window.setInterval(update, 250);
75
+ document.addEventListener("visibilitychange", update);
76
+ return () => {
77
+ window.clearInterval(timer);
78
+ document.removeEventListener("visibilitychange", update);
79
+ };
80
+ }, [wait]);
81
+ const startWait = /* @__PURE__ */ __name(() => {
82
+ const seconds = toSeconds(countRef.current);
83
+ setIsReady(false);
84
+ setSecondsLeft(seconds);
85
+ setWait({ seconds });
86
+ }, "startWait");
87
+ const resend = /* @__PURE__ */ __name(async () => {
88
+ if (isSendingRef.current) return;
89
+ const result = onResend();
90
+ if (!isPromiseLike(result)) {
91
+ startWait();
92
+ return;
93
+ }
94
+ isSendingRef.current = true;
95
+ setIsSending(true);
96
+ try {
97
+ await result;
98
+ startWait();
99
+ } finally {
100
+ isSendingRef.current = false;
101
+ setIsSending(false);
102
+ }
103
+ }, "resend");
104
+ return /* @__PURE__ */ jsxRuntime.jsxs("p", { ...pProps, className: `space-x-2 ${className}`, children: [
105
+ /* @__PURE__ */ jsxRuntime.jsx(GradientText, { text: label, type: "silver", className: "text-xs sm:text-sm" }),
106
+ secondsLeft > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { role: "timer", className: "text-muted text-xs sm:text-sm", children: [
107
+ /* @__PURE__ */ jsxRuntime.jsx(GradientText, { text: countdownLabel, type: "silver" }),
108
+ " ",
109
+ /* @__PURE__ */ jsxRuntime.jsx(GradientText, { text: formatSeconds(secondsLeft), type: "accent", className: "font-semibold" })
110
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
111
+ "button",
112
+ {
113
+ type: "button",
114
+ disabled: isSending,
115
+ "aria-busy": isSending || void 0,
116
+ onClick: () => {
117
+ resend().catch((error) => {
118
+ reportError(error);
119
+ });
120
+ },
121
+ className: BUTTON_CSS,
122
+ children: /* @__PURE__ */ jsxRuntime.jsx(
123
+ GradientText,
124
+ {
125
+ type: "accent",
126
+ text: isSending ? sendingLabel : resendLabel,
127
+ className: "text-xs font-semibold sm:text-sm"
128
+ }
129
+ )
130
+ }
131
+ ),
132
+ /* @__PURE__ */ jsxRuntime.jsx("span", { role: "status", className: "sr-only", children: isReady ? readyLabel : "" })
133
+ ] });
134
+ }, "Resend");
135
+
136
+ exports.Resend = Resend;
137
+ //# sourceMappingURL=index.cjs.map
138
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/countdown.ts","../../src/ui/GradientText.tsx","../../src/resend/Resend.tsx"],"names":["jsx","useState","useRef","useEffect","jsxs"],"mappings":"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","file":"index.cjs","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,2 @@
1
+ export { type IResend, Resend } from './Resend.js';
2
+ //# sourceMappingURL=index.d.cts.map
@@ -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"}