@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
package/README.md CHANGED
@@ -29,21 +29,31 @@ generated and the components come out unstyled. (The playground has a page that
29
29
 
30
30
  ## Import paths
31
31
 
32
- Components that most screens use live in `/ui` (and, later, `/layout`). Components that few screens
33
- use have a path of their own, so an app only bundles what it imports.
34
-
35
- | Import path | What it gives |
36
- | --------------------------------------------- | ------------------------------------------------------------------------------------------- |
37
- | `@beautinique/frontend-components` | Everything `/ui` and `/toast` give (the very same values), and the types (`IButton`, ...) |
38
- | `@beautinique/frontend-components/ui` | `Button`, `Tooltip` |
39
- | `@beautinique/frontend-components/toast` | `ToastContainer`, `Toaster`, the types of the toasts, and what works out what a toast shows |
40
- | `@beautinique/frontend-components/source.css` | The `@source` line of the setup above |
41
-
42
- `Button` can be imported from the root or from `/ui`, it is the same component either way:
32
+ Components that most screens use live in `/ui`, `/layout`, `/theme` and `/toast`, and the root gives
33
+ all of them. Components that few screens use have a path of their own and are **not** in the root, so
34
+ an app only bundles what it imports.
35
+
36
+ | Import path | What it gives |
37
+ | -------------------------------------------------- | --------------------------------------------------------------------------------------------- |
38
+ | `@beautinique/frontend-components` | Everything `/ui`, `/layout`, `/theme` and `/toast` give (the very same values), and the types |
39
+ | `@beautinique/frontend-components/ui` | `Badge`, `Button`, `Divider`, `GradientText`, `Tooltip` |
40
+ | `@beautinique/frontend-components/layout` | `ScrollToTop` |
41
+ | `@beautinique/frontend-components/theme` | `Theme` (the dark / light button) |
42
+ | `@beautinique/frontend-components/toast` | `ToastContainer`, `Toaster`, the types of the toasts, and what works out what a toast shows |
43
+ | `@beautinique/frontend-components/auth` | `AuthBottomInstructions` |
44
+ | `@beautinique/frontend-components/linear-gradient` | `LinearGradient` |
45
+ | `@beautinique/frontend-components/resend` | `Resend` |
46
+ | `@beautinique/frontend-components/stepper` | `Stepper` |
47
+ | `@beautinique/frontend-components/source.css` | The `@source` line of the setup above |
48
+
49
+ What the root gives can be imported from the root or from its own path, it is the same component
50
+ either way:
43
51
 
44
52
  ```ts
45
53
  import { Button } from '@beautinique/frontend-components'; // or
46
54
  import { Button } from '@beautinique/frontend-components/ui';
55
+
56
+ import { Stepper } from '@beautinique/frontend-components/stepper'; // only from its own path
47
57
  ```
48
58
 
49
59
  The root only re-exports, it does not copy: an app that imports only `Button` from the root bundles the
@@ -110,6 +120,153 @@ const hint = (
110
120
  that is not there at all: the content is one level deeper in the DOM, so a css rule that looks at
111
121
  the parent of the content (`space-y-*` on a parent, `> :first-child`) sees the wrapper.
112
122
 
123
+ ### Badge, Divider, GradientText
124
+
125
+ ```tsx
126
+ import { Badge, Divider, GradientText } from '@beautinique/frontend-components';
127
+
128
+ <Badge content="New" />; // `content` is any React content: text, a number, an icon and a text
129
+ <Badge content={count > 0 && count} />; // nothing to show (null, undefined, false, '') draws no badge; 0 is shown
130
+ <Divider aria-hidden="true" />; // an <hr>; hide it from screen readers when it is only a line
131
+ <GradientText type="accent" text="Terms" />; // or <GradientText type="silver">any <b>content</b></GradientText>
132
+ <GradientText as={Link} to="/terms" type="accent" text="Terms" />; // the Link of the router, with its props
133
+ ```
134
+
135
+ - All three take the attributes of their element (`id`, `data-*`, `aria-*`, `ref`...), and the
136
+ classes you give are added after their own.
137
+ - `GradientText` takes `text` **or** `children`, not both (the compiler says so). In forced-colors mode
138
+ (Windows high contrast), where a gradient is not drawn, the text takes the colour of the system
139
+ instead of vanishing.
140
+
141
+ ### LinearGradient
142
+
143
+ ```tsx
144
+ import { LinearGradient } from '@beautinique/frontend-components/linear-gradient';
145
+
146
+ <div className="relative overflow-x-auto">
147
+ <LinearGradient position="left" />
148
+ {/* the scrolling content */}
149
+ <LinearGradient position="right" />
150
+ </div>;
151
+ ```
152
+
153
+ A fade in the background colour over one edge (`top`, `bottom`, `left` or `right`) of a `relative`
154
+ container: it shows that there is more to scroll. It is decoration: the pointer goes through it and
155
+ assistive technology skips it.
156
+
157
+ ### Stepper
158
+
159
+ ```tsx
160
+ import { Stepper } from '@beautinique/frontend-components/stepper';
161
+
162
+ <Stepper
163
+ label="Checkout progress"
164
+ activeStep={1}
165
+ steps={[
166
+ { title: 'Account', description: 'Create your account' },
167
+ { title: 'Address', description: 'Where to deliver', icon: 'solar:map-point-linear' },
168
+ { title: 'Payment', disabled: true },
169
+ ]}
170
+ onStepClick={(index) => setStep(index)} // optional: with it the steps are buttons
171
+ >
172
+ {/* optional: shown under the steps */}
173
+ </Stepper>;
174
+ ```
175
+
176
+ - The steps are an ordered list and the current one has `aria-current="step"`. A completed step shows a
177
+ tick, and says "Completed" to a screen reader (`completedLabel` for another word).
178
+ - `activeStep` is kept inside the steps (`99` is the last one, `NaN` the first).
179
+ - With `onStepClick` the steps are real buttons (keyboard, focus ring); a step with `disabled` cannot be
180
+ clicked. Without it nothing in the stepper is a button.
181
+ - The tick is bundled with the package (registered with Iconify when a stepper renders), so it needs no
182
+ network. The `icon` of a step is an Iconify name that Iconify downloads the first time it is shown;
183
+ to have one offline, add it to the app with `addCollection`.
184
+
185
+ ### Resend
186
+
187
+ ```tsx
188
+ import { Resend } from '@beautinique/frontend-components/resend';
189
+
190
+ <Resend
191
+ label="Not received OTP?"
192
+ count={sendCount >= 3 ? 0 : 30} // seconds to wait; 0 = no wait
193
+ onResend={handleResendOtp} // may return a promise
194
+ />;
195
+ ```
196
+
197
+ - "Resend in 30s", and when the wait is over a button. The wait starts when the component is shown and
198
+ again after each resend, with the `count` of that moment. A new email address? Give it a new `key`.
199
+ - **It is measured against the clock**, not counted tick by tick: a background tab, or a laptop that
200
+ slept, does not make people wait longer than the time that really went by.
201
+ - If `onResend` returns a promise the button waits for it (disabled, "Sending…", no double click)
202
+ and the wait starts when it resolves. If it rejects there is no wait, the button can be pressed
203
+ again and the error is reported to `window` (`reportError`). Handle the errors you can recover
204
+ from (a toast) inside it.
205
+ - A screen reader is not told the number every second (`role="timer"`), but is told once when the
206
+ button is back. The words (`resendLabel`, `countdownLabel`, `sendingLabel`, `readyLabel`,
207
+ `formatSeconds`) can be changed for another language.
208
+
209
+ ### Theme
210
+
211
+ ```tsx
212
+ import { Theme } from '@beautinique/frontend-components';
213
+ import { createThemeStore } from '@beautinique/frontend-stores';
214
+
215
+ export const useThemeStore = createThemeStore(); // once, in a module of the app
216
+
217
+ <Theme store={useThemeStore} />;
218
+ ```
219
+
220
+ A button that switches between the dark and the light theme (a sun while it is dark, a moon while it
221
+ is light). It only changes the store, which remembers the choice; putting the theme on the page is the
222
+ app's. Its name for assistive technology follows the theme ("Switch to light theme"; `labels` changes
223
+ the words). The icons move; for a person who asked for less motion (`prefers-reduced-motion`) it
224
+ shows still ones.
225
+
226
+ ### ScrollToTop
227
+
228
+ ```tsx
229
+ import { ScrollToTop } from '@beautinique/frontend-components';
230
+
231
+ const { pathname } = useLocation(); // from the router of the app
232
+
233
+ <div id="main">
234
+ <ScrollToTop pathname={pathname} />
235
+ </div>;
236
+ ```
237
+
238
+ Scrolls the element with `id="main"` to its top each time `pathname` changes (a router does not do it
239
+ for a single page app). It draws nothing. `id`, `behavior`, `block` and `inline` are the `id` of the
240
+ element and the options of `scrollIntoView` (`'auto'`, `'start'` and `'nearest'` unless told another);
241
+ a `'smooth'` scroll is made `'auto'` for a person who asked for less motion. `skipInitial` leaves the
242
+ first page where it is (when the browser restores the scroll on a reload).
243
+
244
+ ### AuthBottomInstructions
245
+
246
+ ```tsx
247
+ import { AuthBottomInstructions } from '@beautinique/frontend-components/auth';
248
+ import { Link } from 'react-router-dom';
249
+
250
+ <AuthBottomInstructions as={Link} terms={{ to: '/terms' }} privacy={{ to: '/privacy' }} />;
251
+ // "By continuing, you agree to our Terms & Conditions and Privacy Policy."
252
+ ```
253
+
254
+ The small print under a sign-in form. `as` is what makes the links (`'a'` unless told another; for `'a'`
255
+ the compiler asks for `href`), `terms` and `privacy` are the props of those links. `termsLabel`,
256
+ `privacyLabel` and `message` change the words:
257
+
258
+ ```tsx
259
+ <AuthBottomInstructions
260
+ terms={{ href: '/terms' }}
261
+ privacy={{ href: '/privacy' }}
262
+ message={({ terms, privacy }) => (
263
+ <>
264
+ Entering means you accept our {terms} and our {privacy}.
265
+ </>
266
+ )}
267
+ />
268
+ ```
269
+
113
270
  ### Toasts
114
271
 
115
272
  This package draws the toasts; the store that keeps them and the `toaster` that shows them are [`@beautinique/frontend-stores`](../stores/README.md)'s. The store is **made by the app**, once, with the toasts of this package, and given to the two things that need it: the `ToastContainer` here, as a prop, and `createToaster` of the stores package. This package holds no store and no toaster of its own, and exports none.
@@ -164,9 +321,18 @@ The icons of the toasts are bundled, so a toast shows its icon without a network
164
321
 
165
322
  ## Rules for the code of this package
166
323
 
167
- - **The package holds no state.** The toast store is made by the app (`createToastStore` of `@beautinique/frontend-stores`, with this package's `TToast`) and given in, to the `ToastContainer` as a prop. The toaster is the stores package's too (`createToaster`). Nothing here makes a store or a toaster, or exports one, so the package can never be the cause of two stores. The root only re-exports `/ui` and `/toast` (`tsup.config.ts` keeps them external, and `built-files.test.ts` checks the built files after `npm run build`).
168
- - **The root has what most screens use** (`/ui`, later `/layout`, and `/toast`). A component that few
169
- screens use gets a path of its own and is not in the root.
324
+ - **The package holds no state.** The toast store is made by the app (`createToastStore` of `@beautinique/frontend-stores`, with this package's `TToast`) and given in, to the `ToastContainer` as a prop. The toaster is the stores package's too (`createToaster`). Nothing here makes a store or a toaster, or exports one, so the package can never be the cause of two stores. The root only re-exports `/ui`, `/layout`, `/theme` and `/toast` (`tsup.config.ts` keeps them external, and `built-files.test.ts` checks the built files after `npm run build`).
325
+ - **The root has what most screens use** (`/ui`, `/layout`, `/theme` and `/toast`). A component that
326
+ few screens use gets a path of its own and is not in the root.
327
+ - **Icons are Iconify icons, bundled and registered.** A component draws its icons with Iconify's
328
+ `Icon`, and the icon data is in the package (`toast-icons.ts`, `stepper-icons.ts`,
329
+ `theme-icons.ts`, registered with `addCollection`), so they show without a network and without
330
+ waiting for the Iconify API. A component registers its own icons when it renders (not when its file
331
+ is imported), so an app only carries the icons of the components it uses. Iconify draws icons in the
332
+ browser only: on a server a placeholder is in their place (fine for the Vite apps).
333
+ - **Components are accessible by default**: real elements (`button`, `ol`, `hr`), names for what has no
334
+ text, a focus ring for the keyboard (`focus-visible`), `prefers-reduced-motion` and forced-colors
335
+ mode taken care of, and nothing that moves or announces every second.
170
336
  - **A component's own css lives next to the component** and is imported by it, not by the styles
171
337
  package. It uses the variables of the styles package (`var(--smoke-eerie)`, ...) and does not
172
338
  import them again: importing them a second time, after the app's own css, would undo the app's
@@ -0,0 +1,42 @@
1
+ import type { ComponentProps, ComponentPropsWithRef, ElementType, ReactNode } from 'react';
2
+ /**
3
+ * What the link to a page needs, as the component that makes the link asks for it: `{ to: '/terms' }`
4
+ * for the `Link` of a router, `{ href: '/terms' }` for a plain `<a>` (which has to have one).
5
+ */
6
+ export type TAuthLinkProps<TAs extends ElementType> = Omit<ComponentPropsWithRef<TAs>, 'children' | 'className' | 'text' | 'type'> & (TAs extends 'a' ? {
7
+ href: string;
8
+ } : unknown);
9
+ /** The two links of the sentence, ready to be put in it. */
10
+ export interface IAuthLinks {
11
+ terms: ReactNode;
12
+ privacy: ReactNode;
13
+ }
14
+ interface IAuthBottomInstructionsOwnProps<TAs extends ElementType> {
15
+ /**
16
+ * What makes the links: `'a'`, or the `Link` of the router of the app. The router is the app's,
17
+ * not this package's.
18
+ * @default 'a'
19
+ */
20
+ as?: TAs;
21
+ /** The link to the terms and conditions, in the props of `as`. */
22
+ terms: TAuthLinkProps<TAs>;
23
+ /** The link to the privacy policy, in the props of `as`. */
24
+ privacy: TAuthLinkProps<TAs>;
25
+ /** @default 'Terms & Conditions' */
26
+ termsLabel?: string;
27
+ /** @default 'Privacy Policy' */
28
+ privacyLabel?: string;
29
+ /**
30
+ * The sentence around the two links, for another wording or another language.
31
+ * @default ({ terms, privacy }) => <>By continuing, you agree to our {terms} and {privacy}.</>
32
+ */
33
+ message?: (links: IAuthLinks) => ReactNode;
34
+ }
35
+ export type IAuthBottomInstructions<TAs extends ElementType = 'a'> = IAuthBottomInstructionsOwnProps<TAs> & Omit<ComponentProps<'p'>, 'children'>;
36
+ /**
37
+ * The small print under a sign-in or sign-up form: that going on means accepting the terms and the
38
+ * privacy policy, with a link to each.
39
+ */
40
+ export declare const AuthBottomInstructions: <TAs extends ElementType = "a">({ as, terms, privacy, termsLabel, privacyLabel, message, className, ...pProps }: IAuthBottomInstructions<TAs>) => import("react").JSX.Element;
41
+ export {};
42
+ //# sourceMappingURL=AuthBottomInstructions.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthBottomInstructions.d.cts","sourceRoot":"","sources":["../../src/auth/AuthBottomInstructions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3F;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,GAAG,SAAS,WAAW,IAAI,IAAI,CACxD,qBAAqB,CAAC,GAAG,CAAC,EAC1B,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,CAC3C,GACC,CAAC,GAAG,SAAS,GAAG,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC;AAEjD,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,SAAS,CAAC;CACpB;AAED,UAAU,+BAA+B,CAAC,GAAG,SAAS,WAAW;IAC/D;;;;OAIG;IACH,EAAE,CAAC,EAAE,GAAG,CAAC;IACT,kEAAkE;IAClE,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC3B,4DAA4D;IAC5D,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC7B,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,SAAS,CAAC;CAC5C;AAED,MAAM,MAAM,uBAAuB,CAAC,GAAG,SAAS,WAAW,GAAG,GAAG,IAC/D,+BAA+B,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;AAU/E;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,GAAG,SAAS,WAAW,GAAG,GAAG,EAAE,iFASnE,uBAAuB,CAAC,GAAG,CAAC,gCA2B9B,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { ComponentProps, ComponentPropsWithRef, ElementType, ReactNode } from 'react';
2
+ /**
3
+ * What the link to a page needs, as the component that makes the link asks for it: `{ to: '/terms' }`
4
+ * for the `Link` of a router, `{ href: '/terms' }` for a plain `<a>` (which has to have one).
5
+ */
6
+ export type TAuthLinkProps<TAs extends ElementType> = Omit<ComponentPropsWithRef<TAs>, 'children' | 'className' | 'text' | 'type'> & (TAs extends 'a' ? {
7
+ href: string;
8
+ } : unknown);
9
+ /** The two links of the sentence, ready to be put in it. */
10
+ export interface IAuthLinks {
11
+ terms: ReactNode;
12
+ privacy: ReactNode;
13
+ }
14
+ interface IAuthBottomInstructionsOwnProps<TAs extends ElementType> {
15
+ /**
16
+ * What makes the links: `'a'`, or the `Link` of the router of the app. The router is the app's,
17
+ * not this package's.
18
+ * @default 'a'
19
+ */
20
+ as?: TAs;
21
+ /** The link to the terms and conditions, in the props of `as`. */
22
+ terms: TAuthLinkProps<TAs>;
23
+ /** The link to the privacy policy, in the props of `as`. */
24
+ privacy: TAuthLinkProps<TAs>;
25
+ /** @default 'Terms & Conditions' */
26
+ termsLabel?: string;
27
+ /** @default 'Privacy Policy' */
28
+ privacyLabel?: string;
29
+ /**
30
+ * The sentence around the two links, for another wording or another language.
31
+ * @default ({ terms, privacy }) => <>By continuing, you agree to our {terms} and {privacy}.</>
32
+ */
33
+ message?: (links: IAuthLinks) => ReactNode;
34
+ }
35
+ export type IAuthBottomInstructions<TAs extends ElementType = 'a'> = IAuthBottomInstructionsOwnProps<TAs> & Omit<ComponentProps<'p'>, 'children'>;
36
+ /**
37
+ * The small print under a sign-in or sign-up form: that going on means accepting the terms and the
38
+ * privacy policy, with a link to each.
39
+ */
40
+ export declare const AuthBottomInstructions: <TAs extends ElementType = "a">({ as, terms, privacy, termsLabel, privacyLabel, message, className, ...pProps }: IAuthBottomInstructions<TAs>) => import("react").JSX.Element;
41
+ export {};
42
+ //# sourceMappingURL=AuthBottomInstructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthBottomInstructions.d.ts","sourceRoot":"","sources":["../../src/auth/AuthBottomInstructions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAI3F;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,GAAG,SAAS,WAAW,IAAI,IAAI,CACxD,qBAAqB,CAAC,GAAG,CAAC,EAC1B,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,CAC3C,GACC,CAAC,GAAG,SAAS,GAAG,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC;AAEjD,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,SAAS,CAAC;CACpB;AAED,UAAU,+BAA+B,CAAC,GAAG,SAAS,WAAW;IAC/D;;;;OAIG;IACH,EAAE,CAAC,EAAE,GAAG,CAAC;IACT,kEAAkE;IAClE,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC3B,4DAA4D;IAC5D,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC7B,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,SAAS,CAAC;CAC5C;AAED,MAAM,MAAM,uBAAuB,CAAC,GAAG,SAAS,WAAW,GAAG,GAAG,IAC/D,+BAA+B,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC;AAU/E;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,GAAG,SAAS,WAAW,GAAG,GAAG,EAAE,iFASnE,uBAAuB,CAAC,GAAG,CAAC,gCA2B9B,CAAC"}
@@ -0,0 +1,76 @@
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 = "text-fill-transparent w-fit";
8
+ var FORCED_COLORS_CSS = "forced-colors:bg-none forced-colors:[-webkit-text-fill-color:currentColor]";
9
+ var GRADIENT_CSS = {
10
+ accent: "bg-accent-duo",
11
+ silver: "bg-silver-duo"
12
+ };
13
+ var GradientText = /* @__PURE__ */ __name(({
14
+ as,
15
+ type,
16
+ text,
17
+ children,
18
+ className = "",
19
+ ...rest
20
+ }) => {
21
+ const Component = as ?? "span";
22
+ return /* @__PURE__ */ jsxRuntime.jsx(
23
+ Component,
24
+ {
25
+ ...rest,
26
+ className: `${BASE_CSS} ${GRADIENT_CSS[type]} ${FORCED_COLORS_CSS} ${className}`,
27
+ children: children ?? text
28
+ }
29
+ );
30
+ }, "GradientText");
31
+ var defaultMessage = /* @__PURE__ */ __name(({ terms, privacy }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
32
+ "By continuing, you agree to our ",
33
+ terms,
34
+ " and ",
35
+ privacy,
36
+ "."
37
+ ] }), "defaultMessage");
38
+ var LINK_CSS = "font-medium";
39
+ var AuthBottomInstructions = /* @__PURE__ */ __name(({
40
+ as,
41
+ terms,
42
+ privacy,
43
+ termsLabel = "Terms & Conditions",
44
+ privacyLabel = "Privacy Policy",
45
+ message = defaultMessage,
46
+ className = "",
47
+ ...pProps
48
+ }) => {
49
+ const Link = as ?? "a";
50
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { ...pProps, className: `text-tertiary text-xs ${className}`, children: message({
51
+ terms: /* @__PURE__ */ jsxRuntime.jsx(
52
+ GradientText,
53
+ {
54
+ ...terms,
55
+ as: Link,
56
+ type: "accent",
57
+ text: termsLabel,
58
+ className: LINK_CSS
59
+ }
60
+ ),
61
+ privacy: /* @__PURE__ */ jsxRuntime.jsx(
62
+ GradientText,
63
+ {
64
+ ...privacy,
65
+ as: Link,
66
+ type: "accent",
67
+ text: privacyLabel,
68
+ className: LINK_CSS
69
+ }
70
+ )
71
+ }) });
72
+ }, "AuthBottomInstructions");
73
+
74
+ exports.AuthBottomInstructions = AuthBottomInstructions;
75
+ //# sourceMappingURL=index.cjs.map
76
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/GradientText.tsx","../../src/auth/AuthBottomInstructions.tsx"],"names":["jsx","jsxs","Fragment"],"mappings":";;;;;;AAwBA,IAAM,QAAA,GAAW,6BAAA;AAGjB,IAAM,iBAAA,GACJ,4EAAA;AAEF,IAAM,YAAA,GAAkD;AAAA,EACtD,MAAA,EAAQ,eAAA;AAAA,EACR,MAAA,EAAQ;AACV,CAAA;AAOO,IAAM,+BAAe,MAAA,CAAA,CAAmC;AAAA,EAC7D,EAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,KAA0B;AACxB,EAAA,MAAM,YAAyB,EAAA,IAAM,MAAA;AAErC,EAAA,uBACEA,cAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACE,GAAG,IAAA;AAAA,MACJ,SAAA,EAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,YAAA,CAAa,IAAI,CAAC,CAAA,CAAA,EAAI,iBAAiB,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,MAE7E,QAAA,EAAA,QAAA,IAAY;AAAA;AAAA,GACf;AAEJ,CAAA,EAlB4B,cAAA,CAAA;ACK5B,IAAM,iCAAiB,MAAA,CAAA,CAAC,EAAE,KAAA,EAAO,OAAA,uBAC/BC,eAAA,CAAAC,mBAAA,EAAA,EAAE,QAAA,EAAA;AAAA,EAAA,kCAAA;AAAA,EACiC,KAAA;AAAA,EAAM,OAAA;AAAA,EAAM,OAAA;AAAA,EAAQ;AAAA,CAAA,EACvD,CAAA,EAHqB,gBAAA,CAAA;AAMvB,IAAM,QAAA,GAAW,aAAA;AAMV,IAAM,yCAAyB,MAAA,CAAA,CAAgC;AAAA,EACpE,EAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA,GAAa,oBAAA;AAAA,EACb,YAAA,GAAe,gBAAA;AAAA,EACf,OAAA,GAAU,cAAA;AAAA,EACV,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,KAAoC;AAClC,EAAA,MAAM,OAAoB,EAAA,IAAM,GAAA;AAEhC,EAAA,uBACEF,eAAC,GAAA,EAAA,EAAG,GAAG,QAAQ,SAAA,EAAW,CAAA,sBAAA,EAAyB,SAAS,CAAA,CAAA,EACzD,QAAA,EAAA,OAAA,CAAQ;AAAA,IACP,uBACEA,cAAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACE,GAAI,KAAA;AAAA,QACL,EAAA,EAAI,IAAA;AAAA,QACJ,IAAA,EAAK,QAAA;AAAA,QACL,IAAA,EAAM,UAAA;AAAA,QACN,SAAA,EAAW;AAAA;AAAA,KACb;AAAA,IAEF,yBACEA,cAAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACE,GAAI,OAAA;AAAA,QACL,EAAA,EAAI,IAAA;AAAA,QACJ,IAAA,EAAK,QAAA;AAAA,QACL,IAAA,EAAM,YAAA;AAAA,QACN,SAAA,EAAW;AAAA;AAAA;AACb,GAEH,CAAA,EACH,CAAA;AAEJ,CAAA,EApCsC,wBAAA","file":"index.cjs","sourcesContent":["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, ComponentPropsWithRef, ElementType, ReactNode } from 'react';\n\nimport { GradientText } from '../ui/GradientText.js';\n\n/**\n * What the link to a page needs, as the component that makes the link asks for it: `{ to: '/terms' }`\n * for the `Link` of a router, `{ href: '/terms' }` for a plain `<a>` (which has to have one).\n */\nexport type TAuthLinkProps<TAs extends ElementType> = Omit<\n ComponentPropsWithRef<TAs>,\n 'children' | 'className' | 'text' | 'type'\n> &\n (TAs extends 'a' ? { href: string } : unknown);\n\n/** The two links of the sentence, ready to be put in it. */\nexport interface IAuthLinks {\n terms: ReactNode;\n privacy: ReactNode;\n}\n\ninterface IAuthBottomInstructionsOwnProps<TAs extends ElementType> {\n /**\n * What makes the links: `'a'`, or the `Link` of the router of the app. The router is the app's,\n * not this package's.\n * @default 'a'\n */\n as?: TAs;\n /** The link to the terms and conditions, in the props of `as`. */\n terms: TAuthLinkProps<TAs>;\n /** The link to the privacy policy, in the props of `as`. */\n privacy: TAuthLinkProps<TAs>;\n /** @default 'Terms & Conditions' */\n termsLabel?: string;\n /** @default 'Privacy Policy' */\n privacyLabel?: string;\n /**\n * The sentence around the two links, for another wording or another language.\n * @default ({ terms, privacy }) => <>By continuing, you agree to our {terms} and {privacy}.</>\n */\n message?: (links: IAuthLinks) => ReactNode;\n}\n\nexport type IAuthBottomInstructions<TAs extends ElementType = 'a'> =\n IAuthBottomInstructionsOwnProps<TAs> & Omit<ComponentProps<'p'>, 'children'>;\n\nconst defaultMessage = ({ terms, privacy }: IAuthLinks) => (\n <>\n By continuing, you agree to our {terms} and {privacy}.\n </>\n);\n\nconst LINK_CSS = 'font-medium';\n\n/**\n * The small print under a sign-in or sign-up form: that going on means accepting the terms and the\n * privacy policy, with a link to each.\n */\nexport const AuthBottomInstructions = <TAs extends ElementType = 'a'>({\n as,\n terms,\n privacy,\n termsLabel = 'Terms & Conditions',\n privacyLabel = 'Privacy Policy',\n message = defaultMessage,\n className = '',\n ...pProps\n}: IAuthBottomInstructions<TAs>) => {\n const Link: ElementType = as ?? 'a';\n\n return (\n <p {...pProps} className={`text-tertiary text-xs ${className}`}>\n {message({\n terms: (\n <GradientText\n {...(terms as object)}\n as={Link}\n type=\"accent\"\n text={termsLabel}\n className={LINK_CSS}\n />\n ),\n privacy: (\n <GradientText\n {...(privacy as object)}\n as={Link}\n type=\"accent\"\n text={privacyLabel}\n className={LINK_CSS}\n />\n ),\n })}\n </p>\n );\n};\n"]}
@@ -0,0 +1,2 @@
1
+ export { AuthBottomInstructions, type IAuthBottomInstructions, type IAuthLinks, type TAuthLinkProps, } from './AuthBottomInstructions.js';
2
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { AuthBottomInstructions, type IAuthBottomInstructions, type IAuthLinks, type TAuthLinkProps, } from './AuthBottomInstructions.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { jsx, jsxs, Fragment } 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 = "text-fill-transparent w-fit";
6
+ var FORCED_COLORS_CSS = "forced-colors:bg-none forced-colors:[-webkit-text-fill-color:currentColor]";
7
+ var GRADIENT_CSS = {
8
+ accent: "bg-accent-duo",
9
+ silver: "bg-silver-duo"
10
+ };
11
+ var GradientText = /* @__PURE__ */ __name(({
12
+ as,
13
+ type,
14
+ text,
15
+ children,
16
+ className = "",
17
+ ...rest
18
+ }) => {
19
+ const Component = as ?? "span";
20
+ return /* @__PURE__ */ jsx(
21
+ Component,
22
+ {
23
+ ...rest,
24
+ className: `${BASE_CSS} ${GRADIENT_CSS[type]} ${FORCED_COLORS_CSS} ${className}`,
25
+ children: children ?? text
26
+ }
27
+ );
28
+ }, "GradientText");
29
+ var defaultMessage = /* @__PURE__ */ __name(({ terms, privacy }) => /* @__PURE__ */ jsxs(Fragment, { children: [
30
+ "By continuing, you agree to our ",
31
+ terms,
32
+ " and ",
33
+ privacy,
34
+ "."
35
+ ] }), "defaultMessage");
36
+ var LINK_CSS = "font-medium";
37
+ var AuthBottomInstructions = /* @__PURE__ */ __name(({
38
+ as,
39
+ terms,
40
+ privacy,
41
+ termsLabel = "Terms & Conditions",
42
+ privacyLabel = "Privacy Policy",
43
+ message = defaultMessage,
44
+ className = "",
45
+ ...pProps
46
+ }) => {
47
+ const Link = as ?? "a";
48
+ return /* @__PURE__ */ jsx("p", { ...pProps, className: `text-tertiary text-xs ${className}`, children: message({
49
+ terms: /* @__PURE__ */ jsx(
50
+ GradientText,
51
+ {
52
+ ...terms,
53
+ as: Link,
54
+ type: "accent",
55
+ text: termsLabel,
56
+ className: LINK_CSS
57
+ }
58
+ ),
59
+ privacy: /* @__PURE__ */ jsx(
60
+ GradientText,
61
+ {
62
+ ...privacy,
63
+ as: Link,
64
+ type: "accent",
65
+ text: privacyLabel,
66
+ className: LINK_CSS
67
+ }
68
+ )
69
+ }) });
70
+ }, "AuthBottomInstructions");
71
+
72
+ export { AuthBottomInstructions };
73
+ //# sourceMappingURL=index.js.map
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/GradientText.tsx","../../src/auth/AuthBottomInstructions.tsx"],"names":["jsx"],"mappings":";;;;AAwBA,IAAM,QAAA,GAAW,6BAAA;AAGjB,IAAM,iBAAA,GACJ,4EAAA;AAEF,IAAM,YAAA,GAAkD;AAAA,EACtD,MAAA,EAAQ,eAAA;AAAA,EACR,MAAA,EAAQ;AACV,CAAA;AAOO,IAAM,+BAAe,MAAA,CAAA,CAAmC;AAAA,EAC7D,EAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,KAA0B;AACxB,EAAA,MAAM,YAAyB,EAAA,IAAM,MAAA;AAErC,EAAA,uBACE,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACE,GAAG,IAAA;AAAA,MACJ,SAAA,EAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,YAAA,CAAa,IAAI,CAAC,CAAA,CAAA,EAAI,iBAAiB,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAAA,MAE7E,QAAA,EAAA,QAAA,IAAY;AAAA;AAAA,GACf;AAEJ,CAAA,EAlB4B,cAAA,CAAA;ACK5B,IAAM,iCAAiB,MAAA,CAAA,CAAC,EAAE,KAAA,EAAO,OAAA,uBAC/B,IAAA,CAAA,QAAA,EAAA,EAAE,QAAA,EAAA;AAAA,EAAA,kCAAA;AAAA,EACiC,KAAA;AAAA,EAAM,OAAA;AAAA,EAAM,OAAA;AAAA,EAAQ;AAAA,CAAA,EACvD,CAAA,EAHqB,gBAAA,CAAA;AAMvB,IAAM,QAAA,GAAW,aAAA;AAMV,IAAM,yCAAyB,MAAA,CAAA,CAAgC;AAAA,EACpE,EAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA,GAAa,oBAAA;AAAA,EACb,YAAA,GAAe,gBAAA;AAAA,EACf,OAAA,GAAU,cAAA;AAAA,EACV,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,KAAoC;AAClC,EAAA,MAAM,OAAoB,EAAA,IAAM,GAAA;AAEhC,EAAA,uBACEA,IAAC,GAAA,EAAA,EAAG,GAAG,QAAQ,SAAA,EAAW,CAAA,sBAAA,EAAyB,SAAS,CAAA,CAAA,EACzD,QAAA,EAAA,OAAA,CAAQ;AAAA,IACP,uBACEA,GAAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACE,GAAI,KAAA;AAAA,QACL,EAAA,EAAI,IAAA;AAAA,QACJ,IAAA,EAAK,QAAA;AAAA,QACL,IAAA,EAAM,UAAA;AAAA,QACN,SAAA,EAAW;AAAA;AAAA,KACb;AAAA,IAEF,yBACEA,GAAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACE,GAAI,OAAA;AAAA,QACL,EAAA,EAAI,IAAA;AAAA,QACJ,IAAA,EAAK,QAAA;AAAA,QACL,IAAA,EAAM,YAAA;AAAA,QACN,SAAA,EAAW;AAAA;AAAA;AACb,GAEH,CAAA,EACH,CAAA;AAEJ,CAAA,EApCsC,wBAAA","file":"index.js","sourcesContent":["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, ComponentPropsWithRef, ElementType, ReactNode } from 'react';\n\nimport { GradientText } from '../ui/GradientText.js';\n\n/**\n * What the link to a page needs, as the component that makes the link asks for it: `{ to: '/terms' }`\n * for the `Link` of a router, `{ href: '/terms' }` for a plain `<a>` (which has to have one).\n */\nexport type TAuthLinkProps<TAs extends ElementType> = Omit<\n ComponentPropsWithRef<TAs>,\n 'children' | 'className' | 'text' | 'type'\n> &\n (TAs extends 'a' ? { href: string } : unknown);\n\n/** The two links of the sentence, ready to be put in it. */\nexport interface IAuthLinks {\n terms: ReactNode;\n privacy: ReactNode;\n}\n\ninterface IAuthBottomInstructionsOwnProps<TAs extends ElementType> {\n /**\n * What makes the links: `'a'`, or the `Link` of the router of the app. The router is the app's,\n * not this package's.\n * @default 'a'\n */\n as?: TAs;\n /** The link to the terms and conditions, in the props of `as`. */\n terms: TAuthLinkProps<TAs>;\n /** The link to the privacy policy, in the props of `as`. */\n privacy: TAuthLinkProps<TAs>;\n /** @default 'Terms & Conditions' */\n termsLabel?: string;\n /** @default 'Privacy Policy' */\n privacyLabel?: string;\n /**\n * The sentence around the two links, for another wording or another language.\n * @default ({ terms, privacy }) => <>By continuing, you agree to our {terms} and {privacy}.</>\n */\n message?: (links: IAuthLinks) => ReactNode;\n}\n\nexport type IAuthBottomInstructions<TAs extends ElementType = 'a'> =\n IAuthBottomInstructionsOwnProps<TAs> & Omit<ComponentProps<'p'>, 'children'>;\n\nconst defaultMessage = ({ terms, privacy }: IAuthLinks) => (\n <>\n By continuing, you agree to our {terms} and {privacy}.\n </>\n);\n\nconst LINK_CSS = 'font-medium';\n\n/**\n * The small print under a sign-in or sign-up form: that going on means accepting the terms and the\n * privacy policy, with a link to each.\n */\nexport const AuthBottomInstructions = <TAs extends ElementType = 'a'>({\n as,\n terms,\n privacy,\n termsLabel = 'Terms & Conditions',\n privacyLabel = 'Privacy Policy',\n message = defaultMessage,\n className = '',\n ...pProps\n}: IAuthBottomInstructions<TAs>) => {\n const Link: ElementType = as ?? 'a';\n\n return (\n <p {...pProps} className={`text-tertiary text-xs ${className}`}>\n {message({\n terms: (\n <GradientText\n {...(terms as object)}\n as={Link}\n type=\"accent\"\n text={termsLabel}\n className={LINK_CSS}\n />\n ),\n privacy: (\n <GradientText\n {...(privacy as object)}\n as={Link}\n type=\"accent\"\n text={privacyLabel}\n className={LINK_CSS}\n />\n ),\n })}\n </p>\n );\n};\n"]}
package/dist/index.cjs CHANGED
@@ -1,10 +1,24 @@
1
1
  'use strict';
2
2
 
3
+ var layout = require('@beautinique/frontend-components/layout');
4
+ var theme = require('@beautinique/frontend-components/theme');
3
5
  var toast = require('@beautinique/frontend-components/toast');
4
6
  var ui = require('@beautinique/frontend-components/ui');
5
7
 
6
8
 
7
9
 
10
+ Object.keys(layout).forEach(function (k) {
11
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
12
+ enumerable: true,
13
+ get: function () { return layout[k]; }
14
+ });
15
+ });
16
+ Object.keys(theme).forEach(function (k) {
17
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
18
+ enumerable: true,
19
+ get: function () { return theme[k]; }
20
+ });
21
+ });
8
22
  Object.keys(toast).forEach(function (k) {
9
23
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
10
24
  enumerable: true,
package/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ export * from './layout/index.js';
2
+ export * from './theme/index.js';
1
3
  export * from './toast/index.js';
2
4
  export type * from './types/component.js';
3
5
  export * from './ui/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,kBAAkB,CAAC;AACjC,mBAAmB,sBAAsB,CAAC;AAC1C,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,mBAAmB,sBAAsB,CAAC;AAC1C,cAAc,eAAe,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export * from './layout/index.js';
2
+ export * from './theme/index.js';
1
3
  export * from './toast/index.js';
2
4
  export type * from './types/component.js';
3
5
  export * from './ui/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,kBAAkB,CAAC;AACjC,mBAAmB,sBAAsB,CAAC;AAC1C,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,mBAAmB,sBAAsB,CAAC;AAC1C,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ export * from '@beautinique/frontend-components/layout';
2
+ export * from '@beautinique/frontend-components/theme';
1
3
  export * from '@beautinique/frontend-components/toast';
2
4
  export * from '@beautinique/frontend-components/ui';
3
5
  //# sourceMappingURL=index.js.map
@@ -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.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollToTop.d.cts","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"}