@alfadocs/ui-kit-debug 0.98.0 → 0.99.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/README.md +11 -4
- package/dist/_chunks/{address-fields-7k59FnJ6.js → address-fields-pHsfkXGk.js} +2 -2
- package/dist/_chunks/{address-fields-7k59FnJ6.js.map → address-fields-pHsfkXGk.js.map} +1 -1
- package/dist/_chunks/{booking-BSpSMmJ2.js → booking-DSOK-bnR.js} +3 -3
- package/dist/_chunks/{booking-BSpSMmJ2.js.map → booking-DSOK-bnR.js.map} +1 -1
- package/dist/_chunks/{country-select-BN9nedAp.js → country-select-UEF9BEeQ.js} +2 -2
- package/dist/_chunks/{country-select-BN9nedAp.js.map → country-select-UEF9BEeQ.js.map} +1 -1
- package/dist/_chunks/flag-DVBE6DnB.js +576 -0
- package/dist/_chunks/flag-DVBE6DnB.js.map +1 -0
- package/dist/_chunks/flag-view-udarpAot.js +131 -0
- package/dist/_chunks/flag-view-udarpAot.js.map +1 -0
- package/dist/_chunks/{header-settings-CLwFqMHI.js → header-settings--pjYOe6g.js} +2 -2
- package/dist/_chunks/{header-settings-CLwFqMHI.js.map → header-settings--pjYOe6g.js.map} +1 -1
- package/dist/_chunks/{link-cell-renderer-flfZh2qY.js → link-cell-renderer-Bt-mXeQY.js} +2 -2
- package/dist/_chunks/{link-cell-renderer-flfZh2qY.js.map → link-cell-renderer-Bt-mXeQY.js.map} +1 -1
- package/dist/_chunks/{locale-picker-EyvPrwDc.js → locale-picker-BfBUdT1m.js} +310 -239
- package/dist/_chunks/locale-picker-BfBUdT1m.js.map +1 -0
- package/dist/_chunks/{marketplace-app-shell-Bv1ZO4XO.js → marketplace-app-shell-pCHBp8bo.js} +2 -2
- package/dist/_chunks/{marketplace-app-shell-Bv1ZO4XO.js.map → marketplace-app-shell-pCHBp8bo.js.map} +1 -1
- package/dist/_chunks/{multi-select-D5QLBJM1.js → multi-select-CC52Qws6.js} +275 -263
- package/dist/_chunks/multi-select-CC52Qws6.js.map +1 -0
- package/dist/_chunks/{patient-details-DrhlTrLf.js → patient-details-UmNcxL0c.js} +3 -3
- package/dist/_chunks/{patient-details-DrhlTrLf.js.map → patient-details-UmNcxL0c.js.map} +1 -1
- package/dist/_chunks/{patient-table-CvZTIM2R.js → patient-table-QEVsFvwq.js} +2 -2
- package/dist/_chunks/{patient-table-CvZTIM2R.js.map → patient-table-QEVsFvwq.js.map} +1 -1
- package/dist/_chunks/{phone-input-D00ZBAm-.js → phone-input-BsZIoE36.js} +2 -2
- package/dist/_chunks/{phone-input-D00ZBAm-.js.map → phone-input-BsZIoE36.js.map} +1 -1
- package/dist/_chunks/{public-header.agent-rX_VA6hd.js → public-header.agent-_8W9biCO.js} +4 -2
- package/dist/_chunks/public-header.agent-_8W9biCO.js.map +1 -0
- package/dist/_chunks/use-theme-CAuo6EYT.js.map +1 -1
- package/dist/_chunks/workflow-map-SIfAhmWz.js.map +1 -1
- package/dist/agent-catalog.json +291 -1
- package/dist/agent-i18n/en.json +23 -0
- package/dist/components/address-fields/index.js +1 -1
- package/dist/components/booking/index.js +1 -1
- package/dist/components/country-select/index.js +1 -1
- package/dist/components/data-table/index.js +1 -1
- package/dist/components/flag/flag-core.d.ts +12 -0
- package/dist/components/flag/flag-core.d.ts.map +1 -0
- package/dist/components/flag/flag-view.d.ts +38 -0
- package/dist/components/flag/flag-view.d.ts.map +1 -0
- package/dist/components/flag/flag.d.ts +3 -10
- package/dist/components/flag/flag.d.ts.map +1 -1
- package/dist/components/flag/index.js +1 -1
- package/dist/components/header-settings/index.js +1 -1
- package/dist/components/locale-picker/index.js +1 -1
- package/dist/components/multi-select/index.js +1 -1
- package/dist/components/multi-select/multi-select.d.ts.map +1 -1
- package/dist/components/patient-details/index.js +1 -1
- package/dist/components/patient-table/index.js +1 -1
- package/dist/components/phone-input/index.js +1 -1
- package/dist/components/public-header/index.js +1 -1
- package/dist/components/public-header/public-header.d.ts.map +1 -1
- package/dist/components/workflow/workflow-types.d.ts +4 -5
- package/dist/components/workflow/workflow-types.d.ts.map +1 -1
- package/dist/hooks/use-theme.d.ts.map +1 -1
- package/dist/host-compat.css +7 -1
- package/dist/index.js +11 -11
- package/dist/patterns/marketplace-app-shell/index.js +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/flag-Cnmivlrl.js +0 -641
- package/dist/_chunks/flag-Cnmivlrl.js.map +0 -1
- package/dist/_chunks/locale-picker-EyvPrwDc.js.map +0 -1
- package/dist/_chunks/multi-select-D5QLBJM1.js.map +0 -1
- package/dist/_chunks/public-header.agent-rX_VA6hd.js.map +0 -1
|
@@ -10,7 +10,7 @@ import { getCountries as qe, parsePhoneNumberFromString as L, AsYouType as q, ge
|
|
|
10
10
|
import { u as He, F as _e } from "./form-field-context-B3APVHKx.js";
|
|
11
11
|
import { c as Ge } from "./compose-refs-C0k0tdqF.js";
|
|
12
12
|
import { n as J } from "./normalize-diacritics-BNGbFNlJ.js";
|
|
13
|
-
import { a as Q } from "./flag-
|
|
13
|
+
import { a as Q } from "./flag-DVBE6DnB.js";
|
|
14
14
|
import { C as Oe } from "./chevron-down-DWE7GSq9.js";
|
|
15
15
|
const Ue = {
|
|
16
16
|
id: "phone-input",
|
|
@@ -577,4 +577,4 @@ export {
|
|
|
577
577
|
pe as c,
|
|
578
578
|
We as p
|
|
579
579
|
};
|
|
580
|
-
//# sourceMappingURL=phone-input-
|
|
580
|
+
//# sourceMappingURL=phone-input-BsZIoE36.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phone-input-D00ZBAm-.js","sources":["../../src/components/phone-input/phone-input.agent.ts","../../src/components/phone-input/phone-input.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — PhoneInput. */\n/* */\n/* See `src/docs/26-agent-readiness.mdx` for the contract. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { PhoneInputHandle } from './phone-input';\n\nexport const phoneInputAgent: AgentAdapter<PhoneInputHandle> = {\n id: 'phone-input',\n // A phone number is a single form value, not free-form rich text — `set_value`\n // (not `edit_inline`, which is reserved for in-place rich/cell editing).\n capabilities: ['set_value'],\n state: {\n value: {\n type: 'string',\n descriptionKey: 'ui.agent.phoneInput.state.value',\n description:\n 'Current E.164 phone number, or empty string when not yet valid.',\n read: (handle) => handle.getValue(),\n },\n isEmpty: {\n type: 'boolean',\n descriptionKey: 'ui.agent.phoneInput.state.isEmpty',\n description: 'Whether the input has no value.',\n read: (handle) => handle.getValue() === '',\n },\n },\n actions: {\n set_value: {\n safety: 'write',\n argsType: '{ value: string }',\n argsSchema: {\n type: 'object',\n properties: {\n value: {\n type: 'string',\n description:\n 'Phone number in E.164 or national format; the parser infers the country.',\n },\n },\n required: ['value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.phoneInput.actions.setValue',\n description:\n 'Replace the phone number (E.164 or national format; parser will infer country).',\n examples: [\n { args: { value: '+393331234567' }, note: 'Set an Italian mobile.' },\n ],\n invoke: (handle, args: { value: string }) => {\n handle.setValue(args.value);\n },\n },\n clear: {\n safety: 'destructive',\n idempotent: true,\n descriptionKey: 'ui.agent.phoneInput.actions.clear',\n description: 'Empty the input. Loses any typed value.',\n invoke: (handle) => {\n handle.clear();\n },\n },\n focus: {\n safety: 'read',\n descriptionKey: 'ui.agent.phoneInput.actions.focus',\n description: 'Move keyboard focus to the phone-number input.',\n invoke: (handle) => {\n handle.focus();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'phone-input',\n description: 'Marks the PhoneInput wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n} from 'react';\nimport { useAgentRegistration } from '../../agent';\nimport { phoneInputAgent } from './phone-input.agent';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\n\n/** Agent-readiness curated handle for PhoneInput. */\nexport interface PhoneInputHandle {\n getValue: () => string;\n setValue: (value: string) => void;\n clear: () => void;\n focus: () => void;\n}\nimport * as Popover from '@radix-ui/react-popover';\nimport { Command, defaultFilter } from 'cmdk';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { ChevronDown } from 'lucide-react';\nimport {\n AsYouType,\n getCountries,\n getCountryCallingCode,\n parsePhoneNumberFromString,\n type CountryCode,\n} from 'libphonenumber-js/min';\nimport {\n FormFieldContext,\n useFormField,\n} from '../form-field/form-field-context';\nimport { composeRefs } from '../_shared/compose-refs';\nimport { normalizeDiacritics } from '../_shared/normalize-diacritics';\nimport { Flag } from '../flag';\n\nexport interface PhoneInputChangeMeta {\n country: CountryCode;\n isValid: boolean;\n isPossible: boolean;\n type?: string;\n}\n\nexport interface PhoneInputProps {\n value?: string;\n defaultValue?: string;\n /**\n * Country pre-selected when no value is supplied. Defaults to `IT`\n * (AlfaDocs's dominant user base is Italian). Pass a different\n * `CountryCode` to override, or enable `detectBrowserCountry` to\n * derive it from the browser locale.\n */\n defaultCountry?: CountryCode;\n /**\n * When true, infer the default country from `navigator.language` /\n * `Intl.Locale` before falling back to `defaultCountry` (or `IT`).\n * Off by default — most AlfaDocs flows should stay on IT regardless\n * of the visitor's browser locale.\n */\n detectBrowserCountry?: boolean;\n onChange?: (e164: string, meta: PhoneInputChangeMeta) => void;\n size?: 'sm' | 'md' | 'lg';\n tone?: 'default' | 'error';\n disabled?: boolean;\n readOnly?: boolean;\n placeholder?: string;\n popularCountries?: CountryCode[];\n /**\n * Restrict the country picker to this set (ISO 3166-1 alpha-2 codes), in\n * the order given. Unsupported or duplicate codes are dropped; an empty /\n * all-invalid list is ignored (every country stays selectable).\n * `popularCountries` is intersected with this set. When the list resolves\n * to a single country the picker collapses to a fixed, non-interactive\n * dial code — only the national-number field stays editable.\n */\n allowedCountries?: CountryCode[];\n className?: string;\n id?: string;\n name?: string;\n required?: boolean;\n 'aria-label'?: string;\n}\n\nconst phoneInputWrapperVariants = cva(\n [\n 'ds:group ds:relative ds:inline-flex ds:items-stretch ds:w-full',\n 'ds:rounded-[var(--radius-sm)] ds:border ds:border-border ds:bg-input',\n 'ds:shadow-[var(--shadow-input)]',\n 'ds:text-foreground',\n 'ds:transition-[colors,box-shadow] ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:focus-within:outline-[length:var(--focus-ring-width)] ds:focus-within:outline-solid',\n 'ds:focus-within:outline-ring ds:focus-within:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-within:outline-[CanvasText]',\n 'ds:has-[:disabled]:cursor-not-allowed ds:has-[:disabled]:opacity-50',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:h-8 ds:text-[length:var(--font-size-sm)]',\n md: 'ds:h-[var(--min-target-size)] ds:text-[length:var(--font-size-base)]',\n lg: 'ds:h-12 ds:text-[length:var(--font-size-lg)]',\n },\n tone: {\n default: '',\n error: 'ds:border-destructive ds:focus-within:outline-destructive',\n },\n },\n defaultVariants: {\n size: 'md',\n tone: 'default',\n },\n },\n);\n\nconst countryTriggerVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:bg-transparent ds:text-foreground',\n 'ds:border-0 ds:border-e ds:border-e-border',\n 'ds:rounded-s-[var(--radius-sm)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:hover:bg-muted',\n 'ds:focus:outline-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:cursor-not-allowed',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:ps-3 ds:pe-2',\n md: 'ds:ps-3 ds:pe-2',\n lg: 'ds:ps-4 ds:pe-3',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst phoneNumberInputVariants = cva(\n [\n 'ds:flex-1 ds:min-w-0 ds:bg-transparent ds:text-foreground ds:placeholder:text-muted-foreground',\n 'ds:border-0 ds:outline-none',\n 'ds:disabled:cursor-not-allowed',\n 'ds:read-only:cursor-default',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:ps-2 ds:pe-3 ds:text-[length:var(--font-size-sm)]',\n md: 'ds:ps-2 ds:pe-3 ds:text-[length:var(--font-size-base)]',\n lg: 'ds:ps-3 ds:pe-4 ds:text-[length:var(--font-size-lg)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst pickerContentClasses = [\n 'ds:z-[var(--z-popover)] ds:overflow-hidden ds:w-72',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-border ds:bg-background ds:text-foreground',\n 'ds:shadow-[var(--shadow-lg)]',\n 'ds:animate-in ds:fade-in ds:zoom-in-95',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:fade-out',\n 'ds:data-[state=closed]:zoom-out-95',\n 'ds:data-[side=bottom]:slide-in-from-top-2',\n 'ds:data-[side=top]:slide-in-from-bottom-2',\n 'ds:motion-reduce:animate-none',\n].join(' ');\n\nconst pickerSearchClasses = [\n 'ds:w-full ds:bg-transparent ds:text-foreground ds:placeholder:text-muted-foreground',\n 'ds:border-0 ds:border-b ds:border-b-border ds:outline-none',\n 'ds:ps-3 ds:pe-3 ds:py-2 ds:text-[var(--font-size-sm)]',\n 'ds:focus:outline-none',\n].join(' ');\n\nconst pickerListClasses =\n 'ds:max-h-72 ds:overflow-y-auto ds:p-[var(--spacing-xs)]';\n\nconst pickerItemClasses = [\n 'ds:relative ds:flex ds:cursor-pointer ds:items-center ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-sm)] ds:ps-3 ds:pe-3 ds:py-2 ds:text-[var(--font-size-sm)]',\n 'ds:text-foreground ds:outline-none ds:select-none',\n 'ds:data-[highlighted]:bg-muted ds:data-[highlighted]:text-foreground',\n 'ds:data-[highlighted]:border-s-2 ds:data-[highlighted]:border-s-primary',\n 'ds:aria-selected:bg-muted',\n].join(' ');\n\nconst pickerGroupHeadingClasses = [\n 'ds:ps-3 ds:pe-3 ds:py-1.5',\n 'type-eyebrow ds:text-muted-foreground',\n].join(' ');\n\nconst pickerEmptyClasses =\n 'ds:ps-3 ds:pe-3 ds:py-2 type-body-sm ds:text-muted-foreground';\n\nconst chevronSizeBySize = {\n sm: 'ds:size-3.5',\n md: 'ds:size-4',\n lg: 'ds:size-5',\n} as const;\n\nconst flagSizeBySize = {\n sm: 'sm',\n md: 'md',\n lg: 'lg',\n} as const satisfies Record<'sm' | 'md' | 'lg', 'sm' | 'md' | 'lg'>;\n\nfunction countDigitsBefore(str: string, position: number): number {\n let digits = 0;\n for (let i = 0; i < position && i < str.length; i++) {\n if (str[i] >= '0' && str[i] <= '9') digits++;\n }\n return digits;\n}\n\nfunction positionAfterDigits(str: string, targetDigits: number): number {\n if (targetDigits === 0) return 0;\n let count = 0;\n for (let i = 0; i < str.length; i++) {\n if (str[i] >= '0' && str[i] <= '9') {\n count++;\n if (count === targetDigits) return i + 1;\n }\n }\n return str.length;\n}\n\nfunction resolveDefaultCountry(\n explicit: CountryCode | undefined,\n supported: Set<string>,\n detectBrowser: boolean,\n locale: string,\n allowedList: CountryCode[] | null,\n): CountryCode {\n const allowed =\n allowedList && allowedList.length ? new Set<string>(allowedList) : null;\n const ok = (code: string) =>\n supported.has(code) && (!allowed || allowed.has(code));\n\n if (explicit && ok(explicit)) return explicit;\n\n if (detectBrowser) {\n const fromNavigator =\n typeof navigator !== 'undefined' ? navigator.language : undefined;\n if (fromNavigator) {\n const match = fromNavigator.match(/-([A-Za-z]{2})/);\n if (match) {\n const candidate = match[1].toUpperCase();\n if (ok(candidate)) return candidate as CountryCode;\n }\n }\n\n try {\n const region = new Intl.Locale(locale).region;\n if (region && ok(region.toUpperCase())) {\n return region.toUpperCase() as CountryCode;\n }\n } catch {\n // Intl.Locale may throw on malformed BCP-47 — fall through.\n }\n }\n\n // A restricted list with no explicit / detected hit defaults to the\n // consumer's first listed country; otherwise AlfaDocs's Italian-first\n // fallback.\n if (allowed) return allowedList![0];\n return 'IT';\n}\n\ninterface CountryRow {\n code: CountryCode;\n name: string;\n dialCode: string;\n}\n\nfunction buildCountryRows(\n locale: string,\n countries: CountryCode[],\n): CountryRow[] {\n let displayNames: Intl.DisplayNames;\n try {\n displayNames = new Intl.DisplayNames([locale], { type: 'region' });\n } catch {\n displayNames = new Intl.DisplayNames(['en'], { type: 'region' });\n }\n const rows = countries.map<CountryRow>((code) => ({\n code,\n name: displayNames.of(code) ?? code,\n dialCode: getCountryCallingCode(code),\n }));\n const collator = new Intl.Collator(locale);\n return rows.sort((a, b) => collator.compare(a.name, b.name));\n}\n\nexport const PhoneInput = forwardRef<HTMLInputElement, PhoneInputProps>(\n function PhoneInput(\n {\n value,\n defaultValue,\n defaultCountry,\n detectBrowserCountry = false,\n onChange,\n size = 'md',\n tone = 'default',\n disabled,\n readOnly,\n placeholder,\n popularCountries,\n allowedCountries,\n className,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n },\n ref,\n ) {\n const { t, i18n } = useTranslation();\n const ctx = useFormField();\n const inFormField = useContext(FormFieldContext) !== null;\n\n const supportedCountries = useMemo(\n () => new Set(getCountries() as string[]),\n [],\n );\n\n // Consumer-restricted country set, filtered to supported + deduped with\n // order preserved. `null` means \"no restriction\" (every country shown).\n const allowedList = useMemo<CountryCode[] | null>(() => {\n if (!allowedCountries || allowedCountries.length === 0) return null;\n const seen = new Set<string>();\n const filtered = allowedCountries.filter((code) => {\n if (!supportedCountries.has(code) || seen.has(code)) return false;\n seen.add(code);\n return true;\n });\n return filtered.length > 0 ? filtered : null;\n }, [allowedCountries, supportedCountries]);\n\n // A single allowed country has nothing to pick — render the dial code as a\n // fixed, non-interactive prefix instead of a one-item picker.\n const isCountryFixed = allowedList !== null && allowedList.length === 1;\n\n const initialState = useMemo(() => {\n const resolvedCountry = resolveDefaultCountry(\n defaultCountry,\n supportedCountries,\n detectBrowserCountry,\n i18n.language,\n allowedList,\n );\n const raw = value ?? defaultValue;\n if (raw) {\n const parsed = parsePhoneNumberFromString(raw);\n if (parsed?.country) {\n // Clamp the parsed country to `allowedCountries` so the rendered dial\n // code can never leave the allowed set — a single-country lock stays\n // on its country even when handed an off-set value.\n if (!allowedList || allowedList.includes(parsed.country)) {\n return {\n country: parsed.country,\n display: parsed.formatNational(),\n pinned: true,\n };\n }\n return {\n country: resolvedCountry,\n display: new AsYouType(resolvedCountry).input(\n parsed.nationalNumber,\n ),\n pinned: true,\n };\n }\n return { country: resolvedCountry, display: raw, pinned: false };\n }\n return { country: resolvedCountry, display: '', pinned: false };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const [country, setCountry] = useState<CountryCode>(initialState.country);\n const [displayValue, setDisplayValue] = useState<string>(\n initialState.display,\n );\n const [pickerOpen, setPickerOpen] = useState(false);\n const [pickerSearch, setPickerSearch] = useState('');\n\n const isControlled = value !== undefined;\n const lastEmittedRef = useRef<string>('');\n const pendingCaretRef = useRef<number | null>(null);\n // Pins the country once the user (or an incoming value) selects one,\n // so the locale-flip effect below stops re-resolving.\n const userSelectedCountryRef = useRef(initialState.pinned);\n\n const inputRef = useRef<HTMLInputElement | null>(null);\n const composedInputRef = composeRefs(ref, inputRef);\n\n // Re-resolve the country when i18n.language flips from the bootstrap\n // fallback to the real locale on first paint, unless the user has\n // already pinned a country.\n useEffect(() => {\n if (userSelectedCountryRef.current) return;\n const next = resolveDefaultCountry(\n defaultCountry,\n supportedCountries,\n detectBrowserCountry,\n i18n.language,\n allowedList,\n );\n setCountry((prev) => (prev === next ? prev : next));\n }, [\n i18n.language,\n defaultCountry,\n supportedCountries,\n detectBrowserCountry,\n allowedList,\n ]);\n\n // Sync state back from a controlled `value` prop change that did not\n // originate from this component (e.g. parent reset). Skip the sync when\n // the incoming value matches what we just emitted.\n useEffect(() => {\n if (!isControlled) return;\n const current = value ?? '';\n if (current === lastEmittedRef.current) return;\n if (current === '') {\n setDisplayValue('');\n return;\n }\n const parsed = parsePhoneNumberFromString(current);\n if (\n parsed?.country &&\n (!allowedList || allowedList.includes(parsed.country))\n ) {\n userSelectedCountryRef.current = true;\n setCountry(parsed.country);\n setDisplayValue(parsed.formatNational());\n } else {\n // Off-set or unparseable controlled value — keep the country within the\n // allowed set; only sync the displayed digits.\n setDisplayValue(parsed ? parsed.formatNational() : current);\n }\n }, [value, isControlled, allowedList]);\n\n useIsomorphicLayoutEffect(() => {\n if (pendingCaretRef.current == null) return;\n const pos = pendingCaretRef.current;\n pendingCaretRef.current = null;\n const node = inputRef.current;\n if (node && document.activeElement === node) {\n node.setSelectionRange(pos, pos);\n }\n });\n\n const emit = useCallback(\n (nextDisplay: string, nextCountry: CountryCode) => {\n const parsed = parsePhoneNumberFromString(nextDisplay, nextCountry);\n const e164 = parsed?.number ?? '';\n lastEmittedRef.current = e164;\n onChange?.(e164, {\n country: nextCountry,\n isValid: parsed?.isValid() ?? false,\n isPossible: parsed?.isPossible() ?? false,\n type: parsed?.getType(),\n });\n },\n [onChange],\n );\n\n const inputId = id ?? (inFormField ? ctx.id : undefined);\n const effectiveDisabled =\n (inFormField ? ctx.disabled : false) || Boolean(disabled);\n const effectiveRequired =\n (inFormField ? ctx.required : false) || Boolean(required);\n const effectiveInvalid = inFormField ? ctx.invalid : false;\n const effectiveTone = effectiveInvalid ? 'error' : tone;\n const describedBy =\n inFormField && ctx.describedBy ? ctx.describedBy : undefined;\n\n const resolvedPlaceholder =\n placeholder ?? t('inputs.phone.placeholder', 'Phone number');\n\n const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {\n if (readOnly) return;\n const target = event.target;\n const raw = target.value;\n const caret = target.selectionStart ?? raw.length;\n const digitsBefore = countDigitsBefore(raw, caret);\n\n const formatter = new AsYouType(country);\n const formatted = formatter.input(raw);\n\n pendingCaretRef.current = positionAfterDigits(formatted, digitsBefore);\n setDisplayValue(formatted);\n emit(formatted, country);\n };\n\n const lastEmittedRefForHandle = lastEmittedRef;\n const agentHandle = useMemo<PhoneInputHandle>(\n () => ({\n getValue: () => lastEmittedRefForHandle.current,\n setValue: (next) => {\n if (next === '') {\n setDisplayValue('');\n emit('', country);\n return;\n }\n const parsed = parsePhoneNumberFromString(next);\n if (parsed?.country) {\n userSelectedCountryRef.current = true;\n setCountry(parsed.country);\n setDisplayValue(parsed.formatNational());\n emit(parsed.formatNational(), parsed.country);\n } else {\n const formatter = new AsYouType(country);\n const formatted = formatter.input(next);\n setDisplayValue(formatted);\n emit(formatted, country);\n }\n },\n clear: () => {\n setDisplayValue('');\n emit('', country);\n },\n focus: () => inputRef.current?.focus(),\n }),\n [country, emit, lastEmittedRefForHandle],\n );\n useAgentRegistration(phoneInputAgent, agentHandle, id);\n\n const handleCountrySelect = (nextCountry: CountryCode) => {\n if (nextCountry === country) {\n setPickerOpen(false);\n return;\n }\n const digits = displayValue.replace(/\\D/g, '');\n const formatter = new AsYouType(nextCountry);\n const reformatted = digits ? formatter.input(digits) : '';\n userSelectedCountryRef.current = true;\n setCountry(nextCountry);\n setDisplayValue(reformatted);\n setPickerOpen(false);\n setPickerSearch('');\n emit(reformatted, nextCountry);\n requestAnimationFrame(() => {\n inputRef.current?.focus();\n });\n };\n\n const countryRows = useMemo(\n () =>\n buildCountryRows(\n i18n.language,\n allowedList ?? (Array.from(supportedCountries) as CountryCode[]),\n ),\n [i18n.language, supportedCountries, allowedList],\n );\n\n const popularRows = useMemo(() => {\n if (!popularCountries || popularCountries.length === 0) return [];\n const byCode = new Map(countryRows.map((row) => [row.code, row]));\n return popularCountries\n .filter((code): code is CountryCode => supportedCountries.has(code))\n .map((code) => byCode.get(code))\n .filter((row): row is CountryRow => Boolean(row));\n }, [popularCountries, countryRows, supportedCountries]);\n\n const popularCodes = useMemo(\n () => new Set(popularRows.map((row) => row.code)),\n [popularRows],\n );\n\n const remainingRows = useMemo(\n () =>\n popularCodes.size > 0\n ? countryRows.filter((row) => !popularCodes.has(row.code))\n : countryRows,\n [countryRows, popularCodes],\n );\n\n const selected = useMemo(\n () => countryRows.find((row) => row.code === country),\n [countryRows, country],\n );\n const selectedDialCode = selected ? selected.dialCode : '';\n const selectedName = selected ? selected.name : country;\n\n const triggerLabel = t('inputs.phone.countryPickerLabel', 'Country');\n const triggerAccessibleName = `${triggerLabel}: ${selectedName}, +${selectedDialCode}`;\n\n const filterFn = useCallback(\n (candidate: string, searchStr: string, keywords?: string[]) => {\n const normalizedCandidate = normalizeDiacritics(candidate);\n const normalizedSearch = normalizeDiacritics(searchStr);\n const normalizedKeywords = keywords?.map(normalizeDiacritics);\n return defaultFilter(\n normalizedCandidate,\n normalizedSearch,\n normalizedKeywords,\n );\n },\n [],\n );\n\n const renderRow = (row: CountryRow) => {\n const accessibleLabel = `${row.name}, +${row.dialCode}`;\n return (\n <Command.Item\n key={row.code}\n value={row.code}\n keywords={[row.name, row.dialCode, `+${row.dialCode}`]}\n onSelect={() => handleCountrySelect(row.code)}\n className={pickerItemClasses}\n aria-label={accessibleLabel}\n >\n <Flag country={row.code} size={flagSizeBySize[size]} />\n <span className=\"ds:flex-1 ds:truncate\">{row.name}</span>\n <bdi dir=\"ltr\" className=\"ds:text-muted-foreground ds:tabular-nums\">\n +{row.dialCode}\n </bdi>\n </Command.Item>\n );\n };\n\n return (\n <div\n data-component=\"phone-input\"\n data-component-id={id}\n data-slot=\"phone-input\"\n className={phoneInputWrapperVariants({\n size,\n tone: effectiveTone,\n className,\n })}\n >\n {isCountryFixed ? (\n <span\n data-slot=\"phone-country-fixed\"\n className={`${countryTriggerVariants({ size })} ds:cursor-default ds:hover:bg-transparent`}\n >\n <span className=\"ds:sr-only\">{`${triggerLabel}: ${selectedName}`}</span>\n <Flag country={country} size={flagSizeBySize[size]} />\n <bdi dir=\"ltr\" className=\"ds:tabular-nums\">\n +{selectedDialCode}\n </bdi>\n </span>\n ) : (\n <Popover.Root open={pickerOpen} onOpenChange={setPickerOpen}>\n <Popover.Trigger asChild>\n <button\n type=\"button\"\n disabled={effectiveDisabled}\n aria-haspopup=\"listbox\"\n aria-expanded={pickerOpen}\n aria-label={triggerAccessibleName}\n className={countryTriggerVariants({ size })}\n >\n <Flag country={country} size={flagSizeBySize[size]} />\n <bdi dir=\"ltr\" className=\"ds:tabular-nums\">\n +{selectedDialCode}\n </bdi>\n <ChevronDown\n aria-hidden=\"true\"\n className={[\n chevronSizeBySize[size],\n 'ds:shrink-0 ds:text-muted-foreground',\n 'ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n pickerOpen ? 'ds:rotate-180' : '',\n ].join(' ')}\n />\n </button>\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n sideOffset={6}\n align=\"start\"\n className={pickerContentClasses}\n >\n <Command\n filter={filterFn}\n shouldFilter\n loop\n label={triggerLabel}\n >\n <Command.Input\n asChild\n value={pickerSearch}\n onValueChange={setPickerSearch}\n >\n <input\n data-phone-country-search=\"true\"\n type=\"text\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n placeholder={t(\n 'inputs.phone.searchCountry',\n 'Search countries…',\n )}\n className={pickerSearchClasses}\n />\n </Command.Input>\n <Command.List className={pickerListClasses}>\n <Command.Empty className={pickerEmptyClasses}>\n {t('inputs.phone.noCountryFound', 'No country found')}\n </Command.Empty>\n {popularRows.length > 0 ? (\n <Command.Group\n heading={\n <span className={pickerGroupHeadingClasses}>\n {t('inputs.phone.popularCountries', 'Popular')}\n </span>\n }\n >\n {popularRows.map(renderRow)}\n </Command.Group>\n ) : null}\n <Command.Group\n heading={\n popularRows.length > 0 ? (\n <span className={pickerGroupHeadingClasses}>\n {t('inputs.phone.allCountries', 'All countries')}\n </span>\n ) : undefined\n }\n >\n {remainingRows.map(renderRow)}\n </Command.Group>\n </Command.List>\n </Command>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n )}\n <input\n ref={composedInputRef}\n id={inputId}\n name={name}\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel-national\"\n value={displayValue}\n placeholder={resolvedPlaceholder}\n disabled={effectiveDisabled}\n readOnly={readOnly}\n required={effectiveRequired}\n aria-label={ariaLabel}\n aria-describedby={describedBy}\n aria-invalid={effectiveInvalid || undefined}\n onChange={handleInputChange}\n className={phoneNumberInputVariants({ size })}\n />\n </div>\n );\n },\n);\n\nPhoneInput.displayName = 'PhoneInput';\n\nexport {\n phoneInputWrapperVariants,\n countryTriggerVariants,\n phoneNumberInputVariants,\n};\n"],"names":["phoneInputAgent","handle","args","phoneInputWrapperVariants","cva","countryTriggerVariants","phoneNumberInputVariants","pickerContentClasses","pickerSearchClasses","pickerListClasses","pickerItemClasses","pickerGroupHeadingClasses","pickerEmptyClasses","chevronSizeBySize","flagSizeBySize","countDigitsBefore","str","position","digits","i","positionAfterDigits","targetDigits","count","resolveDefaultCountry","explicit","supported","detectBrowser","locale","allowedList","allowed","ok","code","fromNavigator","match","candidate","region","buildCountryRows","countries","displayNames","rows","getCountryCallingCode","collator","a","b","PhoneInput","forwardRef","value","defaultValue","defaultCountry","detectBrowserCountry","onChange","size","tone","disabled","readOnly","placeholder","popularCountries","allowedCountries","className","id","name","required","ariaLabel","ref","t","i18n","useTranslation","ctx","useFormField","inFormField","useContext","FormFieldContext","supportedCountries","useMemo","getCountries","seen","filtered","isCountryFixed","initialState","resolvedCountry","raw","parsed","parsePhoneNumberFromString","AsYouType","country","setCountry","useState","displayValue","setDisplayValue","pickerOpen","setPickerOpen","pickerSearch","setPickerSearch","isControlled","lastEmittedRef","useRef","pendingCaretRef","userSelectedCountryRef","inputRef","composedInputRef","composeRefs","useEffect","next","prev","current","useIsomorphicLayoutEffect","pos","node","emit","useCallback","nextDisplay","nextCountry","e164","inputId","effectiveDisabled","effectiveRequired","effectiveInvalid","effectiveTone","describedBy","resolvedPlaceholder","handleInputChange","event","target","caret","digitsBefore","formatted","lastEmittedRefForHandle","agentHandle","_a","useAgentRegistration","handleCountrySelect","formatter","reformatted","countryRows","popularRows","byCode","row","popularCodes","remainingRows","selected","selectedDialCode","selectedName","triggerLabel","triggerAccessibleName","filterFn","searchStr","keywords","normalizedCandidate","normalizeDiacritics","normalizedSearch","normalizedKeywords","defaultFilter","renderRow","accessibleLabel","jsxs","Command","jsx","Flag","Popover","ChevronDown"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"phone-input-BsZIoE36.js","sources":["../../src/components/phone-input/phone-input.agent.ts","../../src/components/phone-input/phone-input.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — PhoneInput. */\n/* */\n/* See `src/docs/26-agent-readiness.mdx` for the contract. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { PhoneInputHandle } from './phone-input';\n\nexport const phoneInputAgent: AgentAdapter<PhoneInputHandle> = {\n id: 'phone-input',\n // A phone number is a single form value, not free-form rich text — `set_value`\n // (not `edit_inline`, which is reserved for in-place rich/cell editing).\n capabilities: ['set_value'],\n state: {\n value: {\n type: 'string',\n descriptionKey: 'ui.agent.phoneInput.state.value',\n description:\n 'Current E.164 phone number, or empty string when not yet valid.',\n read: (handle) => handle.getValue(),\n },\n isEmpty: {\n type: 'boolean',\n descriptionKey: 'ui.agent.phoneInput.state.isEmpty',\n description: 'Whether the input has no value.',\n read: (handle) => handle.getValue() === '',\n },\n },\n actions: {\n set_value: {\n safety: 'write',\n argsType: '{ value: string }',\n argsSchema: {\n type: 'object',\n properties: {\n value: {\n type: 'string',\n description:\n 'Phone number in E.164 or national format; the parser infers the country.',\n },\n },\n required: ['value'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.phoneInput.actions.setValue',\n description:\n 'Replace the phone number (E.164 or national format; parser will infer country).',\n examples: [\n { args: { value: '+393331234567' }, note: 'Set an Italian mobile.' },\n ],\n invoke: (handle, args: { value: string }) => {\n handle.setValue(args.value);\n },\n },\n clear: {\n safety: 'destructive',\n idempotent: true,\n descriptionKey: 'ui.agent.phoneInput.actions.clear',\n description: 'Empty the input. Loses any typed value.',\n invoke: (handle) => {\n handle.clear();\n },\n },\n focus: {\n safety: 'read',\n descriptionKey: 'ui.agent.phoneInput.actions.focus',\n description: 'Move keyboard focus to the phone-number input.',\n invoke: (handle) => {\n handle.focus();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'phone-input',\n description: 'Marks the PhoneInput wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n} from 'react';\nimport { useAgentRegistration } from '../../agent';\nimport { phoneInputAgent } from './phone-input.agent';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\n\n/** Agent-readiness curated handle for PhoneInput. */\nexport interface PhoneInputHandle {\n getValue: () => string;\n setValue: (value: string) => void;\n clear: () => void;\n focus: () => void;\n}\nimport * as Popover from '@radix-ui/react-popover';\nimport { Command, defaultFilter } from 'cmdk';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { ChevronDown } from 'lucide-react';\nimport {\n AsYouType,\n getCountries,\n getCountryCallingCode,\n parsePhoneNumberFromString,\n type CountryCode,\n} from 'libphonenumber-js/min';\nimport {\n FormFieldContext,\n useFormField,\n} from '../form-field/form-field-context';\nimport { composeRefs } from '../_shared/compose-refs';\nimport { normalizeDiacritics } from '../_shared/normalize-diacritics';\nimport { Flag } from '../flag';\n\nexport interface PhoneInputChangeMeta {\n country: CountryCode;\n isValid: boolean;\n isPossible: boolean;\n type?: string;\n}\n\nexport interface PhoneInputProps {\n value?: string;\n defaultValue?: string;\n /**\n * Country pre-selected when no value is supplied. Defaults to `IT`\n * (AlfaDocs's dominant user base is Italian). Pass a different\n * `CountryCode` to override, or enable `detectBrowserCountry` to\n * derive it from the browser locale.\n */\n defaultCountry?: CountryCode;\n /**\n * When true, infer the default country from `navigator.language` /\n * `Intl.Locale` before falling back to `defaultCountry` (or `IT`).\n * Off by default — most AlfaDocs flows should stay on IT regardless\n * of the visitor's browser locale.\n */\n detectBrowserCountry?: boolean;\n onChange?: (e164: string, meta: PhoneInputChangeMeta) => void;\n size?: 'sm' | 'md' | 'lg';\n tone?: 'default' | 'error';\n disabled?: boolean;\n readOnly?: boolean;\n placeholder?: string;\n popularCountries?: CountryCode[];\n /**\n * Restrict the country picker to this set (ISO 3166-1 alpha-2 codes), in\n * the order given. Unsupported or duplicate codes are dropped; an empty /\n * all-invalid list is ignored (every country stays selectable).\n * `popularCountries` is intersected with this set. When the list resolves\n * to a single country the picker collapses to a fixed, non-interactive\n * dial code — only the national-number field stays editable.\n */\n allowedCountries?: CountryCode[];\n className?: string;\n id?: string;\n name?: string;\n required?: boolean;\n 'aria-label'?: string;\n}\n\nconst phoneInputWrapperVariants = cva(\n [\n 'ds:group ds:relative ds:inline-flex ds:items-stretch ds:w-full',\n 'ds:rounded-[var(--radius-sm)] ds:border ds:border-border ds:bg-input',\n 'ds:shadow-[var(--shadow-input)]',\n 'ds:text-foreground',\n 'ds:transition-[colors,box-shadow] ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:focus-within:outline-[length:var(--focus-ring-width)] ds:focus-within:outline-solid',\n 'ds:focus-within:outline-ring ds:focus-within:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-within:outline-[CanvasText]',\n 'ds:has-[:disabled]:cursor-not-allowed ds:has-[:disabled]:opacity-50',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:h-8 ds:text-[length:var(--font-size-sm)]',\n md: 'ds:h-[var(--min-target-size)] ds:text-[length:var(--font-size-base)]',\n lg: 'ds:h-12 ds:text-[length:var(--font-size-lg)]',\n },\n tone: {\n default: '',\n error: 'ds:border-destructive ds:focus-within:outline-destructive',\n },\n },\n defaultVariants: {\n size: 'md',\n tone: 'default',\n },\n },\n);\n\nconst countryTriggerVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:bg-transparent ds:text-foreground',\n 'ds:border-0 ds:border-e ds:border-e-border',\n 'ds:rounded-s-[var(--radius-sm)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:hover:bg-muted',\n 'ds:focus:outline-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:cursor-not-allowed',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:ps-3 ds:pe-2',\n md: 'ds:ps-3 ds:pe-2',\n lg: 'ds:ps-4 ds:pe-3',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst phoneNumberInputVariants = cva(\n [\n 'ds:flex-1 ds:min-w-0 ds:bg-transparent ds:text-foreground ds:placeholder:text-muted-foreground',\n 'ds:border-0 ds:outline-none',\n 'ds:disabled:cursor-not-allowed',\n 'ds:read-only:cursor-default',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:ps-2 ds:pe-3 ds:text-[length:var(--font-size-sm)]',\n md: 'ds:ps-2 ds:pe-3 ds:text-[length:var(--font-size-base)]',\n lg: 'ds:ps-3 ds:pe-4 ds:text-[length:var(--font-size-lg)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst pickerContentClasses = [\n 'ds:z-[var(--z-popover)] ds:overflow-hidden ds:w-72',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-border ds:bg-background ds:text-foreground',\n 'ds:shadow-[var(--shadow-lg)]',\n 'ds:animate-in ds:fade-in ds:zoom-in-95',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:fade-out',\n 'ds:data-[state=closed]:zoom-out-95',\n 'ds:data-[side=bottom]:slide-in-from-top-2',\n 'ds:data-[side=top]:slide-in-from-bottom-2',\n 'ds:motion-reduce:animate-none',\n].join(' ');\n\nconst pickerSearchClasses = [\n 'ds:w-full ds:bg-transparent ds:text-foreground ds:placeholder:text-muted-foreground',\n 'ds:border-0 ds:border-b ds:border-b-border ds:outline-none',\n 'ds:ps-3 ds:pe-3 ds:py-2 ds:text-[var(--font-size-sm)]',\n 'ds:focus:outline-none',\n].join(' ');\n\nconst pickerListClasses =\n 'ds:max-h-72 ds:overflow-y-auto ds:p-[var(--spacing-xs)]';\n\nconst pickerItemClasses = [\n 'ds:relative ds:flex ds:cursor-pointer ds:items-center ds:gap-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-sm)] ds:ps-3 ds:pe-3 ds:py-2 ds:text-[var(--font-size-sm)]',\n 'ds:text-foreground ds:outline-none ds:select-none',\n 'ds:data-[highlighted]:bg-muted ds:data-[highlighted]:text-foreground',\n 'ds:data-[highlighted]:border-s-2 ds:data-[highlighted]:border-s-primary',\n 'ds:aria-selected:bg-muted',\n].join(' ');\n\nconst pickerGroupHeadingClasses = [\n 'ds:ps-3 ds:pe-3 ds:py-1.5',\n 'type-eyebrow ds:text-muted-foreground',\n].join(' ');\n\nconst pickerEmptyClasses =\n 'ds:ps-3 ds:pe-3 ds:py-2 type-body-sm ds:text-muted-foreground';\n\nconst chevronSizeBySize = {\n sm: 'ds:size-3.5',\n md: 'ds:size-4',\n lg: 'ds:size-5',\n} as const;\n\nconst flagSizeBySize = {\n sm: 'sm',\n md: 'md',\n lg: 'lg',\n} as const satisfies Record<'sm' | 'md' | 'lg', 'sm' | 'md' | 'lg'>;\n\nfunction countDigitsBefore(str: string, position: number): number {\n let digits = 0;\n for (let i = 0; i < position && i < str.length; i++) {\n if (str[i] >= '0' && str[i] <= '9') digits++;\n }\n return digits;\n}\n\nfunction positionAfterDigits(str: string, targetDigits: number): number {\n if (targetDigits === 0) return 0;\n let count = 0;\n for (let i = 0; i < str.length; i++) {\n if (str[i] >= '0' && str[i] <= '9') {\n count++;\n if (count === targetDigits) return i + 1;\n }\n }\n return str.length;\n}\n\nfunction resolveDefaultCountry(\n explicit: CountryCode | undefined,\n supported: Set<string>,\n detectBrowser: boolean,\n locale: string,\n allowedList: CountryCode[] | null,\n): CountryCode {\n const allowed =\n allowedList && allowedList.length ? new Set<string>(allowedList) : null;\n const ok = (code: string) =>\n supported.has(code) && (!allowed || allowed.has(code));\n\n if (explicit && ok(explicit)) return explicit;\n\n if (detectBrowser) {\n const fromNavigator =\n typeof navigator !== 'undefined' ? navigator.language : undefined;\n if (fromNavigator) {\n const match = fromNavigator.match(/-([A-Za-z]{2})/);\n if (match) {\n const candidate = match[1].toUpperCase();\n if (ok(candidate)) return candidate as CountryCode;\n }\n }\n\n try {\n const region = new Intl.Locale(locale).region;\n if (region && ok(region.toUpperCase())) {\n return region.toUpperCase() as CountryCode;\n }\n } catch {\n // Intl.Locale may throw on malformed BCP-47 — fall through.\n }\n }\n\n // A restricted list with no explicit / detected hit defaults to the\n // consumer's first listed country; otherwise AlfaDocs's Italian-first\n // fallback.\n if (allowed) return allowedList![0];\n return 'IT';\n}\n\ninterface CountryRow {\n code: CountryCode;\n name: string;\n dialCode: string;\n}\n\nfunction buildCountryRows(\n locale: string,\n countries: CountryCode[],\n): CountryRow[] {\n let displayNames: Intl.DisplayNames;\n try {\n displayNames = new Intl.DisplayNames([locale], { type: 'region' });\n } catch {\n displayNames = new Intl.DisplayNames(['en'], { type: 'region' });\n }\n const rows = countries.map<CountryRow>((code) => ({\n code,\n name: displayNames.of(code) ?? code,\n dialCode: getCountryCallingCode(code),\n }));\n const collator = new Intl.Collator(locale);\n return rows.sort((a, b) => collator.compare(a.name, b.name));\n}\n\nexport const PhoneInput = forwardRef<HTMLInputElement, PhoneInputProps>(\n function PhoneInput(\n {\n value,\n defaultValue,\n defaultCountry,\n detectBrowserCountry = false,\n onChange,\n size = 'md',\n tone = 'default',\n disabled,\n readOnly,\n placeholder,\n popularCountries,\n allowedCountries,\n className,\n id,\n name,\n required,\n 'aria-label': ariaLabel,\n },\n ref,\n ) {\n const { t, i18n } = useTranslation();\n const ctx = useFormField();\n const inFormField = useContext(FormFieldContext) !== null;\n\n const supportedCountries = useMemo(\n () => new Set(getCountries() as string[]),\n [],\n );\n\n // Consumer-restricted country set, filtered to supported + deduped with\n // order preserved. `null` means \"no restriction\" (every country shown).\n const allowedList = useMemo<CountryCode[] | null>(() => {\n if (!allowedCountries || allowedCountries.length === 0) return null;\n const seen = new Set<string>();\n const filtered = allowedCountries.filter((code) => {\n if (!supportedCountries.has(code) || seen.has(code)) return false;\n seen.add(code);\n return true;\n });\n return filtered.length > 0 ? filtered : null;\n }, [allowedCountries, supportedCountries]);\n\n // A single allowed country has nothing to pick — render the dial code as a\n // fixed, non-interactive prefix instead of a one-item picker.\n const isCountryFixed = allowedList !== null && allowedList.length === 1;\n\n const initialState = useMemo(() => {\n const resolvedCountry = resolveDefaultCountry(\n defaultCountry,\n supportedCountries,\n detectBrowserCountry,\n i18n.language,\n allowedList,\n );\n const raw = value ?? defaultValue;\n if (raw) {\n const parsed = parsePhoneNumberFromString(raw);\n if (parsed?.country) {\n // Clamp the parsed country to `allowedCountries` so the rendered dial\n // code can never leave the allowed set — a single-country lock stays\n // on its country even when handed an off-set value.\n if (!allowedList || allowedList.includes(parsed.country)) {\n return {\n country: parsed.country,\n display: parsed.formatNational(),\n pinned: true,\n };\n }\n return {\n country: resolvedCountry,\n display: new AsYouType(resolvedCountry).input(\n parsed.nationalNumber,\n ),\n pinned: true,\n };\n }\n return { country: resolvedCountry, display: raw, pinned: false };\n }\n return { country: resolvedCountry, display: '', pinned: false };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const [country, setCountry] = useState<CountryCode>(initialState.country);\n const [displayValue, setDisplayValue] = useState<string>(\n initialState.display,\n );\n const [pickerOpen, setPickerOpen] = useState(false);\n const [pickerSearch, setPickerSearch] = useState('');\n\n const isControlled = value !== undefined;\n const lastEmittedRef = useRef<string>('');\n const pendingCaretRef = useRef<number | null>(null);\n // Pins the country once the user (or an incoming value) selects one,\n // so the locale-flip effect below stops re-resolving.\n const userSelectedCountryRef = useRef(initialState.pinned);\n\n const inputRef = useRef<HTMLInputElement | null>(null);\n const composedInputRef = composeRefs(ref, inputRef);\n\n // Re-resolve the country when i18n.language flips from the bootstrap\n // fallback to the real locale on first paint, unless the user has\n // already pinned a country.\n useEffect(() => {\n if (userSelectedCountryRef.current) return;\n const next = resolveDefaultCountry(\n defaultCountry,\n supportedCountries,\n detectBrowserCountry,\n i18n.language,\n allowedList,\n );\n setCountry((prev) => (prev === next ? prev : next));\n }, [\n i18n.language,\n defaultCountry,\n supportedCountries,\n detectBrowserCountry,\n allowedList,\n ]);\n\n // Sync state back from a controlled `value` prop change that did not\n // originate from this component (e.g. parent reset). Skip the sync when\n // the incoming value matches what we just emitted.\n useEffect(() => {\n if (!isControlled) return;\n const current = value ?? '';\n if (current === lastEmittedRef.current) return;\n if (current === '') {\n setDisplayValue('');\n return;\n }\n const parsed = parsePhoneNumberFromString(current);\n if (\n parsed?.country &&\n (!allowedList || allowedList.includes(parsed.country))\n ) {\n userSelectedCountryRef.current = true;\n setCountry(parsed.country);\n setDisplayValue(parsed.formatNational());\n } else {\n // Off-set or unparseable controlled value — keep the country within the\n // allowed set; only sync the displayed digits.\n setDisplayValue(parsed ? parsed.formatNational() : current);\n }\n }, [value, isControlled, allowedList]);\n\n useIsomorphicLayoutEffect(() => {\n if (pendingCaretRef.current == null) return;\n const pos = pendingCaretRef.current;\n pendingCaretRef.current = null;\n const node = inputRef.current;\n if (node && document.activeElement === node) {\n node.setSelectionRange(pos, pos);\n }\n });\n\n const emit = useCallback(\n (nextDisplay: string, nextCountry: CountryCode) => {\n const parsed = parsePhoneNumberFromString(nextDisplay, nextCountry);\n const e164 = parsed?.number ?? '';\n lastEmittedRef.current = e164;\n onChange?.(e164, {\n country: nextCountry,\n isValid: parsed?.isValid() ?? false,\n isPossible: parsed?.isPossible() ?? false,\n type: parsed?.getType(),\n });\n },\n [onChange],\n );\n\n const inputId = id ?? (inFormField ? ctx.id : undefined);\n const effectiveDisabled =\n (inFormField ? ctx.disabled : false) || Boolean(disabled);\n const effectiveRequired =\n (inFormField ? ctx.required : false) || Boolean(required);\n const effectiveInvalid = inFormField ? ctx.invalid : false;\n const effectiveTone = effectiveInvalid ? 'error' : tone;\n const describedBy =\n inFormField && ctx.describedBy ? ctx.describedBy : undefined;\n\n const resolvedPlaceholder =\n placeholder ?? t('inputs.phone.placeholder', 'Phone number');\n\n const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {\n if (readOnly) return;\n const target = event.target;\n const raw = target.value;\n const caret = target.selectionStart ?? raw.length;\n const digitsBefore = countDigitsBefore(raw, caret);\n\n const formatter = new AsYouType(country);\n const formatted = formatter.input(raw);\n\n pendingCaretRef.current = positionAfterDigits(formatted, digitsBefore);\n setDisplayValue(formatted);\n emit(formatted, country);\n };\n\n const lastEmittedRefForHandle = lastEmittedRef;\n const agentHandle = useMemo<PhoneInputHandle>(\n () => ({\n getValue: () => lastEmittedRefForHandle.current,\n setValue: (next) => {\n if (next === '') {\n setDisplayValue('');\n emit('', country);\n return;\n }\n const parsed = parsePhoneNumberFromString(next);\n if (parsed?.country) {\n userSelectedCountryRef.current = true;\n setCountry(parsed.country);\n setDisplayValue(parsed.formatNational());\n emit(parsed.formatNational(), parsed.country);\n } else {\n const formatter = new AsYouType(country);\n const formatted = formatter.input(next);\n setDisplayValue(formatted);\n emit(formatted, country);\n }\n },\n clear: () => {\n setDisplayValue('');\n emit('', country);\n },\n focus: () => inputRef.current?.focus(),\n }),\n [country, emit, lastEmittedRefForHandle],\n );\n useAgentRegistration(phoneInputAgent, agentHandle, id);\n\n const handleCountrySelect = (nextCountry: CountryCode) => {\n if (nextCountry === country) {\n setPickerOpen(false);\n return;\n }\n const digits = displayValue.replace(/\\D/g, '');\n const formatter = new AsYouType(nextCountry);\n const reformatted = digits ? formatter.input(digits) : '';\n userSelectedCountryRef.current = true;\n setCountry(nextCountry);\n setDisplayValue(reformatted);\n setPickerOpen(false);\n setPickerSearch('');\n emit(reformatted, nextCountry);\n requestAnimationFrame(() => {\n inputRef.current?.focus();\n });\n };\n\n const countryRows = useMemo(\n () =>\n buildCountryRows(\n i18n.language,\n allowedList ?? (Array.from(supportedCountries) as CountryCode[]),\n ),\n [i18n.language, supportedCountries, allowedList],\n );\n\n const popularRows = useMemo(() => {\n if (!popularCountries || popularCountries.length === 0) return [];\n const byCode = new Map(countryRows.map((row) => [row.code, row]));\n return popularCountries\n .filter((code): code is CountryCode => supportedCountries.has(code))\n .map((code) => byCode.get(code))\n .filter((row): row is CountryRow => Boolean(row));\n }, [popularCountries, countryRows, supportedCountries]);\n\n const popularCodes = useMemo(\n () => new Set(popularRows.map((row) => row.code)),\n [popularRows],\n );\n\n const remainingRows = useMemo(\n () =>\n popularCodes.size > 0\n ? countryRows.filter((row) => !popularCodes.has(row.code))\n : countryRows,\n [countryRows, popularCodes],\n );\n\n const selected = useMemo(\n () => countryRows.find((row) => row.code === country),\n [countryRows, country],\n );\n const selectedDialCode = selected ? selected.dialCode : '';\n const selectedName = selected ? selected.name : country;\n\n const triggerLabel = t('inputs.phone.countryPickerLabel', 'Country');\n const triggerAccessibleName = `${triggerLabel}: ${selectedName}, +${selectedDialCode}`;\n\n const filterFn = useCallback(\n (candidate: string, searchStr: string, keywords?: string[]) => {\n const normalizedCandidate = normalizeDiacritics(candidate);\n const normalizedSearch = normalizeDiacritics(searchStr);\n const normalizedKeywords = keywords?.map(normalizeDiacritics);\n return defaultFilter(\n normalizedCandidate,\n normalizedSearch,\n normalizedKeywords,\n );\n },\n [],\n );\n\n const renderRow = (row: CountryRow) => {\n const accessibleLabel = `${row.name}, +${row.dialCode}`;\n return (\n <Command.Item\n key={row.code}\n value={row.code}\n keywords={[row.name, row.dialCode, `+${row.dialCode}`]}\n onSelect={() => handleCountrySelect(row.code)}\n className={pickerItemClasses}\n aria-label={accessibleLabel}\n >\n <Flag country={row.code} size={flagSizeBySize[size]} />\n <span className=\"ds:flex-1 ds:truncate\">{row.name}</span>\n <bdi dir=\"ltr\" className=\"ds:text-muted-foreground ds:tabular-nums\">\n +{row.dialCode}\n </bdi>\n </Command.Item>\n );\n };\n\n return (\n <div\n data-component=\"phone-input\"\n data-component-id={id}\n data-slot=\"phone-input\"\n className={phoneInputWrapperVariants({\n size,\n tone: effectiveTone,\n className,\n })}\n >\n {isCountryFixed ? (\n <span\n data-slot=\"phone-country-fixed\"\n className={`${countryTriggerVariants({ size })} ds:cursor-default ds:hover:bg-transparent`}\n >\n <span className=\"ds:sr-only\">{`${triggerLabel}: ${selectedName}`}</span>\n <Flag country={country} size={flagSizeBySize[size]} />\n <bdi dir=\"ltr\" className=\"ds:tabular-nums\">\n +{selectedDialCode}\n </bdi>\n </span>\n ) : (\n <Popover.Root open={pickerOpen} onOpenChange={setPickerOpen}>\n <Popover.Trigger asChild>\n <button\n type=\"button\"\n disabled={effectiveDisabled}\n aria-haspopup=\"listbox\"\n aria-expanded={pickerOpen}\n aria-label={triggerAccessibleName}\n className={countryTriggerVariants({ size })}\n >\n <Flag country={country} size={flagSizeBySize[size]} />\n <bdi dir=\"ltr\" className=\"ds:tabular-nums\">\n +{selectedDialCode}\n </bdi>\n <ChevronDown\n aria-hidden=\"true\"\n className={[\n chevronSizeBySize[size],\n 'ds:shrink-0 ds:text-muted-foreground',\n 'ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n pickerOpen ? 'ds:rotate-180' : '',\n ].join(' ')}\n />\n </button>\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n sideOffset={6}\n align=\"start\"\n className={pickerContentClasses}\n >\n <Command\n filter={filterFn}\n shouldFilter\n loop\n label={triggerLabel}\n >\n <Command.Input\n asChild\n value={pickerSearch}\n onValueChange={setPickerSearch}\n >\n <input\n data-phone-country-search=\"true\"\n type=\"text\"\n autoComplete=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n placeholder={t(\n 'inputs.phone.searchCountry',\n 'Search countries…',\n )}\n className={pickerSearchClasses}\n />\n </Command.Input>\n <Command.List className={pickerListClasses}>\n <Command.Empty className={pickerEmptyClasses}>\n {t('inputs.phone.noCountryFound', 'No country found')}\n </Command.Empty>\n {popularRows.length > 0 ? (\n <Command.Group\n heading={\n <span className={pickerGroupHeadingClasses}>\n {t('inputs.phone.popularCountries', 'Popular')}\n </span>\n }\n >\n {popularRows.map(renderRow)}\n </Command.Group>\n ) : null}\n <Command.Group\n heading={\n popularRows.length > 0 ? (\n <span className={pickerGroupHeadingClasses}>\n {t('inputs.phone.allCountries', 'All countries')}\n </span>\n ) : undefined\n }\n >\n {remainingRows.map(renderRow)}\n </Command.Group>\n </Command.List>\n </Command>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n )}\n <input\n ref={composedInputRef}\n id={inputId}\n name={name}\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel-national\"\n value={displayValue}\n placeholder={resolvedPlaceholder}\n disabled={effectiveDisabled}\n readOnly={readOnly}\n required={effectiveRequired}\n aria-label={ariaLabel}\n aria-describedby={describedBy}\n aria-invalid={effectiveInvalid || undefined}\n onChange={handleInputChange}\n className={phoneNumberInputVariants({ size })}\n />\n </div>\n );\n },\n);\n\nPhoneInput.displayName = 'PhoneInput';\n\nexport {\n phoneInputWrapperVariants,\n countryTriggerVariants,\n phoneNumberInputVariants,\n};\n"],"names":["phoneInputAgent","handle","args","phoneInputWrapperVariants","cva","countryTriggerVariants","phoneNumberInputVariants","pickerContentClasses","pickerSearchClasses","pickerListClasses","pickerItemClasses","pickerGroupHeadingClasses","pickerEmptyClasses","chevronSizeBySize","flagSizeBySize","countDigitsBefore","str","position","digits","i","positionAfterDigits","targetDigits","count","resolveDefaultCountry","explicit","supported","detectBrowser","locale","allowedList","allowed","ok","code","fromNavigator","match","candidate","region","buildCountryRows","countries","displayNames","rows","getCountryCallingCode","collator","a","b","PhoneInput","forwardRef","value","defaultValue","defaultCountry","detectBrowserCountry","onChange","size","tone","disabled","readOnly","placeholder","popularCountries","allowedCountries","className","id","name","required","ariaLabel","ref","t","i18n","useTranslation","ctx","useFormField","inFormField","useContext","FormFieldContext","supportedCountries","useMemo","getCountries","seen","filtered","isCountryFixed","initialState","resolvedCountry","raw","parsed","parsePhoneNumberFromString","AsYouType","country","setCountry","useState","displayValue","setDisplayValue","pickerOpen","setPickerOpen","pickerSearch","setPickerSearch","isControlled","lastEmittedRef","useRef","pendingCaretRef","userSelectedCountryRef","inputRef","composedInputRef","composeRefs","useEffect","next","prev","current","useIsomorphicLayoutEffect","pos","node","emit","useCallback","nextDisplay","nextCountry","e164","inputId","effectiveDisabled","effectiveRequired","effectiveInvalid","effectiveTone","describedBy","resolvedPlaceholder","handleInputChange","event","target","caret","digitsBefore","formatted","lastEmittedRefForHandle","agentHandle","_a","useAgentRegistration","handleCountrySelect","formatter","reformatted","countryRows","popularRows","byCode","row","popularCodes","remainingRows","selected","selectedDialCode","selectedName","triggerLabel","triggerAccessibleName","filterFn","searchStr","keywords","normalizedCandidate","normalizeDiacritics","normalizedSearch","normalizedKeywords","defaultFilter","renderRow","accessibleLabel","jsxs","Command","jsx","Flag","Popover","ChevronDown"],"mappings":"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"}
|
|
@@ -168,7 +168,9 @@ const g = se("desktop"), ie = C(
|
|
|
168
168
|
// Position the Viewport flush below the pill / nav row. Radix sets
|
|
169
169
|
// `--radix-navigation-menu-viewport-width` and `…-height` so the
|
|
170
170
|
// panel sizes itself to whichever Content is currently active.
|
|
171
|
-
|
|
171
|
+
// Logical inset + physical translate: flip the shift in RTL, where
|
|
172
|
+
// `start-1/2` resolves to `right: 50%` (same idiom as CommandPalette).
|
|
173
|
+
"ds:absolute ds:top-full ds:start-1/2 ds:-translate-x-1/2 ds:rtl:translate-x-1/2",
|
|
172
174
|
"ds:mt-[var(--spacing-sm)]",
|
|
173
175
|
"ds:w-[var(--radix-navigation-menu-viewport-width)]",
|
|
174
176
|
"ds:h-[var(--radix-navigation-menu-viewport-height)]",
|
|
@@ -598,4 +600,4 @@ export {
|
|
|
598
600
|
Le as P,
|
|
599
601
|
Pe as p
|
|
600
602
|
};
|
|
601
|
-
//# sourceMappingURL=public-header.agent-
|
|
603
|
+
//# sourceMappingURL=public-header.agent-_8W9biCO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-header.agent-_8W9biCO.js","sources":["../../src/components/public-header/public-header.tsx","../../src/components/public-header/public-header.agent.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* PublicHeader — anonymous / pre-auth site header. */\n/* */\n/* Distinct from the kit's `<Header>` (authenticated app shell): this */\n/* one is for booking-website, signature-website, marketing surfaces — */\n/* anywhere the chrome should read as continuous with `alfadocs.com`, */\n/* not as the logged-in app. */\n/* */\n/* DOM (default / transparent / dark / brand variants): */\n/* <header role=banner data-component=public-header data-stuck=…> */\n/* <a aria-label=homeLabel>{logo}</a> */\n/* <Radix.NavigationMenu>…{navSlot}</Radix.NavigationMenu> ≥ md */\n/* <div class=actions>{actionsSlot}</div> ≥ md only */\n/* <IconButton menu trigger /> ↔ Sheet (partial drawer) < md only */\n/* </header> */\n/* */\n/* DOM (pill variant — matches alfadocs.com floating-pill header): */\n/* <header role=banner data-component=public-header> transparent */\n/* <div data-component=public-header-container> rounded */\n/* …same children as flat variants… */\n/* </div> */\n/* </header> */\n/* ------------------------------------------------------------------ */\n\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useImperativeHandle,\n useRef,\n useState,\n type AnchorHTMLAttributes,\n type ComponentPropsWithoutRef,\n type ElementRef,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { ChevronDown, Menu, X } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu';\nimport { IconButton } from '../button/icon-button';\nimport { Sheet } from '../sheet';\nimport { useVisualViewportPin } from '../../hooks/use-visual-viewport-pin';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface PublicHeaderHandle {\n openMenu: () => void;\n closeMenu: () => void;\n getMenuOpen: () => boolean;\n}\n\nexport interface PublicHeaderProps\n extends\n Omit<ComponentPropsWithoutRef<'header'>, 'children'>,\n VariantProps<typeof rootVariants> {\n /** Logo lockup, typically `<Logo variant=\"wordmark\" tone=\"primary\" size=\"md\" />`. */\n logo: ReactNode;\n /** Optional href for the logo link. Defaults to `/`. */\n homeHref?: string;\n /** Accessible label for the logo link (falls back to `t('publicHeader.homeLabel')`). */\n homeLabel?: string;\n /** Primary nav links rendered inline ≥ md, collapsed into the mobile sheet below md. */\n navSlot?: ReactNode;\n /** Right-side actions (sign-in / register / role-switch CTAs). */\n actionsSlot?: ReactNode;\n /** Sticky behaviour. Default `none`. */\n sticky?: 'none' | 'top' | 'scroll-up';\n /** Pixel offset above which `sticky=\"scroll-up\"` engages and `transparent` fades to default. */\n stickyOffset?: number;\n /** Override the mobile menu trigger's accessible label (`t('publicHeader.openMenu')`). */\n menuLabel?: string;\n /** Override the navigation landmark label (`t('publicHeader.primaryNavLabel')`). */\n navLabel?: string;\n /**\n * Keep `actionsSlot` visible inline at every breakpoint instead of\n * collapsing it into the mobile menu. When there is no `navSlot`, the\n * mobile menu trigger is omitted entirely (nothing left to collapse) — use\n * this for a header whose only action is a single always-relevant control,\n * e.g. a public signer's \"Sign out\". Default `false`.\n */\n persistActions?: boolean;\n /** Consumer-supplied instance id, surfaced as `data-component-id`. */\n id?: string;\n}\n\nexport interface PublicHeaderNavLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n 'children'\n> {\n href: string;\n active?: boolean;\n children: ReactNode;\n}\n\nexport interface PublicHeaderNavDropdownProps {\n /** Trigger label (the visible nav item text). */\n label: ReactNode;\n /**\n * `false` (default) renders a single-card dropdown anchored near the trigger\n * (Settori-style). `true` renders a wide multi-card mega-menu spanning the\n * pill's full inline width (Soluzioni-style).\n */\n wide?: boolean;\n /** Cards / content inside the dropdown panel. */\n children: ReactNode;\n /** Optional id; not required — used only for analytics / test selectors. */\n id?: string;\n}\n\nexport interface PublicHeaderDropdownCardProps {\n /**\n * Optional href — when provided the card title becomes a clickable\n * anchor with a `→` arrow indicator. Without href, the title is\n * a static heading (Settori-style).\n */\n href?: string;\n title: ReactNode;\n /** Optional subtitle / muted helper text under the title. */\n subtitle?: ReactNode;\n /** Sub-list contents (typically PublicHeader.DropdownLink children). */\n children?: ReactNode;\n}\n\nexport interface PublicHeaderDropdownLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n 'children'\n> {\n href: string;\n active?: boolean;\n children: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* Context — drives the rendering surface for nav subcomponents. */\n/* */\n/* The same navSlot is rendered twice: once inside the desktop Radix */\n/* NavigationMenu (≥ md, flyout mode) and once inside the mobile Sheet */\n/* drawer (< md, disclosure mode). NavLink / NavDropdown / DropdownCard */\n/* / DropdownLink switch their rendering off this flag so the mobile */\n/* tree doesn't depend on the Radix Root being present. */\n/* ------------------------------------------------------------------ */\n\ntype PublicHeaderSurface = 'desktop' | 'mobile';\n\nconst PublicHeaderSurfaceContext =\n createContext<PublicHeaderSurface>('desktop');\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva(\n [\n 'ds:w-full ds:z-[var(--z-sticky)]',\n 'ds:transition-[background-color,box-shadow,transform] ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n variant: {\n // Transparent fades to a solid pill chrome once data-stuck=\"true\"\n // (set by the rAF scroll listener). The fade is handled via the\n // same background utility so `data-stuck` swaps cleanly between\n // states.\n transparent: [\n 'ds:flex ds:items-center ds:gap-[var(--spacing-md)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:sm:ps-[var(--spacing-lg)] ds:sm:pe-[var(--spacing-lg)]',\n 'ds:h-14 ds:md:h-16',\n 'ds:bg-transparent ds:text-[var(--foreground)]',\n 'ds:data-[stuck=true]:bg-[var(--card)]',\n 'ds:data-[stuck=true]:shadow-[var(--shadow-md)]',\n // Once stuck, pair the elevation shadow with a tokenized\n // block-end border so the header has a clear edge against the\n // page below it — shadow alone fades to a faint halo on dark\n // surfaces, leaving the bar reading as unbounded.\n 'ds:data-[stuck=true]:border-block-end ds:data-[stuck=true]:border-[color:var(--border)]',\n 'ds:forced-colors:border-b ds:forced-colors:border-[CanvasText]',\n ].join(' '),\n // `dark` flips its own scope into the dark token set so nav\n // links + buttons inside read off-white text on the dark\n // surface. Without the `theme-dark` scope the children inherit\n // the light-theme `--foreground` and fail color-contrast at\n // ~1.3:1. `--background` already resolves to a dark step under\n // `theme-dark`, so we compose the surface from the standard\n // theme token rather than locking to a raw ramp step.\n dark: [\n 'ds:flex ds:items-center ds:gap-[var(--spacing-md)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:sm:ps-[var(--spacing-lg)] ds:sm:pe-[var(--spacing-lg)]',\n 'ds:h-14 ds:md:h-16',\n 'theme-dark ds:bg-[var(--background)] ds:text-[var(--foreground)]',\n ].join(' '),\n // brand: on-primary bar for public signer/marketing surfaces —\n // e.g. sign.alfadocs.com signing nav. Tokens only; logo should be\n // <Logo variant=\"monochrome\" tone=\"inherit\" /> to read on the\n // primary fill.\n brand: [\n 'ds:flex ds:items-center ds:gap-[var(--spacing-md)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:sm:ps-[var(--spacing-lg)] ds:sm:pe-[var(--spacing-lg)]',\n 'ds:h-14 ds:md:h-16',\n 'ds:bg-[var(--primary)] ds:text-[var(--primary-foreground)]',\n ].join(' '),\n // `pill` matches alfadocs.com's floating rounded-pill header.\n // The outer header is fully transparent (no border, no bg) and\n // simply provides a top-side gutter so the inner container reads\n // as floating. The inner `data-component=\"public-header-container\"`\n // div is the visible chrome — border, rounded, card background.\n pill: [\n 'ds:bg-transparent',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-md)]',\n 'ds:sm:ps-[var(--spacing-lg)] ds:sm:pe-[var(--spacing-lg)] ds:sm:pt-[var(--spacing-md)]',\n ].join(' '),\n },\n sticky: {\n none: '',\n top: 'ds:sticky ds:top-0',\n // `scroll-up` is sticky but translates off-screen when scrolling\n // down past `stickyOffset` (handled by data-hidden=\"true\") and\n // slides back in when scrolling up.\n 'scroll-up': [\n 'ds:sticky ds:top-0',\n 'ds:data-[hidden=true]:-translate-y-full',\n ].join(' '),\n },\n },\n defaultVariants: { variant: 'pill', sticky: 'none' },\n },\n);\n\n// Inner pill chrome — only rendered when variant === 'pill'. The actual\n// \"header look\" (rounded, border, card surface) lives here so the outer\n// <header> can stay transparent and provide just the floating offset.\nconst pillContainerClasses = [\n 'ds:flex ds:items-center ds:gap-[var(--spacing-md)]',\n 'ds:w-full ds:max-w-[1400px] ds:mx-auto',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:sm:ps-[var(--spacing-lg)] ds:sm:pe-[var(--spacing-lg)]',\n // 76px isn't a kit token; the closest is h-16 (64px) which reads tight\n // against alfadocs.com's chrome — keep an explicit min-block-size so\n // the pill reads as substantial without bloating beyond live reference.\n 'ds:min-h-16 ds:md:min-h-[76px]',\n 'ds:bg-[var(--card)] ds:text-[var(--foreground)]',\n 'ds:rounded-[var(--radius-lg)]',\n // Surface-border hairline shared with `Card`. Tuned to ~14% black\n // (light) / 16% white (dark) at the token level so the edge stays\n // visible against tinted page backgrounds — see `--card-border` in\n // `src/tokens/index.css`. Matches alfadocs.com's slate-200 reference\n // pill border without introducing a pill-specific token.\n 'ds:border ds:border-[color:var(--card-border)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n].join(' ');\n\nconst navLinkVariants = cva(\n [\n 'ds:inline-flex ds:items-center',\n 'type-body-sm ds:font-medium',\n // Muted-foreground reads as secondary text and matches alfadocs.com's\n // nav-link colour (the reference's slate-grey). `--foreground` was too\n // dark vs the ref.\n 'ds:text-[var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:py-[var(--spacing-xs)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:hover:text-[var(--primary)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:data-[active=true]:text-[var(--primary)]',\n 'ds:data-[active=true]:[box-shadow:inset_0_-2px_0_var(--primary)]',\n ].join(' '),\n);\n\n// Trigger styling shares the link look + adds the chevron rotation hook.\nconst dropdownTriggerClasses = [\n 'ds:group ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'type-body-sm ds:font-medium',\n // Mirror the navLinkVariants colour + inline padding so triggers and\n // plain links share weight, colour and target size.\n 'ds:text-[var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:bg-transparent ds:border-0 ds:cursor-pointer',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:py-[var(--spacing-xs)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:hover:text-[var(--primary)]',\n 'ds:data-[state=open]:text-[var(--primary)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n].join(' ');\n\nconst dropdownChevronClasses = [\n 'ds:size-3 ds:shrink-0',\n 'ds:transition-transform ds:duration-[var(--animation-duration)]',\n 'ds:group-data-[state=open]:rotate-180',\n 'ds:motion-reduce:transition-none',\n].join(' ');\n\nconst dropdownContentVariants = cva(\n [\n // Radix portals Content into the Viewport; sizing + position is\n // governed by the Viewport — keep Content's own paint to padding\n // + inner layout so motion / position stay consistent across\n // trigger placements.\n 'ds:data-[motion=from-start]:animate-in ds:data-[motion=from-start]:fade-in-0',\n 'ds:data-[motion=from-end]:animate-in ds:data-[motion=from-end]:fade-in-0',\n 'ds:data-[motion=to-start]:animate-out ds:data-[motion=to-start]:fade-out-0',\n 'ds:data-[motion=to-end]:animate-out ds:data-[motion=to-end]:fade-out-0',\n 'ds:motion-reduce:animate-none',\n ].join(' '),\n {\n variants: {\n wide: {\n true: [\n 'ds:grid ds:grid-cols-2 ds:lg:grid-cols-4',\n 'ds:gap-[var(--spacing-lg)]',\n 'ds:p-[var(--spacing-lg)]',\n 'ds:w-screen ds:max-w-[1400px]',\n ].join(' '),\n false: ['ds:p-[var(--spacing-md)]', 'ds:min-w-[280px]'].join(' '),\n },\n },\n defaultVariants: { wide: false },\n },\n);\n\nconst viewportClasses = [\n // Position the Viewport flush below the pill / nav row. Radix sets\n // `--radix-navigation-menu-viewport-width` and `…-height` so the\n // panel sizes itself to whichever Content is currently active.\n // Logical inset + physical translate: flip the shift in RTL, where\n // `start-1/2` resolves to `right: 50%` (same idiom as CommandPalette).\n 'ds:absolute ds:top-full ds:start-1/2 ds:-translate-x-1/2 ds:rtl:translate-x-1/2',\n 'ds:mt-[var(--spacing-sm)]',\n 'ds:w-[var(--radix-navigation-menu-viewport-width)]',\n 'ds:h-[var(--radix-navigation-menu-viewport-height)]',\n 'ds:overflow-hidden',\n 'ds:bg-[var(--card)] ds:text-[var(--foreground)]',\n 'ds:rounded-[var(--radius-lg)] ds:border ds:border-[color:var(--border)]',\n 'ds:shadow-[var(--shadow-lg)]',\n 'ds:data-[state=open]:animate-in ds:data-[state=closed]:animate-out',\n 'ds:data-[state=open]:fade-in-0 ds:data-[state=closed]:fade-out-0',\n 'ds:data-[state=open]:zoom-in-95 ds:data-[state=closed]:zoom-out-95',\n 'ds:origin-top ds:transition-[width,height] ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:animate-none ds:motion-reduce:transition-none',\n].join(' ');\n\nconst cardLinkClasses = [\n 'ds:flex ds:items-start ds:justify-between ds:gap-[var(--spacing-sm)]',\n 'ds:no-underline ds:text-[var(--foreground)]',\n 'ds:pb-[var(--spacing-sm)]',\n // alfadocs.com renders the card header flush against the sub-list with\n // no separator rule — match that by dropping the border-b that earlier\n // versions of this component carried.\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:hover:[&_[data-card-title]]:text-[var(--primary)]',\n 'ds:hover:[&_[data-card-arrow]]:text-[var(--primary)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:group/cardlink',\n].join(' ');\n\nconst cardHeaderStaticClasses = [\n 'ds:pb-[var(--spacing-sm)]',\n // Match the linked-card variant: no separator rule under the title.\n].join(' ');\n\nconst subLinkClasses = [\n 'ds:block ds:no-underline',\n 'type-body-sm ds:font-medium ds:text-[var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-xs)]',\n 'ds:hover:bg-[var(--muted)]/30 ds:hover:text-[var(--primary)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:data-[active=true]:text-[var(--primary)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n].join(' ');\n\n/* ------------------------------------------------------------------ */\n/* Reduced motion */\n/* ------------------------------------------------------------------ */\n\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = useState(() => {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n });\n useEffect(() => {\n if (typeof window === 'undefined' || !window.matchMedia) return;\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n const onChange = (): void => setReduced(mq.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/* ------------------------------------------------------------------ */\n/* Scroll listener */\n/* */\n/* rAF-throttled tracker for two flags on the header root: */\n/* - `data-stuck`: scrollY > stickyOffset (drives the transparent → */\n/* solid fade + the scroll-up shadow lift). */\n/* - `data-hidden`: scrolling DOWN past stickyOffset hides the header */\n/* via transform; scrolling UP slides it back. Only emitted when */\n/* `sticky === 'scroll-up'`. */\n/* ------------------------------------------------------------------ */\n\nfunction useHeaderScrollState(\n enabled: boolean,\n sticky: NonNullable<PublicHeaderProps['sticky']>,\n stickyOffset: number,\n): {\n stuck: boolean;\n hidden: boolean;\n} {\n const [stuck, setStuck] = useState(false);\n const [hidden, setHidden] = useState(false);\n const lastYRef = useRef(0);\n const tickingRef = useRef(false);\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return;\n lastYRef.current = window.scrollY;\n\n const update = (): void => {\n const y = window.scrollY;\n setStuck(y > stickyOffset);\n if (sticky === 'scroll-up') {\n const delta = y - lastYRef.current;\n // Hide on a meaningful downward delta past the offset; show on\n // any upward delta. 4px deadband keeps minor wheel inertia from\n // flapping the bar.\n if (delta > 4 && y > stickyOffset) setHidden(true);\n else if (delta < -4 || y <= stickyOffset) setHidden(false);\n }\n lastYRef.current = y;\n tickingRef.current = false;\n };\n const onScroll = (): void => {\n if (tickingRef.current) return;\n tickingRef.current = true;\n window.requestAnimationFrame(update);\n };\n window.addEventListener('scroll', onScroll, { passive: true });\n // Initial sync so we don't miss state when mounted past the offset.\n update();\n return () => {\n window.removeEventListener('scroll', onScroll);\n };\n }, [enabled, sticky, stickyOffset]);\n\n return { stuck, hidden };\n}\n\n/* ------------------------------------------------------------------ */\n/* Inner content (shared between flat + pill variants) */\n/* ------------------------------------------------------------------ */\n\ninterface InnerContentProps {\n logo: ReactNode;\n homeHref: string;\n resolvedHomeLabel: string;\n resolvedNavLabel: string;\n resolvedMenuLabel: string;\n navSlot?: ReactNode;\n actionsSlot?: ReactNode;\n persistActions: boolean;\n menuOpen: boolean;\n setMenuOpen: (open: boolean) => void;\n sheetId: string;\n}\n\nfunction InnerContent({\n logo,\n homeHref,\n resolvedHomeLabel,\n resolvedNavLabel,\n resolvedMenuLabel,\n navSlot,\n actionsSlot,\n persistActions,\n menuOpen,\n setMenuOpen,\n sheetId,\n}: InnerContentProps): React.JSX.Element {\n // The mobile menu only needs to exist when something would otherwise be\n // hidden on small screens: the nav (always collapses) or — unless the\n // consumer pins them — the actions. With persisted actions and no nav there\n // is nothing to collapse, so no hamburger renders at all.\n const showMobileMenu =\n Boolean(navSlot) || (Boolean(actionsSlot) && !persistActions);\n return (\n <>\n <a\n href={homeHref}\n aria-label={resolvedHomeLabel}\n className=\"ds:inline-flex ds:items-center ds:rounded-[var(--radius-sm)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]\"\n >\n {logo}\n </a>\n\n {navSlot ? (\n <PublicHeaderSurfaceContext.Provider value=\"desktop\">\n <NavigationMenuPrimitive.Root\n aria-label={resolvedNavLabel}\n // Radix wraps the List in an unstyled `<div>` between Root and\n // <ul>. The `[&>div:first-of-type]:flex` arbitrary selector\n // forces that wrapper into flex so the List's flex-1 actually\n // stretches it across the available width.\n className=\"ds:hidden ds:md:flex ds:relative ds:items-center ds:ms-[var(--spacing-lg)] ds:flex-1 ds:[&>div:first-of-type]:flex ds:[&>div:first-of-type]:flex-1\"\n delayDuration={150}\n >\n <NavigationMenuPrimitive.List className=\"ds:flex ds:flex-1 ds:items-center ds:justify-center ds:gap-[var(--spacing-md)] ds:list-none ds:m-0 ds:ps-0\">\n {navSlot}\n </NavigationMenuPrimitive.List>\n {/* Viewport renders the active panel below the nav row. The\n wrapper centres it horizontally relative to the trigger row;\n Radix sets viewport-width/-height vars so the panel sizes\n to whichever Content is open. */}\n <div className=\"ds:absolute ds:top-full ds:start-0 ds:end-0 ds:flex ds:justify-center ds:pointer-events-none\">\n <div className=\"ds:pointer-events-auto\">\n <NavigationMenuPrimitive.Viewport className={viewportClasses} />\n </div>\n </div>\n </NavigationMenuPrimitive.Root>\n </PublicHeaderSurfaceContext.Provider>\n ) : null}\n\n {actionsSlot ? (\n <div\n className={[\n // Pinned actions stay inline at every breakpoint; otherwise they\n // are hidden < md and collapse into the mobile menu below.\n persistActions ? 'ds:flex' : 'ds:hidden ds:md:flex',\n 'ds:items-center ds:gap-[var(--spacing-sm)] ds:ms-auto',\n ].join(' ')}\n >\n {actionsSlot}\n </div>\n ) : null}\n\n {/* Mobile menu trigger (< md). Wired to a Sheet (partial drawer\n from inline-end) so navSlot + actionsSlot collapse there. Omitted\n entirely when there is nothing left to collapse (persisted actions,\n no nav). */}\n {showMobileMenu ? (\n <Sheet open={menuOpen} onOpenChange={setMenuOpen}>\n <Sheet.Trigger asChild>\n <IconButton\n size=\"md\"\n intent=\"ghost\"\n className=\"ds:ms-auto ds:md:hidden\"\n icon={\n menuOpen ? (\n <X aria-hidden=\"true\" />\n ) : (\n <Menu aria-hidden=\"true\" />\n )\n }\n aria-controls={sheetId}\n aria-expanded={menuOpen}\n aria-label={resolvedMenuLabel}\n />\n </Sheet.Trigger>\n <Sheet.Content side=\"end\" size=\"md\" id={sheetId}>\n <Sheet.Header>\n <Sheet.Title>{resolvedNavLabel}</Sheet.Title>\n </Sheet.Header>\n <Sheet.Body>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {navSlot ? (\n <PublicHeaderSurfaceContext.Provider value=\"mobile\">\n <nav\n aria-label={resolvedNavLabel}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\"\n data-mobile-nav=\"true\"\n >\n {navSlot}\n </nav>\n </PublicHeaderSurfaceContext.Provider>\n ) : null}\n {actionsSlot && !persistActions ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:mt-[var(--spacing-md)]\">\n {actionsSlot}\n </div>\n ) : null}\n </div>\n </Sheet.Body>\n </Sheet.Content>\n </Sheet>\n ) : null}\n </>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Root */\n/* ------------------------------------------------------------------ */\n\nconst PublicHeaderRoot = forwardRef<HTMLElement, PublicHeaderProps>(\n (\n {\n logo,\n homeHref = '/',\n homeLabel,\n navSlot,\n actionsSlot,\n variant = 'pill',\n sticky = 'none',\n stickyOffset = 80,\n menuLabel,\n navLabel,\n persistActions = false,\n id,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const reduced = usePrefersReducedMotion();\n const autoId = useId();\n const sheetId = `${id ?? autoId}-sheet`;\n const [menuOpen, setMenuOpen] = useState(false);\n\n // Scroll listener is only active when `transparent` or `scroll-up`\n // need it. Hidden state is disabled under reduced motion (the bar\n // stays visible — translating off-screen is precisely the motion\n // class of user the preference targets).\n const scrollEnabled = variant === 'transparent' || sticky === 'scroll-up';\n const { stuck, hidden } = useHeaderScrollState(\n scrollEnabled,\n sticky,\n stickyOffset,\n );\n const effectiveHidden = reduced ? false : hidden;\n\n // Sticky headers stick to the LAYOUT viewport — pinch-zoom pans them off\n // the visible screen, so glue the bar to the visual viewport. Suspended\n // while the scroll-up hide transform owns the element or when not sticky.\n const pinRef = useVisualViewportPin<HTMLElement>({\n disabled: sticky === 'none' || effectiveHidden,\n });\n\n const openMenu = useCallback((): void => setMenuOpen(true), []);\n const closeMenu = useCallback((): void => setMenuOpen(false), []);\n\n useImperativeHandle(\n ref as React.Ref<PublicHeaderHandle> | null | undefined,\n () => ({\n openMenu,\n closeMenu,\n getMenuOpen: () => menuOpen,\n }),\n [openMenu, closeMenu, menuOpen],\n );\n\n const resolvedHomeLabel =\n homeLabel ?? t('publicHeader.homeLabel', 'AlfaDocs — home');\n const resolvedNavLabel =\n navLabel ?? t('publicHeader.primaryNavLabel', 'Primary');\n const resolvedMenuLabel =\n menuLabel ??\n (menuOpen\n ? t('publicHeader.closeMenu', 'Close menu')\n : t('publicHeader.openMenu', 'Open menu'));\n\n const innerProps: InnerContentProps = {\n logo,\n homeHref,\n resolvedHomeLabel,\n resolvedNavLabel,\n resolvedMenuLabel,\n navSlot,\n actionsSlot,\n persistActions,\n menuOpen,\n setMenuOpen,\n sheetId,\n };\n\n return (\n <header\n {...rest}\n ref={pinRef}\n data-component=\"public-header\"\n data-component-id={id}\n data-variant={variant}\n data-stuck={stuck ? 'true' : undefined}\n data-hidden={effectiveHidden ? 'true' : undefined}\n data-state={reduced ? 'reduced-motion' : undefined}\n className={rootVariants({ variant, sticky, className })}\n >\n {variant === 'pill' ? (\n <div\n data-component=\"public-header-container\"\n className={pillContainerClasses}\n >\n <InnerContent {...innerProps} />\n </div>\n ) : (\n <InnerContent {...innerProps} />\n )}\n </header>\n );\n },\n);\nPublicHeaderRoot.displayName = 'PublicHeader';\n\n/* ------------------------------------------------------------------ */\n/* NavLink */\n/* ------------------------------------------------------------------ */\n\nconst PublicHeaderNavLink = forwardRef<\n HTMLAnchorElement,\n PublicHeaderNavLinkProps\n>(({ active, className, children, ...rest }, ref) => {\n const surface = useContext(PublicHeaderSurfaceContext);\n // Desktop: NavigationMenu.Item + .Link give Radix the keyboard / focus\n // semantics across the trigger row. We render the actual <a> via\n // asChild so consumers can swap in router links transparently.\n // Mobile: plain <a> inside the Sheet's <nav> — no Radix Root in scope,\n // so we render outside the NavigationMenu primitive.\n if (surface === 'mobile') {\n return (\n <a\n ref={ref}\n {...rest}\n data-active={active ? 'true' : undefined}\n aria-current={active ? 'page' : undefined}\n className={[\n 'ds:block ds:no-underline',\n 'type-body-sm ds:font-medium',\n 'ds:text-[var(--foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-sm)]',\n 'ds:hover:bg-[var(--muted)]/30 ds:hover:text-[var(--primary)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:data-[active=true]:text-[var(--primary)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {children}\n </a>\n );\n }\n return (\n <NavigationMenuPrimitive.Item>\n <NavigationMenuPrimitive.Link asChild active={active}>\n <a\n ref={ref}\n {...rest}\n data-active={active ? 'true' : undefined}\n aria-current={active ? 'page' : undefined}\n className={[navLinkVariants(), className].filter(Boolean).join(' ')}\n >\n {children}\n </a>\n </NavigationMenuPrimitive.Link>\n </NavigationMenuPrimitive.Item>\n );\n});\nPublicHeaderNavLink.displayName = 'PublicHeader.NavLink';\n\n/* ------------------------------------------------------------------ */\n/* NavDropdown */\n/* ------------------------------------------------------------------ */\n\nconst PublicHeaderNavDropdown = forwardRef<\n ElementRef<typeof NavigationMenuPrimitive.Trigger>,\n PublicHeaderNavDropdownProps\n>(({ label, wide = false, children, id }, ref) => {\n const surface = useContext(PublicHeaderSurfaceContext);\n if (surface === 'mobile') {\n // <details>/<summary> gives semantic disclosure for free —\n // Enter/Space toggles, summary is focusable, content collapses\n // when not open. No JS needed.\n return (\n <details\n id={id}\n data-component=\"public-header-nav-dropdown\"\n className=\"ds:group ds:border-b ds:border-[color:var(--border)]\"\n >\n <summary\n className={[\n 'ds:flex ds:items-center ds:justify-between ds:cursor-pointer',\n 'type-body-sm ds:font-medium ds:text-[var(--foreground)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:py-[var(--spacing-sm)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:list-none ds:[&::-webkit-details-marker]:hidden',\n ].join(' ')}\n >\n <span>{label}</span>\n <ChevronDown\n aria-hidden=\"true\"\n className=\"ds:size-3 ds:shrink-0 ds:transition-transform ds:duration-[var(--animation-duration)] ds:group-[[open]]:rotate-180 ds:motion-reduce:transition-none\"\n />\n </summary>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:py-[var(--spacing-sm)]\">\n {children}\n </div>\n </details>\n );\n }\n return (\n <NavigationMenuPrimitive.Item data-component=\"public-header-nav-dropdown\">\n <NavigationMenuPrimitive.Trigger\n ref={ref}\n id={id}\n className={dropdownTriggerClasses}\n // Radix already wires aria-expanded + aria-controls. The\n // group / data-state hooks let the chevron rotate via Tailwind.\n >\n {label}\n <ChevronDown aria-hidden=\"true\" className={dropdownChevronClasses} />\n </NavigationMenuPrimitive.Trigger>\n <NavigationMenuPrimitive.Content\n className={dropdownContentVariants({ wide })}\n >\n {children}\n </NavigationMenuPrimitive.Content>\n </NavigationMenuPrimitive.Item>\n );\n});\nPublicHeaderNavDropdown.displayName = 'PublicHeader.NavDropdown';\n\n/* ------------------------------------------------------------------ */\n/* DropdownCard */\n/* ------------------------------------------------------------------ */\n\nconst PublicHeaderDropdownCard = forwardRef<\n HTMLDivElement,\n PublicHeaderDropdownCardProps\n>(({ href, title, subtitle, children }, ref) => {\n const surface = useContext(PublicHeaderSurfaceContext);\n // The Card header. In mobile we render a plain <a> when href is set —\n // no Radix Link wrapper because the NavigationMenu root isn't in scope\n // inside the Sheet.\n const titleNode = (\n <span\n data-card-title\n className=\"type-title-card ds:text-[var(--foreground)] ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none\"\n >\n {title}\n </span>\n );\n const subtitleNode = subtitle ? (\n <span className=\"type-body-sm ds:text-[var(--muted-foreground)]\">\n {subtitle}\n </span>\n ) : null;\n\n const arrow = (\n <span\n data-card-arrow\n aria-hidden=\"true\"\n className=\"ds:flex-none ds:text-[var(--muted-foreground)] ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none ds:rtl:-scale-x-100\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </span>\n );\n\n const linkInner = (\n <>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {titleNode}\n {subtitleNode}\n </div>\n {arrow}\n </>\n );\n\n let header: React.JSX.Element;\n if (href) {\n if (surface === 'mobile') {\n header = (\n <a href={href} className={cardLinkClasses}>\n {linkInner}\n </a>\n );\n } else {\n header = (\n <NavigationMenuPrimitive.Link asChild>\n <a href={href} className={cardLinkClasses}>\n {linkInner}\n </a>\n </NavigationMenuPrimitive.Link>\n );\n }\n } else {\n header = (\n <div className={cardHeaderStaticClasses}>\n {titleNode}\n {subtitle ? (\n <span className=\"ds:block type-body-sm ds:text-[var(--muted-foreground)] ds:mt-[var(--spacing-xs)]\">\n {subtitle}\n </span>\n ) : null}\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n data-component=\"public-header-dropdown-card\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {header}\n {children ? (\n <ul className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:list-none ds:m-0 ds:ps-0\">\n {children}\n </ul>\n ) : null}\n </div>\n );\n});\nPublicHeaderDropdownCard.displayName = 'PublicHeader.DropdownCard';\n\n/* ------------------------------------------------------------------ */\n/* DropdownLink */\n/* ------------------------------------------------------------------ */\n\nconst PublicHeaderDropdownLink = forwardRef<\n HTMLAnchorElement,\n PublicHeaderDropdownLinkProps\n>(({ active, className, children, ...rest }, ref) => {\n const surface = useContext(PublicHeaderSurfaceContext);\n const anchor = (\n <a\n ref={ref}\n {...rest}\n data-active={active ? 'true' : undefined}\n aria-current={active ? 'page' : undefined}\n className={[subLinkClasses, className].filter(Boolean).join(' ')}\n >\n {children}\n </a>\n );\n return (\n <li data-component=\"public-header-dropdown-link\">\n {surface === 'mobile' ? (\n anchor\n ) : (\n <NavigationMenuPrimitive.Link asChild active={active}>\n {anchor}\n </NavigationMenuPrimitive.Link>\n )}\n </li>\n );\n});\nPublicHeaderDropdownLink.displayName = 'PublicHeader.DropdownLink';\n\n/* ------------------------------------------------------------------ */\n/* Public surface */\n/* ------------------------------------------------------------------ */\n\nexport const PublicHeader = Object.assign(PublicHeaderRoot, {\n NavLink: PublicHeaderNavLink,\n NavDropdown: PublicHeaderNavDropdown,\n DropdownCard: PublicHeaderDropdownCard,\n DropdownLink: PublicHeaderDropdownLink,\n});\n","import type { AgentAdapter } from '../../agent/types';\nimport type { PublicHeaderHandle } from './public-header';\n\nexport const publicHeaderAgent: AgentAdapter<PublicHeaderHandle> = {\n id: 'public-header',\n capabilities: ['open', 'close'],\n state: {\n menuOpen: {\n type: 'boolean',\n descriptionKey: 'ui.agent.publicHeader.state.menuOpen',\n description: 'Whether the mobile drawer is currently open.',\n read: (handle) => handle.getMenuOpen(),\n },\n },\n actions: {\n open_menu: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.publicHeader.actions.openMenu',\n description: 'Open the mobile drawer.',\n invoke: (handle) => {\n handle.openMenu();\n },\n },\n close_menu: {\n safety: 'read',\n idempotent: true,\n descriptionKey: 'ui.agent.publicHeader.actions.closeMenu',\n description: 'Close the mobile drawer.',\n invoke: (handle) => {\n handle.closeMenu();\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'public-header' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop on PublicHeader.',\n },\n },\n};\n"],"names":["PublicHeaderSurfaceContext","createContext","rootVariants","cva","pillContainerClasses","navLinkVariants","dropdownTriggerClasses","dropdownChevronClasses","dropdownContentVariants","viewportClasses","cardLinkClasses","cardHeaderStaticClasses","subLinkClasses","usePrefersReducedMotion","reduced","setReduced","useState","useEffect","mq","onChange","useHeaderScrollState","enabled","sticky","stickyOffset","stuck","setStuck","hidden","setHidden","lastYRef","useRef","tickingRef","update","y","delta","onScroll","InnerContent","logo","homeHref","resolvedHomeLabel","resolvedNavLabel","resolvedMenuLabel","navSlot","actionsSlot","persistActions","menuOpen","setMenuOpen","sheetId","showMobileMenu","jsxs","Fragment","jsx","NavigationMenuPrimitive","Sheet","IconButton","X","Menu","PublicHeaderRoot","forwardRef","homeLabel","variant","menuLabel","navLabel","id","className","rest","ref","t","useTranslation","autoId","useId","scrollEnabled","effectiveHidden","pinRef","useVisualViewportPin","openMenu","useCallback","closeMenu","useImperativeHandle","innerProps","PublicHeaderNavLink","active","children","useContext","PublicHeaderNavDropdown","label","wide","ChevronDown","PublicHeaderDropdownCard","href","title","subtitle","surface","titleNode","linkInner","header","PublicHeaderDropdownLink","anchor","PublicHeader","publicHeaderAgent","handle"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-theme-CAuo6EYT.js","sources":["../../src/hooks/use-theme.ts"],"sourcesContent":["import { useCallback, useEffect, useSyncExternalStore } from 'react';\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* */\n/* Modelled as two orthogonal axes per `src/docs/04-theming.mdx` — base */\n/* colour scheme and AAA contrast modifier — plus an explicit `system` */\n/* value meaning \"no user override, follow OS preferences\". The four */\n/* concrete applied themes called out in `hooks-gaps.mdx` are exposed */\n/* via `resolvedTheme` for code that needs to branch on the composed */\n/* result (e.g. data-visualisation libraries with their own theme */\n/* machinery). */\n/* -------------------------------------------------------------------- */\n\nexport type ThemeBase = 'light' | 'dark';\nexport type ThemePreference = ThemeBase | 'system';\nexport type AccessibilityPreference = 'default' | 'accessible' | 'system';\nexport type ResolvedTheme =\n | 'light'\n | 'dark'\n | 'light-accessible'\n | 'dark-accessible';\n\nexport interface UseThemeReturn {\n /** User's stored base-theme preference, or `'system'` when no override is set. */\n theme: ThemePreference;\n /** User's stored AAA-contrast preference, or `'system'` when no override is set. */\n accessibility: AccessibilityPreference;\n /** The theme actually applied to the DOM once the preferences + OS signals are resolved. */\n resolvedTheme: ResolvedTheme;\n /** Persist a new base-theme preference. Pass `'system'` to clear the override. */\n setTheme: (next: ThemePreference) => void;\n /** Persist a new AAA-contrast preference. Pass `'system'` to clear the override. */\n setAccessibility: (next: AccessibilityPreference) => void;\n}\n\n/* -------------------------------------------------------------------- */\n/* SSR-safe named constants */\n/* */\n/* Exported so a consuming app's SSR shim can plant the correct classes */\n/* on `<html>` at render time without importing the whole hook (which */\n/* pulls in React, `useSyncExternalStore`, etc.). See the acceptance */\n/* criterion in `src/user_stories/to-do/review-findings/hooks-gaps.mdx`. */\n/* -------------------------------------------------------------------- */\n\nexport const THEME_STORAGE_KEY = 'alfadocs-ui.theme';\nexport const ACCESSIBILITY_STORAGE_KEY = 'alfadocs-ui.accessibility';\n\nexport const THEME_CLASS = {\n light: 'theme-light',\n dark: 'theme-dark',\n accessible: 'theme-accessible',\n} as const;\n\nconst COLOR_SCHEME_QUERY = '(prefers-color-scheme: dark)';\nconst CONTRAST_QUERY = '(prefers-contrast: more)';\n\n/* -------------------------------------------------------------------- */\n/* Persistence — guarded against private-mode / disabled-storage throws */\n/* -------------------------------------------------------------------- */\n\nfunction safeReadStorage(key: string): string | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction safeWriteStorage(key: string, value: string | null): void {\n if (typeof window === 'undefined') return;\n try {\n if (value === null) {\n window.localStorage.removeItem(key);\n } else {\n window.localStorage.setItem(key, value);\n }\n } catch {\n /* Storage can throw in private mode or when quota is exceeded — the\n in-memory state is the source of truth, so a write failure is\n recoverable. Silent-fail matches how `usePrefersReducedMotion`\n handles `matchMedia` being unavailable. */\n }\n}\n\nfunction isValidTheme(value: unknown): value is ThemePreference {\n return value === 'light' || value === 'dark' || value === 'system';\n}\n\nfunction isValidAccessibility(\n value: unknown,\n): value is AccessibilityPreference {\n return value === 'default' || value === 'accessible' || value === 'system';\n}\n\n/* -------------------------------------------------------------------- */\n/* External store — singleton so every `useTheme()` consumer in the */\n/* tree reads the same snapshot and only one `matchMedia` / storage */\n/* subscription is registered per tab. */\n/* -------------------------------------------------------------------- */\n\ninterface ThemeSnapshot {\n theme: ThemePreference;\n accessibility: AccessibilityPreference;\n prefersDark: boolean;\n prefersMoreContrast: boolean;\n}\n\nfunction readInitialSnapshot(): ThemeSnapshot {\n const storedTheme = safeReadStorage(THEME_STORAGE_KEY);\n const storedA11y = safeReadStorage(ACCESSIBILITY_STORAGE_KEY);\n\n const hasMatchMedia =\n typeof window !== 'undefined' && typeof window.matchMedia === 'function';\n\n /* When storage has no explicit override, fall back to whatever theme\n classes are already on `<html>` (set by SSR, a consuming app's shell,\n or Storybook's `withThemeByClassName` decorator). Without this, a\n nested `<ThemeRoot>` driven by `resolvedTheme` would clobber the\n outer cascade with `theme-light` (the OS-preference default) and\n bomb the closest-ancestor token resolution for every child. */\n const htmlClasses =\n typeof document !== 'undefined' ? document.documentElement.classList : null;\n const htmlThemeOverride: ThemePreference | null = htmlClasses\n ? htmlClasses.contains(THEME_CLASS.dark)\n ? 'dark'\n : htmlClasses.contains(THEME_CLASS.light)\n ? 'light'\n : null\n : null;\n const htmlA11yOverride: AccessibilityPreference | null =\n htmlClasses?.contains(THEME_CLASS.accessible) ? 'accessible' : null;\n\n return {\n theme: isValidTheme(storedTheme)\n ? storedTheme\n : (htmlThemeOverride ?? 'system'),\n accessibility: isValidAccessibility(storedA11y)\n ? storedA11y\n : (htmlA11yOverride ?? 'system'),\n prefersDark: hasMatchMedia\n ? window.matchMedia(COLOR_SCHEME_QUERY).matches\n : false,\n prefersMoreContrast: hasMatchMedia\n ? window.matchMedia(CONTRAST_QUERY).matches\n : false,\n };\n}\n\nlet snapshot: ThemeSnapshot = readInitialSnapshot();\nconst listeners = new Set<() => void>();\n\nfunction commit(next: ThemeSnapshot): void {\n if (\n next.theme === snapshot.theme &&\n next.accessibility === snapshot.accessibility &&\n next.prefersDark === snapshot.prefersDark &&\n next.prefersMoreContrast === snapshot.prefersMoreContrast\n ) {\n return;\n }\n snapshot = next;\n listeners.forEach((listener) => listener());\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nfunction getSnapshot(): ThemeSnapshot {\n return snapshot;\n}\n\nfunction getServerSnapshot(): ThemeSnapshot {\n return {\n theme: 'system',\n accessibility: 'system',\n prefersDark: false,\n prefersMoreContrast: false,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Media-query subscriptions — mounted once per tab, on the first time */\n/* `useTheme` is called in the browser. Cleanup happens via a ref-count */\n/* in the subscription effect below. */\n/* -------------------------------------------------------------------- */\n\nlet mediaSubscriberCount = 0;\nlet colorSchemeMql: MediaQueryList | null = null;\nlet contrastMql: MediaQueryList | null = null;\nlet storageHandler: ((event: StorageEvent) => void) | null = null;\n\nfunction onColorSchemeChange(event: MediaQueryListEvent): void {\n commit({ ...snapshot, prefersDark: event.matches });\n}\n\nfunction onContrastChange(event: MediaQueryListEvent): void {\n commit({ ...snapshot, prefersMoreContrast: event.matches });\n}\n\nfunction onStorage(event: StorageEvent): void {\n if (event.key === THEME_STORAGE_KEY) {\n const next = isValidTheme(event.newValue) ? event.newValue : 'system';\n commit({ ...snapshot, theme: next });\n } else if (event.key === ACCESSIBILITY_STORAGE_KEY) {\n const next = isValidAccessibility(event.newValue)\n ? event.newValue\n : 'system';\n commit({ ...snapshot, accessibility: next });\n }\n}\n\n/* The `<html>` class fallback from `readInitialSnapshot` must stay LIVE:\n Storybook's `withThemeByClassName` decorator (and host apps' shells)\n rewrite the documentElement classes after this module's initial read,\n and a one-shot sample leaves the hook pinned to a stale preference —\n a nested `<ThemeRoot>` then clobbers the outer cascade (e.g. a\n `theme-light` root inside a `theme-dark` page). Stored preferences\n still win, mirroring the initial-read precedence. */\nfunction rederiveFromHtmlClasses(): void {\n if (typeof document === 'undefined') return;\n const classes = document.documentElement.classList;\n const storedTheme = safeReadStorage(THEME_STORAGE_KEY);\n const storedA11y = safeReadStorage(ACCESSIBILITY_STORAGE_KEY);\n const htmlTheme: ThemePreference | null = classes.contains(THEME_CLASS.dark)\n ? 'dark'\n : classes.contains(THEME_CLASS.light)\n ? 'light'\n : null;\n const htmlA11y: AccessibilityPreference | null = classes.contains(\n THEME_CLASS.accessible,\n )\n ? 'accessible'\n : null;\n commit({\n ...snapshot,\n theme: isValidTheme(storedTheme) ? storedTheme : (htmlTheme ?? 'system'),\n accessibility: isValidAccessibility(storedA11y)\n ? storedA11y\n : (htmlA11y ?? 'system'),\n });\n}\n\nfunction attachMqlListener(\n mql: MediaQueryList,\n handler: (event: MediaQueryListEvent) => void,\n): () => void {\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n }\n // Safari < 14 fallback.\n mql.addListener(handler);\n return () => mql.removeListener(handler);\n}\n\nfunction mountMediaSubscriptions(): () => void {\n mediaSubscriberCount += 1;\n if (mediaSubscriberCount > 1) {\n return disposeOneSubscriber;\n }\n\n if (typeof window === 'undefined') {\n return disposeOneSubscriber;\n }\n\n if (typeof window.matchMedia === 'function') {\n colorSchemeMql = window.matchMedia(COLOR_SCHEME_QUERY);\n contrastMql = window.matchMedia(CONTRAST_QUERY);\n // Re-sync the module snapshot in case the OS preferences changed\n // between initial read and first effect run (hydration window).\n commit({\n ...snapshot,\n prefersDark: colorSchemeMql.matches,\n prefersMoreContrast: contrastMql.matches,\n });\n detachColorScheme = attachMqlListener(colorSchemeMql, onColorSchemeChange);\n detachContrast = attachMqlListener(contrastMql, onContrastChange);\n }\n\n // Cross-tab sync — `storage` only fires on *other* tabs, so the local\n // tab still needs setTheme to update snapshot directly (it does).\n storageHandler = onStorage;\n window.addEventListener('storage', storageHandler);\n\n // Live `<html>` class tracking — decorators / host shells may rewrite\n // the theme classes after mount; re-derive (and re-sync once now, in\n // case the classes changed inside the hydration window).\n if (\n typeof document !== 'undefined' &&\n typeof MutationObserver !== 'undefined'\n ) {\n rederiveFromHtmlClasses();\n htmlClassObserver = new MutationObserver(rederiveFromHtmlClasses);\n htmlClassObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class'],\n });\n }\n\n return disposeOneSubscriber;\n}\n\nlet detachColorScheme: (() => void) | null = null;\nlet detachContrast: (() => void) | null = null;\nlet htmlClassObserver: MutationObserver | null = null;\n\nfunction disposeOneSubscriber(): void {\n mediaSubscriberCount -= 1;\n if (mediaSubscriberCount > 0) return;\n detachColorScheme?.();\n detachContrast?.();\n detachColorScheme = null;\n detachContrast = null;\n colorSchemeMql = null;\n contrastMql = null;\n if (storageHandler && typeof window !== 'undefined') {\n window.removeEventListener('storage', storageHandler);\n }\n storageHandler = null;\n htmlClassObserver?.disconnect();\n htmlClassObserver = null;\n}\n\n/* -------------------------------------------------------------------- */\n/* Resolution helpers */\n/* -------------------------------------------------------------------- */\n\nexport function resolveTheme(state: ThemeSnapshot): ResolvedTheme {\n const base: ThemeBase =\n state.theme === 'system'\n ? state.prefersDark\n ? 'dark'\n : 'light'\n : state.theme;\n const accessible =\n state.accessibility === 'system'\n ? state.prefersMoreContrast\n : state.accessibility === 'accessible';\n if (base === 'dark' && accessible) return 'dark-accessible';\n if (base === 'dark') return 'dark';\n if (accessible) return 'light-accessible';\n return 'light';\n}\n\nexport function themeClassList(resolved: ResolvedTheme): string[] {\n switch (resolved) {\n case 'light':\n return [THEME_CLASS.light];\n case 'dark':\n return [THEME_CLASS.dark];\n case 'light-accessible':\n return [THEME_CLASS.light, THEME_CLASS.accessible];\n case 'dark-accessible':\n return [THEME_CLASS.dark, THEME_CLASS.accessible];\n }\n}\n\nconst ALL_THEME_CLASSES = [\n THEME_CLASS.light,\n THEME_CLASS.dark,\n THEME_CLASS.accessible,\n];\n\nfunction applyToDocument(resolved: ResolvedTheme): void {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n const next = new Set(themeClassList(resolved));\n ALL_THEME_CLASSES.forEach((cls) => {\n if (next.has(cls)) {\n root.classList.add(cls);\n } else {\n root.classList.remove(cls);\n }\n });\n}\n\n/* -------------------------------------------------------------------- */\n/* Public hook */\n/* -------------------------------------------------------------------- */\n\n/**\n * Read and update the DS theme preference.\n *\n * - `theme` / `accessibility` are the stored user preferences, with\n * `'system'` meaning \"no override — follow OS\".\n * - `resolvedTheme` composes preferences + live `matchMedia` state into\n * one of the four concrete themes.\n * - `setTheme` / `setAccessibility` persist the override to\n * `localStorage` and re-apply classes to `document.documentElement`\n * synchronously. Pass `'system'` to clear.\n *\n * Singleton under the hood — every hook call shares one `matchMedia`\n * subscription, so mounting the hook many times does not multiply the\n * listener count. The hook also listens for cross-tab `storage` events\n * so a theme change in tab A is reflected in tab B without a reload.\n */\nexport function useTheme(): UseThemeReturn {\n const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n\n useEffect(() => mountMediaSubscriptions(), []);\n\n useEffect(() => {\n applyToDocument(resolveTheme(state));\n }, [state]);\n\n const setTheme = useCallback((next: ThemePreference) => {\n safeWriteStorage(THEME_STORAGE_KEY, next === 'system' ? null : next);\n commit({ ...snapshot, theme: next });\n }, []);\n\n const setAccessibility = useCallback((next: AccessibilityPreference) => {\n safeWriteStorage(\n ACCESSIBILITY_STORAGE_KEY,\n next === 'system' ? null : next,\n );\n commit({ ...snapshot, accessibility: next });\n }, []);\n\n return {\n theme: state.theme,\n accessibility: state.accessibility,\n resolvedTheme: resolveTheme(state),\n setTheme,\n setAccessibility,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Test hook — reset module-level state between tests. Not exported */\n/* from the barrel. */\n/* -------------------------------------------------------------------- */\n\nexport function __resetThemeStoreForTests(): void {\n snapshot = readInitialSnapshot();\n listeners.clear();\n mediaSubscriberCount = 0;\n detachColorScheme?.();\n detachContrast?.();\n detachColorScheme = null;\n detachContrast = null;\n colorSchemeMql = null;\n contrastMql = null;\n if (storageHandler && typeof window !== 'undefined') {\n window.removeEventListener('storage', storageHandler);\n }\n storageHandler = null;\n}\n"],"names":["THEME_STORAGE_KEY","ACCESSIBILITY_STORAGE_KEY","THEME_CLASS","COLOR_SCHEME_QUERY","CONTRAST_QUERY","safeReadStorage","key","safeWriteStorage","value","isValidTheme","isValidAccessibility","readInitialSnapshot","storedTheme","storedA11y","hasMatchMedia","htmlClasses","htmlThemeOverride","htmlA11yOverride","snapshot","listeners","commit","next","listener","subscribe","getSnapshot","getServerSnapshot","mediaSubscriberCount","colorSchemeMql","contrastMql","storageHandler","onColorSchemeChange","event","onContrastChange","onStorage","rederiveFromHtmlClasses","classes","htmlTheme","htmlA11y","attachMqlListener","mql","handler","mountMediaSubscriptions","detachColorScheme","detachContrast","htmlClassObserver","disposeOneSubscriber","resolveTheme","state","base","accessible","themeClassList","resolved","ALL_THEME_CLASSES","applyToDocument","root","cls","useTheme","useSyncExternalStore","useEffect","setTheme","useCallback","setAccessibility"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-theme-CAuo6EYT.js","sources":["../../src/hooks/use-theme.ts"],"sourcesContent":["import { useCallback, useEffect, useSyncExternalStore } from 'react';\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* */\n/* Modelled as two orthogonal axes per `src/docs/04-theming.mdx` — base */\n/* colour scheme and AAA contrast modifier — plus an explicit `system` */\n/* value meaning \"no user override, follow OS preferences\". The four */\n/* concrete applied themes called out in `hooks-gaps.mdx` are exposed */\n/* via `resolvedTheme` for code that needs to branch on the composed */\n/* result (e.g. data-visualisation libraries with their own theme */\n/* machinery). */\n/* -------------------------------------------------------------------- */\n\nexport type ThemeBase = 'light' | 'dark';\nexport type ThemePreference = ThemeBase | 'system';\nexport type AccessibilityPreference = 'default' | 'accessible' | 'system';\nexport type ResolvedTheme =\n | 'light'\n | 'dark'\n | 'light-accessible'\n | 'dark-accessible';\n\nexport interface UseThemeReturn {\n /** User's stored base-theme preference, or `'system'` when no override is set. */\n theme: ThemePreference;\n /** User's stored AAA-contrast preference, or `'system'` when no override is set. */\n accessibility: AccessibilityPreference;\n /** The theme actually applied to the DOM once the preferences + OS signals are resolved. */\n resolvedTheme: ResolvedTheme;\n /** Persist a new base-theme preference. Pass `'system'` to clear the override. */\n setTheme: (next: ThemePreference) => void;\n /** Persist a new AAA-contrast preference. Pass `'system'` to clear the override. */\n setAccessibility: (next: AccessibilityPreference) => void;\n}\n\n/* -------------------------------------------------------------------- */\n/* SSR-safe named constants */\n/* */\n/* Exported so a consuming app's SSR shim can plant the correct classes */\n/* on `<html>` at render time without importing the whole hook (which */\n/* pulls in React, `useSyncExternalStore`, etc.). */\n/* -------------------------------------------------------------------- */\n\nexport const THEME_STORAGE_KEY = 'alfadocs-ui.theme';\nexport const ACCESSIBILITY_STORAGE_KEY = 'alfadocs-ui.accessibility';\n\nexport const THEME_CLASS = {\n light: 'theme-light',\n dark: 'theme-dark',\n accessible: 'theme-accessible',\n} as const;\n\nconst COLOR_SCHEME_QUERY = '(prefers-color-scheme: dark)';\nconst CONTRAST_QUERY = '(prefers-contrast: more)';\n\n/* -------------------------------------------------------------------- */\n/* Persistence — guarded against private-mode / disabled-storage throws */\n/* -------------------------------------------------------------------- */\n\nfunction safeReadStorage(key: string): string | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction safeWriteStorage(key: string, value: string | null): void {\n if (typeof window === 'undefined') return;\n try {\n if (value === null) {\n window.localStorage.removeItem(key);\n } else {\n window.localStorage.setItem(key, value);\n }\n } catch {\n /* Storage can throw in private mode or when quota is exceeded — the\n in-memory state is the source of truth, so a write failure is\n recoverable. Silent-fail matches how `usePrefersReducedMotion`\n handles `matchMedia` being unavailable. */\n }\n}\n\nfunction isValidTheme(value: unknown): value is ThemePreference {\n return value === 'light' || value === 'dark' || value === 'system';\n}\n\nfunction isValidAccessibility(\n value: unknown,\n): value is AccessibilityPreference {\n return value === 'default' || value === 'accessible' || value === 'system';\n}\n\n/* -------------------------------------------------------------------- */\n/* External store — singleton so every `useTheme()` consumer in the */\n/* tree reads the same snapshot and only one `matchMedia` / storage */\n/* subscription is registered per tab. */\n/* -------------------------------------------------------------------- */\n\ninterface ThemeSnapshot {\n theme: ThemePreference;\n accessibility: AccessibilityPreference;\n prefersDark: boolean;\n prefersMoreContrast: boolean;\n}\n\nfunction readInitialSnapshot(): ThemeSnapshot {\n const storedTheme = safeReadStorage(THEME_STORAGE_KEY);\n const storedA11y = safeReadStorage(ACCESSIBILITY_STORAGE_KEY);\n\n const hasMatchMedia =\n typeof window !== 'undefined' && typeof window.matchMedia === 'function';\n\n /* When storage has no explicit override, fall back to whatever theme\n classes are already on `<html>` (set by SSR, a consuming app's shell,\n or Storybook's `withThemeByClassName` decorator). Without this, a\n nested `<ThemeRoot>` driven by `resolvedTheme` would clobber the\n outer cascade with `theme-light` (the OS-preference default) and\n bomb the closest-ancestor token resolution for every child. */\n const htmlClasses =\n typeof document !== 'undefined' ? document.documentElement.classList : null;\n const htmlThemeOverride: ThemePreference | null = htmlClasses\n ? htmlClasses.contains(THEME_CLASS.dark)\n ? 'dark'\n : htmlClasses.contains(THEME_CLASS.light)\n ? 'light'\n : null\n : null;\n const htmlA11yOverride: AccessibilityPreference | null =\n htmlClasses?.contains(THEME_CLASS.accessible) ? 'accessible' : null;\n\n return {\n theme: isValidTheme(storedTheme)\n ? storedTheme\n : (htmlThemeOverride ?? 'system'),\n accessibility: isValidAccessibility(storedA11y)\n ? storedA11y\n : (htmlA11yOverride ?? 'system'),\n prefersDark: hasMatchMedia\n ? window.matchMedia(COLOR_SCHEME_QUERY).matches\n : false,\n prefersMoreContrast: hasMatchMedia\n ? window.matchMedia(CONTRAST_QUERY).matches\n : false,\n };\n}\n\nlet snapshot: ThemeSnapshot = readInitialSnapshot();\nconst listeners = new Set<() => void>();\n\nfunction commit(next: ThemeSnapshot): void {\n if (\n next.theme === snapshot.theme &&\n next.accessibility === snapshot.accessibility &&\n next.prefersDark === snapshot.prefersDark &&\n next.prefersMoreContrast === snapshot.prefersMoreContrast\n ) {\n return;\n }\n snapshot = next;\n listeners.forEach((listener) => listener());\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nfunction getSnapshot(): ThemeSnapshot {\n return snapshot;\n}\n\nfunction getServerSnapshot(): ThemeSnapshot {\n return {\n theme: 'system',\n accessibility: 'system',\n prefersDark: false,\n prefersMoreContrast: false,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Media-query subscriptions — mounted once per tab, on the first time */\n/* `useTheme` is called in the browser. Cleanup happens via a ref-count */\n/* in the subscription effect below. */\n/* -------------------------------------------------------------------- */\n\nlet mediaSubscriberCount = 0;\nlet colorSchemeMql: MediaQueryList | null = null;\nlet contrastMql: MediaQueryList | null = null;\nlet storageHandler: ((event: StorageEvent) => void) | null = null;\n\nfunction onColorSchemeChange(event: MediaQueryListEvent): void {\n commit({ ...snapshot, prefersDark: event.matches });\n}\n\nfunction onContrastChange(event: MediaQueryListEvent): void {\n commit({ ...snapshot, prefersMoreContrast: event.matches });\n}\n\nfunction onStorage(event: StorageEvent): void {\n if (event.key === THEME_STORAGE_KEY) {\n const next = isValidTheme(event.newValue) ? event.newValue : 'system';\n commit({ ...snapshot, theme: next });\n } else if (event.key === ACCESSIBILITY_STORAGE_KEY) {\n const next = isValidAccessibility(event.newValue)\n ? event.newValue\n : 'system';\n commit({ ...snapshot, accessibility: next });\n }\n}\n\n/* The `<html>` class fallback from `readInitialSnapshot` must stay LIVE:\n Storybook's `withThemeByClassName` decorator (and host apps' shells)\n rewrite the documentElement classes after this module's initial read,\n and a one-shot sample leaves the hook pinned to a stale preference —\n a nested `<ThemeRoot>` then clobbers the outer cascade (e.g. a\n `theme-light` root inside a `theme-dark` page). Stored preferences\n still win, mirroring the initial-read precedence. */\nfunction rederiveFromHtmlClasses(): void {\n if (typeof document === 'undefined') return;\n const classes = document.documentElement.classList;\n const storedTheme = safeReadStorage(THEME_STORAGE_KEY);\n const storedA11y = safeReadStorage(ACCESSIBILITY_STORAGE_KEY);\n const htmlTheme: ThemePreference | null = classes.contains(THEME_CLASS.dark)\n ? 'dark'\n : classes.contains(THEME_CLASS.light)\n ? 'light'\n : null;\n const htmlA11y: AccessibilityPreference | null = classes.contains(\n THEME_CLASS.accessible,\n )\n ? 'accessible'\n : null;\n commit({\n ...snapshot,\n theme: isValidTheme(storedTheme) ? storedTheme : (htmlTheme ?? 'system'),\n accessibility: isValidAccessibility(storedA11y)\n ? storedA11y\n : (htmlA11y ?? 'system'),\n });\n}\n\nfunction attachMqlListener(\n mql: MediaQueryList,\n handler: (event: MediaQueryListEvent) => void,\n): () => void {\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', handler);\n return () => mql.removeEventListener('change', handler);\n }\n // Safari < 14 fallback.\n mql.addListener(handler);\n return () => mql.removeListener(handler);\n}\n\nfunction mountMediaSubscriptions(): () => void {\n mediaSubscriberCount += 1;\n if (mediaSubscriberCount > 1) {\n return disposeOneSubscriber;\n }\n\n if (typeof window === 'undefined') {\n return disposeOneSubscriber;\n }\n\n if (typeof window.matchMedia === 'function') {\n colorSchemeMql = window.matchMedia(COLOR_SCHEME_QUERY);\n contrastMql = window.matchMedia(CONTRAST_QUERY);\n // Re-sync the module snapshot in case the OS preferences changed\n // between initial read and first effect run (hydration window).\n commit({\n ...snapshot,\n prefersDark: colorSchemeMql.matches,\n prefersMoreContrast: contrastMql.matches,\n });\n detachColorScheme = attachMqlListener(colorSchemeMql, onColorSchemeChange);\n detachContrast = attachMqlListener(contrastMql, onContrastChange);\n }\n\n // Cross-tab sync — `storage` only fires on *other* tabs, so the local\n // tab still needs setTheme to update snapshot directly (it does).\n storageHandler = onStorage;\n window.addEventListener('storage', storageHandler);\n\n // Live `<html>` class tracking — decorators / host shells may rewrite\n // the theme classes after mount; re-derive (and re-sync once now, in\n // case the classes changed inside the hydration window).\n if (\n typeof document !== 'undefined' &&\n typeof MutationObserver !== 'undefined'\n ) {\n rederiveFromHtmlClasses();\n htmlClassObserver = new MutationObserver(rederiveFromHtmlClasses);\n htmlClassObserver.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class'],\n });\n }\n\n return disposeOneSubscriber;\n}\n\nlet detachColorScheme: (() => void) | null = null;\nlet detachContrast: (() => void) | null = null;\nlet htmlClassObserver: MutationObserver | null = null;\n\nfunction disposeOneSubscriber(): void {\n mediaSubscriberCount -= 1;\n if (mediaSubscriberCount > 0) return;\n detachColorScheme?.();\n detachContrast?.();\n detachColorScheme = null;\n detachContrast = null;\n colorSchemeMql = null;\n contrastMql = null;\n if (storageHandler && typeof window !== 'undefined') {\n window.removeEventListener('storage', storageHandler);\n }\n storageHandler = null;\n htmlClassObserver?.disconnect();\n htmlClassObserver = null;\n}\n\n/* -------------------------------------------------------------------- */\n/* Resolution helpers */\n/* -------------------------------------------------------------------- */\n\nexport function resolveTheme(state: ThemeSnapshot): ResolvedTheme {\n const base: ThemeBase =\n state.theme === 'system'\n ? state.prefersDark\n ? 'dark'\n : 'light'\n : state.theme;\n const accessible =\n state.accessibility === 'system'\n ? state.prefersMoreContrast\n : state.accessibility === 'accessible';\n if (base === 'dark' && accessible) return 'dark-accessible';\n if (base === 'dark') return 'dark';\n if (accessible) return 'light-accessible';\n return 'light';\n}\n\nexport function themeClassList(resolved: ResolvedTheme): string[] {\n switch (resolved) {\n case 'light':\n return [THEME_CLASS.light];\n case 'dark':\n return [THEME_CLASS.dark];\n case 'light-accessible':\n return [THEME_CLASS.light, THEME_CLASS.accessible];\n case 'dark-accessible':\n return [THEME_CLASS.dark, THEME_CLASS.accessible];\n }\n}\n\nconst ALL_THEME_CLASSES = [\n THEME_CLASS.light,\n THEME_CLASS.dark,\n THEME_CLASS.accessible,\n];\n\nfunction applyToDocument(resolved: ResolvedTheme): void {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n const next = new Set(themeClassList(resolved));\n ALL_THEME_CLASSES.forEach((cls) => {\n if (next.has(cls)) {\n root.classList.add(cls);\n } else {\n root.classList.remove(cls);\n }\n });\n}\n\n/* -------------------------------------------------------------------- */\n/* Public hook */\n/* -------------------------------------------------------------------- */\n\n/**\n * Read and update the DS theme preference.\n *\n * - `theme` / `accessibility` are the stored user preferences, with\n * `'system'` meaning \"no override — follow OS\".\n * - `resolvedTheme` composes preferences + live `matchMedia` state into\n * one of the four concrete themes.\n * - `setTheme` / `setAccessibility` persist the override to\n * `localStorage` and re-apply classes to `document.documentElement`\n * synchronously. Pass `'system'` to clear.\n *\n * Singleton under the hood — every hook call shares one `matchMedia`\n * subscription, so mounting the hook many times does not multiply the\n * listener count. The hook also listens for cross-tab `storage` events\n * so a theme change in tab A is reflected in tab B without a reload.\n */\nexport function useTheme(): UseThemeReturn {\n const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n\n useEffect(() => mountMediaSubscriptions(), []);\n\n useEffect(() => {\n applyToDocument(resolveTheme(state));\n }, [state]);\n\n const setTheme = useCallback((next: ThemePreference) => {\n safeWriteStorage(THEME_STORAGE_KEY, next === 'system' ? null : next);\n commit({ ...snapshot, theme: next });\n }, []);\n\n const setAccessibility = useCallback((next: AccessibilityPreference) => {\n safeWriteStorage(\n ACCESSIBILITY_STORAGE_KEY,\n next === 'system' ? null : next,\n );\n commit({ ...snapshot, accessibility: next });\n }, []);\n\n return {\n theme: state.theme,\n accessibility: state.accessibility,\n resolvedTheme: resolveTheme(state),\n setTheme,\n setAccessibility,\n };\n}\n\n/* -------------------------------------------------------------------- */\n/* Test hook — reset module-level state between tests. Not exported */\n/* from the barrel. */\n/* -------------------------------------------------------------------- */\n\nexport function __resetThemeStoreForTests(): void {\n snapshot = readInitialSnapshot();\n listeners.clear();\n mediaSubscriberCount = 0;\n detachColorScheme?.();\n detachContrast?.();\n detachColorScheme = null;\n detachContrast = null;\n colorSchemeMql = null;\n contrastMql = null;\n if (storageHandler && typeof window !== 'undefined') {\n window.removeEventListener('storage', storageHandler);\n }\n storageHandler = null;\n}\n"],"names":["THEME_STORAGE_KEY","ACCESSIBILITY_STORAGE_KEY","THEME_CLASS","COLOR_SCHEME_QUERY","CONTRAST_QUERY","safeReadStorage","key","safeWriteStorage","value","isValidTheme","isValidAccessibility","readInitialSnapshot","storedTheme","storedA11y","hasMatchMedia","htmlClasses","htmlThemeOverride","htmlA11yOverride","snapshot","listeners","commit","next","listener","subscribe","getSnapshot","getServerSnapshot","mediaSubscriberCount","colorSchemeMql","contrastMql","storageHandler","onColorSchemeChange","event","onContrastChange","onStorage","rederiveFromHtmlClasses","classes","htmlTheme","htmlA11y","attachMqlListener","mql","handler","mountMediaSubscriptions","detachColorScheme","detachContrast","htmlClassObserver","disposeOneSubscriber","resolveTheme","state","base","accessible","themeClassList","resolved","ALL_THEME_CLASSES","applyToDocument","root","cls","useTheme","useSyncExternalStore","useEffect","setTheme","useCallback","setAccessibility"],"mappings":"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"}
|