compote-ui 0.72.1 → 0.74.1
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.
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
form,
|
|
8
8
|
field,
|
|
9
9
|
helperText,
|
|
10
|
+
errorText,
|
|
11
|
+
hideMessageLine = false,
|
|
10
12
|
class: className,
|
|
11
13
|
invalid,
|
|
12
14
|
required,
|
|
@@ -14,9 +16,15 @@
|
|
|
14
16
|
...rest
|
|
15
17
|
}: FieldRootProps = $props();
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
// Declared before isInvalid, which reads it.
|
|
20
|
+
const resolvedError = $derived(
|
|
21
|
+
form && field ? (form.errors[field]?.[0] ?? null) : (errorText ?? null)
|
|
22
|
+
);
|
|
23
|
+
// An error that does not also mark the field invalid renders nothing at all:
|
|
24
|
+
// Ark's Field.ErrorText is gated on the field context's `invalid`. So a bare
|
|
25
|
+
// `errorText` implies it, while an explicit `invalid` still wins.
|
|
26
|
+
const isInvalid = $derived(form && field ? form.invalid(field) : (invalid ?? !!resolvedError));
|
|
18
27
|
const isRequired = $derived(form && field ? form.isRequired(field) : (required ?? false));
|
|
19
|
-
const errorText = $derived(form && field ? (form.errors[field]?.[0] ?? null) : null);
|
|
20
28
|
</script>
|
|
21
29
|
|
|
22
30
|
<Field.Root
|
|
@@ -26,9 +34,19 @@
|
|
|
26
34
|
class={cn('group flex flex-col gap-1.5', className)}
|
|
27
35
|
>
|
|
28
36
|
{@render children?.()}
|
|
29
|
-
{#if
|
|
30
|
-
<Field.ErrorText>{
|
|
37
|
+
{#if resolvedError}
|
|
38
|
+
<Field.ErrorText>{resolvedError}</Field.ErrorText>
|
|
31
39
|
{:else if helperText}
|
|
32
40
|
<Field.HelperText>{helperText}</Field.HelperText>
|
|
41
|
+
{:else if !hideMessageLine}
|
|
42
|
+
<!--
|
|
43
|
+
Holds the message line open so a field does not change height when an
|
|
44
|
+
error appears. `1lh` resolves against this element's own line-height, so
|
|
45
|
+
it tracks the theme's font rather than assuming a pixel height. A plain
|
|
46
|
+
div and not an empty Field.HelperText: Ark gives that one an id which
|
|
47
|
+
aria-describedby points at, which would describe the control as having a
|
|
48
|
+
description that isn't there.
|
|
49
|
+
-->
|
|
50
|
+
<div class="h-lh text-xs" aria-hidden="true"></div>
|
|
33
51
|
{/if}
|
|
34
52
|
</Field.Root>
|
|
@@ -11,6 +11,21 @@ export interface FieldRootProps extends FieldRootBaseProps {
|
|
|
11
11
|
form?: FormAdapter;
|
|
12
12
|
field?: string;
|
|
13
13
|
helperText?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Error to render below the control, for validation that does not go through
|
|
16
|
+
* a {@link FormAdapter} (e.g. a SvelteKit remote form's `field.issues()`).
|
|
17
|
+
* Passing it also marks the field invalid unless `invalid` is set explicitly.
|
|
18
|
+
* Ignored when `form` and `field` are given — the adapter wins, as it does
|
|
19
|
+
* for `invalid` and `required`.
|
|
20
|
+
*/
|
|
21
|
+
errorText?: string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to drop the spacer that otherwise holds the message line open
|
|
24
|
+
* when there is no error or helper text to show. Defaults to `false`. Set
|
|
25
|
+
* to `true` for a field that never surfaces validation (a toolbar search
|
|
26
|
+
* box, say), so it doesn't reserve height it will never use.
|
|
27
|
+
*/
|
|
28
|
+
hideMessageLine?: boolean;
|
|
14
29
|
}
|
|
15
30
|
export interface FieldLabelProps extends FieldLabelBaseProps {
|
|
16
31
|
class?: ClassValue;
|
package/package.json
CHANGED
|
@@ -19,6 +19,32 @@ With a form adapter, `Field.Root` derives invalid/required state and renders the
|
|
|
19
19
|
</Field.Root>
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
`errorText` is the per-field route to that same line, for validation that doesn't fit the adapter's
|
|
23
|
+
`Record<string, string[]>` shape — a SvelteKit remote form's `field.issues()`, say. **Passing it
|
|
24
|
+
marks the field invalid**; an explicit `invalid` still wins, and the adapter wins over both.
|
|
25
|
+
|
|
26
|
+
```svelte
|
|
27
|
+
<Field.Root required errorText={fields.password.issues()?.[0]?.message}>
|
|
28
|
+
<PasswordInput label="Password" name={fields.password.as('password').name} />
|
|
29
|
+
</Field.Root>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
One message, not a list: several errors on one value are usually stages of the same judgement, and a
|
|
33
|
+
stack that grows and shrinks per keystroke is what makes a form jump. Independent requirements (a
|
|
34
|
+
password policy) belong in a persistent checklist shown from the start, not in error text. Where you
|
|
35
|
+
genuinely need several, `<Field.ErrorText>` children still work.
|
|
36
|
+
|
|
37
|
+
The message line is **always held open**, so a field never changes height when an error appears and
|
|
38
|
+
nothing below it moves. Errors and `helperText` share that line — an error replaces the helper
|
|
39
|
+
rather than stacking under it. For a field that never surfaces validation (a toolbar search box,
|
|
40
|
+
say), pass `hideMessageLine` to drop the spacer entirely:
|
|
41
|
+
|
|
42
|
+
```svelte
|
|
43
|
+
<Field.Root hideMessageLine>
|
|
44
|
+
<Field.Input bind:value={search} placeholder="Search..." />
|
|
45
|
+
</Field.Root>
|
|
46
|
+
```
|
|
47
|
+
|
|
22
48
|
List controls use `{ value, label }` items:
|
|
23
49
|
|
|
24
50
|
```svelte
|