@marianmeres/stuic 3.159.0 → 3.161.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/RegisterForm/README.md +9 -1
- package/dist/components/RegisterForm/RegisterForm.svelte +59 -22
- package/dist/components/RegisterForm/RegisterForm.svelte.d.ts +16 -0
- package/dist/components/RegisterForm/RegisterFormModal.svelte +5 -0
- package/dist/components/RegisterForm/RegisterFormModal.svelte.d.ts +2 -0
- package/dist/components/RegisterForm/index.css +17 -0
- package/docs/domains/components.md +24 -23
- package/package.json +1 -1
|
@@ -58,6 +58,7 @@ interface RegisterFieldConfig {
|
|
|
58
58
|
| `passwordConfirmFieldProps` | `Partial<FieldInputProps>` | - | Passthrough props for the built-in confirm field. |
|
|
59
59
|
| `extraFields` | `RegisterFieldConfig[]` | `[]` | Declarative extra fields. Rendered as `FieldInput`s positioned top or bottom. |
|
|
60
60
|
| `extraFieldsSlot` | `Snippet<[{ formData, fieldError }]>` | - | Escape hatch for non-FieldInput extras. Rendered after declarative bottom fields. |
|
|
61
|
+
| `topFieldsSeparator` | `boolean` | `true` | Section rule closing the top-position extra-field group. Drawn whenever at least one such field exists, whatever follows it. `false` opts out. |
|
|
61
62
|
| `submitLabel` | `string` | i18n | Override the CTA label. |
|
|
62
63
|
| `submittingLabel` | `string` | i18n | Override the CTA label while submitting. |
|
|
63
64
|
| `submitButton` | `Snippet<[{ isSubmitting, disabled }]>` | - | Override the entire CTA section. `disabled` is `isSubmitting \|\| submitDisabled`. |
|
|
@@ -82,7 +83,7 @@ interface RegisterFieldConfig {
|
|
|
82
83
|
|
|
83
84
|
```
|
|
84
85
|
general error alert (`error`)
|
|
85
|
-
top-position extra fields
|
|
86
|
+
top-position extra fields ← wrapped as a group, optionally closed with a section rule
|
|
86
87
|
social block ← only when socialPosition="top" (divider BELOW the buttons)
|
|
87
88
|
email / password / confirm ← each present only if its show* prop is true
|
|
88
89
|
credentialsSlot
|
|
@@ -214,6 +215,8 @@ Once an external party has confirmed who the user is, the credential fields are
|
|
|
214
215
|
|
|
215
216
|
Call `form.focusField("tenant_id")` right after the provider confirms: the button the user clicked is about to unmount, and without an explicit move focus falls to `<body>` — a keyboard user's next Tab restarts at the top of the document.
|
|
216
217
|
|
|
218
|
+
Those top-position fields are closed off with a section rule. At the form's ordinary field rhythm the last of them sits one field-gap above the first provider button and reads as a caption for it; the break says instead that the workspace id is settled and what follows is the account. The rule is not conditioned on what comes next — provider buttons, a `credentialsSlot`, or the plain credentials all get it — so it does not blink in and out as this flow moves from "pick a provider" to "signing up as jane@…", and a reader of the call site does not have to work out which combination of other props switched it on. `topFieldsSeparator={false}` opts out.
|
|
219
|
+
|
|
217
220
|
Other shapes the same three props cover:
|
|
218
221
|
|
|
219
222
|
| Flow | Props |
|
|
@@ -299,11 +302,16 @@ Prefix: `--stuic-register-form-*`
|
|
|
299
302
|
| `--stuic-register-form-social-divider-margin-top` | Divider top margin (`socialPosition="top"`) |
|
|
300
303
|
| `--stuic-register-form-credentials-margin-bottom` | Bottom margin of the `credentialsSlot` wrapper |
|
|
301
304
|
| `--stuic-register-form-field-margin-bottom` | Bottom margin of each field inside the form |
|
|
305
|
+
| `--stuic-register-form-fields-top-padding-bottom` | Space added above the top-fields section rule |
|
|
306
|
+
| `--stuic-register-form-fields-top-margin-bottom` | Space below the top-fields section rule |
|
|
307
|
+
| `--stuic-register-form-fields-top-border-color` | Color of the top-fields section rule |
|
|
302
308
|
|
|
303
309
|
The social block carries `data-position="top" \| "bottom"` (suppressed under `unstyled`) if you want to target either placement from your own CSS. Note that the `top` variant hard-resets `margin-top` to `0`, so `--stuic-register-form-social-margin-top` applies to the default position only.
|
|
304
310
|
|
|
305
311
|
`credentialsSlot` content is wrapped in `.stuic-register-form-credentials` (suppressed under `unstyled`) so it inherits the same bottom rhythm the fields have — the form itself is a zero-gap flex column.
|
|
306
312
|
|
|
313
|
+
Top-position extra fields are wrapped in `.stuic-register-form-fields-top` (suppressed under `unstyled`), which carries `data-separator` unless `topFieldsSeparator={false}`. The wrapper is otherwise inert — the fields keep their own margins — so targeting `[data-separator]` is the way to restyle the break without touching the ungrouped case. The default padding stacks on top of the last field's own `margin-bottom`, which is why the two spacing tokens are not equal.
|
|
314
|
+
|
|
307
315
|
## Gotchas
|
|
308
316
|
|
|
309
317
|
- **Empty social slot.** `socialLogins` is gated on the _snippet being passed_, not on it rendering anything. A consumer that passes a placeholder snippet while it discovers which providers exist gets the wrapper margins and a divider around nothing — pass `socialLogins={undefined}` until you know, or reserve the height inside the snippet and pass `socialDividerLabel={false}`.
|
|
@@ -124,6 +124,23 @@
|
|
|
124
124
|
]
|
|
125
125
|
>;
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Close the top-position extra fields with a section break — a hairline
|
|
129
|
+
* rule plus extra space below the group.
|
|
130
|
+
*
|
|
131
|
+
* A field is at the top position because it is about something other than
|
|
132
|
+
* the credentials (a workspace id, an invite code, an org name), so the
|
|
133
|
+
* group is drawn as its own section. Without the break the last of them
|
|
134
|
+
* sits at the ordinary field gap above whatever comes next and reads as a
|
|
135
|
+
* caption for it — a label over the first provider button, or just another
|
|
136
|
+
* row of the credential column.
|
|
137
|
+
*
|
|
138
|
+
* Default: true whenever there is at least one top-position field,
|
|
139
|
+
* regardless of what follows. Set `false` for a form whose top fields
|
|
140
|
+
* really do belong to the same column as the credentials.
|
|
141
|
+
*/
|
|
142
|
+
topFieldsSeparator?: boolean;
|
|
143
|
+
|
|
127
144
|
/** Override CTA label */
|
|
128
145
|
submitLabel?: string;
|
|
129
146
|
|
|
@@ -217,6 +234,7 @@
|
|
|
217
234
|
passwordConfirmFieldProps,
|
|
218
235
|
extraFields = [],
|
|
219
236
|
extraFieldsSlot,
|
|
237
|
+
topFieldsSeparator,
|
|
220
238
|
submitLabel,
|
|
221
239
|
submittingLabel,
|
|
222
240
|
submitButton,
|
|
@@ -249,6 +267,16 @@
|
|
|
249
267
|
// validated against it).
|
|
250
268
|
let renderPasswordConfirm = $derived(showPassword && showPasswordConfirm);
|
|
251
269
|
|
|
270
|
+
// A field at the top position is there because it is about something other
|
|
271
|
+
// than the credentials — the workspace, the invite, the org — so it is closed
|
|
272
|
+
// off as its own section whichever way the sign-up continues below it.
|
|
273
|
+
// Deliberately NOT conditioned on what follows (provider buttons, a
|
|
274
|
+
// credentialsSlot, plain inputs): the last top field otherwise sits one field
|
|
275
|
+
// gap above the next thing and reads as a caption for it either way, and a
|
|
276
|
+
// rule that comes and goes with an unrelated prop is not explicable from the
|
|
277
|
+
// call site. `topFieldsSeparator={false}` opts out.
|
|
278
|
+
let renderTopFieldsSeparator = $derived(topFieldsSeparator ?? true);
|
|
279
|
+
|
|
252
280
|
// Internal validation errors (set on submit)
|
|
253
281
|
let internalErrors = $state<RegisterFormValidationError[]>([]);
|
|
254
282
|
|
|
@@ -511,28 +539,37 @@
|
|
|
511
539
|
<!-- General error alert -->
|
|
512
540
|
<DismissibleMessage message={error} intent="destructive" />
|
|
513
541
|
|
|
514
|
-
<!-- Top-position extra fields
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
542
|
+
<!-- Top-position extra fields. Wrapped as a group so `topFieldsSeparator` can
|
|
543
|
+
close it off from the sign-up choice below — the wrapper is inert without
|
|
544
|
+
`data-separator`, the fields keep their own rhythm. -->
|
|
545
|
+
{#if topFields.length}
|
|
546
|
+
<div
|
|
547
|
+
class={unstyled ? undefined : "stuic-register-form-fields-top"}
|
|
548
|
+
data-separator={unstyled || !renderTopFieldsSeparator ? undefined : ""}
|
|
549
|
+
>
|
|
550
|
+
{#each topFields as cfg (cfg.name)}
|
|
551
|
+
<FieldInput
|
|
552
|
+
bind:this={extraFieldRefs[cfg.name]}
|
|
553
|
+
value={extraValue(cfg)}
|
|
554
|
+
oninput={(e: Event) =>
|
|
555
|
+
setExtraValue(cfg, (e.currentTarget as HTMLInputElement).value)}
|
|
556
|
+
label={cfg.label}
|
|
557
|
+
type={cfg.type ?? "text"}
|
|
558
|
+
placeholder={cfg.placeholder}
|
|
559
|
+
autocomplete={cfg.autocomplete}
|
|
560
|
+
required={cfg.required}
|
|
561
|
+
name={`register-extra-${cfg.name}`}
|
|
562
|
+
labelLeftBreakpoint={0}
|
|
563
|
+
validate={{
|
|
564
|
+
customValidator() {
|
|
565
|
+
return fieldError(cfg.name) || "";
|
|
566
|
+
},
|
|
567
|
+
}}
|
|
568
|
+
{...cfg.props}
|
|
569
|
+
/>
|
|
570
|
+
{/each}
|
|
571
|
+
</div>
|
|
572
|
+
{/if}
|
|
536
573
|
|
|
537
574
|
<!-- Social logins above the credentials -->
|
|
538
575
|
{#if socialPosition === "top"}
|
|
@@ -97,6 +97,22 @@ export interface Props extends Omit<HTMLAttributes<HTMLFormElement>, "children">
|
|
|
97
97
|
fieldError: (name: string) => string | undefined;
|
|
98
98
|
}
|
|
99
99
|
]>;
|
|
100
|
+
/**
|
|
101
|
+
* Close the top-position extra fields with a section break — a hairline
|
|
102
|
+
* rule plus extra space below the group.
|
|
103
|
+
*
|
|
104
|
+
* A field is at the top position because it is about something other than
|
|
105
|
+
* the credentials (a workspace id, an invite code, an org name), so the
|
|
106
|
+
* group is drawn as its own section. Without the break the last of them
|
|
107
|
+
* sits at the ordinary field gap above whatever comes next and reads as a
|
|
108
|
+
* caption for it — a label over the first provider button, or just another
|
|
109
|
+
* row of the credential column.
|
|
110
|
+
*
|
|
111
|
+
* Default: true whenever there is at least one top-position field,
|
|
112
|
+
* regardless of what follows. Set `false` for a form whose top fields
|
|
113
|
+
* really do belong to the same column as the credentials.
|
|
114
|
+
*/
|
|
115
|
+
topFieldsSeparator?: boolean;
|
|
100
116
|
/** Override CTA label */
|
|
101
117
|
submitLabel?: string;
|
|
102
118
|
/** Override CTA label while submitting */
|
|
@@ -75,6 +75,9 @@
|
|
|
75
75
|
]
|
|
76
76
|
>;
|
|
77
77
|
|
|
78
|
+
/** Section break below the top-position extra fields. Default: true. */
|
|
79
|
+
topFieldsSeparator?: InnerProps["topFieldsSeparator"];
|
|
80
|
+
|
|
78
81
|
/** Override CTA label */
|
|
79
82
|
submitLabel?: string;
|
|
80
83
|
|
|
@@ -164,6 +167,7 @@
|
|
|
164
167
|
passwordConfirmFieldProps,
|
|
165
168
|
extraFields,
|
|
166
169
|
extraFieldsSlot,
|
|
170
|
+
topFieldsSeparator,
|
|
167
171
|
submitLabel,
|
|
168
172
|
submittingLabel,
|
|
169
173
|
submitButton,
|
|
@@ -273,6 +277,7 @@
|
|
|
273
277
|
{passwordConfirmFieldProps}
|
|
274
278
|
{extraFields}
|
|
275
279
|
{extraFieldsSlot}
|
|
280
|
+
{topFieldsSeparator}
|
|
276
281
|
{submitLabel}
|
|
277
282
|
{submittingLabel}
|
|
278
283
|
{submitButton}
|
|
@@ -51,6 +51,8 @@ export interface Props {
|
|
|
51
51
|
fieldError: (name: string) => string | undefined;
|
|
52
52
|
}
|
|
53
53
|
]>;
|
|
54
|
+
/** Section break below the top-position extra fields. Default: true. */
|
|
55
|
+
topFieldsSeparator?: InnerProps["topFieldsSeparator"];
|
|
54
56
|
/** Override CTA label */
|
|
55
57
|
submitLabel?: string;
|
|
56
58
|
/** Override CTA label while submitting */
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
/* credentialsSlot wrapper + the per-field rhythm inside the form */
|
|
19
19
|
--stuic-register-form-credentials-margin-bottom: 1rem;
|
|
20
20
|
--stuic-register-form-field-margin-bottom: 1rem;
|
|
21
|
+
|
|
22
|
+
/* Section break below the top-position extra fields (`topFieldsSeparator`).
|
|
23
|
+
The padding stacks on top of the last field's own margin-bottom, so the
|
|
24
|
+
default reads as 1.5rem of air on either side of the rule. */
|
|
25
|
+
--stuic-register-form-fields-top-padding-bottom: 0.5rem;
|
|
26
|
+
--stuic-register-form-fields-top-margin-bottom: 1.5rem;
|
|
27
|
+
--stuic-register-form-fields-top-border-color: var(--stuic-color-border);
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
@layer components {
|
|
@@ -39,6 +46,16 @@
|
|
|
39
46
|
margin-bottom: var(--stuic-register-form-credentials-margin-bottom);
|
|
40
47
|
}
|
|
41
48
|
|
|
49
|
+
/* Top-position extra fields. `data-separator` is on unless the consumer opts
|
|
50
|
+
out, so the group reads as its own section rather than as the first rows of
|
|
51
|
+
the credential column; the wrapper is inert without it, leaving the fields
|
|
52
|
+
their own margins. */
|
|
53
|
+
.stuic-register-form-fields-top[data-separator] {
|
|
54
|
+
padding-bottom: var(--stuic-register-form-fields-top-padding-bottom);
|
|
55
|
+
margin-bottom: var(--stuic-register-form-fields-top-margin-bottom);
|
|
56
|
+
border-bottom: 1px solid var(--stuic-register-form-fields-top-border-color);
|
|
57
|
+
}
|
|
58
|
+
|
|
42
59
|
/* Social login container */
|
|
43
60
|
.stuic-register-form-social {
|
|
44
61
|
margin-top: var(--stuic-register-form-social-margin-top);
|
|
@@ -300,7 +300,7 @@ Prefix: `--stuic-login-form-*`
|
|
|
300
300
|
|
|
301
301
|
Standalone registration form. Mirrors `LoginForm` conventions: `formData`, `onSubmit`, validation, errors, i18n, notifications, social-logins. Adds declarative `extraFields` (top/bottom positioning, custom validators) and an `extraFieldsSlot` escape hatch (e.g., terms checkbox).
|
|
302
302
|
|
|
303
|
-
Also covers **identity-first signup** (identity established by an OAuth provider / invite / magic link before the account exists): `showEmail` / `showPassword` unmount the credential fields, `credentialsSlot` replaces them, `socialPosition="top"` moves the provider buttons above the credentials.
|
|
303
|
+
Also covers **identity-first signup** (identity established by an OAuth provider / invite / magic link before the account exists): `showEmail` / `showPassword` unmount the credential fields, `credentialsSlot` replaces them, `socialPosition="top"` moves the provider buttons above the credentials, and the top-position extra fields (workspace id, invite code — required on every path) are closed off with a section rule so they don't read as a caption for the first provider button (`topFieldsSeparator`, on by default whenever such a field exists).
|
|
304
304
|
|
|
305
305
|
### Exports
|
|
306
306
|
|
|
@@ -317,28 +317,29 @@ Also covers **identity-first signup** (identity established by an OAuth provider
|
|
|
317
317
|
|
|
318
318
|
### Key Props
|
|
319
319
|
|
|
320
|
-
| Prop | Type | Default | Description
|
|
321
|
-
| ---------------------------------------------------------------------- | ------------------------------- | ---------- |
|
|
322
|
-
| `formData` | `RegisterFormData` | empty | Bindable form data
|
|
323
|
-
| `onSubmit` | `(data) => void` | required | Submit callback
|
|
324
|
-
| `isSubmitting` | `boolean` | `false` | Disables CTA
|
|
325
|
-
| `submitDisabled` | `boolean` | `false` | Consumer-owned block: disables CTA + blocks submit
|
|
326
|
-
| `errors` | `RegisterFormValidationError[]` | `[]` | Server field errors — self-clearing (see below)
|
|
327
|
-
| `error` | `string` | — | General error (alert above form)
|
|
328
|
-
| `showEmail` | `boolean` | `true` | Render (mount) the email field
|
|
329
|
-
| `showPassword` | `boolean` | `true` | Render (mount) the password + confirm fields
|
|
330
|
-
| `showPasswordConfirm` | `boolean` | `true` | Render password-confirm field
|
|
331
|
-
| `passwordMinLength` | `number` | `8` | Min password length (input + validator)
|
|
332
|
-
| `credentialsSlot` | `Snippet` | — | Content at the credentials position
|
|
333
|
-
| `emailFieldProps` / `passwordFieldProps` / `passwordConfirmFieldProps` | `Partial<FieldInputProps>` | — | Passthrough props per core field (`validate` composed, `value` ignored)
|
|
334
|
-
| `extraFields` | `RegisterFieldConfig[]` | `[]` | Declarative extra fields (top/bottom)
|
|
335
|
-
| `extraFieldsSlot` | `Snippet` | — | Escape-hatch for non-FieldInput extras
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
338
|
-
| `
|
|
339
|
-
| `
|
|
340
|
-
| `
|
|
341
|
-
| `
|
|
320
|
+
| Prop | Type | Default | Description |
|
|
321
|
+
| ---------------------------------------------------------------------- | ------------------------------- | ---------- | ------------------------------------------------------------------------- |
|
|
322
|
+
| `formData` | `RegisterFormData` | empty | Bindable form data |
|
|
323
|
+
| `onSubmit` | `(data) => void` | required | Submit callback |
|
|
324
|
+
| `isSubmitting` | `boolean` | `false` | Disables CTA |
|
|
325
|
+
| `submitDisabled` | `boolean` | `false` | Consumer-owned block: disables CTA + blocks submit |
|
|
326
|
+
| `errors` | `RegisterFormValidationError[]` | `[]` | Server field errors — self-clearing (see below) |
|
|
327
|
+
| `error` | `string` | — | General error (alert above form) |
|
|
328
|
+
| `showEmail` | `boolean` | `true` | Render (mount) the email field |
|
|
329
|
+
| `showPassword` | `boolean` | `true` | Render (mount) the password + confirm fields |
|
|
330
|
+
| `showPasswordConfirm` | `boolean` | `true` | Render password-confirm field |
|
|
331
|
+
| `passwordMinLength` | `number` | `8` | Min password length (input + validator) |
|
|
332
|
+
| `credentialsSlot` | `Snippet` | — | Content at the credentials position |
|
|
333
|
+
| `emailFieldProps` / `passwordFieldProps` / `passwordConfirmFieldProps` | `Partial<FieldInputProps>` | — | Passthrough props per core field (`validate` composed, `value` ignored) |
|
|
334
|
+
| `extraFields` | `RegisterFieldConfig[]` | `[]` | Declarative extra fields (top/bottom) |
|
|
335
|
+
| `extraFieldsSlot` | `Snippet` | — | Escape-hatch for non-FieldInput extras |
|
|
336
|
+
| `topFieldsSeparator` | `boolean` | `true` | Section rule closing the top-position extra-field group; `false` opts out |
|
|
337
|
+
| `submitButton` | `Snippet` | — | Custom CTA section |
|
|
338
|
+
| `socialLogins` | `Snippet` | — | OAuth buttons |
|
|
339
|
+
| `socialPosition` | `"top" \| "bottom"` | `"bottom"` | Social block above the credentials or after the CTA |
|
|
340
|
+
| `footer` | `Snippet` | — | Content below form |
|
|
341
|
+
| `notifications` | `NotificationsStack` | — | Route errors to notifications |
|
|
342
|
+
| `t` | `TranslateFn` | built-in | Translation function |
|
|
342
343
|
|
|
343
344
|
**Imperative** (`bind:this`): `validate()`, `scrollToFirstError(opts?)`, `focusField(name)` — also forwarded by `RegisterFormModal`.
|
|
344
345
|
|