compote-ui 0.72.0 → 0.74.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.
@@ -25,6 +25,7 @@
25
25
  locale: localeProp,
26
26
  granularity,
27
27
  hourCycle,
28
+ hideTimeZone = true,
28
29
  onValueChange
29
30
  }: DateFieldProps = $props();
30
31
 
@@ -89,6 +90,7 @@
89
90
  locale: resolvedLocale,
90
91
  granularity: granularity ?? 'day',
91
92
  hourCycle,
93
+ hideTimeZone,
92
94
  value: datePicker().value,
93
95
  min,
94
96
  max,
@@ -18,4 +18,10 @@ export interface DateFieldProps extends Omit<DatePickerRootBaseProps, 'value' |
18
18
  name?: string;
19
19
  granularity?: 'day' | 'hour' | 'minute' | 'second';
20
20
  hourCycle?: 12 | 24;
21
+ /**
22
+ * Hide the trailing time-zone segment (e.g. `GMT+2`) that a zoned value shows
23
+ * at time granularity. Defaults to `true`, matching `DateInput`. Not inherited
24
+ * from Ark: `hideTimeZone` is a date-input prop, not a date-picker one.
25
+ */
26
+ hideTimeZone?: boolean;
21
27
  }
@@ -28,6 +28,7 @@
28
28
  timeZone,
29
29
  granularity,
30
30
  hourCycle,
31
+ hideTimeZone = true,
31
32
  onValueChange
32
33
  }: DateRangeFieldProps = $props();
33
34
 
@@ -102,6 +103,7 @@
102
103
  selectionMode: 'range',
103
104
  granularity: granularity ?? 'day',
104
105
  hourCycle,
106
+ hideTimeZone,
105
107
  value: datePicker().value,
106
108
  min,
107
109
  max,
@@ -26,4 +26,10 @@ export interface DateRangeFieldProps extends Omit<DatePickerRootBaseProps, 'valu
26
26
  endName?: string;
27
27
  granularity?: 'day' | 'hour' | 'minute' | 'second';
28
28
  hourCycle?: 12 | 24;
29
+ /**
30
+ * Hide the trailing time-zone segment (e.g. `GMT+2`) that a zoned value shows
31
+ * at time granularity. Defaults to `true`, matching `DateInput`. Not inherited
32
+ * from Ark: `hideTimeZone` is a date-input prop, not a date-picker one.
33
+ */
34
+ hideTimeZone?: boolean;
29
35
  }
@@ -7,6 +7,7 @@
7
7
  form,
8
8
  field,
9
9
  helperText,
10
+ errorText,
10
11
  class: className,
11
12
  invalid,
12
13
  required,
@@ -14,9 +15,15 @@
14
15
  ...rest
15
16
  }: FieldRootProps = $props();
16
17
 
17
- const isInvalid = $derived(form && field ? form.invalid(field) : (invalid ?? false));
18
+ // Declared before isInvalid, which reads it.
19
+ const resolvedError = $derived(
20
+ form && field ? (form.errors[field]?.[0] ?? null) : (errorText ?? null)
21
+ );
22
+ // An error that does not also mark the field invalid renders nothing at all:
23
+ // Ark's Field.ErrorText is gated on the field context's `invalid`. So a bare
24
+ // `errorText` implies it, while an explicit `invalid` still wins.
25
+ const isInvalid = $derived(form && field ? form.invalid(field) : (invalid ?? !!resolvedError));
18
26
  const isRequired = $derived(form && field ? form.isRequired(field) : (required ?? false));
19
- const errorText = $derived(form && field ? (form.errors[field]?.[0] ?? null) : null);
20
27
  </script>
21
28
 
22
29
  <Field.Root
@@ -26,9 +33,19 @@
26
33
  class={cn('group flex flex-col gap-1.5', className)}
27
34
  >
28
35
  {@render children?.()}
29
- {#if errorText}
30
- <Field.ErrorText>{errorText}</Field.ErrorText>
36
+ {#if resolvedError}
37
+ <Field.ErrorText>{resolvedError}</Field.ErrorText>
31
38
  {:else if helperText}
32
39
  <Field.HelperText>{helperText}</Field.HelperText>
40
+ {:else}
41
+ <!--
42
+ Holds the message line open so a field does not change height when an
43
+ error appears. `1lh` resolves against this element's own line-height, so
44
+ it tracks the theme's font rather than assuming a pixel height. A plain
45
+ div and not an empty Field.HelperText: Ark gives that one an id which
46
+ aria-describedby points at, which would describe the control as having a
47
+ description that isn't there.
48
+ -->
49
+ <div class="h-lh text-xs" aria-hidden="true"></div>
33
50
  {/if}
34
51
  </Field.Root>
@@ -11,6 +11,14 @@ 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;
14
22
  }
15
23
  export interface FieldLabelProps extends FieldLabelBaseProps {
16
24
  class?: ClassValue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compote-ui",
3
- "version": "0.72.0",
3
+ "version": "0.74.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev --open",
@@ -19,6 +19,25 @@ 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.
40
+
22
41
  List controls use `{ value, label }` items:
23
42
 
24
43
  ```svelte