@brandlab-digital/site-next 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.d.ts +25 -12
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +23 -1
- package/dist/analytics.js.map +1 -1
- package/dist/contact/client.d.ts +6 -0
- package/dist/contact/client.d.ts.map +1 -1
- package/dist/contact/client.js +10 -2
- package/dist/contact/client.js.map +1 -1
- package/dist/contact/form-client.d.ts.map +1 -1
- package/dist/contact/form-client.js +26 -3
- package/dist/contact/form-client.js.map +1 -1
- package/dist/ui-kit/brandlab-skin.d.ts +17 -0
- package/dist/ui-kit/brandlab-skin.d.ts.map +1 -0
- package/dist/ui-kit/brandlab-skin.js +2 -0
- package/dist/ui-kit/brandlab-skin.js.map +1 -0
- package/dist/ui-kit/icons.d.ts +15 -0
- package/dist/ui-kit/icons.d.ts.map +1 -0
- package/dist/ui-kit/icons.js +72 -0
- package/dist/ui-kit/icons.js.map +1 -0
- package/dist/ui-kit/primitives/index.d.ts +6 -0
- package/dist/ui-kit/primitives/index.d.ts.map +1 -0
- package/dist/ui-kit/primitives/index.js +6 -0
- package/dist/ui-kit/primitives/index.js.map +1 -0
- package/dist/ui-kit/primitives/layout.d.ts +73 -0
- package/dist/ui-kit/primitives/layout.d.ts.map +1 -0
- package/dist/ui-kit/primitives/layout.js +34 -0
- package/dist/ui-kit/primitives/layout.js.map +1 -0
- package/dist/ui-kit/primitives/links.d.ts +48 -0
- package/dist/ui-kit/primitives/links.d.ts.map +1 -0
- package/dist/ui-kit/primitives/links.js +36 -0
- package/dist/ui-kit/primitives/links.js.map +1 -0
- package/dist/ui-kit/primitives/quote-button.d.ts +20 -0
- package/dist/ui-kit/primitives/quote-button.d.ts.map +1 -0
- package/dist/ui-kit/primitives/quote-button.js +15 -0
- package/dist/ui-kit/primitives/quote-button.js.map +1 -0
- package/dist/ui-kit/skin-index.d.ts +3 -0
- package/dist/ui-kit/skin-index.d.ts.map +1 -0
- package/dist/ui-kit/skin-index.js +2 -0
- package/dist/ui-kit/skin-index.js.map +1 -0
- package/dist/ui-kit/skin.d.ts +48 -0
- package/dist/ui-kit/skin.d.ts.map +1 -0
- package/dist/ui-kit/skin.js +47 -0
- package/dist/ui-kit/skin.js.map +1 -0
- package/dist/ui-kit/structure.css +123 -0
- package/dist/ui-kit/types.d.ts +27 -0
- package/dist/ui-kit/types.d.ts.map +1 -0
- package/dist/ui-kit/types.js +6 -0
- package/dist/ui-kit/types.js.map +1 -0
- package/package.json +17 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-client.js","sourceRoot":"","sources":["../../src/contact/form-client.ts"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport {\n DEFAULT_CONTACT_ATTACHMENT_POLICY,\n DOCUMENT_CONTACT_ATTACHMENT_POLICY,\n defineContactContract,\n type BrandlabContactContract,\n type ContactActionResult,\n} from '@brandlab-digital/site-core/contact'\nimport { normalizeBrandlabRoutePath } from '@brandlab-digital/site-core/routes'\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type FormEvent,\n type RefObject,\n} from 'react'\nimport type { BrandlabAnalytics, LeadFormErrorStage } from '../analytics.js'\nimport { leadBindingFor, safeSlug } from '../lead-registry.js'\nimport { reportAcceptedLead } from '../lead-reporting.js'\nimport {\n useBrandlabContactAttachments,\n type UseBrandlabContactAttachmentsOptions,\n type UseBrandlabContactAttachmentsResult,\n} from './attachments-client.js'\nimport {\n createContactHttpSubmissionController,\n type ContactSubmissionPhase,\n type ContactSubmissionState,\n} from './client.js'\n\n/** An accepted enquiry, or a silently accepted spam-protected submission (`accepted: false`). */\nexport type BrandlabContactAcceptedResult = Extract<ContactActionResult, { ok: true }>\nexport type BrandlabContactFailedResult = Extract<ContactActionResult, { ok: false }>\n\nexport interface BrandlabContactSubmitStart {\n form: HTMLFormElement\n /**\n * The fields about to be sent, read from the form. Add or replace entries\n * here (for example `service_interest`); prepared photos are appended after.\n */\n formData: FormData\n}\n\nexport interface UseBrandlabContactFormOptions {\n /** The contact contract from src/config/contact.ts. */\n contract: BrandlabContactContract\n /** The site's defineBrandlabAnalytics binding: the declared lead after each accepted enquiry, plus lead diagnostics. */\n analytics?: BrandlabAnalytics\n /** The current pathname, for example `usePathname()`. */\n pagePath: string\n /** Reviewed label for the enquiry email's page row. */\n pageLabel?: string\n /**\n * The form element's id and the lead diagnostics' form_id. Without it the form\n * gets no id (the hook never takes a React id, so the form's own ids are\n * unchanged), diagnostics use `contact-form` and error ids start `contact-form-`.\n * The element keeps the id exactly as given; diagnostics report it as a slug\n * (React `useId()` parts removed, see `contactFormSlug`).\n */\n formId?: string\n /** Page to open after an accepted enquiry; `null` or omitted keeps the visitor on the page. */\n redirectTo?: string | null\n /** Client navigation such as `router.push`; defaults to `window.location.assign`. */\n navigate?: (href: string) => void\n /** Delay before the redirect, in milliseconds (default 1500); 0 navigates at once. */\n redirectDelayMs?: number\n /**\n * Runs when the visitor submits, before anything is sent: clear the form's\n * own notices, adjust `formData`, or return `false` to cancel (for example\n * after `form.reportValidity()`). An unchanged resubmission runs it too;\n * `retry()` does not.\n */\n onSubmitStart?: (submission: BrandlabContactSubmitStart) => void | false\n /**\n * Runs after every attempt, before onAccepted or onFailed: an accepted\n * enquiry, a silently accepted submission (even when ignored) or a failure.\n */\n onSettled?: (result: ContactActionResult) => void\n /** Runs after an accepted enquiry, once the form has been reset and before any redirect. */\n onAccepted?: (result: BrandlabContactAcceptedResult) => void\n /** Runs after a failed attempt; typed values and prepared photos are kept. */\n onFailed?: (result: BrandlabContactFailedResult) => void\n /**\n * A silently accepted submission (spam protection): `complete` (default)\n * resets, runs onAccepted and redirects like an accepted enquiry; `ignore`\n * leaves the form as it is and runs only onSettled.\n */\n silentAcceptance?: 'complete' | 'ignore'\n /** Photo preparation options; the policy follows the contract's attachment mode by default. */\n attachments?: UseBrandlabContactAttachmentsOptions\n /** Multipart field prefix for prepared files (default `attachments`, sent as `attachments[0]` …). */\n attachmentFieldPrefix?: string\n /** Live Consent Mode decision for ad_user_data; without it Enhanced Conversions stay off, unless the analytics contract's mode is `disclosed`. */\n adUserDataConsent?: boolean\n contactEndpoint?: string\n fetcher?: typeof fetch\n}\n\n/**\n * Spread onto the `<form>`: `method=\"post\"` with no `action` or `encType`, so a\n * submission before hydration posts back to the page and never puts fields in\n * a URL; the hook submits through fetch. `id` only when `formId` is given.\n */\nexport interface BrandlabContactFormProps {\n id?: string\n ref: RefObject<HTMLFormElement | null>\n method: 'post'\n 'aria-busy': boolean\n onSubmit: (event: FormEvent<HTMLFormElement>) => void\n onFocusCapture: () => void\n}\n\n/** Spread onto the submit button: disabled until hydration, while sending or preparing photos. */\nexport interface BrandlabContactSubmitProps {\n type: 'submit'\n disabled: boolean\n}\n\nexport interface UseBrandlabContactFormResult {\n /** The `formId` option, or `contact-form` when none was given. */\n formId: string\n formProps: BrandlabContactFormProps\n submitProps: BrandlabContactSubmitProps\n /** False during server rendering and hydration. */\n hydrated: boolean\n /** The submit button's `disabled` (also in submitProps): until hydration, while sending or preparing photos. */\n submitDisabled: boolean\n pending: boolean\n phase: ContactSubmissionPhase\n result: ContactActionResult | undefined\n /** An accepted enquiry (a silently accepted spam-protected submission looks the same). */\n succeeded: boolean\n /** A retryable failure: retry(), or submitting the same fields again, reuses its submission ID. */\n canRetry: boolean\n fieldErrors: Readonly<Record<string, readonly string[]>>\n fieldError(name: string): string | undefined\n /** Id for a field's error message element: `${formId}-${name}-error`. */\n errorId(name: string): string\n /** Attach to the status element; it receives focus when an error has no invalid field. */\n statusRef: RefObject<HTMLElement | null>\n /** Resends the failed enquiry with the same submission ID. */\n retry(): Promise<ContactActionResult | undefined>\n attachments: UseBrandlabContactAttachmentsResult\n /** Contract field keys accepted by contactFieldProps. */\n fieldNames: ReadonlySet<string>\n}\n\nconst INITIAL_STATE: ContactSubmissionState = {\n phase: 'idle',\n pending: false,\n canRetry: false,\n}\nconst DEFAULT_REDIRECT_DELAY_MS = 1_500\nconst FOCUSABLE_INVALID_FIELD = [\n 'input[aria-invalid=\"true\"]:not([disabled]):not([type=\"hidden\"])',\n 'select[aria-invalid=\"true\"]:not([disabled])',\n 'textarea[aria-invalid=\"true\"]:not([disabled])',\n 'button[aria-invalid=\"true\"]:not([disabled])',\n '[aria-invalid=\"true\"][tabindex]:not([aria-disabled=\"true\"])',\n].join(',')\n\nconst subscribeToNothing = () => () => undefined\nconst getClientSnapshot = () => true\nconst getServerSnapshot = () => false\n\n/** React `useId()` values: `_R_1a_`/`_r_1a_` (19.2), `«r1a»` (19.0 and 19.1), `:r1a:` (18). */\nconst REACT_ID = /_[rR]_[0-9A-Za-z]*_|«[rR][0-9A-Za-z]*»|:[rR][0-9A-Za-z]*:/g\n\n/**\n * The lead diagnostics' form_id for any formId. A valid slug is kept (trimmed and\n * lowercased). Otherwise React `useId()` parts are removed, so an id made unique for\n * the page still reports as one stable form, every run of other characters becomes\n * one hyphen, and the result is at most 80 characters; `contact-form` when nothing\n * is left. Without this, a formId such as `contact-form-_R_5e35f9db_` silently\n * dropped lead_form_start and lead_form_error.\n */\nexport function contactFormSlug(formId: string): string {\n const slug = safeSlug(String(formId))\n if (slug) return slug\n const derived = String(formId)\n .replace(REACT_ID, '-')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+/, '')\n .slice(0, 80)\n .replace(/-+$/, '')\n return derived || 'contact-form'\n}\n\nfunction safePagePath(pagePath: string) {\n try {\n return normalizeBrandlabRoutePath(pagePath.split(/[?#]/, 1)[0] || '/')\n } catch {\n return '/'\n }\n}\n\nfunction errorStage(result: BrandlabContactFailedResult): LeadFormErrorStage {\n return result.code === 'validation' ? 'validation' : 'delivery'\n}\n\nfunction assertLifecycleOptions(options: UseBrandlabContactFormOptions) {\n const delay = options.redirectDelayMs\n if (delay !== undefined && (!Number.isInteger(delay) || delay < 0 || delay > 60_000)) {\n throw new TypeError('redirectDelayMs must be a whole number of milliseconds from 0 to 60000.')\n }\n if (options.silentAcceptance !== undefined && !['complete', 'ignore'].includes(options.silentAcceptance)) {\n throw new TypeError('silentAcceptance must be \"complete\" or \"ignore\".')\n }\n if (options.analytics !== undefined && !leadBindingFor(options.analytics)) {\n throw new TypeError('analytics must be the binding returned by defineBrandlabAnalytics.')\n }\n}\n\n/**\n * Contact form behaviour for project-owned markup and copy. The form posts\n * through fetch with FormData; before hydration the submit button stays\n * disabled. Typed values (uncontrolled fields) and prepared photos are kept\n * after any failure, retry() and an unchanged resubmission reuse the same\n * submission ID (so a resend cannot duplicate the email), the first invalid\n * field is focused, and an accepted enquiry resets the form and redirects.\n * The lead is reported exactly as the site's defineBrandlabAnalytics binding\n * declares, once per accepted enquiry. Browser validation is left exactly as\n * the form's own markup declares it.\n */\nexport function useBrandlabContactForm(options: UseBrandlabContactFormOptions): UseBrandlabContactFormResult {\n const contract = useMemo(() => defineContactContract(options.contract), [options.contract])\n assertLifecycleOptions(options)\n const hydrated = useSyncExternalStore(subscribeToNothing, getClientSnapshot, getServerSnapshot)\n const formId = options.formId ?? 'contact-form'\n const diagnosticsFormId = contactFormSlug(formId)\n const rawPagePath = options.pagePath\n const pagePath = safePagePath(rawPagePath)\n const formRef = useRef<HTMLFormElement>(null)\n const statusRef = useRef<HTMLElement>(null)\n const [state, setState] = useState<ContactSubmissionState>(INITIAL_STATE)\n const lockRef = useRef(false)\n const startedRef = useRef(false)\n const redirectTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const latest = useRef(options)\n latest.current = options\n\n const policy = options.attachments?.policy ??\n (contract.attachments.mode === 'images-and-pdfs-optional'\n ? DOCUMENT_CONTACT_ATTACHMENT_POLICY\n : DEFAULT_CONTACT_ATTACHMENT_POLICY)\n const attachments = useBrandlabContactAttachments({ ...options.attachments, policy })\n const attachmentPrefix = options.attachmentFieldPrefix ?? 'attachments'\n\n const fieldNames = useMemo(() => new Set(contract.fields.map((field) => field.key)), [contract])\n const roles = useMemo(\n () => ({ fields: contract.fields.map((field) => ({ name: field.key, role: field.role })) }),\n [contract],\n )\n const { analytics, pageLabel, contactEndpoint, fetcher, adUserDataConsent } = options\n const controller = useMemo(() => {\n const lead = leadBindingFor(analytics)\n return createContactHttpSubmissionController({\n pagePath,\n ...(pageLabel ? { pageLabel } : {}),\n ...(contactEndpoint ? { contactEndpoint } : {}),\n ...(fetcher ? { fetcher } : {}),\n ...(lead\n ? {\n reportAccepted: (result, formData) => {\n reportAcceptedLead(lead, result, {\n pagePath: rawPagePath,\n formData,\n fieldRoles: roles,\n ...(adUserDataConsent !== undefined ? { adUserDataConsent } : {}),\n })\n },\n }\n : {}),\n onStateChange: setState,\n })\n }, [adUserDataConsent, analytics, contactEndpoint, fetcher, pageLabel, pagePath, rawPagePath, roles])\n\n useEffect(() => () => clearTimeout(redirectTimerRef.current), [])\n\n useEffect(() => {\n if (state.phase !== 'error') return\n const invalid = formRef.current?.querySelector<HTMLElement>(FOCUSABLE_INVALID_FIELD)\n ;(invalid ?? statusRef.current)?.focus()\n }, [state])\n\n const trackStart = useCallback(() => {\n if (startedRef.current) return\n startedRef.current = true\n latest.current.analytics?.trackLeadFormStart({ pagePath: rawPagePath, formId: diagnosticsFormId })\n }, [diagnosticsFormId, rawPagePath])\n\n const handleResult = useCallback(\n (result: ContactActionResult) => {\n const current = latest.current\n current.onSettled?.(result)\n if (!result.ok) {\n current.analytics?.trackLeadFormError({\n pagePath: rawPagePath,\n formId: diagnosticsFormId,\n stage: errorStage(result),\n errorCount: result.code === 'validation'\n ? Math.min(50, Math.max(1, Object.keys(result.fieldErrors ?? {}).length))\n : 1,\n })\n current.onFailed?.(result)\n return\n }\n if (!result.accepted && current.silentAcceptance === 'ignore') return\n formRef.current?.reset()\n attachments.clearImages()\n current.onAccepted?.(result)\n const redirectTo = current.redirectTo\n if (!redirectTo) return\n const navigate = current.navigate ?? ((href: string) => window.location.assign(href))\n const delay = current.redirectDelayMs ?? DEFAULT_REDIRECT_DELAY_MS\n clearTimeout(redirectTimerRef.current)\n if (delay === 0) {\n navigate(redirectTo)\n return\n }\n redirectTimerRef.current = setTimeout(() => navigate(redirectTo), delay)\n },\n [attachments, diagnosticsFormId, rawPagePath],\n )\n\n const send = useCallback(\n async (attempt: () => Promise<ContactActionResult>) => {\n lockRef.current = true\n try {\n const result = await attempt()\n handleResult(result)\n return result\n } finally {\n lockRef.current = false\n }\n },\n [handleResult],\n )\n\n const onSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n if (lockRef.current || attachments.isProcessing) return\n const form = event.currentTarget\n const formData = new FormData(form)\n if (latest.current.onSubmitStart?.({ form, formData }) === false) return\n trackStart()\n attachments.clearErrorMessage()\n attachments.images.forEach((image, index) => {\n formData.append(`${attachmentPrefix}[${index}]`, image.file)\n })\n // The controller reports every failure as a result; nothing is left to handle here.\n send(() => controller.submit(formData)).catch(() => undefined)\n },\n [attachmentPrefix, attachments, controller, send, trackStart],\n )\n\n const retry = useCallback(async () => {\n if (lockRef.current || !controller.getState().canRetry) return undefined\n return send(() => controller.retry())\n }, [controller, send])\n\n const fieldErrors = state.result && !state.result.ok && state.result.code === 'validation'\n ? state.result.fieldErrors ?? {}\n : {}\n\n const submitDisabled = !hydrated || state.pending || attachments.isProcessing\n return {\n formId,\n formProps: {\n ...(options.formId ? { id: options.formId } : {}),\n ref: formRef,\n method: 'post',\n 'aria-busy': state.pending || attachments.isProcessing,\n onSubmit,\n onFocusCapture: trackStart,\n },\n submitProps: { type: 'submit', disabled: submitDisabled },\n hydrated,\n submitDisabled,\n pending: state.pending,\n phase: state.phase,\n result: state.result,\n succeeded: state.result?.ok === true,\n canRetry: state.canRetry,\n fieldErrors,\n fieldError: (name: string) => fieldErrors[name]?.[0],\n errorId: (name: string) => `${formId}-${name}-error`,\n statusRef,\n retry,\n attachments,\n fieldNames,\n }\n}\n\nexport interface ContactFieldPropsOptions {\n /** Ids of help text already describing the field; the error id is appended. */\n describedBy?: string\n /** Defaults to disabled while an enquiry is sending. */\n disabled?: boolean\n}\n\n/**\n * Props for one uncontrolled contract field: name, disabled while sending and\n * the error relationship. It never adds value, required or length attributes,\n * so the form's existing validation is unchanged.\n */\nexport function contactFieldProps(\n form: UseBrandlabContactFormResult,\n name: string,\n options: ContactFieldPropsOptions = {},\n) {\n if (!form.fieldNames.has(name)) {\n throw new Error(`\"${name}\" is not a field in the contact contract.`)\n }\n const error = form.fieldError(name)\n const describedBy = [options.describedBy, error ? form.errorId(name) : undefined]\n .filter(Boolean)\n .join(' ')\n return {\n name,\n disabled: options.disabled ?? form.pending,\n 'aria-invalid': error ? (true as const) : undefined,\n 'aria-describedby': describedBy || undefined,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"form-client.js","sourceRoot":"","sources":["../../src/contact/form-client.ts"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport {\n DEFAULT_CONTACT_ATTACHMENT_POLICY,\n DOCUMENT_CONTACT_ATTACHMENT_POLICY,\n defineContactContract,\n type BrandlabContactContract,\n type ContactActionResult,\n} from '@brandlab-digital/site-core/contact'\nimport { normalizeBrandlabRoutePath } from '@brandlab-digital/site-core/routes'\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type FormEvent,\n type RefObject,\n} from 'react'\nimport type { BrandlabAnalytics, LeadFormErrorReason, LeadFormErrorStage } from '../analytics.js'\nimport { leadBindingFor, safeSlug } from '../lead-registry.js'\nimport { reportAcceptedLead } from '../lead-reporting.js'\nimport {\n useBrandlabContactAttachments,\n type UseBrandlabContactAttachmentsOptions,\n type UseBrandlabContactAttachmentsResult,\n} from './attachments-client.js'\nimport {\n createContactHttpSubmissionController,\n isContactClientFailure,\n type ContactSubmissionPhase,\n type ContactSubmissionState,\n} from './client.js'\n\n/** An accepted enquiry, or a silently accepted spam-protected submission (`accepted: false`). */\nexport type BrandlabContactAcceptedResult = Extract<ContactActionResult, { ok: true }>\nexport type BrandlabContactFailedResult = Extract<ContactActionResult, { ok: false }>\n\nexport interface BrandlabContactSubmitStart {\n form: HTMLFormElement\n /**\n * The fields about to be sent, read from the form. Add or replace entries\n * here (for example `service_interest`); prepared photos are appended after.\n */\n formData: FormData\n}\n\nexport interface UseBrandlabContactFormOptions {\n /** The contact contract from src/config/contact.ts. */\n contract: BrandlabContactContract\n /** The site's defineBrandlabAnalytics binding: the declared lead after each accepted enquiry, plus lead diagnostics. */\n analytics?: BrandlabAnalytics\n /** The current pathname, for example `usePathname()`. */\n pagePath: string\n /** Reviewed label for the enquiry email's page row. */\n pageLabel?: string\n /**\n * The form element's id and the lead diagnostics' form_id. Without it the form\n * gets no id (the hook never takes a React id, so the form's own ids are\n * unchanged), diagnostics use `contact-form` and error ids start `contact-form-`.\n * The element keeps the id exactly as given; diagnostics report it as a slug\n * (React `useId()` parts removed, see `contactFormSlug`).\n */\n formId?: string\n /** Page to open after an accepted enquiry; `null` or omitted keeps the visitor on the page. */\n redirectTo?: string | null\n /** Client navigation such as `router.push`; defaults to `window.location.assign`. */\n navigate?: (href: string) => void\n /** Delay before the redirect, in milliseconds (default 1500); 0 navigates at once. */\n redirectDelayMs?: number\n /**\n * Runs when the visitor submits, before anything is sent: clear the form's\n * own notices, adjust `formData`, or return `false` to cancel (for example\n * after `form.reportValidity()`). An unchanged resubmission runs it too;\n * `retry()` does not.\n */\n onSubmitStart?: (submission: BrandlabContactSubmitStart) => void | false\n /**\n * Runs after every attempt, before onAccepted or onFailed: an accepted\n * enquiry, a silently accepted submission (even when ignored) or a failure.\n */\n onSettled?: (result: ContactActionResult) => void\n /** Runs after an accepted enquiry, once the form has been reset and before any redirect. */\n onAccepted?: (result: BrandlabContactAcceptedResult) => void\n /** Runs after a failed attempt; typed values and prepared photos are kept. */\n onFailed?: (result: BrandlabContactFailedResult) => void\n /**\n * A silently accepted submission (spam protection): `complete` (default)\n * resets, runs onAccepted and redirects like an accepted enquiry; `ignore`\n * leaves the form as it is and runs only onSettled.\n */\n silentAcceptance?: 'complete' | 'ignore'\n /** Photo preparation options; the policy follows the contract's attachment mode by default. */\n attachments?: UseBrandlabContactAttachmentsOptions\n /** Multipart field prefix for prepared files (default `attachments`, sent as `attachments[0]` …). */\n attachmentFieldPrefix?: string\n /** Live Consent Mode decision for ad_user_data; without it Enhanced Conversions stay off, unless the analytics contract's mode is `disclosed`. */\n adUserDataConsent?: boolean\n contactEndpoint?: string\n fetcher?: typeof fetch\n}\n\n/**\n * Spread onto the `<form>`: `method=\"post\"` with no `action` or `encType`, so a\n * submission before hydration posts back to the page and never puts fields in\n * a URL; the hook submits through fetch. `id` only when `formId` is given.\n */\nexport interface BrandlabContactFormProps {\n id?: string\n ref: RefObject<HTMLFormElement | null>\n method: 'post'\n 'aria-busy': boolean\n onSubmit: (event: FormEvent<HTMLFormElement>) => void\n onFocusCapture: () => void\n}\n\n/** Spread onto the submit button: disabled until hydration, while sending or preparing photos. */\nexport interface BrandlabContactSubmitProps {\n type: 'submit'\n disabled: boolean\n}\n\nexport interface UseBrandlabContactFormResult {\n /** The `formId` option, or `contact-form` when none was given. */\n formId: string\n formProps: BrandlabContactFormProps\n submitProps: BrandlabContactSubmitProps\n /** False during server rendering and hydration. */\n hydrated: boolean\n /** The submit button's `disabled` (also in submitProps): until hydration, while sending or preparing photos. */\n submitDisabled: boolean\n pending: boolean\n phase: ContactSubmissionPhase\n result: ContactActionResult | undefined\n /** An accepted enquiry (a silently accepted spam-protected submission looks the same). */\n succeeded: boolean\n /** A retryable failure: retry(), or submitting the same fields again, reuses its submission ID. */\n canRetry: boolean\n fieldErrors: Readonly<Record<string, readonly string[]>>\n fieldError(name: string): string | undefined\n /** Id for a field's error message element: `${formId}-${name}-error`. */\n errorId(name: string): string\n /** Attach to the status element; it receives focus when an error has no invalid field. */\n statusRef: RefObject<HTMLElement | null>\n /** Resends the failed enquiry with the same submission ID. */\n retry(): Promise<ContactActionResult | undefined>\n attachments: UseBrandlabContactAttachmentsResult\n /** Contract field keys accepted by contactFieldProps. */\n fieldNames: ReadonlySet<string>\n}\n\nconst INITIAL_STATE: ContactSubmissionState = {\n phase: 'idle',\n pending: false,\n canRetry: false,\n}\nconst DEFAULT_REDIRECT_DELAY_MS = 1_500\nconst FOCUSABLE_INVALID_FIELD = [\n 'input[aria-invalid=\"true\"]:not([disabled]):not([type=\"hidden\"])',\n 'select[aria-invalid=\"true\"]:not([disabled])',\n 'textarea[aria-invalid=\"true\"]:not([disabled])',\n 'button[aria-invalid=\"true\"]:not([disabled])',\n '[aria-invalid=\"true\"][tabindex]:not([aria-disabled=\"true\"])',\n].join(',')\n\nconst subscribeToNothing = () => () => undefined\nconst getClientSnapshot = () => true\nconst getServerSnapshot = () => false\n\n/** React `useId()` values: `_R_1a_`/`_r_1a_` (19.2), `«r1a»` (19.0 and 19.1), `:r1a:` (18). */\nconst REACT_ID = /_[rR]_[0-9A-Za-z]*_|«[rR][0-9A-Za-z]*»|:[rR][0-9A-Za-z]*:/g\n\n/**\n * The lead diagnostics' form_id for any formId. A valid slug is kept (trimmed and\n * lowercased). Otherwise React `useId()` parts are removed, so an id made unique for\n * the page still reports as one stable form, every run of other characters becomes\n * one hyphen, and the result is at most 80 characters; `contact-form` when nothing\n * is left. Without this, a formId such as `contact-form-_R_5e35f9db_` silently\n * dropped lead_form_start and lead_form_error.\n */\nexport function contactFormSlug(formId: string): string {\n const slug = safeSlug(String(formId))\n if (slug) return slug\n const derived = String(formId)\n .replace(REACT_ID, '-')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+/, '')\n .slice(0, 80)\n .replace(/-+$/, '')\n return derived || 'contact-form'\n}\n\nfunction safePagePath(pagePath: string) {\n try {\n return normalizeBrandlabRoutePath(pagePath.split(/[?#]/, 1)[0] || '/')\n } catch {\n return '/'\n }\n}\n\nfunction errorStage(result: BrandlabContactFailedResult): LeadFormErrorStage {\n return result.code === 'validation' ? 'validation' : 'delivery'\n}\n\n/**\n * Separates a field the visitor can correct from a request the server refused,\n * rejected photos, a server delivery failure and a browser-side failure, which\n * error_stage alone reports as only `validation` or `delivery`.\n */\nfunction errorReason(result: BrandlabContactFailedResult): LeadFormErrorReason {\n if (result.code === 'validation') {\n return Object.keys(result.fieldErrors ?? {}).length > 0 ? 'field' : 'request'\n }\n if (result.code === 'attachment_rejected') return 'attachment'\n return isContactClientFailure(result) ? 'network' : 'delivery'\n}\n\n/** The first rejected field in contract order: a contract key, never a value. */\nfunction firstErrorField(result: BrandlabContactFailedResult, fieldOrder: readonly string[]) {\n const fieldErrors = result.fieldErrors ?? {}\n return fieldOrder.find((key) => fieldErrors[key]?.length) ?? Object.keys(fieldErrors)[0]\n}\n\nfunction assertLifecycleOptions(options: UseBrandlabContactFormOptions) {\n const delay = options.redirectDelayMs\n if (delay !== undefined && (!Number.isInteger(delay) || delay < 0 || delay > 60_000)) {\n throw new TypeError('redirectDelayMs must be a whole number of milliseconds from 0 to 60000.')\n }\n if (options.silentAcceptance !== undefined && !['complete', 'ignore'].includes(options.silentAcceptance)) {\n throw new TypeError('silentAcceptance must be \"complete\" or \"ignore\".')\n }\n if (options.analytics !== undefined && !leadBindingFor(options.analytics)) {\n throw new TypeError('analytics must be the binding returned by defineBrandlabAnalytics.')\n }\n}\n\n/**\n * Contact form behaviour for project-owned markup and copy. The form posts\n * through fetch with FormData; before hydration the submit button stays\n * disabled. Typed values (uncontrolled fields) and prepared photos are kept\n * after any failure, retry() and an unchanged resubmission reuse the same\n * submission ID (so a resend cannot duplicate the email), the first invalid\n * field is focused, and an accepted enquiry resets the form and redirects.\n * The lead is reported exactly as the site's defineBrandlabAnalytics binding\n * declares, once per accepted enquiry. Browser validation is left exactly as\n * the form's own markup declares it.\n */\nexport function useBrandlabContactForm(options: UseBrandlabContactFormOptions): UseBrandlabContactFormResult {\n const contract = useMemo(() => defineContactContract(options.contract), [options.contract])\n assertLifecycleOptions(options)\n const hydrated = useSyncExternalStore(subscribeToNothing, getClientSnapshot, getServerSnapshot)\n const formId = options.formId ?? 'contact-form'\n const diagnosticsFormId = contactFormSlug(formId)\n const rawPagePath = options.pagePath\n const pagePath = safePagePath(rawPagePath)\n const formRef = useRef<HTMLFormElement>(null)\n const statusRef = useRef<HTMLElement>(null)\n const [state, setState] = useState<ContactSubmissionState>(INITIAL_STATE)\n const lockRef = useRef(false)\n const startedRef = useRef(false)\n const redirectTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const latest = useRef(options)\n latest.current = options\n\n const policy = options.attachments?.policy ??\n (contract.attachments.mode === 'images-and-pdfs-optional'\n ? DOCUMENT_CONTACT_ATTACHMENT_POLICY\n : DEFAULT_CONTACT_ATTACHMENT_POLICY)\n const attachments = useBrandlabContactAttachments({ ...options.attachments, policy })\n const attachmentPrefix = options.attachmentFieldPrefix ?? 'attachments'\n\n const fieldOrder = useMemo(() => contract.fields.map((field) => field.key), [contract])\n const fieldNames = useMemo(() => new Set(fieldOrder), [fieldOrder])\n const roles = useMemo(\n () => ({ fields: contract.fields.map((field) => ({ name: field.key, role: field.role })) }),\n [contract],\n )\n const { analytics, pageLabel, contactEndpoint, fetcher, adUserDataConsent } = options\n const controller = useMemo(() => {\n const lead = leadBindingFor(analytics)\n return createContactHttpSubmissionController({\n pagePath,\n ...(pageLabel ? { pageLabel } : {}),\n ...(contactEndpoint ? { contactEndpoint } : {}),\n ...(fetcher ? { fetcher } : {}),\n ...(lead\n ? {\n reportAccepted: (result, formData) => {\n reportAcceptedLead(lead, result, {\n pagePath: rawPagePath,\n formData,\n fieldRoles: roles,\n ...(adUserDataConsent !== undefined ? { adUserDataConsent } : {}),\n })\n },\n }\n : {}),\n onStateChange: setState,\n })\n }, [adUserDataConsent, analytics, contactEndpoint, fetcher, pageLabel, pagePath, rawPagePath, roles])\n\n useEffect(() => () => clearTimeout(redirectTimerRef.current), [])\n\n useEffect(() => {\n if (state.phase !== 'error') return\n const invalid = formRef.current?.querySelector<HTMLElement>(FOCUSABLE_INVALID_FIELD)\n ;(invalid ?? statusRef.current)?.focus()\n }, [state])\n\n const trackStart = useCallback(() => {\n if (startedRef.current) return\n startedRef.current = true\n latest.current.analytics?.trackLeadFormStart({ pagePath: rawPagePath, formId: diagnosticsFormId })\n }, [diagnosticsFormId, rawPagePath])\n\n const handleResult = useCallback(\n (result: ContactActionResult) => {\n const current = latest.current\n current.onSettled?.(result)\n if (!result.ok) {\n const reason = errorReason(result)\n const field = reason === 'field' ? firstErrorField(result, fieldOrder) : undefined\n current.analytics?.trackLeadFormError({\n pagePath: rawPagePath,\n formId: diagnosticsFormId,\n stage: errorStage(result),\n errorCount: result.code === 'validation'\n ? Math.min(50, Math.max(1, Object.keys(result.fieldErrors ?? {}).length))\n : 1,\n reason,\n ...(field ? { field } : {}),\n })\n current.onFailed?.(result)\n return\n }\n if (!result.accepted && current.silentAcceptance === 'ignore') return\n formRef.current?.reset()\n attachments.clearImages()\n current.onAccepted?.(result)\n const redirectTo = current.redirectTo\n if (!redirectTo) return\n const navigate = current.navigate ?? ((href: string) => window.location.assign(href))\n const delay = current.redirectDelayMs ?? DEFAULT_REDIRECT_DELAY_MS\n clearTimeout(redirectTimerRef.current)\n if (delay === 0) {\n navigate(redirectTo)\n return\n }\n redirectTimerRef.current = setTimeout(() => navigate(redirectTo), delay)\n },\n [attachments, diagnosticsFormId, fieldOrder, rawPagePath],\n )\n\n const send = useCallback(\n async (attempt: () => Promise<ContactActionResult>) => {\n lockRef.current = true\n try {\n const result = await attempt()\n handleResult(result)\n return result\n } finally {\n lockRef.current = false\n }\n },\n [handleResult],\n )\n\n const onSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n if (lockRef.current || attachments.isProcessing) return\n const form = event.currentTarget\n const formData = new FormData(form)\n if (latest.current.onSubmitStart?.({ form, formData }) === false) return\n trackStart()\n attachments.clearErrorMessage()\n attachments.images.forEach((image, index) => {\n formData.append(`${attachmentPrefix}[${index}]`, image.file)\n })\n // The controller reports every failure as a result; nothing is left to handle here.\n send(() => controller.submit(formData)).catch(() => undefined)\n },\n [attachmentPrefix, attachments, controller, send, trackStart],\n )\n\n const retry = useCallback(async () => {\n if (lockRef.current || !controller.getState().canRetry) return undefined\n return send(() => controller.retry())\n }, [controller, send])\n\n const fieldErrors = state.result && !state.result.ok && state.result.code === 'validation'\n ? state.result.fieldErrors ?? {}\n : {}\n\n const submitDisabled = !hydrated || state.pending || attachments.isProcessing\n return {\n formId,\n formProps: {\n ...(options.formId ? { id: options.formId } : {}),\n ref: formRef,\n method: 'post',\n 'aria-busy': state.pending || attachments.isProcessing,\n onSubmit,\n onFocusCapture: trackStart,\n },\n submitProps: { type: 'submit', disabled: submitDisabled },\n hydrated,\n submitDisabled,\n pending: state.pending,\n phase: state.phase,\n result: state.result,\n succeeded: state.result?.ok === true,\n canRetry: state.canRetry,\n fieldErrors,\n fieldError: (name: string) => fieldErrors[name]?.[0],\n errorId: (name: string) => `${formId}-${name}-error`,\n statusRef,\n retry,\n attachments,\n fieldNames,\n }\n}\n\nexport interface ContactFieldPropsOptions {\n /** Ids of help text already describing the field; the error id is appended. */\n describedBy?: string\n /** Defaults to disabled while an enquiry is sending. */\n disabled?: boolean\n}\n\n/**\n * Props for one uncontrolled contract field: name, disabled while sending and\n * the error relationship. It never adds value, required or length attributes,\n * so the form's existing validation is unchanged.\n */\nexport function contactFieldProps(\n form: UseBrandlabContactFormResult,\n name: string,\n options: ContactFieldPropsOptions = {},\n) {\n if (!form.fieldNames.has(name)) {\n throw new Error(`\"${name}\" is not a field in the contact contract.`)\n }\n const error = form.fieldError(name)\n const describedBy = [options.describedBy, error ? form.errorId(name) : undefined]\n .filter(Boolean)\n .join(' ')\n return {\n name,\n disabled: options.disabled ?? form.pending,\n 'aria-invalid': error ? (true as const) : undefined,\n 'aria-describedby': describedBy || undefined,\n }\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A site's whole look for the ui-kit: one entry per shared component it renders (`src/site/skin.ts`, written with
|
|
3
|
+
* `satisfies BrandlabSkin`). Each release adds the entries for its components. K2 checks that every rendered
|
|
4
|
+
* component has an entry and that no reference-skin class ships.
|
|
5
|
+
*/
|
|
6
|
+
import type { ButtonLinkSkin, PhoneLinkSkin, TextLinkSkin } from './primitives/links.js';
|
|
7
|
+
import type { QuoteButtonSkin } from './primitives/quote-button.js';
|
|
8
|
+
import type { ContainerSkin, SectionHeadingSkin } from './primitives/layout.js';
|
|
9
|
+
export interface BrandlabSkin {
|
|
10
|
+
readonly buttonLink?: ButtonLinkSkin;
|
|
11
|
+
readonly textLink?: TextLinkSkin;
|
|
12
|
+
readonly phoneLink?: PhoneLinkSkin;
|
|
13
|
+
readonly quoteButton?: QuoteButtonSkin;
|
|
14
|
+
readonly container?: ContainerSkin;
|
|
15
|
+
readonly sectionHeading?: SectionHeadingSkin;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=brandlab-skin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brandlab-skin.d.ts","sourceRoot":"","sources":["../../src/ui-kit/brandlab-skin.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACxF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAE/E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,CAAA;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,eAAe,CAAA;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;IAClC,QAAQ,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAA;CAC7C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brandlab-skin.js","sourceRoot":"","sources":["../../src/ui-kit/brandlab-skin.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * A site's whole look for the ui-kit: one entry per shared component it renders (`src/site/skin.ts`, written with\n * `satisfies BrandlabSkin`). Each release adds the entries for its components. K2 checks that every rendered\n * component has an entry and that no reference-skin class ships.\n */\nimport type { ButtonLinkSkin, PhoneLinkSkin, TextLinkSkin } from './primitives/links.js'\nimport type { QuoteButtonSkin } from './primitives/quote-button.js'\nimport type { ContainerSkin, SectionHeadingSkin } from './primitives/layout.js'\n\nexport interface BrandlabSkin {\n readonly buttonLink?: ButtonLinkSkin\n readonly textLink?: TextLinkSkin\n readonly phoneLink?: PhoneLinkSkin\n readonly quoteButton?: QuoteButtonSkin\n readonly container?: ContainerSkin\n readonly sectionHeading?: SectionHeadingSkin\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { UiSocialKind } from './types.js';
|
|
2
|
+
export interface IconProps {
|
|
3
|
+
readonly className?: string | undefined;
|
|
4
|
+
}
|
|
5
|
+
export declare const UI_ICON_NAMES: readonly ["phone", "chevron", "arrow", "arrow-up", "menu", "close", "star", "check", "camera", "mail", "facebook", "instagram", "google", "linkedin", "youtube", "tiktok"];
|
|
6
|
+
export type UiIconName = (typeof UI_ICON_NAMES)[number];
|
|
7
|
+
/** One icon from the set by name. */
|
|
8
|
+
export declare function Icon({ name, className }: IconProps & {
|
|
9
|
+
name: UiIconName;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
/** The icon for a social or Business Profile link. */
|
|
12
|
+
export declare function SocialIcon({ kind, className }: IconProps & {
|
|
13
|
+
kind: UiSocialKind;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../src/ui-kit/icons.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC;AAED,eAAO,MAAM,aAAa,4KAiBhB,CAAA;AACV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAwGvD,qCAAqC;AACrC,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,2CAOzE;AAED,sDAAsD;AACtD,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,2CAEjF"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export const UI_ICON_NAMES = [
|
|
3
|
+
'phone',
|
|
4
|
+
'chevron',
|
|
5
|
+
'arrow',
|
|
6
|
+
'arrow-up',
|
|
7
|
+
'menu',
|
|
8
|
+
'close',
|
|
9
|
+
'star',
|
|
10
|
+
'check',
|
|
11
|
+
'camera',
|
|
12
|
+
'mail',
|
|
13
|
+
'facebook',
|
|
14
|
+
'instagram',
|
|
15
|
+
'google',
|
|
16
|
+
'linkedin',
|
|
17
|
+
'youtube',
|
|
18
|
+
'tiktok',
|
|
19
|
+
];
|
|
20
|
+
function Svg({ name, viewBox = '0 0 24 24', className, children }) {
|
|
21
|
+
return (_jsx("svg", { "aria-hidden": "true", focusable: "false", viewBox: viewBox, className: className, "data-icon": name, children: children }));
|
|
22
|
+
}
|
|
23
|
+
const stroke = { fill: 'none', stroke: 'currentColor', strokeLinecap: 'round', strokeLinejoin: 'round' };
|
|
24
|
+
const GLYPHS = {
|
|
25
|
+
phone: {
|
|
26
|
+
body: (_jsx("path", { ...stroke, strokeWidth: "1.6", d: "M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106a1.125 1.125 0 0 0-1.173.417l-.97 1.293a1.125 1.125 0 0 1-1.21.38 12.035 12.035 0 0 1-7.143-7.143 1.125 1.125 0 0 1 .38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z" })),
|
|
27
|
+
},
|
|
28
|
+
chevron: { body: _jsx("path", { ...stroke, strokeWidth: "1.8", d: "m6 9 6 6 6-6" }) },
|
|
29
|
+
arrow: { body: _jsx("path", { ...stroke, strokeWidth: "1.6", d: "M13.5 4.5 21 12l-7.5 7.5M3 12h17.25" }) },
|
|
30
|
+
'arrow-up': { body: _jsx("path", { ...stroke, strokeWidth: "1.8", d: "M12 20V4m0 0-7 7m7-7 7 7" }) },
|
|
31
|
+
menu: { body: _jsx("path", { ...stroke, strokeWidth: "1.7", d: "M4 7h16M4 12h16M4 17h16" }) },
|
|
32
|
+
close: { body: _jsx("path", { ...stroke, strokeWidth: "1.7", d: "M6 18 18 6M6 6l12 12" }) },
|
|
33
|
+
star: {
|
|
34
|
+
viewBox: '0 0 20 20',
|
|
35
|
+
body: _jsx("path", { fill: "currentColor", d: "M10 1.6 12.6 7l5.9.7-4.4 4 1.2 5.8L10 14.6l-5.3 2.9L5.9 11.7l-4.4-4L7.4 7 10 1.6Z" }),
|
|
36
|
+
},
|
|
37
|
+
check: { body: _jsx("path", { ...stroke, strokeWidth: "2", d: "m4.5 12.75 6 6 9-13.5" }) },
|
|
38
|
+
camera: {
|
|
39
|
+
body: (_jsxs(_Fragment, { children: [_jsx("path", { ...stroke, strokeWidth: "1.6", d: "M3 8.25A2.25 2.25 0 0 1 5.25 6h1.372c.516 0 .966-.351 1.091-.852l.38-1.52A1.125 1.125 0 0 1 9.184 2.7h5.632c.516 0 .966.351 1.091.852l.38 1.52c.125.5.575.852 1.091.852h1.372A2.25 2.25 0 0 1 21 8.25v9.75a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18V8.25Z" }), _jsx("circle", { ...stroke, strokeWidth: "1.6", cx: "12", cy: "12.75", r: "3.75" })] })),
|
|
40
|
+
},
|
|
41
|
+
mail: {
|
|
42
|
+
body: (_jsx("path", { ...stroke, strokeWidth: "1.6", d: "M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0-8.69 5.6a2.25 2.25 0 0 1-2.12 0l-8.69-5.6" })),
|
|
43
|
+
},
|
|
44
|
+
facebook: {
|
|
45
|
+
body: (_jsx("path", { fill: "currentColor", d: "M9 8H6v4h3v12h5V12h3.642L18 8h-4V6.333C14 5.378 14.192 5 15.115 5H18V0h-3.808C10.596 0 9 1.583 9 4.615V8Z" })),
|
|
46
|
+
},
|
|
47
|
+
instagram: {
|
|
48
|
+
body: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "5", fill: "none", stroke: "currentColor", strokeWidth: "2" }), _jsx("circle", { cx: "12", cy: "12", r: "4.2", fill: "none", stroke: "currentColor", strokeWidth: "2" }), _jsx("circle", { cx: "17.4", cy: "6.6", r: "1.3", fill: "currentColor" })] })),
|
|
49
|
+
},
|
|
50
|
+
google: {
|
|
51
|
+
body: (_jsx("path", { fill: "currentColor", d: "M21.6 12.23c0-.68-.06-1.33-.17-1.96H12v3.7h5.39a4.6 4.6 0 0 1-2 3.02v2.5h3.24c1.9-1.75 2.97-4.32 2.97-7.26ZM12 22c2.7 0 4.96-.9 6.62-2.43l-3.24-2.5c-.9.6-2.04.95-3.38.95-2.6 0-4.8-1.75-5.59-4.11H3.07v2.59A10 10 0 0 0 12 22Zm-5.59-8.09A6 6 0 0 1 6.1 12c0-.66.11-1.31.31-1.91V7.5H3.07A10 10 0 0 0 2 12c0 1.61.39 3.14 1.07 4.5l3.34-2.59ZM12 5.98c1.47 0 2.79.5 3.83 1.5l2.87-2.87A9.6 9.6 0 0 0 12 2a10 10 0 0 0-8.93 5.5l3.34 2.59C7.2 7.73 9.4 5.98 12 5.98Z" })),
|
|
52
|
+
},
|
|
53
|
+
linkedin: {
|
|
54
|
+
body: (_jsx("path", { fill: "currentColor", d: "M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3 9.75h4v11.5H3V9.75Zm6.5 0h3.83v1.57h.06c.53-1 1.84-2.07 3.79-2.07 4.05 0 4.8 2.67 4.8 6.13v5.87h-4v-5.2c0-1.24-.02-2.84-1.73-2.84-1.73 0-2 1.35-2 2.75v5.29h-4V9.75Z" })),
|
|
55
|
+
},
|
|
56
|
+
youtube: {
|
|
57
|
+
body: (_jsx("path", { fill: "currentColor", d: "M23 7.2a3 3 0 0 0-2.1-2.1C19 4.6 12 4.6 12 4.6s-7 0-8.9.5A3 3 0 0 0 1 7.2 31 31 0 0 0 .5 12a31 31 0 0 0 .5 4.8 3 3 0 0 0 2.1 2.1c1.9.5 8.9.5 8.9.5s7 0 8.9-.5a3 3 0 0 0 2.1-2.1 31 31 0 0 0 .5-4.8 31 31 0 0 0-.5-4.8ZM9.75 15.02V8.98L15.5 12l-5.75 3.02Z" })),
|
|
58
|
+
},
|
|
59
|
+
tiktok: {
|
|
60
|
+
body: (_jsx("path", { fill: "currentColor", d: "M16.6 2h-3.4v13.4a2.9 2.9 0 1 1-2.9-2.9c.3 0 .6 0 .9.1V9.1a6.3 6.3 0 1 0 5.4 6.3V8.6a8.1 8.1 0 0 0 4.4 1.3V6.5a4.5 4.5 0 0 1-4.4-4.5Z" })),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
/** One icon from the set by name. */
|
|
64
|
+
export function Icon({ name, className }) {
|
|
65
|
+
const glyph = GLYPHS[name];
|
|
66
|
+
return (_jsx(Svg, { name: name, viewBox: glyph.viewBox, className: className, children: glyph.body }));
|
|
67
|
+
}
|
|
68
|
+
/** The icon for a social or Business Profile link. */
|
|
69
|
+
export function SocialIcon({ kind, className }) {
|
|
70
|
+
return _jsx(Icon, { name: kind, className: className });
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=icons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../../src/ui-kit/icons.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * The ui-kit's inline SVG icon set (no icon font). Every icon is decorative (`aria-hidden`, not focusable): the\n * control around it carries the accessible name. Stroke and fill use `currentColor`, so a skin colours them through\n * its classes or tokens. `data-icon` names the glyph for skins and tests.\n */\nimport type { ReactNode } from 'react'\nimport type { UiSocialKind } from './types.js'\n\nexport interface IconProps {\n readonly className?: string | undefined\n}\n\nexport const UI_ICON_NAMES = [\n 'phone',\n 'chevron',\n 'arrow',\n 'arrow-up',\n 'menu',\n 'close',\n 'star',\n 'check',\n 'camera',\n 'mail',\n 'facebook',\n 'instagram',\n 'google',\n 'linkedin',\n 'youtube',\n 'tiktok',\n] as const\nexport type UiIconName = (typeof UI_ICON_NAMES)[number]\n\nfunction Svg({ name, viewBox = '0 0 24 24', className, children }: IconProps & { name: UiIconName; viewBox?: string; children: ReactNode }) {\n return (\n <svg aria-hidden=\"true\" focusable=\"false\" viewBox={viewBox} className={className} data-icon={name}>\n {children}\n </svg>\n )\n}\n\nconst stroke = { fill: 'none', stroke: 'currentColor', strokeLinecap: 'round', strokeLinejoin: 'round' } as const\n\nconst GLYPHS: Record<UiIconName, { viewBox?: string; body: ReactNode }> = {\n phone: {\n body: (\n <path\n {...stroke}\n strokeWidth=\"1.6\"\n d=\"M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106a1.125 1.125 0 0 0-1.173.417l-.97 1.293a1.125 1.125 0 0 1-1.21.38 12.035 12.035 0 0 1-7.143-7.143 1.125 1.125 0 0 1 .38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z\"\n />\n ),\n },\n chevron: { body: <path {...stroke} strokeWidth=\"1.8\" d=\"m6 9 6 6 6-6\" /> },\n arrow: { body: <path {...stroke} strokeWidth=\"1.6\" d=\"M13.5 4.5 21 12l-7.5 7.5M3 12h17.25\" /> },\n 'arrow-up': { body: <path {...stroke} strokeWidth=\"1.8\" d=\"M12 20V4m0 0-7 7m7-7 7 7\" /> },\n menu: { body: <path {...stroke} strokeWidth=\"1.7\" d=\"M4 7h16M4 12h16M4 17h16\" /> },\n close: { body: <path {...stroke} strokeWidth=\"1.7\" d=\"M6 18 18 6M6 6l12 12\" /> },\n star: {\n viewBox: '0 0 20 20',\n body: <path fill=\"currentColor\" d=\"M10 1.6 12.6 7l5.9.7-4.4 4 1.2 5.8L10 14.6l-5.3 2.9L5.9 11.7l-4.4-4L7.4 7 10 1.6Z\" />,\n },\n check: { body: <path {...stroke} strokeWidth=\"2\" d=\"m4.5 12.75 6 6 9-13.5\" /> },\n camera: {\n body: (\n <>\n <path\n {...stroke}\n strokeWidth=\"1.6\"\n d=\"M3 8.25A2.25 2.25 0 0 1 5.25 6h1.372c.516 0 .966-.351 1.091-.852l.38-1.52A1.125 1.125 0 0 1 9.184 2.7h5.632c.516 0 .966.351 1.091.852l.38 1.52c.125.5.575.852 1.091.852h1.372A2.25 2.25 0 0 1 21 8.25v9.75a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18V8.25Z\"\n />\n <circle {...stroke} strokeWidth=\"1.6\" cx=\"12\" cy=\"12.75\" r=\"3.75\" />\n </>\n ),\n },\n mail: {\n body: (\n <path\n {...stroke}\n strokeWidth=\"1.6\"\n d=\"M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0-8.69 5.6a2.25 2.25 0 0 1-2.12 0l-8.69-5.6\"\n />\n ),\n },\n facebook: {\n body: (\n <path\n fill=\"currentColor\"\n d=\"M9 8H6v4h3v12h5V12h3.642L18 8h-4V6.333C14 5.378 14.192 5 15.115 5H18V0h-3.808C10.596 0 9 1.583 9 4.615V8Z\"\n />\n ),\n },\n instagram: {\n body: (\n <>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <circle cx=\"12\" cy=\"12\" r=\"4.2\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <circle cx=\"17.4\" cy=\"6.6\" r=\"1.3\" fill=\"currentColor\" />\n </>\n ),\n },\n google: {\n body: (\n <path\n fill=\"currentColor\"\n d=\"M21.6 12.23c0-.68-.06-1.33-.17-1.96H12v3.7h5.39a4.6 4.6 0 0 1-2 3.02v2.5h3.24c1.9-1.75 2.97-4.32 2.97-7.26ZM12 22c2.7 0 4.96-.9 6.62-2.43l-3.24-2.5c-.9.6-2.04.95-3.38.95-2.6 0-4.8-1.75-5.59-4.11H3.07v2.59A10 10 0 0 0 12 22Zm-5.59-8.09A6 6 0 0 1 6.1 12c0-.66.11-1.31.31-1.91V7.5H3.07A10 10 0 0 0 2 12c0 1.61.39 3.14 1.07 4.5l3.34-2.59ZM12 5.98c1.47 0 2.79.5 3.83 1.5l2.87-2.87A9.6 9.6 0 0 0 12 2a10 10 0 0 0-8.93 5.5l3.34 2.59C7.2 7.73 9.4 5.98 12 5.98Z\"\n />\n ),\n },\n linkedin: {\n body: (\n <path\n fill=\"currentColor\"\n d=\"M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5ZM3 9.75h4v11.5H3V9.75Zm6.5 0h3.83v1.57h.06c.53-1 1.84-2.07 3.79-2.07 4.05 0 4.8 2.67 4.8 6.13v5.87h-4v-5.2c0-1.24-.02-2.84-1.73-2.84-1.73 0-2 1.35-2 2.75v5.29h-4V9.75Z\"\n />\n ),\n },\n youtube: {\n body: (\n <path\n fill=\"currentColor\"\n d=\"M23 7.2a3 3 0 0 0-2.1-2.1C19 4.6 12 4.6 12 4.6s-7 0-8.9.5A3 3 0 0 0 1 7.2 31 31 0 0 0 .5 12a31 31 0 0 0 .5 4.8 3 3 0 0 0 2.1 2.1c1.9.5 8.9.5 8.9.5s7 0 8.9-.5a3 3 0 0 0 2.1-2.1 31 31 0 0 0 .5-4.8 31 31 0 0 0-.5-4.8ZM9.75 15.02V8.98L15.5 12l-5.75 3.02Z\"\n />\n ),\n },\n tiktok: {\n body: (\n <path\n fill=\"currentColor\"\n d=\"M16.6 2h-3.4v13.4a2.9 2.9 0 1 1-2.9-2.9c.3 0 .6 0 .9.1V9.1a6.3 6.3 0 1 0 5.4 6.3V8.6a8.1 8.1 0 0 0 4.4 1.3V6.5a4.5 4.5 0 0 1-4.4-4.5Z\"\n />\n ),\n },\n}\n\n/** One icon from the set by name. */\nexport function Icon({ name, className }: IconProps & { name: UiIconName }) {\n const glyph = GLYPHS[name]\n return (\n <Svg name={name} viewBox={glyph.viewBox} className={className}>\n {glyph.body}\n </Svg>\n )\n}\n\n/** The icon for a social or Business Profile link. */\nexport function SocialIcon({ kind, className }: IconProps & { kind: UiSocialKind }) {\n return <Icon name={kind} className={className} />\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { BUTTON_VARIANTS, ButtonLink, PhoneLink, TextLink, type ButtonLinkProps, type ButtonLinkSkin, type ButtonLinkSlot, type ButtonVariant, type PhoneLinkProps, type PhoneLinkSkin, type PhoneLinkSlot, type TextLinkProps, type TextLinkSkin, type TextLinkSlot, } from './links.js';
|
|
2
|
+
export { CONTAINER_WIDTHS, Container, RatingStars, SECTION_TONES, Section, SectionHeading, VisuallyHidden, type ContainerSkin, type ContainerWidth, type SectionHeadingProps, type SectionHeadingSkin, type SectionHeadingSlot, type SectionProps, type SectionSlot, type SectionTone, } from './layout.js';
|
|
3
|
+
export { QuoteButton, type QuoteButtonProps, type QuoteButtonSkin, type QuoteButtonSlot } from './quote-button.js';
|
|
4
|
+
export { Icon, SocialIcon, UI_ICON_NAMES, type IconProps, type UiIconName } from '../icons.js';
|
|
5
|
+
export { UI_SOCIAL_KINDS, type UiLink, type UiPhone, type UiSocial, type UiSocialKind } from '../types.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/primitives/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,UAAU,EACV,SAAS,EACT,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,cAAc,EACd,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAClH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC9F,OAAO,EAAE,eAAe,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { BUTTON_VARIANTS, ButtonLink, PhoneLink, TextLink, } from './links.js';
|
|
2
|
+
export { CONTAINER_WIDTHS, Container, RatingStars, SECTION_TONES, Section, SectionHeading, VisuallyHidden, } from './layout.js';
|
|
3
|
+
export { QuoteButton } from './quote-button.js';
|
|
4
|
+
export { Icon, SocialIcon, UI_ICON_NAMES } from '../icons.js';
|
|
5
|
+
export { UI_SOCIAL_KINDS } from '../types.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui-kit/primitives/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,UAAU,EACV,SAAS,EACT,QAAQ,GAWT,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,cAAc,EACd,cAAc,GASf,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,WAAW,EAAqE,MAAM,mBAAmB,CAAA;AAClH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAmC,MAAM,aAAa,CAAA;AAC9F,OAAO,EAAE,eAAe,EAA+D,MAAM,aAAa,CAAA","sourcesContent":["export {\n BUTTON_VARIANTS,\n ButtonLink,\n PhoneLink,\n TextLink,\n type ButtonLinkProps,\n type ButtonLinkSkin,\n type ButtonLinkSlot,\n type ButtonVariant,\n type PhoneLinkProps,\n type PhoneLinkSkin,\n type PhoneLinkSlot,\n type TextLinkProps,\n type TextLinkSkin,\n type TextLinkSlot,\n} from './links.js'\nexport {\n CONTAINER_WIDTHS,\n Container,\n RatingStars,\n SECTION_TONES,\n Section,\n SectionHeading,\n VisuallyHidden,\n type ContainerSkin,\n type ContainerWidth,\n type SectionHeadingProps,\n type SectionHeadingSkin,\n type SectionHeadingSlot,\n type SectionProps,\n type SectionSlot,\n type SectionTone,\n} from './layout.js'\nexport { QuoteButton, type QuoteButtonProps, type QuoteButtonSkin, type QuoteButtonSlot } from './quote-button.js'\nexport { Icon, SocialIcon, UI_ICON_NAMES, type IconProps, type UiIconName } from '../icons.js'\nexport { UI_SOCIAL_KINDS, type UiLink, type UiPhone, type UiSocial, type UiSocialKind } from '../types.js'\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout primitives: `Container`, `Section`, `SectionHeading`, `Eyebrow`, `RatingStars` and `VisuallyHidden`.
|
|
3
|
+
* Server components. Heading levels come from the page outline (one H1 in the hero or page header, an H2 per
|
|
4
|
+
* section), never from a look.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactNode } from 'react';
|
|
7
|
+
import type { PhoneCallPlacement } from '../../analytics.js';
|
|
8
|
+
import { type SkinEntry } from '../skin.js';
|
|
9
|
+
export declare const CONTAINER_WIDTHS: readonly ["default", "narrow", "wide"];
|
|
10
|
+
export type ContainerWidth = (typeof CONTAINER_WIDTHS)[number];
|
|
11
|
+
export type ContainerSkin = SkinEntry<'root', never, {
|
|
12
|
+
width?: ContainerWidth;
|
|
13
|
+
}>;
|
|
14
|
+
/** One container width shared by the header, the page and the footer (`--bl-container`, `--bl-gutter`). */
|
|
15
|
+
export declare function Container({ children, layout, classes }: ContainerSkin & {
|
|
16
|
+
readonly children: ReactNode;
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
/** The four section tones `SectionSequence` assigns: two alternating grounds, one optional band, one photo ground. */
|
|
19
|
+
export declare const SECTION_TONES: readonly ["canvas", "surface", "band", "photo"];
|
|
20
|
+
export type SectionTone = (typeof SECTION_TONES)[number];
|
|
21
|
+
export type SectionSlot = 'root' | 'inner';
|
|
22
|
+
export interface SectionProps {
|
|
23
|
+
/** The matrix id, rendered as `data-bl-section` (for example `reviews`, `cta`, `faqs`). */
|
|
24
|
+
readonly id: string;
|
|
25
|
+
/** The id of the section's H2. A section is a `<section>` labelled by its heading. */
|
|
26
|
+
readonly labelledBy: string;
|
|
27
|
+
readonly tone?: SectionTone | undefined;
|
|
28
|
+
readonly variant?: string | undefined;
|
|
29
|
+
readonly classes?: Readonly<Partial<Record<SectionSlot, string>>> | undefined;
|
|
30
|
+
/** Signature art, rendered `aria-hidden`. */
|
|
31
|
+
readonly decoration?: ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* Extra layout hooks on the root, for example `{ 'data-columns': '3' }`. They cannot override `data-bl-section`,
|
|
34
|
+
* `data-variant`, `data-tone` or `data-phone-location`.
|
|
35
|
+
*/
|
|
36
|
+
readonly hooks?: Readonly<Record<`data-${string}`, string | undefined>>;
|
|
37
|
+
/** The placement a `tel:` link inside the section reports when it has none of its own. */
|
|
38
|
+
readonly phonePlacement?: PhoneCallPlacement | undefined;
|
|
39
|
+
readonly children: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The root every section component renders: `<section data-bl-section aria-labelledby>`, its tone and variant, an
|
|
43
|
+
* optional `aria-hidden` decoration and the inner container slot.
|
|
44
|
+
*/
|
|
45
|
+
export declare function Section({ id, labelledBy, tone, variant, classes, decoration, hooks, phonePlacement, children }: SectionProps): import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
export type SectionHeadingSlot = 'head' | 'eyebrow' | 'heading' | 'lead';
|
|
47
|
+
export type SectionHeadingSkin = SkinEntry<SectionHeadingSlot, never, {
|
|
48
|
+
align?: 'start' | 'center';
|
|
49
|
+
}>;
|
|
50
|
+
export interface SectionHeadingProps extends SectionHeadingSkin {
|
|
51
|
+
/** The H2's id, which the section's `aria-labelledby` points at. */
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly title: ReactNode;
|
|
54
|
+
/** A short label above the H2 (at least 13 px; never the only label for the section). */
|
|
55
|
+
readonly eyebrow?: ReactNode;
|
|
56
|
+
readonly lead?: ReactNode;
|
|
57
|
+
}
|
|
58
|
+
/** A section's heading block: an optional eyebrow, the H2 and an optional lead paragraph. */
|
|
59
|
+
export declare function SectionHeading({ id, title, eyebrow, lead, layout, classes }: SectionHeadingProps): import("react/jsx-runtime").JSX.Element;
|
|
60
|
+
/**
|
|
61
|
+
* Five stars with one text alternative ("Rated 4.9 out of 5"). The stars use `--bl-color-star`; whole stars only,
|
|
62
|
+
* rounded to the nearest. The rating always comes from the feed, never typed by hand (T4).
|
|
63
|
+
*/
|
|
64
|
+
export declare function RatingStars({ rating, label, className }: {
|
|
65
|
+
readonly rating: number;
|
|
66
|
+
readonly label?: string;
|
|
67
|
+
readonly className?: string;
|
|
68
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
69
|
+
/** Text for assistive technology only. */
|
|
70
|
+
export declare function VisuallyHidden({ children }: {
|
|
71
|
+
readonly children: ReactNode;
|
|
72
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
73
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/primitives/layout.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Icon } from '../icons.js';
|
|
3
|
+
import { slot } from '../skin.js';
|
|
4
|
+
export const CONTAINER_WIDTHS = ['default', 'narrow', 'wide'];
|
|
5
|
+
/** One container width shared by the header, the page and the footer (`--bl-container`, `--bl-gutter`). */
|
|
6
|
+
export function Container({ children, layout, classes }) {
|
|
7
|
+
return (_jsx("div", { "data-bl": "container", "data-width": layout?.width ?? 'default', className: classes?.root, children: children }));
|
|
8
|
+
}
|
|
9
|
+
/** The four section tones `SectionSequence` assigns: two alternating grounds, one optional band, one photo ground. */
|
|
10
|
+
export const SECTION_TONES = ['canvas', 'surface', 'band', 'photo'];
|
|
11
|
+
/**
|
|
12
|
+
* The root every section component renders: `<section data-bl-section aria-labelledby>`, its tone and variant, an
|
|
13
|
+
* optional `aria-hidden` decoration and the inner container slot.
|
|
14
|
+
*/
|
|
15
|
+
export function Section({ id, labelledBy, tone, variant, classes, decoration, hooks, phonePlacement, children }) {
|
|
16
|
+
return (_jsxs("section", { ...hooks, "data-bl-section": id, "data-variant": variant, "data-tone": tone, "data-phone-location": phonePlacement, "aria-labelledby": labelledBy, className: classes?.root, children: [decoration ? (_jsx("div", { "data-slot": "decoration", "aria-hidden": "true", children: decoration })) : null, _jsx("div", { ...slot(classes, 'inner'), children: children })] }));
|
|
17
|
+
}
|
|
18
|
+
/** A section's heading block: an optional eyebrow, the H2 and an optional lead paragraph. */
|
|
19
|
+
export function SectionHeading({ id, title, eyebrow, lead, layout, classes }) {
|
|
20
|
+
return (_jsxs("div", { ...slot(classes, 'head'), "data-align": layout?.align ?? 'start', children: [eyebrow ? _jsx("p", { ...slot(classes, 'eyebrow'), children: eyebrow }) : null, _jsx("h2", { id: id, ...slot(classes, 'heading'), children: title }), lead ? _jsx("p", { ...slot(classes, 'lead'), children: lead }) : null] }));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Five stars with one text alternative ("Rated 4.9 out of 5"). The stars use `--bl-color-star`; whole stars only,
|
|
24
|
+
* rounded to the nearest. The rating always comes from the feed, never typed by hand (T4).
|
|
25
|
+
*/
|
|
26
|
+
export function RatingStars({ rating, label, className }) {
|
|
27
|
+
const filled = Math.max(0, Math.min(5, Math.round(rating)));
|
|
28
|
+
return (_jsx("span", { "data-bl": "stars", role: "img", "aria-label": label ?? `Rated ${rating.toFixed(1)} out of 5`, className: className, children: Array.from({ length: 5 }, (_, index) => (_jsx("span", { "data-slot": "star", "data-state": index < filled ? 'on' : 'off', children: _jsx(Icon, { name: "star" }) }, index))) }));
|
|
29
|
+
}
|
|
30
|
+
/** Text for assistive technology only. */
|
|
31
|
+
export function VisuallyHidden({ children }) {
|
|
32
|
+
return _jsx("span", { "data-bl-visually-hidden": "", children: children });
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/ui-kit/primitives/layout.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Layout primitives: `Container`, `Section`, `SectionHeading`, `Eyebrow`, `RatingStars` and `VisuallyHidden`.\n * Server components. Heading levels come from the page outline (one H1 in the hero or page header, an H2 per\n * section), never from a look.\n */\nimport type { ReactNode } from 'react'\nimport type { PhoneCallPlacement } from '../../analytics.js'\nimport { Icon } from '../icons.js'\nimport { slot, type SkinEntry } from '../skin.js'\n\nexport const CONTAINER_WIDTHS = ['default', 'narrow', 'wide'] as const\nexport type ContainerWidth = (typeof CONTAINER_WIDTHS)[number]\nexport type ContainerSkin = SkinEntry<'root', never, { width?: ContainerWidth }>\n\n/** One container width shared by the header, the page and the footer (`--bl-container`, `--bl-gutter`). */\nexport function Container({ children, layout, classes }: ContainerSkin & { readonly children: ReactNode }) {\n return (\n <div data-bl=\"container\" data-width={layout?.width ?? 'default'} className={classes?.root}>\n {children}\n </div>\n )\n}\n\n/** The four section tones `SectionSequence` assigns: two alternating grounds, one optional band, one photo ground. */\nexport const SECTION_TONES = ['canvas', 'surface', 'band', 'photo'] as const\nexport type SectionTone = (typeof SECTION_TONES)[number]\n\nexport type SectionSlot = 'root' | 'inner'\n\nexport interface SectionProps {\n /** The matrix id, rendered as `data-bl-section` (for example `reviews`, `cta`, `faqs`). */\n readonly id: string\n /** The id of the section's H2. A section is a `<section>` labelled by its heading. */\n readonly labelledBy: string\n readonly tone?: SectionTone | undefined\n readonly variant?: string | undefined\n readonly classes?: Readonly<Partial<Record<SectionSlot, string>>> | undefined\n /** Signature art, rendered `aria-hidden`. */\n readonly decoration?: ReactNode\n /**\n * Extra layout hooks on the root, for example `{ 'data-columns': '3' }`. They cannot override `data-bl-section`,\n * `data-variant`, `data-tone` or `data-phone-location`.\n */\n readonly hooks?: Readonly<Record<`data-${string}`, string | undefined>>\n /** The placement a `tel:` link inside the section reports when it has none of its own. */\n readonly phonePlacement?: PhoneCallPlacement | undefined\n readonly children: ReactNode\n}\n\n/**\n * The root every section component renders: `<section data-bl-section aria-labelledby>`, its tone and variant, an\n * optional `aria-hidden` decoration and the inner container slot.\n */\nexport function Section({ id, labelledBy, tone, variant, classes, decoration, hooks, phonePlacement, children }: SectionProps) {\n return (\n <section\n {...hooks}\n data-bl-section={id}\n data-variant={variant}\n data-tone={tone}\n data-phone-location={phonePlacement}\n aria-labelledby={labelledBy}\n className={classes?.root}\n >\n {decoration ? (\n <div data-slot=\"decoration\" aria-hidden=\"true\">\n {decoration}\n </div>\n ) : null}\n <div {...slot(classes, 'inner')}>{children}</div>\n </section>\n )\n}\n\nexport type SectionHeadingSlot = 'head' | 'eyebrow' | 'heading' | 'lead'\nexport type SectionHeadingSkin = SkinEntry<SectionHeadingSlot, never, { align?: 'start' | 'center' }>\n\nexport interface SectionHeadingProps extends SectionHeadingSkin {\n /** The H2's id, which the section's `aria-labelledby` points at. */\n readonly id: string\n readonly title: ReactNode\n /** A short label above the H2 (at least 13 px; never the only label for the section). */\n readonly eyebrow?: ReactNode\n readonly lead?: ReactNode\n}\n\n/** A section's heading block: an optional eyebrow, the H2 and an optional lead paragraph. */\nexport function SectionHeading({ id, title, eyebrow, lead, layout, classes }: SectionHeadingProps) {\n return (\n <div {...slot(classes, 'head')} data-align={layout?.align ?? 'start'}>\n {eyebrow ? <p {...slot(classes, 'eyebrow')}>{eyebrow}</p> : null}\n <h2 id={id} {...slot(classes, 'heading')}>\n {title}\n </h2>\n {lead ? <p {...slot(classes, 'lead')}>{lead}</p> : null}\n </div>\n )\n}\n\n/**\n * Five stars with one text alternative (\"Rated 4.9 out of 5\"). The stars use `--bl-color-star`; whole stars only,\n * rounded to the nearest. The rating always comes from the feed, never typed by hand (T4).\n */\nexport function RatingStars({ rating, label, className }: { readonly rating: number; readonly label?: string; readonly className?: string }) {\n const filled = Math.max(0, Math.min(5, Math.round(rating)))\n return (\n <span data-bl=\"stars\" role=\"img\" aria-label={label ?? `Rated ${rating.toFixed(1)} out of 5`} className={className}>\n {Array.from({ length: 5 }, (_, index) => (\n <span key={index} data-slot=\"star\" data-state={index < filled ? 'on' : 'off'}>\n <Icon name=\"star\" />\n </span>\n ))}\n </span>\n )\n}\n\n/** Text for assistive technology only. */\nexport function VisuallyHidden({ children }: { readonly children: ReactNode }) {\n return <span data-bl-visually-hidden=\"\">{children}</span>\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { PhoneCallPlacement } from '../../analytics.js';
|
|
3
|
+
import { type UiIconName } from '../icons.js';
|
|
4
|
+
import { type SkinEntry } from '../skin.js';
|
|
5
|
+
import type { UiPhone } from '../types.js';
|
|
6
|
+
export type ButtonLinkSlot = 'root' | 'label' | 'icon';
|
|
7
|
+
export declare const BUTTON_VARIANTS: readonly ["primary", "secondary", "quiet"];
|
|
8
|
+
export type ButtonVariant = (typeof BUTTON_VARIANTS)[number];
|
|
9
|
+
export type ButtonLinkSkin = SkinEntry<ButtonLinkSlot, ButtonVariant, {
|
|
10
|
+
size?: 'regular' | 'large';
|
|
11
|
+
}>;
|
|
12
|
+
export interface ButtonLinkProps extends ButtonLinkSkin {
|
|
13
|
+
readonly href: string;
|
|
14
|
+
readonly children: ReactNode;
|
|
15
|
+
/** An icon after the label (`arrow`) or before it (`phone`). */
|
|
16
|
+
readonly icon?: UiIconName;
|
|
17
|
+
readonly iconPosition?: 'start' | 'end';
|
|
18
|
+
/** Force external handling; by default `http(s)://` links are external. */
|
|
19
|
+
readonly external?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** A link styled as a button. A primary action is at least 44 px tall (structure.css). */
|
|
22
|
+
export declare function ButtonLink({ href, children, icon, iconPosition, external, variant, layout, classes }: ButtonLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export type TextLinkSlot = 'root';
|
|
24
|
+
export type TextLinkSkin = SkinEntry<TextLinkSlot, 'inline' | 'standalone'>;
|
|
25
|
+
export interface TextLinkProps extends TextLinkSkin {
|
|
26
|
+
readonly href: string;
|
|
27
|
+
readonly children: ReactNode;
|
|
28
|
+
readonly external?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** An inline or standalone text link. */
|
|
31
|
+
export declare function TextLink({ href, children, external, variant, classes }: TextLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export type PhoneLinkSlot = 'root' | 'icon' | 'label' | 'number';
|
|
33
|
+
export type PhoneLinkSkin = SkinEntry<PhoneLinkSlot, 'text' | 'button'>;
|
|
34
|
+
export interface PhoneLinkProps extends PhoneLinkSkin {
|
|
35
|
+
readonly phone: UiPhone;
|
|
36
|
+
/** Where the link sits, for `phone_link_click`. Required: a section sets its own. */
|
|
37
|
+
readonly placement: PhoneCallPlacement;
|
|
38
|
+
/** Optional words before the number, for example "Call". The accessible name is always the visible text. */
|
|
39
|
+
readonly label?: string;
|
|
40
|
+
/** The phone icon on the left (header and hero). */
|
|
41
|
+
readonly showIcon?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A `tel:` link. Its accessible name is its visible text, which always includes the number (WCAG 2.5.3). Use it for
|
|
45
|
+
* every call link: a literal `tel:` anywhere else fails lint (K1) and C10 checks the href against the evidence file.
|
|
46
|
+
*/
|
|
47
|
+
export declare function PhoneLink({ phone, placement, label, showIcon, variant, classes }: PhoneLinkProps): import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
//# sourceMappingURL=links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/primitives/links.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAwB1C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;AACtD,eAAO,MAAM,eAAe,4CAA6C,CAAA;AACzE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAC5D,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,cAAc,EAAE,aAAa,EAAE;IAAE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,CAAC,CAAA;AAErG,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAA;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACvC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,0FAA0F;AAC1F,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAoB,EAAE,QAAQ,EAAE,OAAmB,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,eAAe,2CAazI;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAA;AACjC,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,EAAE,QAAQ,GAAG,YAAY,CAAC,CAAA;AAE3E,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAkB,EAAE,OAAO,EAAE,EAAE,aAAa,2CAMhG;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;AAChE,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAA;AAEvE,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,qFAAqF;IACrF,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,4GAA4G;IAC5G,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAgB,EAAE,OAAgB,EAAE,OAAO,EAAE,EAAE,cAAc,2CAgBjH"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Link primitives: `ButtonLink`, `TextLink` and `PhoneLink`. Server components; every link is a real `<a href>`.
|
|
4
|
+
* External links open in a new tab with `rel="noopener noreferrer"`. `PhoneLink` requires a typed placement, which the
|
|
5
|
+
* shared instrumentation reads to send one `phone_link_click` per tap; nothing here emits events itself.
|
|
6
|
+
*/
|
|
7
|
+
import Link from 'next/link';
|
|
8
|
+
import { Icon } from '../icons.js';
|
|
9
|
+
import { slot } from '../skin.js';
|
|
10
|
+
function isExternal(href) {
|
|
11
|
+
return /^https?:\/\//i.test(href);
|
|
12
|
+
}
|
|
13
|
+
function Anchor({ href, external, hooks, children }) {
|
|
14
|
+
if (external ?? isExternal(href)) {
|
|
15
|
+
return (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", ...hooks, children: children }));
|
|
16
|
+
}
|
|
17
|
+
return (_jsx(Link, { href: href, ...hooks, children: children }));
|
|
18
|
+
}
|
|
19
|
+
export const BUTTON_VARIANTS = ['primary', 'secondary', 'quiet'];
|
|
20
|
+
/** A link styled as a button. A primary action is at least 44 px tall (structure.css). */
|
|
21
|
+
export function ButtonLink({ href, children, icon, iconPosition = 'end', external, variant = 'primary', layout, classes }) {
|
|
22
|
+
const iconNode = icon ? _jsx("span", { ...slot(classes, 'icon'), children: _jsx(Icon, { name: icon }) }) : null;
|
|
23
|
+
return (_jsxs(Anchor, { href: href, external: external, hooks: { 'data-bl': 'button', 'data-variant': variant, 'data-size': layout?.size ?? 'regular', className: classes?.root }, children: [iconPosition === 'start' ? iconNode : null, _jsx("span", { ...slot(classes, 'label'), children: children }), iconPosition === 'end' ? iconNode : null] }));
|
|
24
|
+
}
|
|
25
|
+
/** An inline or standalone text link. */
|
|
26
|
+
export function TextLink({ href, children, external, variant = 'inline', classes }) {
|
|
27
|
+
return (_jsx(Anchor, { href: href, external: external, hooks: { 'data-bl': 'text-link', 'data-variant': variant, className: classes?.root }, children: children }));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A `tel:` link. Its accessible name is its visible text, which always includes the number (WCAG 2.5.3). Use it for
|
|
31
|
+
* every call link: a literal `tel:` anywhere else fails lint (K1) and C10 checks the href against the evidence file.
|
|
32
|
+
*/
|
|
33
|
+
export function PhoneLink({ phone, placement, label, showIcon = false, variant = 'text', classes }) {
|
|
34
|
+
return (_jsxs("a", { href: phone.telHref, "data-bl": "phone-link", "data-variant": variant, "data-phone-location": placement, className: classes?.root, children: [showIcon ? (_jsx("span", { ...slot(classes, 'icon'), children: _jsx(Icon, { name: "phone" }) })) : null, label ? (_jsxs(_Fragment, { children: [_jsx("span", { ...slot(classes, 'label'), children: label }), ' '] })) : null, _jsx("span", { ...slot(classes, 'number'), children: phone.display })] }));
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../../src/ui-kit/primitives/links.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Link primitives: `ButtonLink`, `TextLink` and `PhoneLink`. Server components; every link is a real `<a href>`.\n * External links open in a new tab with `rel=\"noopener noreferrer\"`. `PhoneLink` requires a typed placement, which the\n * shared instrumentation reads to send one `phone_link_click` per tap; nothing here emits events itself.\n */\nimport Link from 'next/link'\nimport type { ReactNode } from 'react'\nimport type { PhoneCallPlacement } from '../../analytics.js'\nimport { Icon, type UiIconName } from '../icons.js'\nimport { slot, type SkinEntry } from '../skin.js'\nimport type { UiPhone } from '../types.js'\n\nfunction isExternal(href: string) {\n return /^https?:\\/\\//i.test(href)\n}\n\n/** Data hooks and the skin class for an anchor's root. */\ntype AnchorHooks = { readonly className: string | undefined } & Readonly<Record<`data-${string}`, string | undefined>>\n\nfunction Anchor({ href, external, hooks, children }: { href: string; external?: boolean | undefined; hooks: AnchorHooks; children: ReactNode }) {\n if (external ?? isExternal(href)) {\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer\" {...hooks}>\n {children}\n </a>\n )\n }\n return (\n <Link href={href} {...hooks}>\n {children}\n </Link>\n )\n}\n\nexport type ButtonLinkSlot = 'root' | 'label' | 'icon'\nexport const BUTTON_VARIANTS = ['primary', 'secondary', 'quiet'] as const\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number]\nexport type ButtonLinkSkin = SkinEntry<ButtonLinkSlot, ButtonVariant, { size?: 'regular' | 'large' }>\n\nexport interface ButtonLinkProps extends ButtonLinkSkin {\n readonly href: string\n readonly children: ReactNode\n /** An icon after the label (`arrow`) or before it (`phone`). */\n readonly icon?: UiIconName\n readonly iconPosition?: 'start' | 'end'\n /** Force external handling; by default `http(s)://` links are external. */\n readonly external?: boolean\n}\n\n/** A link styled as a button. A primary action is at least 44 px tall (structure.css). */\nexport function ButtonLink({ href, children, icon, iconPosition = 'end', external, variant = 'primary', layout, classes }: ButtonLinkProps) {\n const iconNode = icon ? <span {...slot(classes, 'icon')}><Icon name={icon} /></span> : null\n return (\n <Anchor\n href={href}\n external={external}\n hooks={{ 'data-bl': 'button', 'data-variant': variant, 'data-size': layout?.size ?? 'regular', className: classes?.root }}\n >\n {iconPosition === 'start' ? iconNode : null}\n <span {...slot(classes, 'label')}>{children}</span>\n {iconPosition === 'end' ? iconNode : null}\n </Anchor>\n )\n}\n\nexport type TextLinkSlot = 'root'\nexport type TextLinkSkin = SkinEntry<TextLinkSlot, 'inline' | 'standalone'>\n\nexport interface TextLinkProps extends TextLinkSkin {\n readonly href: string\n readonly children: ReactNode\n readonly external?: boolean\n}\n\n/** An inline or standalone text link. */\nexport function TextLink({ href, children, external, variant = 'inline', classes }: TextLinkProps) {\n return (\n <Anchor href={href} external={external} hooks={{ 'data-bl': 'text-link', 'data-variant': variant, className: classes?.root }}>\n {children}\n </Anchor>\n )\n}\n\nexport type PhoneLinkSlot = 'root' | 'icon' | 'label' | 'number'\nexport type PhoneLinkSkin = SkinEntry<PhoneLinkSlot, 'text' | 'button'>\n\nexport interface PhoneLinkProps extends PhoneLinkSkin {\n readonly phone: UiPhone\n /** Where the link sits, for `phone_link_click`. Required: a section sets its own. */\n readonly placement: PhoneCallPlacement\n /** Optional words before the number, for example \"Call\". The accessible name is always the visible text. */\n readonly label?: string\n /** The phone icon on the left (header and hero). */\n readonly showIcon?: boolean\n}\n\n/**\n * A `tel:` link. Its accessible name is its visible text, which always includes the number (WCAG 2.5.3). Use it for\n * every call link: a literal `tel:` anywhere else fails lint (K1) and C10 checks the href against the evidence file.\n */\nexport function PhoneLink({ phone, placement, label, showIcon = false, variant = 'text', classes }: PhoneLinkProps) {\n return (\n <a href={phone.telHref} data-bl=\"phone-link\" data-variant={variant} data-phone-location={placement} className={classes?.root}>\n {showIcon ? (\n <span {...slot(classes, 'icon')}>\n <Icon name=\"phone\" />\n </span>\n ) : null}\n {label ? (\n <>\n <span {...slot(classes, 'label')}>{label}</span>{' '}\n </>\n ) : null}\n <span {...slot(classes, 'number')}>{phone.display}</span>\n </a>\n )\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LeadCtaPlacement } from '../../analytics.js';
|
|
2
|
+
import { type SkinEntry } from '../skin.js';
|
|
3
|
+
import type { ButtonVariant } from './links.js';
|
|
4
|
+
export type QuoteButtonSlot = 'root' | 'label' | 'icon';
|
|
5
|
+
export type QuoteButtonSkin = SkinEntry<QuoteButtonSlot, ButtonVariant, {
|
|
6
|
+
size?: 'regular' | 'large';
|
|
7
|
+
icon?: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
export interface QuoteButtonProps extends QuoteButtonSkin {
|
|
10
|
+
/** The site's recorded quote label, or one from the closed list (C5), for example "Get a free quote". */
|
|
11
|
+
readonly label: string;
|
|
12
|
+
/** The quote form this falls back to without scripts, for example `/contact#quote`. */
|
|
13
|
+
readonly href: string;
|
|
14
|
+
/** Where the button sits, for `lead_cta_click`. */
|
|
15
|
+
readonly placement: LeadCtaPlacement;
|
|
16
|
+
/** A stable id for `lead_cta_click`, for example `quote-header` or `quote-cta`. */
|
|
17
|
+
readonly ctaId: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function QuoteButton({ label, href, placement, ctaId, variant, layout, classes }: QuoteButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=quote-button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-button.d.ts","sourceRoot":"","sources":["../../../src/ui-kit/primitives/quote-button.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;AACvD,MAAM,MAAM,eAAe,GAAG,SAAS,CAAC,eAAe,EAAE,aAAa,EAAE;IAAE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAEvH,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,yGAAyG;IACzG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAA;IACpC,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAmB,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAmBpH"}
|