@alfadocs/ui-kit-debug 0.6.0 → 0.7.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.
@@ -0,0 +1,73 @@
1
+ import { useState as w, useEffect as d, useRef as h, useCallback as p } from "react";
2
+ import "../i18n/config.js";
3
+ import i from "i18next";
4
+ function E(e) {
5
+ const [o, r] = w(() => typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia(e).matches);
6
+ return d(() => {
7
+ if (typeof window > "u" || typeof window.matchMedia != "function")
8
+ return;
9
+ const t = window.matchMedia(e);
10
+ r(t.matches);
11
+ const n = (a) => r(a.matches);
12
+ return typeof t.addEventListener == "function" ? (t.addEventListener("change", n), () => t.removeEventListener("change", n)) : (t.addListener(n), () => t.removeListener(n));
13
+ }, [e]), o;
14
+ }
15
+ function S(e) {
16
+ return e != null && e.current ? e.current : typeof document > "u" ? null : document;
17
+ }
18
+ function y(e, o) {
19
+ const r = typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(e) : (
20
+ // SSR / jsdom fallback — drop every character that's
21
+ // significant in a CSS attribute selector. Conservative but
22
+ // robust enough for the test environment.
23
+ e.replace(/["'\\\[\]]/g, "")
24
+ ), t = `[data-error-anchor="${r}"], [name="${r}"], [name$="[${r}]"]`, n = Array.from(
25
+ o.querySelectorAll(t)
26
+ );
27
+ return n.length === 0 ? null : n.find((s) => s.offsetParent !== null) ?? n[0];
28
+ }
29
+ function M(e, o, r = {}) {
30
+ const {
31
+ scope: t,
32
+ scrollOptions: n = { behavior: "smooth", block: "center" },
33
+ focus: a = !0,
34
+ skipInitialMount: s = !0
35
+ } = r, m = h(!0), c = h(null), u = o.find((l) => !!e[l]) ?? null;
36
+ d(() => {
37
+ if (m.current && (m.current = !1, s)) {
38
+ c.current = u;
39
+ return;
40
+ }
41
+ if (!u) {
42
+ c.current = null;
43
+ return;
44
+ }
45
+ if (u === c.current || typeof window > "u") return;
46
+ const l = window.requestAnimationFrame(() => {
47
+ const g = S(t);
48
+ if (!g) return;
49
+ const f = y(u, g);
50
+ f && (f.scrollIntoView(n), a && f.focus({ preventScroll: !0 }), c.current = u);
51
+ });
52
+ return () => window.cancelAnimationFrame(l);
53
+ }, [u]);
54
+ }
55
+ function b() {
56
+ const [e, o] = w(() => i.language);
57
+ d(() => {
58
+ const n = (a) => o(a);
59
+ return i.on("languageChanged", n), i.language !== e && o(i.language), () => {
60
+ i.off("languageChanged", n);
61
+ };
62
+ }, [e]);
63
+ const r = p(async (n) => {
64
+ await i.changeLanguage(n);
65
+ }, []), t = i.dir(e) === "rtl" ? "rtl" : "ltr";
66
+ return { locale: e, dir: t, setLocale: r };
67
+ }
68
+ export {
69
+ E as a,
70
+ M as b,
71
+ b as u
72
+ };
73
+ //# sourceMappingURL=use-locale-BBDCG265.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-locale-BBDCG265.js","sources":["../../src/hooks/use-media-query.ts","../../src/hooks/use-scroll-to-first-error.ts","../../src/hooks/use-locale.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n/**\n * Subscribe to an arbitrary media query and return whether it currently\n * matches. Useful for responsive components that need to branch on\n * viewport size without hand-rolling `matchMedia` plumbing.\n *\n * SSR-safe: returns `false` when `window` is undefined. The effect is\n * client-only, so hydration mismatches are avoided without a null\n * sentinel at the type level.\n *\n * Mirrors the shape of `usePrefersReducedMotion` — Safari < 14 fallback\n * via `addListener` / `removeListener` is included.\n *\n * @example\n * const isMobile = useMediaQuery('(max-width: 640px)');\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState<boolean>(() => {\n if (\n typeof window === 'undefined' ||\n typeof window.matchMedia !== 'function'\n ) {\n return false;\n }\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (\n typeof window === 'undefined' ||\n typeof window.matchMedia !== 'function'\n ) {\n return;\n }\n const mql = window.matchMedia(query);\n // Re-sync in case the query (or the OS state) changed between the\n // initial useState read and the first effect.\n setMatches(mql.matches);\n const onChange = (ev: MediaQueryListEvent) => setMatches(ev.matches);\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }\n mql.addListener(onChange);\n return () => mql.removeListener(onChange);\n }, [query]);\n\n return matches;\n}\n","import { useEffect, useRef, type RefObject } from 'react';\n\n/* -------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------- */\n\nexport interface UseScrollToFirstErrorOptions {\n /**\n * Limits the DOM query to this element. Useful when the page renders\n * more than one form, or when an unrelated component happens to share\n * a field name. Defaults to `document`.\n */\n scope?: RefObject<HTMLElement | null>;\n /**\n * Forwarded to `scrollIntoView`. Defaults to\n * `{ behavior: 'smooth', block: 'center' }`. Pass\n * `{ behavior: 'auto' }` (or omit `behavior`) on consumers that\n * respect `prefers-reduced-motion` upstream — the kit doesn't shadow\n * the user's motion preference inside the hook, since some consumers\n * deliberately keep a near-instant snap for power-user form flows.\n */\n scrollOptions?: ScrollIntoViewOptions;\n /**\n * Move keyboard focus to the errored field after scrolling. Default\n * `true`. The focus call uses `preventScroll: true` so the smooth\n * scroll the hook just did isn't replaced by a snap.\n *\n * Setting this `false` defeats the keyboard-user side of WCAG 3.3.1\n * — the viewport will scroll but the active element stays on the\n * submit button, forcing a Shift+Tab back into the form. Only flip\n * this off when an upstream concern (e.g. a custom error summary\n * panel that owns its own focus) is taking responsibility.\n */\n focus?: boolean;\n /**\n * On the very first render, do nothing even if `errors` already has\n * entries. Default `true`. Without this the hook would yank the\n * viewport whenever a form is hydrated with server-rendered errors\n * from a no-JS fallback path — bad UX, since the user landed on the\n * page and hasn't asked for any movement yet. Set `false` only when\n * the consumer explicitly wants the initial-mount scroll (rare).\n */\n skipInitialMount?: boolean;\n}\n\n/* -------------------------------------------------------------------- */\n/* Helpers */\n/* -------------------------------------------------------------------- */\n\nfunction getScopeRoot(\n scope: RefObject<HTMLElement | null> | undefined,\n): ParentNode | null {\n if (scope?.current) return scope.current;\n if (typeof document === 'undefined') return null;\n return document;\n}\n\n/**\n * Resolve the DOM element to scroll/focus for a given errored field\n * name. The match order is:\n *\n * 1. `[data-error-anchor=\"<name>\"]` — the consumer's explicit hook,\n * used for Radix-based fields (Checkbox / Select / Switch) where\n * the named `<input>` is hidden behind a visible `<button>`.\n * Consumers set this on the trigger.\n * 2. `[name=\"<name>\"]` — the literal field name.\n * 3. `[name$=\"[<name>]\"]` — Symfony form-prefixed names like\n * `privacy_generator[fiscal_code]`. Suffix match keeps the hook\n * consumer-agnostic; no need to thread the form prefix through.\n *\n * Returns the first visible element among the matches. \"Visible\" here\n * means `offsetParent !== null` — that's the cheap heuristic Radix's\n * hidden inputs (display:none-equivalent positioning) fail and the\n * visible triggers pass. Falls back to the first match if every match\n * is hidden, so a field can still be scrolled into view even when the\n * trigger uses a non-standard layout.\n */\nfunction findErrorElement(\n name: string,\n root: ParentNode,\n): HTMLElement | null {\n // `CSS.escape` is load-bearing here, not cosmetic. The Symfony\n // suffix-match arm embeds `name` inside `[name$=\"[${safe}]\"]`, so a\n // field name containing `]` would otherwise close the attribute\n // selector early and turn the rest of the string into stray\n // selector tokens. We don't expect adversarial input in practice\n // (server-controlled field names), but a server returning a `]`\n // in a 422 payload shouldn't be able to break the query.\n const safe =\n typeof CSS !== 'undefined' && typeof CSS.escape === 'function'\n ? CSS.escape(name)\n : // SSR / jsdom fallback — drop every character that's\n // significant in a CSS attribute selector. Conservative but\n // robust enough for the test environment.\n name.replace(/[\"'\\\\\\[\\]]/g, '');\n const selector =\n `[data-error-anchor=\"${safe}\"], ` +\n `[name=\"${safe}\"], ` +\n `[name$=\"[${safe}]\"]`;\n\n const matches = Array.from(\n root.querySelectorAll<HTMLElement>(selector),\n );\n if (matches.length === 0) return null;\n\n const visible = matches.find((el) => el.offsetParent !== null);\n return visible ?? matches[0];\n}\n\n/* -------------------------------------------------------------------- */\n/* Hook */\n/* -------------------------------------------------------------------- */\n\n/**\n * Scrolls the first errored field into view and (by default) focuses\n * it, after `errors` changes. Backs the standard AlfaDocs rebrand\n * validation-error UX: after a 422 with `{ errors: { field: message } }`\n * is rendered inline, the user shouldn't be left staring at red labels\n * they can't see. Implements WCAG 3.3.1 / 3.3.3.\n *\n * The hook is stateless and never mutates the DOM beyond the\n * `scrollIntoView` + `focus` calls.\n *\n * @example\n * const fieldOrder = ['firstName', 'email', 'fiscalCode'] as const;\n * const [errors, setErrors] = useState<Record<string, string>>({});\n * useScrollToFirstError(errors, fieldOrder);\n *\n * @example Radix-based field\n * <Checkbox data-error-anchor=\"consent\" name=\"consent\" />\n * // hook scrolls to the visible <button>, not Radix's hidden input.\n *\n * @example Scoped to one form\n * const formRef = useRef<HTMLFormElement>(null);\n * useScrollToFirstError(errors, fieldOrder, { scope: formRef });\n */\nexport function useScrollToFirstError(\n errors: Readonly<Record<string, string | undefined>>,\n fieldOrder: ReadonlyArray<string>,\n options: UseScrollToFirstErrorOptions = {},\n): void {\n const {\n scope,\n scrollOptions = { behavior: 'smooth', block: 'center' },\n focus = true,\n skipInitialMount = true,\n } = options;\n\n const isInitialMount = useRef(true);\n\n // Track the previous \"first errored field name\" so a re-render that\n // leaves the same field errored doesn't re-scroll. We intentionally\n // depend on the resolved name (not the whole errors object) — that\n // way callers can mutate `errors` shape freely as long as the head\n // of the validation list is stable, which is the typical pattern\n // after a server returns the same 422 twice in a row.\n const lastScrolledFieldRef = useRef<string | null>(null);\n\n // Resolve the first errored field by visual order. A field is\n // \"errored\" iff its key has a truthy string value — the kit's form\n // contract is `Record<string, string | undefined>` where `undefined`\n // / missing keys mean \"no error\".\n const firstErroredField =\n fieldOrder.find((name) => Boolean(errors[name])) ?? null;\n\n useEffect(() => {\n if (isInitialMount.current) {\n isInitialMount.current = false;\n if (skipInitialMount) {\n // Record what we skipped so the next render doesn't try to\n // scroll to a field that was already errored at mount.\n lastScrolledFieldRef.current = firstErroredField;\n return;\n }\n }\n\n if (!firstErroredField) {\n lastScrolledFieldRef.current = null;\n return;\n }\n\n if (firstErroredField === lastScrolledFieldRef.current) return;\n\n if (typeof window === 'undefined') return;\n\n // rAF defers the scroll until after the browser has laid out the\n // freshly-rendered error labels. Calling scrollIntoView synchronously\n // inside useEffect would fire before the new DOM is painted on\n // some browsers, leaving the viewport pointing at the old layout.\n const handle = window.requestAnimationFrame(() => {\n const root = getScopeRoot(scope);\n if (!root) return;\n const el = findErrorElement(firstErroredField, root);\n if (!el) return;\n\n el.scrollIntoView(scrollOptions);\n if (focus) {\n // preventScroll: true so the browser doesn't override the\n // smooth scroll above with a snap to put the element at the\n // top of the viewport.\n el.focus({ preventScroll: true });\n }\n lastScrolledFieldRef.current = firstErroredField;\n });\n\n return () => window.cancelAnimationFrame(handle);\n // `scrollOptions` / `focus` / `scope` / `skipInitialMount` are\n // option bag values — re-running on their identity change would\n // be surprising. The hook re-fires only when the field at the\n // head of the errored list changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [firstErroredField]);\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport i18n from '../i18n/config';\n\nexport type LocaleDir = 'ltr' | 'rtl';\n\nexport interface UseLocaleReturn {\n /** Current i18next language tag (e.g. `'en'`, `'ar'`, `'ja'`). */\n locale: string;\n /** Reading direction for the current locale, derived from `i18n.dir(locale)`. */\n dir: LocaleDir;\n /** Change the active i18next language. */\n setLocale: (next: string) => Promise<void>;\n}\n\n/**\n * Read the active i18n locale and its reading direction. Subscribes to\n * `i18next.on('languageChanged')` so consumers re-render when the locale\n * changes (e.g. via `<select>` in a settings page).\n *\n * SSR-safe: the initial state is read synchronously from the i18next\n * instance, which itself ships an SSR-friendly default. The\n * `languageChanged` listener attaches in `useEffect`, so it only runs\n * client-side.\n *\n * Thin wrapper — the underlying source of truth is the i18next instance\n * exported from `src/i18n/config.ts`. Use this hook over reading\n * `i18n.language` directly so components can:\n * 1. Re-render on language change without ad-hoc state.\n * 2. Get `dir` derived correctly (RTL for `ar`).\n */\nexport function useLocale(): UseLocaleReturn {\n const [locale, setLocaleState] = useState<string>(() => i18n.language);\n\n useEffect(() => {\n const onChange = (next: string) => setLocaleState(next);\n i18n.on('languageChanged', onChange);\n // Re-sync — the `i18n.language` may have settled between the\n // initial useState read and effect attach (async language load).\n if (i18n.language !== locale) setLocaleState(i18n.language);\n return () => {\n i18n.off('languageChanged', onChange);\n };\n }, [locale]);\n\n const setLocale = useCallback(async (next: string) => {\n await i18n.changeLanguage(next);\n }, []);\n\n // `i18n.dir(locale)` returns `'ltr' | 'rtl'` — the i18next types declare\n // it as `string`, so we narrow.\n const dir = (i18n.dir(locale) === 'rtl' ? 'rtl' : 'ltr') as LocaleDir;\n\n return { locale, dir, setLocale };\n}\n"],"names":["useMediaQuery","query","matches","setMatches","useState","useEffect","mql","onChange","ev","getScopeRoot","scope","findErrorElement","name","root","safe","selector","el","useScrollToFirstError","errors","fieldOrder","options","scrollOptions","focus","skipInitialMount","isInitialMount","useRef","lastScrolledFieldRef","firstErroredField","handle","useLocale","locale","setLocaleState","i18n","next","setLocale","useCallback","dir"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "packageVersion": "0.6.0",
3
+ "packageVersion": "0.7.0",
4
4
  "components": [
5
5
  {
6
6
  "kind": "component",
@@ -1,5 +1,5 @@
1
- export { usePrefersReducedMotion, useMediaQuery, useLocale, useTheme, resolveTheme, themeClassList, THEME_CLASS, THEME_STORAGE_KEY, ACCESSIBILITY_STORAGE_KEY, } from '../hooks';
2
- export type { LocaleDir, UseLocaleReturn, ThemePreference, AccessibilityPreference, ResolvedTheme, UseThemeReturn, } from '../hooks';
1
+ export { usePrefersReducedMotion, useMediaQuery, useScrollToFirstError, useLocale, useTheme, resolveTheme, themeClassList, THEME_CLASS, THEME_STORAGE_KEY, ACCESSIBILITY_STORAGE_KEY, } from '../hooks';
2
+ export type { LocaleDir, UseLocaleReturn, UseScrollToFirstErrorOptions, ThemePreference, AccessibilityPreference, ResolvedTheme, UseThemeReturn, } from '../hooks';
3
3
  export * from './button';
4
4
  export * from './button-group';
5
5
  export * from './floating-action-button';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ type GlyphPreset = keyof typeof GLYPH_PRESETS;
9
9
  declare const wrapperVariants: (props?: ({
10
10
  background?: "solid" | "transparent" | null | undefined;
11
11
  vibe?: "ghost" | "terminal" | "clean" | "hologram" | "synthwave" | "oscilloscope" | null | undefined;
12
- mask?: "none" | "center" | null | undefined;
12
+ mask?: "center" | "none" | null | undefined;
13
13
  maskRadius?: "sm" | "md" | "lg" | "xl" | "xs" | null | undefined;
14
14
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
15
  type Tone = 'primary' | 'accent' | 'success' | 'error' | 'info';
@@ -2,7 +2,7 @@ import { type ComponentPropsWithoutRef, type HTMLAttributes, type ReactNode } fr
2
2
  import * as RadixDialog from '@radix-ui/react-dialog';
3
3
  import { type VariantProps } from 'class-variance-authority';
4
4
  declare const contentVariants: (props?: ({
5
- side?: "top" | "end" | "bottom" | "start" | null | undefined;
5
+ side?: "end" | "start" | "top" | "bottom" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export interface SheetHandle {
8
8
  getIsOpen: () => boolean;
@@ -3,7 +3,7 @@ import { type VariantProps } from 'class-variance-authority';
3
3
  declare const statVariants: (props?: ({
4
4
  variant?: "default" | "compact" | "outlined" | "elevated" | null | undefined;
5
5
  size?: "sm" | "md" | "lg" | null | undefined;
6
- align?: "end" | "start" | "center" | null | undefined;
6
+ align?: "center" | "end" | "start" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  export interface StatProps extends HTMLAttributes<HTMLDivElement>, VariantProps<typeof statVariants> {
9
9
  /** Metric label rendered above the value. */
@@ -2,7 +2,7 @@ import { type HTMLAttributes } from 'react';
2
2
  import { type VariantProps } from 'class-variance-authority';
3
3
  import { type AccessibilityPreference, type ThemePreference } from '../../hooks';
4
4
  declare const wrapperVariants: (props?: ({
5
- variant?: "split" | "menu" | "compact" | null | undefined;
5
+ variant?: "menu" | "split" | "compact" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export type ThemeToggleVariant = 'menu' | 'compact' | 'split';
8
8
  interface ThemeToggleBaseProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'>, VariantProps<typeof wrapperVariants> {
@@ -1,5 +1,7 @@
1
1
  export { usePrefersReducedMotion } from './use-prefers-reduced-motion';
2
2
  export { useMediaQuery } from './use-media-query';
3
+ export { useScrollToFirstError } from './use-scroll-to-first-error';
4
+ export type { UseScrollToFirstErrorOptions } from './use-scroll-to-first-error';
3
5
  export { useLocale } from './use-locale';
4
6
  export type { LocaleDir, UseLocaleReturn } from './use-locale';
5
7
  export { useTheme, resolveTheme, themeClassList, THEME_STORAGE_KEY, ACCESSIBILITY_STORAGE_KEY, THEME_CLASS, } from './use-theme';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,yBAAyB,EACzB,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,cAAc,GACf,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,yBAAyB,EACzB,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,cAAc,GACf,MAAM,aAAa,CAAC"}
@@ -1,15 +1,16 @@
1
- import { u as a } from "../_chunks/use-prefers-reduced-motion-BMwIQRjB.js";
2
- import { u as o, a as u } from "../_chunks/use-locale-BuXR_Zl9.js";
3
- import { A as T, T as t, a as m, r as S, t as A, u as _ } from "../_chunks/use-theme-B1cwAXJR.js";
1
+ import { u as r } from "../_chunks/use-prefers-reduced-motion-BMwIQRjB.js";
2
+ import { u as o, a as u, b as E } from "../_chunks/use-locale-BBDCG265.js";
3
+ import { A as t, T as S, a as m, r as l, t as A, u as _ } from "../_chunks/use-theme-B1cwAXJR.js";
4
4
  export {
5
- T as ACCESSIBILITY_STORAGE_KEY,
6
- t as THEME_CLASS,
5
+ t as ACCESSIBILITY_STORAGE_KEY,
6
+ S as THEME_CLASS,
7
7
  m as THEME_STORAGE_KEY,
8
- S as resolveTheme,
8
+ l as resolveTheme,
9
9
  A as themeClassList,
10
10
  o as useLocale,
11
11
  u as useMediaQuery,
12
- a as usePrefersReducedMotion,
12
+ r as usePrefersReducedMotion,
13
+ E as useScrollToFirstError,
13
14
  _ as useTheme
14
15
  };
15
16
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,64 @@
1
+ import { type RefObject } from 'react';
2
+ export interface UseScrollToFirstErrorOptions {
3
+ /**
4
+ * Limits the DOM query to this element. Useful when the page renders
5
+ * more than one form, or when an unrelated component happens to share
6
+ * a field name. Defaults to `document`.
7
+ */
8
+ scope?: RefObject<HTMLElement | null>;
9
+ /**
10
+ * Forwarded to `scrollIntoView`. Defaults to
11
+ * `{ behavior: 'smooth', block: 'center' }`. Pass
12
+ * `{ behavior: 'auto' }` (or omit `behavior`) on consumers that
13
+ * respect `prefers-reduced-motion` upstream — the kit doesn't shadow
14
+ * the user's motion preference inside the hook, since some consumers
15
+ * deliberately keep a near-instant snap for power-user form flows.
16
+ */
17
+ scrollOptions?: ScrollIntoViewOptions;
18
+ /**
19
+ * Move keyboard focus to the errored field after scrolling. Default
20
+ * `true`. The focus call uses `preventScroll: true` so the smooth
21
+ * scroll the hook just did isn't replaced by a snap.
22
+ *
23
+ * Setting this `false` defeats the keyboard-user side of WCAG 3.3.1
24
+ * — the viewport will scroll but the active element stays on the
25
+ * submit button, forcing a Shift+Tab back into the form. Only flip
26
+ * this off when an upstream concern (e.g. a custom error summary
27
+ * panel that owns its own focus) is taking responsibility.
28
+ */
29
+ focus?: boolean;
30
+ /**
31
+ * On the very first render, do nothing even if `errors` already has
32
+ * entries. Default `true`. Without this the hook would yank the
33
+ * viewport whenever a form is hydrated with server-rendered errors
34
+ * from a no-JS fallback path — bad UX, since the user landed on the
35
+ * page and hasn't asked for any movement yet. Set `false` only when
36
+ * the consumer explicitly wants the initial-mount scroll (rare).
37
+ */
38
+ skipInitialMount?: boolean;
39
+ }
40
+ /**
41
+ * Scrolls the first errored field into view and (by default) focuses
42
+ * it, after `errors` changes. Backs the standard AlfaDocs rebrand
43
+ * validation-error UX: after a 422 with `{ errors: { field: message } }`
44
+ * is rendered inline, the user shouldn't be left staring at red labels
45
+ * they can't see. Implements WCAG 3.3.1 / 3.3.3.
46
+ *
47
+ * The hook is stateless and never mutates the DOM beyond the
48
+ * `scrollIntoView` + `focus` calls.
49
+ *
50
+ * @example
51
+ * const fieldOrder = ['firstName', 'email', 'fiscalCode'] as const;
52
+ * const [errors, setErrors] = useState<Record<string, string>>({});
53
+ * useScrollToFirstError(errors, fieldOrder);
54
+ *
55
+ * @example Radix-based field
56
+ * <Checkbox data-error-anchor="consent" name="consent" />
57
+ * // hook scrolls to the visible <button>, not Radix's hidden input.
58
+ *
59
+ * @example Scoped to one form
60
+ * const formRef = useRef<HTMLFormElement>(null);
61
+ * useScrollToFirstError(errors, fieldOrder, { scope: formRef });
62
+ */
63
+ export declare function useScrollToFirstError(errors: Readonly<Record<string, string | undefined>>, fieldOrder: ReadonlyArray<string>, options?: UseScrollToFirstErrorOptions): void;
64
+ //# sourceMappingURL=use-scroll-to-first-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-scroll-to-first-error.d.ts","sourceRoot":"","sources":["../../src/hooks/use-scroll-to-first-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAM1D,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAsED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EACpD,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,EACjC,OAAO,GAAE,4BAAiC,GACzC,IAAI,CAwEN"}