@alfadocs/ui-kit-debug 0.44.0 → 0.45.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/_chunks/{alia-sidebar-BpX4z_af.js → alia-sidebar-Be8FhKYd.js} +332 -237
- package/dist/_chunks/alia-sidebar-Be8FhKYd.js.map +1 -0
- package/dist/_chunks/{autocomplete-DIgdhCGJ.js → autocomplete-CDqxB68B.js} +2 -2
- package/dist/_chunks/{autocomplete-DIgdhCGJ.js.map → autocomplete-CDqxB68B.js.map} +1 -1
- package/dist/_chunks/bmi-calculator-CQqXTVNL.js +258 -0
- package/dist/_chunks/bmi-calculator-CQqXTVNL.js.map +1 -0
- package/dist/_chunks/{booking-CtLwaxkK.js → booking-DlDVuWMd.js} +2 -2
- package/dist/_chunks/{booking-CtLwaxkK.js.map → booking-DlDVuWMd.js.map} +1 -1
- package/dist/_chunks/{cycle-calculator-ChHBcjet.js → cycle-calculator-KxA8dqDf.js} +31 -20
- package/dist/_chunks/cycle-calculator-KxA8dqDf.js.map +1 -0
- package/dist/_chunks/{due-date-calculator-CYXKLoof.js → due-date-calculator-mFxpHLml.js} +51 -39
- package/dist/_chunks/due-date-calculator-mFxpHLml.js.map +1 -0
- package/dist/_chunks/{editable-currency-cell-renderer-9jqwDv5x.js → editable-currency-cell-renderer-BEBUQl9P.js} +2 -2
- package/dist/_chunks/{editable-currency-cell-renderer-9jqwDv5x.js.map → editable-currency-cell-renderer-BEBUQl9P.js.map} +1 -1
- package/dist/_chunks/{freemium-paywall-BLXESpH4.js → freemium-paywall-BYist2sJ.js} +2 -2
- package/dist/_chunks/{freemium-paywall-BLXESpH4.js.map → freemium-paywall-BYist2sJ.js.map} +1 -1
- package/dist/_chunks/{gestational-age-calculator-sRmoqgVr.js → gestational-age-calculator-gWI_uRA1.js} +52 -39
- package/dist/_chunks/gestational-age-calculator-gWI_uRA1.js.map +1 -0
- package/dist/_chunks/insert-result-C5ABnzDl.js +711 -0
- package/dist/_chunks/insert-result-C5ABnzDl.js.map +1 -0
- package/dist/_chunks/{marketplace-app-shell-Dc5cTIt8.js → marketplace-app-shell-Gfsf78ge.js} +2 -2
- package/dist/_chunks/{marketplace-app-shell-Dc5cTIt8.js.map → marketplace-app-shell-Gfsf78ge.js.map} +1 -1
- package/dist/_chunks/{patient-search-DPe2ZYEL.js → patient-search-CocVcGJ3.js} +2 -2
- package/dist/_chunks/{patient-search-DPe2ZYEL.js.map → patient-search-CocVcGJ3.js.map} +1 -1
- package/dist/_chunks/{payment-form-BzVsG6Ks.js → payment-form-DqEiEJRO.js} +247 -195
- package/dist/_chunks/payment-form-DqEiEJRO.js.map +1 -0
- package/dist/_chunks/{pdf-viewer-B6MC6VTx.js → pdf-viewer-CWEXTlwq.js} +2 -2
- package/dist/_chunks/{pdf-viewer-B6MC6VTx.js.map → pdf-viewer-CWEXTlwq.js.map} +1 -1
- package/dist/_chunks/{practice-results-CrLpEiiW.js → practice-results-DDi-kvaD.js} +2 -2
- package/dist/_chunks/{practice-results-CrLpEiiW.js.map → practice-results-DDi-kvaD.js.map} +1 -1
- package/dist/_chunks/{pregnancy-weight-gain-C5YhfYnL.js → pregnancy-weight-gain-BtEHaSqy.js} +26 -13
- package/dist/_chunks/pregnancy-weight-gain-BtEHaSqy.js.map +1 -0
- package/dist/_chunks/{search-bar-CP6wUJFY.js → search-bar-CvN_S0jW.js} +2 -2
- package/dist/_chunks/{search-bar-CP6wUJFY.js.map → search-bar-CvN_S0jW.js.map} +1 -1
- package/dist/_chunks/{search-input-C1C3jQpD.js → search-input-D3aMvi4l.js} +2 -2
- package/dist/_chunks/{search-input-C1C3jQpD.js.map → search-input-D3aMvi4l.js.map} +1 -1
- package/dist/_chunks/{sign-document-B-3k_0LO.js → sign-document-BCyLpFHJ.js} +2 -2
- package/dist/_chunks/{sign-document-B-3k_0LO.js.map → sign-document-BCyLpFHJ.js.map} +1 -1
- package/dist/_chunks/{sign-in-with-alfadocs-button-DeHBFRNS.js → sign-in-with-alfadocs-button-CuYn_kKP.js} +2 -2
- package/dist/_chunks/{sign-in-with-alfadocs-button-DeHBFRNS.js.map → sign-in-with-alfadocs-button-CuYn_kKP.js.map} +1 -1
- package/dist/_chunks/{social-sign-in-button-X54ySJr1.js → social-sign-in-button-uJYLM366.js} +2 -2
- package/dist/_chunks/{social-sign-in-button-X54ySJr1.js.map → social-sign-in-button-uJYLM366.js.map} +1 -1
- package/dist/_chunks/{spinner-CCByyvcb.js → spinner-OjQNn8oN.js} +7 -3
- package/dist/_chunks/spinner-OjQNn8oN.js.map +1 -0
- package/dist/_chunks/{transcript-panel-CR7VY1uw.js → transcript-panel-B4HiC7ed.js} +2 -2
- package/dist/_chunks/{transcript-panel-CR7VY1uw.js.map → transcript-panel-B4HiC7ed.js.map} +1 -1
- package/dist/_chunks/{unit-converter-Ds9jalbN.js → unit-converter-u3CwNDpP.js} +63 -52
- package/dist/_chunks/unit-converter-u3CwNDpP.js.map +1 -0
- package/dist/_chunks/{wallet-pay-button-DK4ESYge.js → wallet-pay-button-DuDPBlCO.js} +2 -2
- package/dist/_chunks/{wallet-pay-button-DK4ESYge.js.map → wallet-pay-button-DuDPBlCO.js.map} +1 -1
- package/dist/agent-catalog.json +1 -1
- package/dist/components/_shared/banded-gauge.d.ts +193 -0
- package/dist/components/_shared/banded-gauge.d.ts.map +1 -0
- package/dist/components/_shared/insert-result.d.ts +81 -8
- package/dist/components/_shared/insert-result.d.ts.map +1 -1
- package/dist/components/autocomplete/index.js +1 -1
- package/dist/components/bmi-calculator/bmi-calculator.d.ts +2 -2
- package/dist/components/bmi-calculator/bmi-calculator.d.ts.map +1 -1
- package/dist/components/bmi-calculator/index.js +1 -1
- package/dist/components/booking/index.js +1 -1
- package/dist/components/cycle-calculator/cycle-calculator.d.ts +2 -2
- package/dist/components/cycle-calculator/cycle-calculator.d.ts.map +1 -1
- package/dist/components/cycle-calculator/index.js +1 -1
- package/dist/components/data-table/index.js +1 -1
- package/dist/components/due-date-calculator/due-date-calculator.d.ts +2 -2
- package/dist/components/due-date-calculator/due-date-calculator.d.ts.map +1 -1
- package/dist/components/due-date-calculator/index.js +1 -1
- package/dist/components/freemium-paywall/index.js +1 -1
- package/dist/components/gestational-age-calculator/gestational-age-calculator.d.ts +2 -2
- package/dist/components/gestational-age-calculator/gestational-age-calculator.d.ts.map +1 -1
- package/dist/components/gestational-age-calculator/index.js +1 -1
- package/dist/components/patient-search/index.js +1 -1
- package/dist/components/payment-form/index.js +1 -1
- package/dist/components/payment-form/payment-form.d.ts +24 -2
- package/dist/components/payment-form/payment-form.d.ts.map +1 -1
- package/dist/components/pdf-viewer/index.js +1 -1
- package/dist/components/practice-results/index.js +1 -1
- package/dist/components/pregnancy-weight-gain/index.js +1 -1
- package/dist/components/pregnancy-weight-gain/pregnancy-weight-gain.d.ts +2 -2
- package/dist/components/pregnancy-weight-gain/pregnancy-weight-gain.d.ts.map +1 -1
- package/dist/components/search-bar/index.js +1 -1
- package/dist/components/search-input/index.js +1 -1
- package/dist/components/sign-document/index.js +1 -1
- package/dist/components/sign-in-with-alfadocs-button/index.js +1 -1
- package/dist/components/social-sign-in-button/index.js +1 -1
- package/dist/components/spinner/index.js +1 -1
- package/dist/components/spinner/spinner.d.ts +2 -2
- package/dist/components/spinner/spinner.d.ts.map +1 -1
- package/dist/components/transcript-panel/index.js +1 -1
- package/dist/components/unit-converter/index.js +1 -1
- package/dist/components/unit-converter/unit-converter.d.ts +2 -2
- package/dist/components/unit-converter/unit-converter.d.ts.map +1 -1
- package/dist/components/wallet-pay-button/index.js +1 -1
- package/dist/i18n/locales/ar.d.ts +1 -1
- package/dist/i18n/locales/ar.js +1 -1
- package/dist/i18n/locales/ar.js.map +1 -1
- package/dist/i18n/locales/de.d.ts +1 -1
- package/dist/i18n/locales/de.js +1 -1
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/el.d.ts +1 -1
- package/dist/i18n/locales/el.js +1 -1
- package/dist/i18n/locales/el.js.map +1 -1
- package/dist/i18n/locales/en.d.ts +1 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +1 -1
- package/dist/i18n/locales/es.js +1 -1
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +1 -1
- package/dist/i18n/locales/fr.js +1 -1
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hi.d.ts +1 -1
- package/dist/i18n/locales/hi.js +1 -1
- package/dist/i18n/locales/hi.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +1 -1
- package/dist/i18n/locales/it.js +1 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/ja.d.ts +1 -1
- package/dist/i18n/locales/ja.js +1 -1
- package/dist/i18n/locales/ja.js.map +1 -1
- package/dist/i18n/locales/nl.d.ts +1 -1
- package/dist/i18n/locales/nl.js +1 -1
- package/dist/i18n/locales/nl.js.map +1 -1
- package/dist/i18n/locales/pl.d.ts +1 -1
- package/dist/i18n/locales/pl.js +1 -1
- package/dist/i18n/locales/pl.js.map +1 -1
- package/dist/i18n/locales/pt.d.ts +1 -1
- package/dist/i18n/locales/pt.js +1 -1
- package/dist/i18n/locales/pt.js.map +1 -1
- package/dist/i18n/locales/ro.d.ts +1 -1
- package/dist/i18n/locales/ro.js +1 -1
- package/dist/i18n/locales/ro.js.map +1 -1
- package/dist/i18n/locales/ru.d.ts +1 -1
- package/dist/i18n/locales/ru.js +1 -1
- package/dist/i18n/locales/ru.js.map +1 -1
- package/dist/i18n/locales/sq.d.ts +1 -1
- package/dist/i18n/locales/sq.js +1 -1
- package/dist/i18n/locales/sq.js.map +1 -1
- package/dist/i18n/locales/sv.d.ts +1 -1
- package/dist/i18n/locales/sv.js +1 -1
- package/dist/i18n/locales/sv.js.map +1 -1
- package/dist/i18n/locales/tr.d.ts +1 -1
- package/dist/i18n/locales/tr.js +1 -1
- package/dist/i18n/locales/tr.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +1 -1
- package/dist/i18n/locales/zh.js +1 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.js +25 -25
- package/dist/locales/ar.json +1 -1
- package/dist/locales/de.json +1 -1
- package/dist/locales/el.json +1 -1
- package/dist/locales/en.json +1 -1
- package/dist/locales/es.json +1 -1
- package/dist/locales/fr.json +1 -1
- package/dist/locales/hi.json +1 -1
- package/dist/locales/it.json +1 -1
- package/dist/locales/ja.json +1 -1
- package/dist/locales/nl.json +1 -1
- package/dist/locales/pl.json +1 -1
- package/dist/locales/pt.json +1 -1
- package/dist/locales/ro.json +1 -1
- package/dist/locales/ru.json +1 -1
- package/dist/locales/sq.json +1 -1
- package/dist/locales/sv.json +1 -1
- package/dist/locales/tr.json +1 -1
- package/dist/locales/zh.json +1 -1
- package/dist/patterns/alia-assistant/alia-chat-surface.d.ts.map +1 -1
- package/dist/patterns/alia-assistant/alia-types.d.ts +20 -0
- package/dist/patterns/alia-assistant/alia-types.d.ts.map +1 -1
- package/dist/patterns/alia-assistant/index.js +1 -1
- package/dist/patterns/marketplace-app-shell/index.js +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/alia-sidebar-BpX4z_af.js.map +0 -1
- package/dist/_chunks/bmi-calculator-DFPWL2OJ.js +0 -273
- package/dist/_chunks/bmi-calculator-DFPWL2OJ.js.map +0 -1
- package/dist/_chunks/cycle-calculator-ChHBcjet.js.map +0 -1
- package/dist/_chunks/due-date-calculator-CYXKLoof.js.map +0 -1
- package/dist/_chunks/gestational-age-calculator-sRmoqgVr.js.map +0 -1
- package/dist/_chunks/insert-result-CoC1oo6R.js +0 -334
- package/dist/_chunks/insert-result-CoC1oo6R.js.map +0 -1
- package/dist/_chunks/payment-form-BzVsG6Ks.js.map +0 -1
- package/dist/_chunks/pregnancy-weight-gain-C5YhfYnL.js.map +0 -1
- package/dist/_chunks/spinner-CCByyvcb.js.map +0 -1
- package/dist/_chunks/unit-converter-Ds9jalbN.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript-panel-CR7VY1uw.js","sources":["../../src/components/transcript-panel/transcript-panel.agent.ts","../../src/components/transcript-panel/transcript-panel.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — TranscriptPanel. */\n/* */\n/* `data-turn-id` carries the line's stable index (opaque id) — never */\n/* the dictated text. Transcript content frequently contains PHI; the */\n/* DOM hook MUST NOT leak it. See PRS §26 §6.1. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { TranscriptPanelHandle } from './transcript-panel';\n\nexport const transcriptPanelAgent: AgentAdapter<TranscriptPanelHandle> = {\n id: 'transcript-panel',\n capabilities: ['select_single'],\n state: {\n selectedTurn: {\n type: 'string | null',\n descriptionKey: 'ui.agent.transcriptPanel.state.selectedTurn',\n description:\n 'Opaque id (line index) of the currently-active turn, or null when none is active.',\n read: (handle) => handle.getSelectedTurn(),\n },\n },\n actions: {\n focus_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.focusTurn',\n description: 'Scroll the given turn into view and focus its seek button.',\n invoke: (handle, args: { id: string }) => {\n handle.focusTurn(args.id);\n },\n },\n copy_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.copyTurn',\n description: \"Copy the given turn's text to the clipboard.\",\n invoke: (handle, args: { id: string }) => handle.copyTurn(args.id),\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'transcript-panel',\n description: 'Marks the TranscriptPanel wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-turn-id',\n description:\n \"Stable opaque turn id (line index). Never carries the turn's dictated text.\",\n },\n },\n};\n","import {\n forwardRef,\n Fragment,\n useCallback,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { transcriptPanelAgent } from './transcript-panel.agent';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { SearchInput } from '../search-input';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport interface TranscriptLine {\n speaker: string;\n text: string;\n /** Seconds, floating-point — matches HTMLMediaElement.currentTime. */\n start: number;\n end: number;\n /** 0..1 — rendered with a dotted underline when below 0.6. */\n confidence?: number;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface TranscriptPanelHandle {\n /** Opaque id (line index) of the active turn, or null. */\n getSelectedTurn: () => string | null;\n /** Scroll the turn with the given id into view and focus its seek button. */\n focusTurn: (id: string) => void;\n /** Copy the turn's text to the clipboard. */\n copyTurn: (id: string) => Promise<void> | void;\n}\n\nexport interface TranscriptPanelProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof rootVariants> {\n lines: TranscriptLine[];\n /** Current playback time in seconds — drives the active line highlight. */\n currentTime?: number;\n onSeek?: (seconds: number) => void;\n /** Show the search bar. */\n searchable?: boolean;\n}\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:w-full ds:min-h-0',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-card)] ds:[.theme-accessible_&]:border-2',\n 'ds:bg-background',\n 'ds:focus-within:border-[color:var(--primary)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:text-[length:var(--font-size-sm)]',\n md: 'ds:text-[length:var(--font-size-base)]',\n lg: 'ds:text-[length:var(--font-size-lg)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst VIRTUALIZATION_THRESHOLD = 100;\nconst SPEAKER_SLOT_COUNT = 8;\n\nfunction formatTime(seconds: number, locale: string): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n const fmt = (n: number) =>\n new Intl.NumberFormat(locale, { minimumIntegerDigits: 2 }).format(n);\n return `${fmt(m)}:${fmt(s)}`;\n}\n\n/** ISO-8601 duration (PT#M#S) for <time datetime=\"...\">. */\nfunction secondsToIsoDuration(seconds: number): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `PT${m}M${s}S`;\n}\n\n/** Deterministic speaker slot (0..7) based on name hash. */\nfunction speakerSlot(name: string): number {\n let hash = 0;\n for (let i = 0; i < name.length; i += 1) {\n hash = (hash * 31 + name.charCodeAt(i)) | 0;\n }\n return Math.abs(hash) % SPEAKER_SLOT_COUNT;\n}\n\n/** Per-slot background classes. Indices align with the speaker palette tokens\n * in tokens/index.css. Static strings so Tailwind can pick them up at build. */\nconst SPEAKER_BG_CLASSES = [\n 'ds:bg-[color:var(--transcript-speaker-1)]',\n 'ds:bg-[color:var(--transcript-speaker-2)]',\n 'ds:bg-[color:var(--transcript-speaker-3)]',\n 'ds:bg-[color:var(--transcript-speaker-4)]',\n 'ds:bg-[color:var(--transcript-speaker-5)]',\n 'ds:bg-[color:var(--transcript-speaker-6)]',\n 'ds:bg-[color:var(--transcript-speaker-7)]',\n 'ds:bg-[color:var(--transcript-speaker-8)]',\n] as const;\n\n/** Escape a literal string for safe use inside a RegExp. */\nfunction escapeRegExp(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\ninterface HighlightedTextProps {\n text: string;\n query: string;\n}\n\n/** Renders text with <mark> wrapping around case-insensitive matches.\n * No dangerouslySetInnerHTML — splits text nodes only. */\nfunction HighlightedText({ text, query }: HighlightedTextProps): ReactNode {\n if (!query) return text;\n const escaped = escapeRegExp(query);\n const parts = text.split(new RegExp(`(${escaped})`, 'gi'));\n return parts.map((part, i) =>\n part.toLocaleLowerCase() === query.toLocaleLowerCase() ? (\n <mark\n key={i}\n className=\"ds:bg-[color:var(--warning)]/30 ds:text-[color:var(--foreground)] ds:rounded-[var(--radius-sm)]\"\n >\n {part}\n </mark>\n ) : (\n <Fragment key={i}>{part}</Fragment>\n ),\n );\n}\n\nexport const TranscriptPanel = forwardRef<HTMLDivElement, TranscriptPanelProps>(\n (\n {\n lines,\n currentTime = 0,\n onSeek,\n searchable = true,\n size = 'md',\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const [query, setQuery] = useState('');\n const scrollRef = useRef<HTMLDivElement | null>(null);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const searchId = useId();\n\n /* Active-line index */\n const activeIndex = useMemo(() => {\n if (!Number.isFinite(currentTime)) return -1;\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (line.start <= currentTime && currentTime < line.end) return i;\n }\n return -1;\n }, [lines, currentTime]);\n\n /* Match count for search */\n const matchCount = useMemo(() => {\n if (!query) return 0;\n let count = 0;\n const needle = query.toLocaleLowerCase();\n for (const line of lines) {\n const hay = line.text.toLocaleLowerCase();\n let idx = hay.indexOf(needle);\n while (idx !== -1) {\n count += 1;\n idx = hay.indexOf(needle, idx + needle.length);\n }\n }\n return count;\n }, [lines, query]);\n\n /* Ctrl/Cmd+F shortcut — focuses the search field when focus is within. */\n const handleRootKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (\n (e.metaKey || e.ctrlKey) &&\n e.key.toLowerCase() === 'f' &&\n searchable\n ) {\n e.preventDefault();\n searchInputRef.current?.focus();\n }\n },\n [searchable],\n );\n\n /* Virtualisation above 100 lines */\n const virtualized = lines.length > VIRTUALIZATION_THRESHOLD;\n const virtualizer = useVirtualizer({\n count: virtualized ? lines.length : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 48,\n overscan: 8,\n getItemKey: (i) => i,\n });\n\n /* Auto-scroll active line into view when it changes. */\n useIsomorphicLayoutEffect(() => {\n if (activeIndex < 0) return;\n const scroller = scrollRef.current;\n if (!scroller) return;\n const target = scroller.querySelector(\n `[data-line-index=\"${activeIndex}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n target.scrollIntoView({\n block: 'center',\n behavior: prefersReducedMotion ? 'auto' : 'smooth',\n });\n }, [activeIndex]);\n\n const renderLine = useCallback(\n (\n line: TranscriptLine,\n index: number,\n extras?: {\n liRef?: (node: HTMLLIElement | null) => void;\n dataIndex?: number;\n // Style is an inline-style escape hatch — virtualizer offsets\n // cannot be expressed as static Tailwind. See 23-constraints §4.\n style?: CSSProperties;\n absolutePositioning?: boolean;\n },\n ) => {\n const isActive = index === activeIndex;\n const slot = speakerSlot(line.speaker);\n const lowConfidence =\n typeof line.confidence === 'number' && line.confidence < 0.6;\n return (\n <li\n key={index}\n ref={extras?.liRef}\n data-line-index={index}\n data-turn-id={String(index)}\n data-index={extras?.dataIndex}\n aria-current={isActive ? 'true' : undefined}\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer translateY offset, runtime-computed\n style={extras?.style}\n className={[\n 'ds:flex ds:items-start ds:gap-[var(--spacing-sm)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n isActive\n ? 'ds:bg-[color:var(--accent)]/12 ds:border-s-[3px] ds:border-[color:var(--primary)]'\n : 'ds:border-s-[3px] ds:border-transparent',\n extras?.absolutePositioning\n ? 'ds:absolute ds:start-0 ds:end-0 ds:top-0 ds:w-full'\n : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <button\n type=\"button\"\n onClick={() => onSeek?.(line.start)}\n aria-label={t('chat.transcript.seekTo', {\n time: formatTime(line.start, i18n.language),\n })}\n className={[\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-[family-name:var(--font-mono)]',\n 'type-meta',\n 'ds:tabular-nums',\n 'ds:text-[color:var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n // WCAG 2.5.5 target size: 44×44 default, 48×48 in accessible\n // themes — matches the DS `--min-target-size` token.\n 'ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]',\n 'ds:hover:bg-muted/20',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' ')}\n >\n <time dir=\"ltr\" dateTime={secondsToIsoDuration(line.start)}>\n {formatTime(line.start, i18n.language)}\n </time>\n </button>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span\n aria-hidden=\"true\"\n className={`ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${SPEAKER_BG_CLASSES[slot]}`}\n />\n <strong className=\"type-title-item\">\n {t('chat.transcript.speaker', { name: line.speaker })}\n </strong>\n {lowConfidence ? (\n <span className=\"type-meta ds:italic ds:text-[color:var(--muted-foreground)]\">\n ({t('chat.transcript.lowConfidence')})\n </span>\n ) : null}\n </div>\n <span\n dir=\"auto\"\n className={[\n 'ds:block',\n lowConfidence\n ? 'ds:underline ds:decoration-dotted ds:decoration-[color:var(--muted-foreground)]'\n : '',\n ].join(' ')}\n >\n <HighlightedText text={line.text} query={query} />\n </span>\n </div>\n </li>\n );\n },\n [activeIndex, i18n.language, onSeek, query, t],\n );\n\n const rootRef = useRef<HTMLDivElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement, []);\n\n const agentHandle = useMemo<TranscriptPanelHandle>(\n () => ({\n getSelectedTurn: () => (activeIndex >= 0 ? String(activeIndex) : null),\n focusTurn: (id: string) => {\n const target = scrollRef.current?.querySelector(\n `[data-turn-id=\"${id}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n target.scrollIntoView({ block: 'center', behavior: 'auto' });\n const btn = target.querySelector(\n 'button',\n ) as HTMLButtonElement | null;\n btn?.focus();\n },\n copyTurn: (id: string) => {\n const index = Number.parseInt(id, 10);\n if (!Number.isFinite(index)) return;\n const line = lines[index];\n if (!line) return;\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n return navigator.clipboard.writeText(line.text);\n }\n },\n }),\n [activeIndex, lines],\n );\n useAgentRegistration(\n transcriptPanelAgent,\n agentHandle,\n rest.id as string | undefined,\n );\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- root keyboard shortcuts (j/k navigation, /) bubble up from focused descendants\n <div\n ref={rootRef}\n onKeyDown={handleRootKeyDown}\n data-component=\"transcript-panel\"\n data-component-id={rest.id}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {searchable ? (\n <div className=\"ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-xs)] ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:border-b ds:border-border\">\n <SearchInput\n ref={searchInputRef}\n id={searchId}\n placeholder={t('chat.transcript.search')}\n aria-label={t('chat.transcript.search')}\n aria-keyshortcuts=\"Meta+F Control+F\"\n debounceMs={150}\n onChange={(v) => setQuery(v)}\n size=\"sm\"\n />\n {query ? (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)] ds:tabular-nums\"\n >\n {matchCount === 0\n ? t('chat.transcript.noMatches')\n : t('chat.transcript.matchCount', { count: matchCount })}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <div\n ref={scrollRef}\n className=\"ds:flex-1 ds:min-h-0 ds:overflow-y-auto ds:overflow-x-hidden\"\n >\n {virtualized ? (\n <ol\n className=\"ds:relative ds:list-none ds:ps-0 ds:m-0\"\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer total size, runtime-computed\n style={{ blockSize: `${virtualizer.getTotalSize()}px` }}\n >\n {virtualizer.getVirtualItems().map((vi) => {\n const line = lines[vi.index];\n if (!line) return null;\n // Put the measureElement ref on the <li> directly — a <div>\n // child of <ol> is invalid and breaks list semantics for\n // assistive tech.\n return renderLine(line, vi.index, {\n liRef: virtualizer.measureElement,\n dataIndex: vi.index,\n style: { transform: `translateY(${vi.start}px)` },\n absolutePositioning: true,\n });\n })}\n </ol>\n ) : (\n <ol className=\"ds:list-none ds:ps-0 ds:m-0\">\n {lines.map((line, index) => renderLine(line, index))}\n </ol>\n )}\n </div>\n </div>\n );\n },\n);\n\nTranscriptPanel.displayName = 'TranscriptPanel';\n"],"names":["transcriptPanelAgent","handle","args","rootVariants","cva","VIRTUALIZATION_THRESHOLD","SPEAKER_SLOT_COUNT","formatTime","seconds","locale","total","m","s","fmt","n","secondsToIsoDuration","speakerSlot","name","hash","i","SPEAKER_BG_CLASSES","escapeRegExp","input","HighlightedText","text","query","escaped","part","jsx","Fragment","TranscriptPanel","forwardRef","lines","currentTime","onSeek","searchable","size","className","rest","ref","t","i18n","useTranslation","setQuery","useState","scrollRef","useRef","searchInputRef","searchId","useId","activeIndex","useMemo","line","matchCount","count","needle","hay","idx","handleRootKeyDown","useCallback","_a","virtualized","virtualizer","useVirtualizer","useIsomorphicLayoutEffect","scroller","target","prefersReducedMotion","renderLine","index","extras","isActive","slot","lowConfidence","jsxs","rootRef","useImperativeHandle","agentHandle","id","btn","useAgentRegistration","SearchInput","v","vi"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transcript-panel-B4HiC7ed.js","sources":["../../src/components/transcript-panel/transcript-panel.agent.ts","../../src/components/transcript-panel/transcript-panel.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — TranscriptPanel. */\n/* */\n/* `data-turn-id` carries the line's stable index (opaque id) — never */\n/* the dictated text. Transcript content frequently contains PHI; the */\n/* DOM hook MUST NOT leak it. See PRS §26 §6.1. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { TranscriptPanelHandle } from './transcript-panel';\n\nexport const transcriptPanelAgent: AgentAdapter<TranscriptPanelHandle> = {\n id: 'transcript-panel',\n capabilities: ['select_single'],\n state: {\n selectedTurn: {\n type: 'string | null',\n descriptionKey: 'ui.agent.transcriptPanel.state.selectedTurn',\n description:\n 'Opaque id (line index) of the currently-active turn, or null when none is active.',\n read: (handle) => handle.getSelectedTurn(),\n },\n },\n actions: {\n focus_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.focusTurn',\n description: 'Scroll the given turn into view and focus its seek button.',\n invoke: (handle, args: { id: string }) => {\n handle.focusTurn(args.id);\n },\n },\n copy_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.copyTurn',\n description: \"Copy the given turn's text to the clipboard.\",\n invoke: (handle, args: { id: string }) => handle.copyTurn(args.id),\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'transcript-panel',\n description: 'Marks the TranscriptPanel wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-turn-id',\n description:\n \"Stable opaque turn id (line index). Never carries the turn's dictated text.\",\n },\n },\n};\n","import {\n forwardRef,\n Fragment,\n useCallback,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { transcriptPanelAgent } from './transcript-panel.agent';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { SearchInput } from '../search-input';\nimport { useIsomorphicLayoutEffect } from '../../hooks/use-isomorphic-layout-effect';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport interface TranscriptLine {\n speaker: string;\n text: string;\n /** Seconds, floating-point — matches HTMLMediaElement.currentTime. */\n start: number;\n end: number;\n /** 0..1 — rendered with a dotted underline when below 0.6. */\n confidence?: number;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface TranscriptPanelHandle {\n /** Opaque id (line index) of the active turn, or null. */\n getSelectedTurn: () => string | null;\n /** Scroll the turn with the given id into view and focus its seek button. */\n focusTurn: (id: string) => void;\n /** Copy the turn's text to the clipboard. */\n copyTurn: (id: string) => Promise<void> | void;\n}\n\nexport interface TranscriptPanelProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof rootVariants> {\n lines: TranscriptLine[];\n /** Current playback time in seconds — drives the active line highlight. */\n currentTime?: number;\n onSeek?: (seconds: number) => void;\n /** Show the search bar. */\n searchable?: boolean;\n}\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:w-full ds:min-h-0',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-card)] ds:[.theme-accessible_&]:border-2',\n 'ds:bg-background',\n 'ds:focus-within:border-[color:var(--primary)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:text-[length:var(--font-size-sm)]',\n md: 'ds:text-[length:var(--font-size-base)]',\n lg: 'ds:text-[length:var(--font-size-lg)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst VIRTUALIZATION_THRESHOLD = 100;\nconst SPEAKER_SLOT_COUNT = 8;\n\nfunction formatTime(seconds: number, locale: string): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n const fmt = (n: number) =>\n new Intl.NumberFormat(locale, { minimumIntegerDigits: 2 }).format(n);\n return `${fmt(m)}:${fmt(s)}`;\n}\n\n/** ISO-8601 duration (PT#M#S) for <time datetime=\"...\">. */\nfunction secondsToIsoDuration(seconds: number): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `PT${m}M${s}S`;\n}\n\n/** Deterministic speaker slot (0..7) based on name hash. */\nfunction speakerSlot(name: string): number {\n let hash = 0;\n for (let i = 0; i < name.length; i += 1) {\n hash = (hash * 31 + name.charCodeAt(i)) | 0;\n }\n return Math.abs(hash) % SPEAKER_SLOT_COUNT;\n}\n\n/** Per-slot background classes. Indices align with the speaker palette tokens\n * in tokens/index.css. Static strings so Tailwind can pick them up at build. */\nconst SPEAKER_BG_CLASSES = [\n 'ds:bg-[color:var(--transcript-speaker-1)]',\n 'ds:bg-[color:var(--transcript-speaker-2)]',\n 'ds:bg-[color:var(--transcript-speaker-3)]',\n 'ds:bg-[color:var(--transcript-speaker-4)]',\n 'ds:bg-[color:var(--transcript-speaker-5)]',\n 'ds:bg-[color:var(--transcript-speaker-6)]',\n 'ds:bg-[color:var(--transcript-speaker-7)]',\n 'ds:bg-[color:var(--transcript-speaker-8)]',\n] as const;\n\n/** Escape a literal string for safe use inside a RegExp. */\nfunction escapeRegExp(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\ninterface HighlightedTextProps {\n text: string;\n query: string;\n}\n\n/** Renders text with <mark> wrapping around case-insensitive matches.\n * No dangerouslySetInnerHTML — splits text nodes only. */\nfunction HighlightedText({ text, query }: HighlightedTextProps): ReactNode {\n if (!query) return text;\n const escaped = escapeRegExp(query);\n const parts = text.split(new RegExp(`(${escaped})`, 'gi'));\n return parts.map((part, i) =>\n part.toLocaleLowerCase() === query.toLocaleLowerCase() ? (\n <mark\n key={i}\n className=\"ds:bg-[color:var(--warning)]/30 ds:text-[color:var(--foreground)] ds:rounded-[var(--radius-sm)]\"\n >\n {part}\n </mark>\n ) : (\n <Fragment key={i}>{part}</Fragment>\n ),\n );\n}\n\nexport const TranscriptPanel = forwardRef<HTMLDivElement, TranscriptPanelProps>(\n (\n {\n lines,\n currentTime = 0,\n onSeek,\n searchable = true,\n size = 'md',\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const [query, setQuery] = useState('');\n const scrollRef = useRef<HTMLDivElement | null>(null);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const searchId = useId();\n\n /* Active-line index */\n const activeIndex = useMemo(() => {\n if (!Number.isFinite(currentTime)) return -1;\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (line.start <= currentTime && currentTime < line.end) return i;\n }\n return -1;\n }, [lines, currentTime]);\n\n /* Match count for search */\n const matchCount = useMemo(() => {\n if (!query) return 0;\n let count = 0;\n const needle = query.toLocaleLowerCase();\n for (const line of lines) {\n const hay = line.text.toLocaleLowerCase();\n let idx = hay.indexOf(needle);\n while (idx !== -1) {\n count += 1;\n idx = hay.indexOf(needle, idx + needle.length);\n }\n }\n return count;\n }, [lines, query]);\n\n /* Ctrl/Cmd+F shortcut — focuses the search field when focus is within. */\n const handleRootKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (\n (e.metaKey || e.ctrlKey) &&\n e.key.toLowerCase() === 'f' &&\n searchable\n ) {\n e.preventDefault();\n searchInputRef.current?.focus();\n }\n },\n [searchable],\n );\n\n /* Virtualisation above 100 lines */\n const virtualized = lines.length > VIRTUALIZATION_THRESHOLD;\n const virtualizer = useVirtualizer({\n count: virtualized ? lines.length : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 48,\n overscan: 8,\n getItemKey: (i) => i,\n });\n\n /* Auto-scroll active line into view when it changes. */\n useIsomorphicLayoutEffect(() => {\n if (activeIndex < 0) return;\n const scroller = scrollRef.current;\n if (!scroller) return;\n const target = scroller.querySelector(\n `[data-line-index=\"${activeIndex}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n target.scrollIntoView({\n block: 'center',\n behavior: prefersReducedMotion ? 'auto' : 'smooth',\n });\n }, [activeIndex]);\n\n const renderLine = useCallback(\n (\n line: TranscriptLine,\n index: number,\n extras?: {\n liRef?: (node: HTMLLIElement | null) => void;\n dataIndex?: number;\n // Style is an inline-style escape hatch — virtualizer offsets\n // cannot be expressed as static Tailwind. See 23-constraints §4.\n style?: CSSProperties;\n absolutePositioning?: boolean;\n },\n ) => {\n const isActive = index === activeIndex;\n const slot = speakerSlot(line.speaker);\n const lowConfidence =\n typeof line.confidence === 'number' && line.confidence < 0.6;\n return (\n <li\n key={index}\n ref={extras?.liRef}\n data-line-index={index}\n data-turn-id={String(index)}\n data-index={extras?.dataIndex}\n aria-current={isActive ? 'true' : undefined}\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer translateY offset, runtime-computed\n style={extras?.style}\n className={[\n 'ds:flex ds:items-start ds:gap-[var(--spacing-sm)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n isActive\n ? 'ds:bg-[color:var(--accent)]/12 ds:border-s-[3px] ds:border-[color:var(--primary)]'\n : 'ds:border-s-[3px] ds:border-transparent',\n extras?.absolutePositioning\n ? 'ds:absolute ds:start-0 ds:end-0 ds:top-0 ds:w-full'\n : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <button\n type=\"button\"\n onClick={() => onSeek?.(line.start)}\n aria-label={t('chat.transcript.seekTo', {\n time: formatTime(line.start, i18n.language),\n })}\n className={[\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-[family-name:var(--font-mono)]',\n 'type-meta',\n 'ds:tabular-nums',\n 'ds:text-[color:var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n // WCAG 2.5.5 target size: 44×44 default, 48×48 in accessible\n // themes — matches the DS `--min-target-size` token.\n 'ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]',\n 'ds:hover:bg-muted/20',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' ')}\n >\n <time dir=\"ltr\" dateTime={secondsToIsoDuration(line.start)}>\n {formatTime(line.start, i18n.language)}\n </time>\n </button>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span\n aria-hidden=\"true\"\n className={`ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${SPEAKER_BG_CLASSES[slot]}`}\n />\n <strong className=\"type-title-item\">\n {t('chat.transcript.speaker', { name: line.speaker })}\n </strong>\n {lowConfidence ? (\n <span className=\"type-meta ds:italic ds:text-[color:var(--muted-foreground)]\">\n ({t('chat.transcript.lowConfidence')})\n </span>\n ) : null}\n </div>\n <span\n dir=\"auto\"\n className={[\n 'ds:block',\n lowConfidence\n ? 'ds:underline ds:decoration-dotted ds:decoration-[color:var(--muted-foreground)]'\n : '',\n ].join(' ')}\n >\n <HighlightedText text={line.text} query={query} />\n </span>\n </div>\n </li>\n );\n },\n [activeIndex, i18n.language, onSeek, query, t],\n );\n\n const rootRef = useRef<HTMLDivElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement, []);\n\n const agentHandle = useMemo<TranscriptPanelHandle>(\n () => ({\n getSelectedTurn: () => (activeIndex >= 0 ? String(activeIndex) : null),\n focusTurn: (id: string) => {\n const target = scrollRef.current?.querySelector(\n `[data-turn-id=\"${id}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n target.scrollIntoView({ block: 'center', behavior: 'auto' });\n const btn = target.querySelector(\n 'button',\n ) as HTMLButtonElement | null;\n btn?.focus();\n },\n copyTurn: (id: string) => {\n const index = Number.parseInt(id, 10);\n if (!Number.isFinite(index)) return;\n const line = lines[index];\n if (!line) return;\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n return navigator.clipboard.writeText(line.text);\n }\n },\n }),\n [activeIndex, lines],\n );\n useAgentRegistration(\n transcriptPanelAgent,\n agentHandle,\n rest.id as string | undefined,\n );\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- root keyboard shortcuts (j/k navigation, /) bubble up from focused descendants\n <div\n ref={rootRef}\n onKeyDown={handleRootKeyDown}\n data-component=\"transcript-panel\"\n data-component-id={rest.id}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {searchable ? (\n <div className=\"ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-xs)] ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:border-b ds:border-border\">\n <SearchInput\n ref={searchInputRef}\n id={searchId}\n placeholder={t('chat.transcript.search')}\n aria-label={t('chat.transcript.search')}\n aria-keyshortcuts=\"Meta+F Control+F\"\n debounceMs={150}\n onChange={(v) => setQuery(v)}\n size=\"sm\"\n />\n {query ? (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)] ds:tabular-nums\"\n >\n {matchCount === 0\n ? t('chat.transcript.noMatches')\n : t('chat.transcript.matchCount', { count: matchCount })}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <div\n ref={scrollRef}\n className=\"ds:flex-1 ds:min-h-0 ds:overflow-y-auto ds:overflow-x-hidden\"\n >\n {virtualized ? (\n <ol\n className=\"ds:relative ds:list-none ds:ps-0 ds:m-0\"\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer total size, runtime-computed\n style={{ blockSize: `${virtualizer.getTotalSize()}px` }}\n >\n {virtualizer.getVirtualItems().map((vi) => {\n const line = lines[vi.index];\n if (!line) return null;\n // Put the measureElement ref on the <li> directly — a <div>\n // child of <ol> is invalid and breaks list semantics for\n // assistive tech.\n return renderLine(line, vi.index, {\n liRef: virtualizer.measureElement,\n dataIndex: vi.index,\n style: { transform: `translateY(${vi.start}px)` },\n absolutePositioning: true,\n });\n })}\n </ol>\n ) : (\n <ol className=\"ds:list-none ds:ps-0 ds:m-0\">\n {lines.map((line, index) => renderLine(line, index))}\n </ol>\n )}\n </div>\n </div>\n );\n },\n);\n\nTranscriptPanel.displayName = 'TranscriptPanel';\n"],"names":["transcriptPanelAgent","handle","args","rootVariants","cva","VIRTUALIZATION_THRESHOLD","SPEAKER_SLOT_COUNT","formatTime","seconds","locale","total","m","s","fmt","n","secondsToIsoDuration","speakerSlot","name","hash","i","SPEAKER_BG_CLASSES","escapeRegExp","input","HighlightedText","text","query","escaped","part","jsx","Fragment","TranscriptPanel","forwardRef","lines","currentTime","onSeek","searchable","size","className","rest","ref","t","i18n","useTranslation","setQuery","useState","scrollRef","useRef","searchInputRef","searchId","useId","activeIndex","useMemo","line","matchCount","count","needle","hay","idx","handleRootKeyDown","useCallback","_a","virtualized","virtualizer","useVirtualizer","useIsomorphicLayoutEffect","scroller","target","prefersReducedMotion","renderLine","index","extras","isActive","slot","lowConfidence","jsxs","rootRef","useImperativeHandle","agentHandle","id","btn","useAgentRegistration","SearchInput","v","vi"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsxs as d, jsx as e } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef as
|
|
2
|
+
import { forwardRef as z, useState as p, useMemo as I, useEffect as A } from "react";
|
|
3
3
|
import { c as Y } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as j } from "react-i18next";
|
|
5
|
-
import { F as
|
|
5
|
+
import { F as v } from "./form-field-BOm9hK35.js";
|
|
6
6
|
import { N as B } from "./number-input-Dj5L3pXK.js";
|
|
7
7
|
import { S as C } from "./select-hsCaJSX3.js";
|
|
8
|
-
import { I as L } from "./insert-result-
|
|
8
|
+
import { I as L } from "./insert-result-C5ABnzDl.js";
|
|
9
9
|
const _ = {
|
|
10
10
|
// weight → base kg
|
|
11
11
|
kg: { id: "kg", category: "weight", factor: 1, offset: 0 },
|
|
@@ -34,14 +34,14 @@ const _ = {
|
|
|
34
34
|
"temperature",
|
|
35
35
|
"glucose"
|
|
36
36
|
];
|
|
37
|
-
function K(
|
|
38
|
-
const o = _[
|
|
39
|
-
if (!o || !
|
|
40
|
-
if (o.category !==
|
|
37
|
+
function K(r, n, f) {
|
|
38
|
+
const o = _[n], a = _[f];
|
|
39
|
+
if (!o || !a) throw new Error(`Unknown unit: ${n} → ${f}`);
|
|
40
|
+
if (o.category !== a.category)
|
|
41
41
|
throw new Error(
|
|
42
|
-
`Cannot convert across categories: ${o.category} → ${
|
|
42
|
+
`Cannot convert across categories: ${o.category} → ${a.category}`
|
|
43
43
|
);
|
|
44
|
-
return (
|
|
44
|
+
return (r * o.factor + o.offset - a.offset) / a.factor;
|
|
45
45
|
}
|
|
46
46
|
const M = Y("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
|
|
47
47
|
variants: {
|
|
@@ -51,64 +51,68 @@ const M = Y("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
|
|
|
51
51
|
}), q = {
|
|
52
52
|
weight: "--info",
|
|
53
53
|
length: "--success",
|
|
54
|
-
temperature: "--warning",
|
|
54
|
+
temperature: "--warning-readable",
|
|
55
55
|
glucose: "--accent"
|
|
56
|
-
}
|
|
56
|
+
};
|
|
57
|
+
function J(r) {
|
|
58
|
+
return q[r];
|
|
59
|
+
}
|
|
60
|
+
const P = z(
|
|
57
61
|
({
|
|
58
|
-
defaultCategory:
|
|
59
|
-
onResultChange:
|
|
62
|
+
defaultCategory: r = "weight",
|
|
63
|
+
onResultChange: n,
|
|
60
64
|
onInsert: f,
|
|
61
65
|
insertVariant: o = "insert",
|
|
62
|
-
onCopy:
|
|
66
|
+
onCopy: a,
|
|
63
67
|
onError: b,
|
|
64
68
|
insertBrand: E,
|
|
65
69
|
id: F,
|
|
66
70
|
width: U,
|
|
67
|
-
className:
|
|
68
|
-
},
|
|
69
|
-
const { t, i18n: $ } = j(), [
|
|
70
|
-
h[
|
|
71
|
-
), [l, N] =
|
|
72
|
-
h[
|
|
71
|
+
className: k
|
|
72
|
+
}, O) => {
|
|
73
|
+
const { t, i18n: $ } = j(), [m, S] = p(r), [c, G] = p(null), [u, w] = p(
|
|
74
|
+
h[r][0]
|
|
75
|
+
), [l, N] = p(
|
|
76
|
+
h[r][1]
|
|
73
77
|
), V = (s) => {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
() =>
|
|
78
|
-
[
|
|
79
|
-
),
|
|
78
|
+
const T = s, y = h[T];
|
|
79
|
+
S(T), w(y[0]), N(y[1] ?? y[0]);
|
|
80
|
+
}, i = I(
|
|
81
|
+
() => c === null ? null : K(c, u, l),
|
|
82
|
+
[c, u, l]
|
|
83
|
+
), g = I(
|
|
80
84
|
() => new Intl.NumberFormat($.language, { maximumFractionDigits: 3 }),
|
|
81
85
|
[$.language]
|
|
82
86
|
);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}, [
|
|
86
|
-
const
|
|
87
|
+
A(() => {
|
|
88
|
+
n == null || n(i);
|
|
89
|
+
}, [i, n]);
|
|
90
|
+
const H = D.map((s) => ({
|
|
87
91
|
value: s,
|
|
88
92
|
label: t(`unitConverter.category.${s}`)
|
|
89
|
-
})), x = h[
|
|
93
|
+
})), x = h[m].map((s) => ({
|
|
90
94
|
value: s,
|
|
91
95
|
label: t(`unitConverter.units.${s}`)
|
|
92
96
|
}));
|
|
93
97
|
return /* @__PURE__ */ d(
|
|
94
98
|
"div",
|
|
95
99
|
{
|
|
96
|
-
ref:
|
|
100
|
+
ref: O,
|
|
97
101
|
"data-component": "unit-converter",
|
|
98
102
|
"data-component-id": F,
|
|
99
|
-
className: M({ width: U, className:
|
|
103
|
+
className: M({ width: U, className: k }),
|
|
100
104
|
children: [
|
|
101
|
-
/* @__PURE__ */ e(
|
|
105
|
+
/* @__PURE__ */ e(v, { label: t("unitConverter.categoryLabel"), children: /* @__PURE__ */ e(
|
|
102
106
|
C,
|
|
103
107
|
{
|
|
104
|
-
options:
|
|
105
|
-
value:
|
|
108
|
+
options: H,
|
|
109
|
+
value: m,
|
|
106
110
|
onValueChange: V
|
|
107
111
|
}
|
|
108
112
|
) }),
|
|
109
113
|
/* @__PURE__ */ d("div", { className: "ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-3", children: [
|
|
110
|
-
/* @__PURE__ */ e(
|
|
111
|
-
/* @__PURE__ */ e(
|
|
114
|
+
/* @__PURE__ */ e(v, { label: t("unitConverter.value"), children: /* @__PURE__ */ e(B, { mode: "decimal", value: c, onChange: G }) }),
|
|
115
|
+
/* @__PURE__ */ e(v, { label: t("unitConverter.from"), children: /* @__PURE__ */ e(
|
|
112
116
|
C,
|
|
113
117
|
{
|
|
114
118
|
options: x,
|
|
@@ -116,7 +120,7 @@ const M = Y("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
|
|
|
116
120
|
onValueChange: w
|
|
117
121
|
}
|
|
118
122
|
) }),
|
|
119
|
-
/* @__PURE__ */ e(
|
|
123
|
+
/* @__PURE__ */ e(v, { label: t("unitConverter.to"), children: /* @__PURE__ */ e(
|
|
120
124
|
C,
|
|
121
125
|
{
|
|
122
126
|
options: x,
|
|
@@ -125,15 +129,15 @@ const M = Y("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
|
|
|
125
129
|
}
|
|
126
130
|
) })
|
|
127
131
|
] }),
|
|
128
|
-
/* @__PURE__ */ e("p", { className: "ds:sr-only", role: "status", "aria-live": "polite", children:
|
|
132
|
+
/* @__PURE__ */ e("p", { className: "ds:sr-only", role: "status", "aria-live": "polite", children: i !== null ? `${g.format(c ?? 0)} ${t(
|
|
129
133
|
`unitConverter.units.${u}`
|
|
130
|
-
)} = ${
|
|
134
|
+
)} = ${g.format(i)} ${t(
|
|
131
135
|
`unitConverter.units.${l}`
|
|
132
136
|
)}` : "" }),
|
|
133
|
-
|
|
137
|
+
i !== null ? /* @__PURE__ */ d("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]", children: [
|
|
134
138
|
/* @__PURE__ */ e("span", { className: "type-label ds:text-muted-foreground", children: t("unitConverter.result") }),
|
|
135
139
|
/* @__PURE__ */ d("span", { className: "type-metric ds:text-foreground", children: [
|
|
136
|
-
|
|
140
|
+
g.format(i),
|
|
137
141
|
" ",
|
|
138
142
|
t(`unitConverter.units.${l}`)
|
|
139
143
|
] }),
|
|
@@ -142,25 +146,32 @@ const M = Y("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
|
|
|
142
146
|
{
|
|
143
147
|
variant: o,
|
|
144
148
|
onInsert: f,
|
|
145
|
-
onCopy:
|
|
149
|
+
onCopy: a,
|
|
146
150
|
onError: b,
|
|
147
151
|
card: {
|
|
148
152
|
title: t("insert.title.unitConverter"),
|
|
149
|
-
|
|
153
|
+
icon: "ruler",
|
|
154
|
+
highlight: t(`unitConverter.category.${m}`),
|
|
150
155
|
// Chip tints by measurement family so the inserted card
|
|
151
|
-
// signals the category at a glance
|
|
152
|
-
|
|
156
|
+
// signals the category at a glance — with the contrast-safe
|
|
157
|
+
// orange override for the `--warning` temperature family.
|
|
158
|
+
highlightToken: J(m),
|
|
153
159
|
brand: E,
|
|
154
160
|
fields: [
|
|
155
161
|
{
|
|
162
|
+
// Source measurement → ruler glyph (matches the header).
|
|
163
|
+
icon: "ruler",
|
|
156
164
|
label: t("unitConverter.from"),
|
|
157
|
-
value: `${
|
|
165
|
+
value: `${g.format(c ?? 0)} ${t(
|
|
158
166
|
`unitConverter.units.${u}`
|
|
159
167
|
)}`
|
|
160
168
|
},
|
|
161
169
|
{
|
|
170
|
+
// Converted result → trending-up (arrow) glyph signals the
|
|
171
|
+
// conversion direction to the output value.
|
|
172
|
+
icon: "trending-up",
|
|
162
173
|
label: t("unitConverter.to"),
|
|
163
|
-
value: `${
|
|
174
|
+
value: `${g.format(i)} ${t(
|
|
164
175
|
`unitConverter.units.${l}`
|
|
165
176
|
)}`
|
|
166
177
|
}
|
|
@@ -174,12 +185,12 @@ const M = Y("ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]", {
|
|
|
174
185
|
);
|
|
175
186
|
}
|
|
176
187
|
);
|
|
177
|
-
|
|
188
|
+
P.displayName = "UnitConverter";
|
|
178
189
|
export {
|
|
179
190
|
D as C,
|
|
180
191
|
_ as U,
|
|
181
192
|
h as a,
|
|
182
|
-
|
|
193
|
+
P as b,
|
|
183
194
|
K as c
|
|
184
195
|
};
|
|
185
|
-
//# sourceMappingURL=unit-converter-
|
|
196
|
+
//# sourceMappingURL=unit-converter-u3CwNDpP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unit-converter-u3CwNDpP.js","sources":["../../src/components/unit-converter/units.ts","../../src/components/unit-converter/unit-converter.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Generic unit conversion — pure, framework-free, unit-testable. */\n/* */\n/* Every unit is described by an affine map to its category's base */\n/* unit: base = value * factor + offset. Conversion is base-in then */\n/* base-out, so the same code handles linear scales (weight, length, */\n/* glucose) and offset scales (temperature) uniformly. */\n/* ------------------------------------------------------------------ */\n\nexport type UnitCategory = 'weight' | 'length' | 'temperature' | 'glucose';\n\nexport interface UnitDef {\n id: string;\n category: UnitCategory;\n /** Multiplier to the category base unit. */\n factor: number;\n /** Additive offset to the base unit (non-zero only for temperature). */\n offset: number;\n}\n\n/* Base units: kg, cm, °C, mmol/L. */\nexport const UNITS: Record<string, UnitDef> = {\n // weight → base kg\n kg: { id: 'kg', category: 'weight', factor: 1, offset: 0 },\n g: { id: 'g', category: 'weight', factor: 0.001, offset: 0 },\n lb: { id: 'lb', category: 'weight', factor: 0.45359237, offset: 0 },\n oz: { id: 'oz', category: 'weight', factor: 0.028349523125, offset: 0 },\n // length → base cm\n cm: { id: 'cm', category: 'length', factor: 1, offset: 0 },\n m: { id: 'm', category: 'length', factor: 100, offset: 0 },\n in: { id: 'in', category: 'length', factor: 2.54, offset: 0 },\n ft: { id: 'ft', category: 'length', factor: 30.48, offset: 0 },\n // temperature → base °C (affine: °C = °F·5/9 − 32·5/9)\n c: { id: 'c', category: 'temperature', factor: 1, offset: 0 },\n f: { id: 'f', category: 'temperature', factor: 5 / 9, offset: (-32 * 5) / 9 },\n // glucose → base mmol/L (1 mg/dL = 1/18.0156 mmol/L)\n mmol_l: { id: 'mmol_l', category: 'glucose', factor: 1, offset: 0 },\n mg_dl: { id: 'mg_dl', category: 'glucose', factor: 1 / 18.0156, offset: 0 },\n};\n\nexport const UNITS_BY_CATEGORY: Record<UnitCategory, string[]> = {\n weight: ['kg', 'g', 'lb', 'oz'],\n length: ['cm', 'm', 'in', 'ft'],\n temperature: ['c', 'f'],\n glucose: ['mmol_l', 'mg_dl'],\n};\n\nexport const CATEGORIES: UnitCategory[] = [\n 'weight',\n 'length',\n 'temperature',\n 'glucose',\n];\n\n/**\n * Convert `value` from one unit to another. Both ids must belong to the same\n * category; mismatched categories throw (a programming error, not user input).\n */\nexport function convertUnits(\n value: number,\n fromId: string,\n toId: string,\n): number {\n const from = UNITS[fromId];\n const to = UNITS[toId];\n if (!from || !to) throw new Error(`Unknown unit: ${fromId} → ${toId}`);\n if (from.category !== to.category) {\n throw new Error(\n `Cannot convert across categories: ${from.category} → ${to.category}`,\n );\n }\n const base = value * from.factor + from.offset;\n return (base - to.offset) / to.factor;\n}\n","/* ------------------------------------------------------------------ */\n/* UnitConverter — generic clinical/everyday unit conversion across */\n/* weight, length, temperature and glucose. */\n/* */\n/* Maths lives in `./units` (pure, separately tested). */\n/* ------------------------------------------------------------------ */\n\nimport { forwardRef, useEffect, useMemo, useState } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { FormField } from '../form-field';\nimport { NumberInput } from '../number-input';\nimport { Select } from '../select';\nimport {\n InsertButton,\n type InsertPayload,\n type InsertVariant,\n type InsertMode,\n} from '../_shared/insert-result';\nimport {\n type UnitCategory,\n CATEGORIES,\n UNITS_BY_CATEGORY,\n convertUnits,\n} from './units';\n\nconst rootVariants = cva('ds:flex ds:flex-col ds:gap-[var(--spacing-lg)]', {\n variants: {\n width: { full: 'ds:w-full', auto: 'ds:inline-flex' },\n },\n defaultVariants: { width: 'full' },\n});\n\n/**\n * DS token NAME backing the result-card category chip, one per measurement\n * family. The converter shows no on-screen badge, but the inserted card carries\n * a category chip so the reader can tell at a glance which family the figure\n * belongs to; each token is a closed-palette semantic alias resolved to a\n * concrete colour at raster time by `InsertButton`.\n */\nconst CATEGORY_HIGHLIGHT_TOKEN: Record<UnitCategory, string> = {\n weight: '--info',\n length: '--success',\n temperature: '--warning-readable',\n glucose: '--accent',\n};\n\n/**\n * Category chip token. The `temperature` family resolves to `--warning-readable`,\n * which the result card's colour probe resolves in the live themed DOM: orange-600\n * in light, non-accessible mode (where bare `--warning` yellow only reaches\n * ~3.2:1 on the white card) and `--warning` elsewhere, whose deepened ramp already\n * clears contrast. Resolving the token NAME at the card means the choice tracks\n * the live theme regardless of where the theme class lives. Other families are\n * unchanged.\n */\nfunction categoryHighlightToken(category: UnitCategory): string {\n return CATEGORY_HIGHLIGHT_TOKEN[category];\n}\n\nexport interface UnitConverterProps extends VariantProps<typeof rootVariants> {\n /** Initial category. Defaults to `'weight'`. */\n defaultCategory?: UnitCategory;\n /** Fires with the converted value (and `null` when input is empty). */\n onResultChange?: (value: number | null) => void;\n /** When provided, shows an \"Insert\" button that emits the result for an editor. */\n onInsert?: (payload: InsertPayload) => void;\n /**\n * Which verb the result button performs. Defaults to `'insert'`.\n * Use `'copy'` in an app-shell surface (no editor to insert into) — the\n * button writes the result to the clipboard as a multi-format `ClipboardItem`.\n */\n insertVariant?: InsertVariant;\n /** `copy` variant only — fired after a successful clipboard write. */\n onCopy?: (mode: InsertMode) => void;\n /** `copy` variant only — fired if the clipboard write can't proceed. */\n onError?: (error: unknown) => void;\n /**\n * Brand wordmark printed in the inserted/copied result-card footer.\n * Omitted → no brand line (and no footer hairline); a string → that custom\n * brand; `false` → no brand line. Brand is opt-in.\n */\n insertBrand?: string | false;\n /** Opaque instance id, emitted as `data-component-id`. */\n id?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport const UnitConverter = forwardRef<HTMLDivElement, UnitConverterProps>(\n (\n {\n defaultCategory = 'weight',\n onResultChange,\n onInsert,\n insertVariant = 'insert',\n onCopy,\n onError,\n insertBrand,\n id,\n width,\n className,\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n\n const [category, setCategory] = useState<UnitCategory>(defaultCategory);\n const [value, setValue] = useState<number | null>(null);\n const [fromId, setFromId] = useState<string>(\n UNITS_BY_CATEGORY[defaultCategory][0],\n );\n const [toId, setToId] = useState<string>(\n UNITS_BY_CATEGORY[defaultCategory][1],\n );\n\n const handleCategoryChange = (next: string): void => {\n const cat = next as UnitCategory;\n const units = UNITS_BY_CATEGORY[cat];\n setCategory(cat);\n setFromId(units[0]);\n setToId(units[1] ?? units[0]);\n };\n\n const converted = useMemo(\n () => (value === null ? null : convertUnits(value, fromId, toId)),\n [value, fromId, toId],\n );\n\n const numberFmt = useMemo(\n () => new Intl.NumberFormat(i18n.language, { maximumFractionDigits: 3 }),\n [i18n.language],\n );\n\n useEffect(() => {\n onResultChange?.(converted);\n }, [converted, onResultChange]);\n\n const categoryOptions = CATEGORIES.map((c) => ({\n value: c,\n label: t(`unitConverter.category.${c}`),\n }));\n const unitOptions = UNITS_BY_CATEGORY[category].map((u) => ({\n value: u,\n label: t(`unitConverter.units.${u}`),\n }));\n\n return (\n <div\n ref={ref}\n data-component=\"unit-converter\"\n data-component-id={id}\n className={rootVariants({ width, className })}\n >\n <FormField label={t('unitConverter.categoryLabel')}>\n <Select\n options={categoryOptions}\n value={category}\n onValueChange={handleCategoryChange}\n />\n </FormField>\n\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:sm:grid-cols-3\">\n <FormField label={t('unitConverter.value')}>\n <NumberInput mode=\"decimal\" value={value} onChange={setValue} />\n </FormField>\n <FormField label={t('unitConverter.from')}>\n <Select\n options={unitOptions}\n value={fromId}\n onValueChange={setFromId}\n />\n </FormField>\n <FormField label={t('unitConverter.to')}>\n <Select\n options={unitOptions}\n value={toId}\n onValueChange={setToId}\n />\n </FormField>\n </div>\n\n <p className=\"ds:sr-only\" role=\"status\" aria-live=\"polite\">\n {converted !== null\n ? `${numberFmt.format(value ?? 0)} ${t(\n `unitConverter.units.${fromId}`,\n )} = ${numberFmt.format(converted)} ${t(\n `unitConverter.units.${toId}`,\n )}`\n : ''}\n </p>\n\n {converted !== null ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <span className=\"type-label ds:text-muted-foreground\">\n {t('unitConverter.result')}\n </span>\n <span className=\"type-metric ds:text-foreground\">\n {numberFmt.format(converted)} {t(`unitConverter.units.${toId}`)}\n </span>\n {insertVariant === 'copy' || onInsert ? (\n <InsertButton\n variant={insertVariant}\n onInsert={onInsert}\n onCopy={onCopy}\n onError={onError}\n card={{\n title: t('insert.title.unitConverter'),\n icon: 'ruler',\n highlight: t(`unitConverter.category.${category}`),\n // Chip tints by measurement family so the inserted card\n // signals the category at a glance — with the contrast-safe\n // orange override for the `--warning` temperature family.\n highlightToken: categoryHighlightToken(category),\n brand: insertBrand,\n fields: [\n {\n // Source measurement → ruler glyph (matches the header).\n icon: 'ruler',\n label: t('unitConverter.from'),\n value: `${numberFmt.format(value ?? 0)} ${t(\n `unitConverter.units.${fromId}`,\n )}`,\n },\n {\n // Converted result → trending-up (arrow) glyph signals the\n // conversion direction to the output value.\n icon: 'trending-up',\n label: t('unitConverter.to'),\n value: `${numberFmt.format(converted)} ${t(\n `unitConverter.units.${toId}`,\n )}`,\n },\n ],\n }}\n />\n ) : null}\n </div>\n ) : (\n <p className=\"type-body ds:text-muted-foreground\">\n {t('unitConverter.empty')}\n </p>\n )}\n </div>\n );\n },\n);\n\nUnitConverter.displayName = 'UnitConverter';\n"],"names":["UNITS","UNITS_BY_CATEGORY","CATEGORIES","convertUnits","value","fromId","toId","from","to","rootVariants","cva","CATEGORY_HIGHLIGHT_TOKEN","categoryHighlightToken","category","UnitConverter","forwardRef","defaultCategory","onResultChange","onInsert","insertVariant","onCopy","onError","insertBrand","id","width","className","ref","i18n","useTranslation","setCategory","useState","setValue","setFromId","setToId","handleCategoryChange","next","cat","units","converted","useMemo","numberFmt","useEffect","categoryOptions","c","unitOptions","u","jsxs","jsx","FormField","Select","NumberInput","InsertButton"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { jsxs as s, jsx as e } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef as B } from "react";
|
|
3
3
|
import { c as S } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as H } from "react-i18next";
|
|
5
|
-
import { S as P } from "./spinner-
|
|
5
|
+
import { S as P } from "./spinner-OjQNn8oN.js";
|
|
6
6
|
function V({ className: a }) {
|
|
7
7
|
return /* @__PURE__ */ s(
|
|
8
8
|
"svg",
|
|
@@ -247,4 +247,4 @@ R.displayName = "WalletPayButton";
|
|
|
247
247
|
export {
|
|
248
248
|
R as W
|
|
249
249
|
};
|
|
250
|
-
//# sourceMappingURL=wallet-pay-button-
|
|
250
|
+
//# sourceMappingURL=wallet-pay-button-DuDPBlCO.js.map
|
package/dist/_chunks/{wallet-pay-button-DK4ESYge.js.map → wallet-pay-button-DuDPBlCO.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wallet-pay-button-DK4ESYge.js","sources":["../../src/brand/provider-marks/apple-pay.tsx","../../src/brand/provider-marks/google-pay.tsx","../../src/components/wallet-pay-button/wallet-pay-button.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Apple Pay brand mark. */\n/* */\n/* Lives under `src/brand/**` — one of the two zones (with */\n/* `src/tokens/**`) where literal hex / brand SVG paths are sanctioned */\n/* by constraint #2. Apple's Marketing Guidelines mandate the official */\n/* Apple-logo-plus-\"Pay\" lockup and forbid recolouring it to a kit */\n/* accent. The mark is MONOCHROME: it paints in `currentColor` so the */\n/* WalletPayButton's brand-locked foreground token (white on the black */\n/* surface, near-black on the white / white-outline surface) drives it */\n/* from a single CVA class — exactly how the WhatsApp glyph inherits */\n/* `--brand-whatsapp-foreground`. Do NOT tint this with a kit colour. */\n/* */\n/* The viewBox keeps the official aspect ratio so the lockup never */\n/* distorts; the consuming button scales it via `[&_svg]:h-*` height */\n/* classes and `width:auto` so the proportion is preserved at every */\n/* size step. */\n/* ------------------------------------------------------------------ */\n\nimport type { ReactNode } from 'react';\n\nexport interface BrandMarkProps {\n /** Extra classes from the consuming button's CVA size axis. */\n className?: string;\n}\n\n/**\n * Apple Pay lockup (Apple logo + \"Pay\"). Decorative — the parent button\n * carries `aria-hidden` semantics by wrapping it next to the visible /\n * sr-only accessible name, so this SVG is marked `aria-hidden=\"true\"`.\n */\nexport function ApplePayMark({ className }: BrandMarkProps): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n role=\"img\"\n viewBox=\"-1 18 158 69\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n >\n {/* Apple logo glyph */}\n <path d=\"M30.07 27.69c-1.93 2.28-5.01 4.08-8.09 3.82-.39-3.08.92-6.34 2.68-8.36 1.93-2.34 5.31-4.01 8.04-4.14.33 3.21-.92 6.34-2.63 8.68m2.6 4.13c-4.47-.26-8.29 2.54-10.41 2.54-2.15 0-5.4-2.41-8.94-2.34-4.6.07-8.88 2.67-11.23 6.82-4.83 8.29-1.26 20.57 3.41 27.32 2.28 3.34 5.01 7.01 8.62 6.88 3.41-.13 4.75-2.21 8.88-2.21 4.16 0 5.34 2.21 8.94 2.15 3.74-.07 6.08-3.34 8.36-6.68 2.6-3.8 3.67-7.5 3.74-7.69-.07-.07-7.21-2.8-7.27-11.02-.07-6.88 5.6-10.15 5.86-10.35-3.21-4.73-8.22-5.26-9.95-5.39\" />\n {/* \"Pay\" wordmark */}\n <path d=\"M71.62 22.42c9.71 0 16.47 6.69 16.47 16.43 0 9.78-6.9 16.5-16.71 16.5H60.62v17.08H52.86V22.42h18.76zM60.62 48.83h8.91c6.76 0 10.61-3.64 10.61-9.95 0-6.31-3.85-9.92-10.58-9.92h-8.94v19.87zM90.11 62.05c0-6.38 4.89-10.3 13.56-10.79l9.99-.59v-2.81c0-4.06-2.74-6.49-7.32-6.49-4.34 0-7.05 2.08-7.71 5.34h-7.08c.42-6.59 6.03-11.45 15.06-11.45 8.86 0 14.51 4.69 14.51 12.01v25.16h-7.18v-6.01h-.17c-2.11 4.06-6.73 6.62-11.52 6.62-7.15 0-12.15-4.44-12.15-11.01zm23.55-3.29v-2.88l-8.99.56c-4.48.31-7.01 2.29-7.01 5.41 0 3.19 2.64 5.27 6.66 5.27 5.24 0 9.34-3.61 9.34-8.36zM124.95 85.78v-6.07c.55.14 1.79.14 2.42.14 3.47 0 5.34-1.46 6.49-5.2 0-.07.66-2.22.66-2.25l-13.18-36.52h8.11l9.23 29.69h.14l9.23-29.69h7.9l-13.66 38.39c-3.12 8.85-6.73 11.69-14.29 11.69-.63 0-2.5-.07-3.05-.21z\" />\n </svg>\n );\n}\n","/* ------------------------------------------------------------------ */\n/* Google Pay brand mark. */\n/* */\n/* Lives under `src/brand/**` — the sanctioned zone (constraint #2) for */\n/* literal brand hex. Google's Pay Brand Guidelines mandate the */\n/* full-colour \"G Pay\" lockup: the four-colour Google \"G\" plus the */\n/* \"Pay\" wordmark. CRITICAL: unlike the Apple Pay mark (which is */\n/* monochrome `currentColor`), the four-colour Google \"G\" must STAY */\n/* FULL-COLOUR even on the white button — the four brand hues are fixed */\n/* and may not be recoloured to a kit token. Hence the literal fills */\n/* below; they are brand constants, not theme tokens. */\n/* */\n/* The \"Pay\" wordmark is the only part that flips with the surface: it */\n/* inherits `currentColor` (driven by the brand foreground token) so it */\n/* reads dark on the white surface and white on the black surface, */\n/* exactly as Google's dark/light button variants specify. The \"G\" is */\n/* never recoloured. */\n/* ------------------------------------------------------------------ */\n\nimport type { ReactNode } from 'react';\nimport type { BrandMarkProps } from './apple-pay';\n\n/**\n * Google Pay lockup (four-colour Google \"G\" + \"Pay\" wordmark). The \"G\"\n * keeps Google's mandated four brand hues at all times; the \"Pay\"\n * wordmark inherits `currentColor` so it reads against either the black\n * or the white surface (brand foreground token). Decorative —\n * `aria-hidden=\"true\"`; the accessible name comes from the parent\n * button.\n */\nexport function GooglePayMark({ className }: BrandMarkProps): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n role=\"img\"\n viewBox=\"1 7 101 36\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n >\n {/* Four-colour Google \"G\" — fixed brand hues, never recoloured. */}\n <g transform=\"translate(2 8)\">\n <path\n fill=\"#4285F4\"\n d=\"M31.5 16.5c0-1.16-.1-2.27-.3-3.34H16.07v6.33h8.65a7.4 7.4 0 0 1-3.21 4.85v4.03h5.19c3.04-2.8 4.8-6.92 4.8-11.87z\"\n />\n <path\n fill=\"#34A853\"\n d=\"M16.07 32c4.34 0 7.99-1.44 10.65-3.89l-5.19-4.03c-1.44.97-3.29 1.54-5.46 1.54-4.2 0-7.76-2.84-9.03-6.65H1.67v4.17C4.32 28.4 9.71 32 16.07 32z\"\n />\n <path\n fill=\"#FBBC04\"\n d=\"M7.04 18.97A9.6 9.6 0 0 1 6.53 16c0-1.03.18-2.03.51-2.97V8.86H1.67A15.96 15.96 0 0 0 0 16c0 2.58.62 5.02 1.67 7.14l5.37-4.17z\"\n />\n <path\n fill=\"#EA4335\"\n d=\"M16.07 6.39c2.37 0 4.5.81 6.17 2.41l4.62-4.62C24.05 1.6 20.41 0 16.07 0 9.71 0 4.32 3.6 1.67 8.86l5.37 4.17c1.27-3.81 4.83-6.64 9.03-6.64z\"\n />\n </g>\n {/* \"Pay\" wordmark — flips with the surface via currentColor. */}\n <g fill=\"currentColor\" transform=\"translate(44 8)\">\n <path d=\"M11.9 17.06v9.94H8.75V2.52h8.36c2.12 0 3.93.71 5.41 2.12 1.51 1.42 2.27 3.14 2.27 5.18 0 2.09-.76 3.82-2.27 5.21-1.46 1.39-3.27 2.07-5.41 2.07H11.9v-.06zm0-11.53v8.55h5.28c1.25 0 2.29-.42 3.12-1.26.84-.84 1.26-1.86 1.26-3.01 0-1.12-.42-2.13-1.26-2.97-.83-.87-1.87-1.31-3.12-1.31H11.9z\" />\n <path d=\"M32.43 9.72c2.33 0 4.16.62 5.51 1.87 1.35 1.25 2.02 2.96 2.02 5.12V27h-3.01v-2.33h-.14c-1.3 1.92-3.05 2.88-5.23 2.88-1.85 0-3.41-.55-4.65-1.65-1.25-1.1-1.87-2.46-1.87-4.11 0-1.74.66-3.11 1.97-4.13 1.31-1.02 3.07-1.54 5.26-1.54 1.87 0 3.41.34 4.62 1.03v-.72c0-1.1-.43-2.02-1.29-2.79-.86-.77-1.87-1.15-3.02-1.15-1.74 0-3.11.72-4.11 2.18l-2.78-1.75c1.5-2.16 3.72-3.24 6.64-3.24zm-4.07 12.21c0 .82.35 1.5 1.04 2.04.69.55 1.5.81 2.43.81 1.31 0 2.48-.49 3.51-1.46 1.02-.97 1.54-2.12 1.54-3.43-.98-.77-2.34-1.16-4.09-1.16-1.28 0-2.34.31-3.2.92-.85.62-1.27 1.39-1.27 2.28z\" />\n <path d=\"M57.16 10.27L46.62 34.5h-3.25l3.91-8.47-6.93-15.76h3.43l5 12.06h.07l4.87-12.06z\" />\n </g>\n </svg>\n );\n}\n","/* ------------------------------------------------------------------ */\n/* WalletPayButton — brand-locked express-payment button. */\n/* */\n/* Renders the official Apple Pay or Google Pay mark on the brand- */\n/* mandated surface (black / white / white-outline) and fires */\n/* `onActivate` on click / Enter / Space. It is PRESENTATIONAL ONLY: */\n/* the branded surface + the trigger. The consumer's `onActivate` */\n/* handler opens the native wallet sheet (Payment Request API / Apple */\n/* Pay JS / Google Pay API) — this component never calls a payment API, */\n/* never holds a publishable key, never knows the amount, and never */\n/* drives the downstream fiscal-receipt / Sistema TS step. Same posture */\n/* as WhatsAppButton (a branded CTA) and SignInWithAlfadocsButton (a */\n/* branded provider button). */\n/* */\n/* BRAND EXCEPTION: Apple's Marketing Guidelines and Google's Pay Brand */\n/* Guidelines mandate exact button surfaces and forbid recolouring the */\n/* marks. The surfaces therefore live as component-private brand tokens */\n/* (`--brand-applepay-*` / `--brand-googlepay-*`, precedent */\n/* `--brand-whatsapp-*`) that do NOT shift in the accessible theme — */\n/* the accessible theme only widens the focus ring and raises the */\n/* target size, which is compliant. The marks are designed to meet */\n/* contrast against their own mandated surfaces, so the axe */\n/* `color-contrast` rule (and only that rule) is waived at story/meta */\n/* level with a rationale, exactly as whatsapp-button / payment-form */\n/* do. Do not reuse these tokens outside this component. */\n/* */\n/* A NATIVE <button> is used (not a Button wrapper, which carries kit */\n/* intent colours, and not an <a> like WhatsAppButton — this opens an */\n/* in-page sheet, not a navigation). The native element gives free */\n/* Enter / Space activation, disabled semantics and the focusable */\n/* accessible-name contract with zero ARIA authoring. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n type ButtonHTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Spinner } from '../spinner';\nimport { ApplePayMark } from '../../brand/provider-marks/apple-pay';\nimport { GooglePayMark } from '../../brand/provider-marks/google-pay';\n\n/* ------------------------------------------------------------------ */\n/* Constants */\n/* ------------------------------------------------------------------ */\n\ntype Provider = 'apple-pay' | 'google-pay';\ntype ButtonStyle = 'black' | 'white' | 'white-outline';\ntype LabelType = 'buy' | 'pay' | 'checkout' | 'book' | 'plain';\n\n/** i18n root segment per provider — drives the bare t() key. */\nconst PROVIDER_I18N_ROOT: Record<Provider, 'applePay' | 'googlePay'> = {\n 'apple-pay': 'applePay',\n 'google-pay': 'googlePay',\n};\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva(\n [\n // `ds:relative` anchors the `::before` pseudo touch-target used by\n // the `sm` size below the `sm:` breakpoint.\n 'ds:relative ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-medium ds:whitespace-nowrap ds:select-none',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:gap-[var(--spacing-xs)]',\n 'ds:transition-[background-color,box-shadow,opacity]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:active:opacity-90',\n // Focus ring is `var(--ring)` at `--focus-ring-width` / offset —\n // deliberately NOT the brand surface colour. A black ring on the\n // black surface, or an invisible ring on the white surface, fails\n // 2.4.7. The token widens 2→3px automatically in the accessible\n // theme. Same reasoning WhatsAppButton documents.\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n // Under Windows High Contrast Mode the brand bg + border are\n // stripped; repaint a solid ButtonBorder edge and swap the focus\n // ring to CanvasText so the affordance stays identifiable.\n 'ds:forced-colors:border ds:forced-colors:border-[ButtonBorder]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed',\n 'ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n provider: {\n 'apple-pay': '',\n 'google-pay': '',\n },\n buttonStyle: {\n black: '',\n white: '',\n 'white-outline': '',\n },\n size: {\n // Size axis sizes the brand mark via a DIRECT-CHILD selector\n // (`[&>svg]`) — the mark is a direct `<svg>` child of the button,\n // whereas the loading Spinner's `<svg>` is nested inside its\n // `role=status` span. A descendant `[&_svg]` would reach into the\n // Spinner and override its own `size-full`, forcing a 20/24px svg\n // inside a 16/20px span (clipped). `[&>svg]` sizes only the mark.\n sm: [\n 'ds:h-8 ds:ps-3 ds:pe-3 ds:text-[length:var(--font-size-sm)]',\n 'ds:[&>svg]:h-4 ds:[&>svg]:w-auto',\n // Expand the hit-target to `--min-target-size` below the\n // `sm:` breakpoint via a `::before` overlay, identical to\n // Button / WhatsAppButton. The visual box stays 32px; touch\n // stays ≥44/48px on mobile.\n 'ds:min-h-[var(--min-target-size)] ds:sm:min-h-0',\n 'ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[\"\"] ds:sm:before:hidden',\n ].join(' '),\n // `md` is the DEFAULT size. Its visual box is 40px (h-10), which is\n // below the 44/48px `--min-target-size`, so it carries the same\n // `::before` hit-target expansion as `sm` to reliably meet WCAG\n // 2.5.5 (44×44) / 2.5.8 (48×48 accessible) on touch viewports. The\n // visual box stays 40px; the touch target lifts to the token below\n // the `sm:` breakpoint.\n md: [\n 'ds:h-10 ds:ps-4 ds:pe-4 ds:text-[length:var(--font-size-base)]',\n 'ds:[&>svg]:h-5 ds:[&>svg]:w-auto',\n 'ds:min-h-[var(--min-target-size)] ds:sm:min-h-0',\n 'ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[\"\"] ds:sm:before:hidden',\n ].join(' '),\n lg: 'ds:h-12 ds:ps-5 ds:pe-5 ds:text-[length:var(--font-size-lg)] ds:[&>svg]:h-6 ds:[&>svg]:w-auto',\n },\n fullWidth: {\n true: 'ds:flex ds:w-full',\n false: '',\n },\n },\n compoundVariants: [\n /* ----- Apple Pay surfaces ----- */\n {\n provider: 'apple-pay',\n buttonStyle: 'black',\n class:\n 'ds:bg-[var(--brand-applepay-black)] ds:text-[var(--brand-applepay-black-foreground)] ds:hover:bg-[var(--brand-applepay-black-hover)]',\n },\n {\n provider: 'apple-pay',\n buttonStyle: 'white',\n class:\n 'ds:bg-[var(--brand-applepay-white)] ds:text-[var(--brand-applepay-white-foreground)] ds:hover:bg-[var(--brand-applepay-white-hover)]',\n },\n {\n provider: 'apple-pay',\n buttonStyle: 'white-outline',\n class:\n 'ds:bg-[var(--brand-applepay-white)] ds:text-[var(--brand-applepay-white-foreground)] ds:hover:bg-[var(--brand-applepay-white-hover)] ds:border ds:border-[var(--brand-applepay-white-border)]',\n },\n /* ----- Google Pay surfaces (no outline type — see fallback) ----- */\n {\n provider: 'google-pay',\n buttonStyle: 'black',\n class:\n 'ds:bg-[var(--brand-googlepay-black)] ds:text-[var(--brand-googlepay-black-foreground)] ds:hover:bg-[var(--brand-googlepay-black-hover)]',\n },\n {\n provider: 'google-pay',\n buttonStyle: 'white',\n class:\n 'ds:bg-[var(--brand-googlepay-white)] ds:text-[var(--brand-googlepay-white-foreground)] ds:hover:bg-[var(--brand-googlepay-white-hover)] ds:border ds:border-[var(--brand-googlepay-white-border)]',\n },\n ],\n defaultVariants: {\n buttonStyle: 'black',\n size: 'md',\n fullWidth: false,\n },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Mark resolver */\n/* ------------------------------------------------------------------ */\n\nfunction WalletMark({ provider }: { provider: Provider }): ReactNode {\n return provider === 'apple-pay' ? <ApplePayMark /> : <GooglePayMark />;\n}\n\n/* ------------------------------------------------------------------ */\n/* Props */\n/* ------------------------------------------------------------------ */\n\nexport interface WalletPayButtonProps\n extends\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type'>,\n Omit<VariantProps<typeof rootVariants>, 'provider'> {\n /**\n * Which wallet the button represents. Drives the inline brand mark,\n * the default label key, the brand-locked surface tokens, and the\n * `data-provider` hook. Required — there is no sensible default\n * wallet.\n */\n provider: Provider;\n /**\n * Brand-mandated button type. Apple Pay supports\n * `black` / `white` / `white-outline`; Google Pay supports\n * `black` / `white` only. Passing `white-outline` with\n * `provider=\"google-pay\"` falls back to `white` and dev-warns —\n * Google's branding has no outline type.\n */\n buttonStyle?: ButtonStyle;\n /**\n * Brand-approved call-to-action phrasing. `plain` renders the bare\n * mark (mark-only) with an sr-only accessible name. The verb portion\n * is i18n; the wallet brand name (\"Apple Pay\" / \"Google Pay\") is a\n * non-translated constant.\n */\n labelType?: LabelType;\n /**\n * Fired on click / Enter / Space when not loading and not disabled.\n * Open the native wallet sheet here (Payment Request API). Named\n * `onActivate` rather than `onClick` to signal it triggers a payment\n * flow. A native `onClick` still forwards through `...rest`.\n */\n onActivate?: (event: MouseEvent<HTMLButtonElement>) => void;\n /**\n * Wallet sheet is opening / payment in flight. Swaps the mark for the\n * kit Spinner, sets `aria-busy=\"true\"`, and suppresses activation so\n * the user cannot double-open the sheet.\n */\n loading?: boolean;\n /**\n * Override the resolved accessible / visible label. Defaults to the\n * `provider` + `labelType` i18n string. The brand wordmark constant\n * (\"Apple Pay\" / \"Google Pay\") is never overridable via this prop —\n * pass a fully-translated verb phrase only.\n */\n label?: ReactNode;\n /**\n * Native button type. Defaults to `'button'` so the wallet trigger\n * never implicitly submits an ambient `<form>`.\n */\n type?: 'button' | 'submit' | 'reset';\n}\n\n/* ------------------------------------------------------------------ */\n/* WalletPayButton */\n/* ------------------------------------------------------------------ */\n\nexport const WalletPayButton = forwardRef<\n HTMLButtonElement,\n WalletPayButtonProps\n>(\n (\n {\n provider,\n buttonStyle = 'black',\n labelType = 'pay',\n size = 'md',\n fullWidth = false,\n onActivate,\n loading = false,\n disabled = false,\n label,\n type = 'button',\n className,\n onClick,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n\n // Google Pay has no white-outline type — fall back to white and\n // dev-warn so the consumer fixes the call site. Done once, before\n // the surface CVA resolves, so no outline class is ever applied.\n let resolvedButtonStyle = buttonStyle;\n if (provider === 'google-pay' && buttonStyle === 'white-outline') {\n resolvedButtonStyle = 'white';\n if (\n typeof import.meta !== 'undefined' &&\n typeof import.meta.env !== 'undefined' &&\n import.meta.env.DEV === true\n ) {\n console.warn(\n `[WalletPayButton] buttonStyle=\"white-outline\" is not a Google Pay ` +\n `brand type — falling back to \"white\". Google's branding has no ` +\n `outline button; use \"black\" or \"white\".`,\n );\n }\n }\n\n const providerRoot = PROVIDER_I18N_ROOT[provider];\n // The default label always carries the wallet brand name as part of\n // the i18n string (\"Pay with Apple Pay\"), so SR users hear the\n // wallet name. `label` overrides only the verb phrase.\n const resolvedLabel =\n label ?? t(`walletPayButton.${providerRoot}.${labelType}`);\n\n const isMarkOnly = labelType === 'plain' && label == null;\n const isInteractive = !loading && !disabled;\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n // Forward the native onClick first (analytics, logging) — it sees\n // every click, including suppressed ones, mirroring WhatsAppButton.\n onClick?.(event);\n if (!isInteractive) return;\n onActivate?.(event);\n };\n\n const composedClassName = [\n rootVariants({\n provider,\n buttonStyle: resolvedButtonStyle,\n size,\n fullWidth,\n }),\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <button\n ref={ref}\n type={type}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n data-component=\"wallet-pay-button\"\n data-provider={provider}\n data-button-style={resolvedButtonStyle}\n className={composedClassName}\n onClick={handleClick}\n {...rest}\n >\n {loading ? (\n <Spinner size={size ?? 'md'} variant=\"pulse\" />\n ) : (\n <WalletMark provider={provider} />\n )}\n {/*\n Label rendering:\n - mark-only (`plain`): sr-only accessible name, no visible\n text. The bare mark still resolves a button name.\n - otherwise: visible label. When loading, the label stays\n visible (aria-busy carries the \"in flight\" semantics) so\n the accessible name never collapses to nothing.\n */}\n {isMarkOnly ? (\n <span className=\"ds:sr-only\">{resolvedLabel}</span>\n ) : (\n // `<bdi>` isolates the label from the surrounding bidi context so\n // the Latin brand wordmark (\"Apple Pay\" / \"Google Pay\") embedded\n // in an RTL verb phrase keeps its own direction — matching the\n // i18n note. Trailing-Latin placement already renders correctly\n // under the UBA today; the isolation also keeps a future\n // mid-phrase wordmark from reordering.\n <bdi>{resolvedLabel}</bdi>\n )}\n </button>\n );\n },\n);\n\nWalletPayButton.displayName = 'WalletPayButton';\n"],"names":["ApplePayMark","className","jsxs","jsx","GooglePayMark","PROVIDER_I18N_ROOT","rootVariants","cva","WalletMark","provider","WalletPayButton","forwardRef","buttonStyle","labelType","size","fullWidth","onActivate","loading","disabled","label","type","onClick","rest","ref","t","useTranslation","resolvedButtonStyle","providerRoot","resolvedLabel","isMarkOnly","isInteractive","handleClick","event","composedClassName","Spinner"],"mappings":";;;;;AA+BO,SAASA,EAAa,EAAE,WAAAC,KAAwC;AACrE,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,MAAK;AAAA,MACL,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,OAAM;AAAA,MACN,WAAAD;AAAA,MAGA,UAAA;AAAA,QAAA,gBAAAE,EAAC,QAAA,EAAK,GAAE,seAAA,CAAse;AAAA,QAE9e,gBAAAA,EAAC,QAAA,EAAK,GAAE,qwBAAA,CAAqwB;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGnxB;ACjBO,SAASC,EAAc,EAAE,WAAAH,KAAwC;AACtE,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,MAAK;AAAA,MACL,SAAQ;AAAA,MACR,OAAM;AAAA,MACN,WAAAD;AAAA,MAGA,UAAA;AAAA,QAAA,gBAAAC,EAAC,KAAA,EAAE,WAAU,kBACX,UAAA;AAAA,UAAA,gBAAAC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,GAAE;AAAA,YAAA;AAAA,UAAA;AAAA,UAEJ,gBAAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,GAAE;AAAA,YAAA;AAAA,UAAA;AAAA,UAEJ,gBAAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,GAAE;AAAA,YAAA;AAAA,UAAA;AAAA,UAEJ,gBAAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,GAAE;AAAA,YAAA;AAAA,UAAA;AAAA,QACJ,GACF;AAAA,QAEA,gBAAAD,EAAC,KAAA,EAAE,MAAK,gBAAe,WAAU,mBAC/B,UAAA;AAAA,UAAA,gBAAAC,EAAC,QAAA,EAAK,GAAE,+RAAA,CAA+R;AAAA,UACvS,gBAAAA,EAAC,QAAA,EAAK,GAAE,ujBAAA,CAAujB;AAAA,UAC/jB,gBAAAA,EAAC,QAAA,EAAK,GAAE,kFAAA,CAAkF;AAAA,QAAA,EAAA,CAC5F;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;ACZA,MAAME,IAAiE;AAAA,EACrE,aAAa;AAAA,EACb,cAAc;AAChB,GAMMC,IAAeC;AAAA,EACnB;AAAA;AAAA;AAAA,IAGE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV;AAAA,IACE,UAAU;AAAA,MACR,UAAU;AAAA,QACR,aAAa;AAAA,QACb,cAAc;AAAA,MAAA;AAAA,MAEhB,aAAa;AAAA,QACX,OAAO;AAAA,QACP,OAAO;AAAA,QACP,iBAAiB;AAAA,MAAA;AAAA,MAEnB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOJ,IAAI;AAAA,UACF;AAAA,UACA;AAAA;AAAA;AAAA;AAAA;AAAA,UAKA;AAAA,UACA;AAAA,QAAA,EACA,KAAK,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOV,IAAI;AAAA,UACF;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QAAA,EACA,KAAK,GAAG;AAAA,QACV,IAAI;AAAA,MAAA;AAAA,MAEN,WAAW;AAAA,QACT,MAAM;AAAA,QACN,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,IAEF,kBAAkB;AAAA;AAAA,MAEhB;AAAA,QACE,UAAU;AAAA,QACV,aAAa;AAAA,QACb,OACE;AAAA,MAAA;AAAA,MAEJ;AAAA,QACE,UAAU;AAAA,QACV,aAAa;AAAA,QACb,OACE;AAAA,MAAA;AAAA,MAEJ;AAAA,QACE,UAAU;AAAA,QACV,aAAa;AAAA,QACb,OACE;AAAA,MAAA;AAAA;AAAA,MAGJ;AAAA,QACE,UAAU;AAAA,QACV,aAAa;AAAA,QACb,OACE;AAAA,MAAA;AAAA,MAEJ;AAAA,QACE,UAAU;AAAA,QACV,aAAa;AAAA,QACb,OACE;AAAA,MAAA;AAAA,IACJ;AAAA,IAEF,iBAAiB;AAAA,MACf,aAAa;AAAA,MACb,MAAM;AAAA,MACN,WAAW;AAAA,IAAA;AAAA,EACb;AAEJ;AAMA,SAASC,EAAW,EAAE,UAAAC,KAA+C;AACnE,SAAOA,MAAa,cAAc,gBAAAN,EAACH,GAAA,CAAA,CAAa,sBAAMI,GAAA,EAAc;AACtE;AA+DO,MAAMM,IAAkBC;AAAA,EAI7B,CACE;AAAA,IACE,UAAAF;AAAA,IACA,aAAAG,IAAc;AAAA,IACd,WAAAC,IAAY;AAAA,IACZ,MAAAC,IAAO;AAAA,IACP,WAAAC,IAAY;AAAA,IACZ,YAAAC;AAAA,IACA,SAAAC,IAAU;AAAA,IACV,UAAAC,IAAW;AAAA,IACX,OAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,WAAAnB;AAAA,IACA,SAAAoB;AAAA,IACA,GAAGC;AAAA,EAAA,GAELC,MACG;AACH,UAAM,EAAE,GAAAC,EAAA,IAAMC,EAAA;AAKd,QAAIC,IAAsBd;AAC1B,IAAIH,MAAa,gBAAgBG,MAAgB,oBAC/Cc,IAAsB;AAcxB,UAAMC,IAAetB,EAAmBI,CAAQ,GAI1CmB,IACJT,KAASK,EAAE,mBAAmBG,CAAY,IAAId,CAAS,EAAE,GAErDgB,IAAahB,MAAc,WAAWM,KAAS,MAC/CW,IAAgB,CAACb,KAAW,CAACC,GAE7Ba,IAAc,CAACC,MAA+C;AAIlE,MADAX,KAAA,QAAAA,EAAUW,IACLF,MACLd,KAAA,QAAAA,EAAagB;AAAA,IACf,GAEMC,IAAoB;AAAA,MACxB3B,EAAa;AAAA,QACX,UAAAG;AAAA,QACA,aAAaiB;AAAA,QACb,MAAAZ;AAAA,QACA,WAAAC;AAAA,MAAA,CACD;AAAA,MACDd;AAAA,IAAA,EAEC,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,WACE,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAqB;AAAA,QACA,MAAAH;AAAA,QACA,UAAUF,KAAYD;AAAA,QACtB,aAAWA,KAAW;AAAA,QACtB,kBAAe;AAAA,QACf,iBAAeR;AAAA,QACf,qBAAmBiB;AAAA,QACnB,WAAWO;AAAA,QACX,SAASF;AAAA,QACR,GAAGT;AAAA,QAEH,UAAA;AAAA,UAAAL,IACC,gBAAAd,EAAC+B,GAAA,EAAQ,MAAMpB,KAAQ,MAAM,SAAQ,QAAA,CAAQ,IAE7C,gBAAAX,EAACK,GAAA,EAAW,UAAAC,GAAoB;AAAA,UAUjCoB,sBACE,QAAA,EAAK,WAAU,cAAc,UAAAD,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAQ5C,gBAAAzB,EAAC,SAAK,UAAAyB,GAAc;AAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAI5B;AACF;AAEAlB,EAAgB,cAAc;"}
|
|
1
|
+
{"version":3,"file":"wallet-pay-button-DuDPBlCO.js","sources":["../../src/brand/provider-marks/apple-pay.tsx","../../src/brand/provider-marks/google-pay.tsx","../../src/components/wallet-pay-button/wallet-pay-button.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* Apple Pay brand mark. */\n/* */\n/* Lives under `src/brand/**` — one of the two zones (with */\n/* `src/tokens/**`) where literal hex / brand SVG paths are sanctioned */\n/* by constraint #2. Apple's Marketing Guidelines mandate the official */\n/* Apple-logo-plus-\"Pay\" lockup and forbid recolouring it to a kit */\n/* accent. The mark is MONOCHROME: it paints in `currentColor` so the */\n/* WalletPayButton's brand-locked foreground token (white on the black */\n/* surface, near-black on the white / white-outline surface) drives it */\n/* from a single CVA class — exactly how the WhatsApp glyph inherits */\n/* `--brand-whatsapp-foreground`. Do NOT tint this with a kit colour. */\n/* */\n/* The viewBox keeps the official aspect ratio so the lockup never */\n/* distorts; the consuming button scales it via `[&_svg]:h-*` height */\n/* classes and `width:auto` so the proportion is preserved at every */\n/* size step. */\n/* ------------------------------------------------------------------ */\n\nimport type { ReactNode } from 'react';\n\nexport interface BrandMarkProps {\n /** Extra classes from the consuming button's CVA size axis. */\n className?: string;\n}\n\n/**\n * Apple Pay lockup (Apple logo + \"Pay\"). Decorative — the parent button\n * carries `aria-hidden` semantics by wrapping it next to the visible /\n * sr-only accessible name, so this SVG is marked `aria-hidden=\"true\"`.\n */\nexport function ApplePayMark({ className }: BrandMarkProps): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n role=\"img\"\n viewBox=\"-1 18 158 69\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n >\n {/* Apple logo glyph */}\n <path d=\"M30.07 27.69c-1.93 2.28-5.01 4.08-8.09 3.82-.39-3.08.92-6.34 2.68-8.36 1.93-2.34 5.31-4.01 8.04-4.14.33 3.21-.92 6.34-2.63 8.68m2.6 4.13c-4.47-.26-8.29 2.54-10.41 2.54-2.15 0-5.4-2.41-8.94-2.34-4.6.07-8.88 2.67-11.23 6.82-4.83 8.29-1.26 20.57 3.41 27.32 2.28 3.34 5.01 7.01 8.62 6.88 3.41-.13 4.75-2.21 8.88-2.21 4.16 0 5.34 2.21 8.94 2.15 3.74-.07 6.08-3.34 8.36-6.68 2.6-3.8 3.67-7.5 3.74-7.69-.07-.07-7.21-2.8-7.27-11.02-.07-6.88 5.6-10.15 5.86-10.35-3.21-4.73-8.22-5.26-9.95-5.39\" />\n {/* \"Pay\" wordmark */}\n <path d=\"M71.62 22.42c9.71 0 16.47 6.69 16.47 16.43 0 9.78-6.9 16.5-16.71 16.5H60.62v17.08H52.86V22.42h18.76zM60.62 48.83h8.91c6.76 0 10.61-3.64 10.61-9.95 0-6.31-3.85-9.92-10.58-9.92h-8.94v19.87zM90.11 62.05c0-6.38 4.89-10.3 13.56-10.79l9.99-.59v-2.81c0-4.06-2.74-6.49-7.32-6.49-4.34 0-7.05 2.08-7.71 5.34h-7.08c.42-6.59 6.03-11.45 15.06-11.45 8.86 0 14.51 4.69 14.51 12.01v25.16h-7.18v-6.01h-.17c-2.11 4.06-6.73 6.62-11.52 6.62-7.15 0-12.15-4.44-12.15-11.01zm23.55-3.29v-2.88l-8.99.56c-4.48.31-7.01 2.29-7.01 5.41 0 3.19 2.64 5.27 6.66 5.27 5.24 0 9.34-3.61 9.34-8.36zM124.95 85.78v-6.07c.55.14 1.79.14 2.42.14 3.47 0 5.34-1.46 6.49-5.2 0-.07.66-2.22.66-2.25l-13.18-36.52h8.11l9.23 29.69h.14l9.23-29.69h7.9l-13.66 38.39c-3.12 8.85-6.73 11.69-14.29 11.69-.63 0-2.5-.07-3.05-.21z\" />\n </svg>\n );\n}\n","/* ------------------------------------------------------------------ */\n/* Google Pay brand mark. */\n/* */\n/* Lives under `src/brand/**` — the sanctioned zone (constraint #2) for */\n/* literal brand hex. Google's Pay Brand Guidelines mandate the */\n/* full-colour \"G Pay\" lockup: the four-colour Google \"G\" plus the */\n/* \"Pay\" wordmark. CRITICAL: unlike the Apple Pay mark (which is */\n/* monochrome `currentColor`), the four-colour Google \"G\" must STAY */\n/* FULL-COLOUR even on the white button — the four brand hues are fixed */\n/* and may not be recoloured to a kit token. Hence the literal fills */\n/* below; they are brand constants, not theme tokens. */\n/* */\n/* The \"Pay\" wordmark is the only part that flips with the surface: it */\n/* inherits `currentColor` (driven by the brand foreground token) so it */\n/* reads dark on the white surface and white on the black surface, */\n/* exactly as Google's dark/light button variants specify. The \"G\" is */\n/* never recoloured. */\n/* ------------------------------------------------------------------ */\n\nimport type { ReactNode } from 'react';\nimport type { BrandMarkProps } from './apple-pay';\n\n/**\n * Google Pay lockup (four-colour Google \"G\" + \"Pay\" wordmark). The \"G\"\n * keeps Google's mandated four brand hues at all times; the \"Pay\"\n * wordmark inherits `currentColor` so it reads against either the black\n * or the white surface (brand foreground token). Decorative —\n * `aria-hidden=\"true\"`; the accessible name comes from the parent\n * button.\n */\nexport function GooglePayMark({ className }: BrandMarkProps): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n role=\"img\"\n viewBox=\"1 7 101 36\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n >\n {/* Four-colour Google \"G\" — fixed brand hues, never recoloured. */}\n <g transform=\"translate(2 8)\">\n <path\n fill=\"#4285F4\"\n d=\"M31.5 16.5c0-1.16-.1-2.27-.3-3.34H16.07v6.33h8.65a7.4 7.4 0 0 1-3.21 4.85v4.03h5.19c3.04-2.8 4.8-6.92 4.8-11.87z\"\n />\n <path\n fill=\"#34A853\"\n d=\"M16.07 32c4.34 0 7.99-1.44 10.65-3.89l-5.19-4.03c-1.44.97-3.29 1.54-5.46 1.54-4.2 0-7.76-2.84-9.03-6.65H1.67v4.17C4.32 28.4 9.71 32 16.07 32z\"\n />\n <path\n fill=\"#FBBC04\"\n d=\"M7.04 18.97A9.6 9.6 0 0 1 6.53 16c0-1.03.18-2.03.51-2.97V8.86H1.67A15.96 15.96 0 0 0 0 16c0 2.58.62 5.02 1.67 7.14l5.37-4.17z\"\n />\n <path\n fill=\"#EA4335\"\n d=\"M16.07 6.39c2.37 0 4.5.81 6.17 2.41l4.62-4.62C24.05 1.6 20.41 0 16.07 0 9.71 0 4.32 3.6 1.67 8.86l5.37 4.17c1.27-3.81 4.83-6.64 9.03-6.64z\"\n />\n </g>\n {/* \"Pay\" wordmark — flips with the surface via currentColor. */}\n <g fill=\"currentColor\" transform=\"translate(44 8)\">\n <path d=\"M11.9 17.06v9.94H8.75V2.52h8.36c2.12 0 3.93.71 5.41 2.12 1.51 1.42 2.27 3.14 2.27 5.18 0 2.09-.76 3.82-2.27 5.21-1.46 1.39-3.27 2.07-5.41 2.07H11.9v-.06zm0-11.53v8.55h5.28c1.25 0 2.29-.42 3.12-1.26.84-.84 1.26-1.86 1.26-3.01 0-1.12-.42-2.13-1.26-2.97-.83-.87-1.87-1.31-3.12-1.31H11.9z\" />\n <path d=\"M32.43 9.72c2.33 0 4.16.62 5.51 1.87 1.35 1.25 2.02 2.96 2.02 5.12V27h-3.01v-2.33h-.14c-1.3 1.92-3.05 2.88-5.23 2.88-1.85 0-3.41-.55-4.65-1.65-1.25-1.1-1.87-2.46-1.87-4.11 0-1.74.66-3.11 1.97-4.13 1.31-1.02 3.07-1.54 5.26-1.54 1.87 0 3.41.34 4.62 1.03v-.72c0-1.1-.43-2.02-1.29-2.79-.86-.77-1.87-1.15-3.02-1.15-1.74 0-3.11.72-4.11 2.18l-2.78-1.75c1.5-2.16 3.72-3.24 6.64-3.24zm-4.07 12.21c0 .82.35 1.5 1.04 2.04.69.55 1.5.81 2.43.81 1.31 0 2.48-.49 3.51-1.46 1.02-.97 1.54-2.12 1.54-3.43-.98-.77-2.34-1.16-4.09-1.16-1.28 0-2.34.31-3.2.92-.85.62-1.27 1.39-1.27 2.28z\" />\n <path d=\"M57.16 10.27L46.62 34.5h-3.25l3.91-8.47-6.93-15.76h3.43l5 12.06h.07l4.87-12.06z\" />\n </g>\n </svg>\n );\n}\n","/* ------------------------------------------------------------------ */\n/* WalletPayButton — brand-locked express-payment button. */\n/* */\n/* Renders the official Apple Pay or Google Pay mark on the brand- */\n/* mandated surface (black / white / white-outline) and fires */\n/* `onActivate` on click / Enter / Space. It is PRESENTATIONAL ONLY: */\n/* the branded surface + the trigger. The consumer's `onActivate` */\n/* handler opens the native wallet sheet (Payment Request API / Apple */\n/* Pay JS / Google Pay API) — this component never calls a payment API, */\n/* never holds a publishable key, never knows the amount, and never */\n/* drives the downstream fiscal-receipt / Sistema TS step. Same posture */\n/* as WhatsAppButton (a branded CTA) and SignInWithAlfadocsButton (a */\n/* branded provider button). */\n/* */\n/* BRAND EXCEPTION: Apple's Marketing Guidelines and Google's Pay Brand */\n/* Guidelines mandate exact button surfaces and forbid recolouring the */\n/* marks. The surfaces therefore live as component-private brand tokens */\n/* (`--brand-applepay-*` / `--brand-googlepay-*`, precedent */\n/* `--brand-whatsapp-*`) that do NOT shift in the accessible theme — */\n/* the accessible theme only widens the focus ring and raises the */\n/* target size, which is compliant. The marks are designed to meet */\n/* contrast against their own mandated surfaces, so the axe */\n/* `color-contrast` rule (and only that rule) is waived at story/meta */\n/* level with a rationale, exactly as whatsapp-button / payment-form */\n/* do. Do not reuse these tokens outside this component. */\n/* */\n/* A NATIVE <button> is used (not a Button wrapper, which carries kit */\n/* intent colours, and not an <a> like WhatsAppButton — this opens an */\n/* in-page sheet, not a navigation). The native element gives free */\n/* Enter / Space activation, disabled semantics and the focusable */\n/* accessible-name contract with zero ARIA authoring. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n type ButtonHTMLAttributes,\n type MouseEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Spinner } from '../spinner';\nimport { ApplePayMark } from '../../brand/provider-marks/apple-pay';\nimport { GooglePayMark } from '../../brand/provider-marks/google-pay';\n\n/* ------------------------------------------------------------------ */\n/* Constants */\n/* ------------------------------------------------------------------ */\n\ntype Provider = 'apple-pay' | 'google-pay';\ntype ButtonStyle = 'black' | 'white' | 'white-outline';\ntype LabelType = 'buy' | 'pay' | 'checkout' | 'book' | 'plain';\n\n/** i18n root segment per provider — drives the bare t() key. */\nconst PROVIDER_I18N_ROOT: Record<Provider, 'applePay' | 'googlePay'> = {\n 'apple-pay': 'applePay',\n 'google-pay': 'googlePay',\n};\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva(\n [\n // `ds:relative` anchors the `::before` pseudo touch-target used by\n // the `sm` size below the `sm:` breakpoint.\n 'ds:relative ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-medium ds:whitespace-nowrap ds:select-none',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:gap-[var(--spacing-xs)]',\n 'ds:transition-[background-color,box-shadow,opacity]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:active:opacity-90',\n // Focus ring is `var(--ring)` at `--focus-ring-width` / offset —\n // deliberately NOT the brand surface colour. A black ring on the\n // black surface, or an invisible ring on the white surface, fails\n // 2.4.7. The token widens 2→3px automatically in the accessible\n // theme. Same reasoning WhatsAppButton documents.\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n // Under Windows High Contrast Mode the brand bg + border are\n // stripped; repaint a solid ButtonBorder edge and swap the focus\n // ring to CanvasText so the affordance stays identifiable.\n 'ds:forced-colors:border ds:forced-colors:border-[ButtonBorder]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed',\n 'ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n provider: {\n 'apple-pay': '',\n 'google-pay': '',\n },\n buttonStyle: {\n black: '',\n white: '',\n 'white-outline': '',\n },\n size: {\n // Size axis sizes the brand mark via a DIRECT-CHILD selector\n // (`[&>svg]`) — the mark is a direct `<svg>` child of the button,\n // whereas the loading Spinner's `<svg>` is nested inside its\n // `role=status` span. A descendant `[&_svg]` would reach into the\n // Spinner and override its own `size-full`, forcing a 20/24px svg\n // inside a 16/20px span (clipped). `[&>svg]` sizes only the mark.\n sm: [\n 'ds:h-8 ds:ps-3 ds:pe-3 ds:text-[length:var(--font-size-sm)]',\n 'ds:[&>svg]:h-4 ds:[&>svg]:w-auto',\n // Expand the hit-target to `--min-target-size` below the\n // `sm:` breakpoint via a `::before` overlay, identical to\n // Button / WhatsAppButton. The visual box stays 32px; touch\n // stays ≥44/48px on mobile.\n 'ds:min-h-[var(--min-target-size)] ds:sm:min-h-0',\n 'ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[\"\"] ds:sm:before:hidden',\n ].join(' '),\n // `md` is the DEFAULT size. Its visual box is 40px (h-10), which is\n // below the 44/48px `--min-target-size`, so it carries the same\n // `::before` hit-target expansion as `sm` to reliably meet WCAG\n // 2.5.5 (44×44) / 2.5.8 (48×48 accessible) on touch viewports. The\n // visual box stays 40px; the touch target lifts to the token below\n // the `sm:` breakpoint.\n md: [\n 'ds:h-10 ds:ps-4 ds:pe-4 ds:text-[length:var(--font-size-base)]',\n 'ds:[&>svg]:h-5 ds:[&>svg]:w-auto',\n 'ds:min-h-[var(--min-target-size)] ds:sm:min-h-0',\n 'ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[\"\"] ds:sm:before:hidden',\n ].join(' '),\n lg: 'ds:h-12 ds:ps-5 ds:pe-5 ds:text-[length:var(--font-size-lg)] ds:[&>svg]:h-6 ds:[&>svg]:w-auto',\n },\n fullWidth: {\n true: 'ds:flex ds:w-full',\n false: '',\n },\n },\n compoundVariants: [\n /* ----- Apple Pay surfaces ----- */\n {\n provider: 'apple-pay',\n buttonStyle: 'black',\n class:\n 'ds:bg-[var(--brand-applepay-black)] ds:text-[var(--brand-applepay-black-foreground)] ds:hover:bg-[var(--brand-applepay-black-hover)]',\n },\n {\n provider: 'apple-pay',\n buttonStyle: 'white',\n class:\n 'ds:bg-[var(--brand-applepay-white)] ds:text-[var(--brand-applepay-white-foreground)] ds:hover:bg-[var(--brand-applepay-white-hover)]',\n },\n {\n provider: 'apple-pay',\n buttonStyle: 'white-outline',\n class:\n 'ds:bg-[var(--brand-applepay-white)] ds:text-[var(--brand-applepay-white-foreground)] ds:hover:bg-[var(--brand-applepay-white-hover)] ds:border ds:border-[var(--brand-applepay-white-border)]',\n },\n /* ----- Google Pay surfaces (no outline type — see fallback) ----- */\n {\n provider: 'google-pay',\n buttonStyle: 'black',\n class:\n 'ds:bg-[var(--brand-googlepay-black)] ds:text-[var(--brand-googlepay-black-foreground)] ds:hover:bg-[var(--brand-googlepay-black-hover)]',\n },\n {\n provider: 'google-pay',\n buttonStyle: 'white',\n class:\n 'ds:bg-[var(--brand-googlepay-white)] ds:text-[var(--brand-googlepay-white-foreground)] ds:hover:bg-[var(--brand-googlepay-white-hover)] ds:border ds:border-[var(--brand-googlepay-white-border)]',\n },\n ],\n defaultVariants: {\n buttonStyle: 'black',\n size: 'md',\n fullWidth: false,\n },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Mark resolver */\n/* ------------------------------------------------------------------ */\n\nfunction WalletMark({ provider }: { provider: Provider }): ReactNode {\n return provider === 'apple-pay' ? <ApplePayMark /> : <GooglePayMark />;\n}\n\n/* ------------------------------------------------------------------ */\n/* Props */\n/* ------------------------------------------------------------------ */\n\nexport interface WalletPayButtonProps\n extends\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'type'>,\n Omit<VariantProps<typeof rootVariants>, 'provider'> {\n /**\n * Which wallet the button represents. Drives the inline brand mark,\n * the default label key, the brand-locked surface tokens, and the\n * `data-provider` hook. Required — there is no sensible default\n * wallet.\n */\n provider: Provider;\n /**\n * Brand-mandated button type. Apple Pay supports\n * `black` / `white` / `white-outline`; Google Pay supports\n * `black` / `white` only. Passing `white-outline` with\n * `provider=\"google-pay\"` falls back to `white` and dev-warns —\n * Google's branding has no outline type.\n */\n buttonStyle?: ButtonStyle;\n /**\n * Brand-approved call-to-action phrasing. `plain` renders the bare\n * mark (mark-only) with an sr-only accessible name. The verb portion\n * is i18n; the wallet brand name (\"Apple Pay\" / \"Google Pay\") is a\n * non-translated constant.\n */\n labelType?: LabelType;\n /**\n * Fired on click / Enter / Space when not loading and not disabled.\n * Open the native wallet sheet here (Payment Request API). Named\n * `onActivate` rather than `onClick` to signal it triggers a payment\n * flow. A native `onClick` still forwards through `...rest`.\n */\n onActivate?: (event: MouseEvent<HTMLButtonElement>) => void;\n /**\n * Wallet sheet is opening / payment in flight. Swaps the mark for the\n * kit Spinner, sets `aria-busy=\"true\"`, and suppresses activation so\n * the user cannot double-open the sheet.\n */\n loading?: boolean;\n /**\n * Override the resolved accessible / visible label. Defaults to the\n * `provider` + `labelType` i18n string. The brand wordmark constant\n * (\"Apple Pay\" / \"Google Pay\") is never overridable via this prop —\n * pass a fully-translated verb phrase only.\n */\n label?: ReactNode;\n /**\n * Native button type. Defaults to `'button'` so the wallet trigger\n * never implicitly submits an ambient `<form>`.\n */\n type?: 'button' | 'submit' | 'reset';\n}\n\n/* ------------------------------------------------------------------ */\n/* WalletPayButton */\n/* ------------------------------------------------------------------ */\n\nexport const WalletPayButton = forwardRef<\n HTMLButtonElement,\n WalletPayButtonProps\n>(\n (\n {\n provider,\n buttonStyle = 'black',\n labelType = 'pay',\n size = 'md',\n fullWidth = false,\n onActivate,\n loading = false,\n disabled = false,\n label,\n type = 'button',\n className,\n onClick,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n\n // Google Pay has no white-outline type — fall back to white and\n // dev-warn so the consumer fixes the call site. Done once, before\n // the surface CVA resolves, so no outline class is ever applied.\n let resolvedButtonStyle = buttonStyle;\n if (provider === 'google-pay' && buttonStyle === 'white-outline') {\n resolvedButtonStyle = 'white';\n if (\n typeof import.meta !== 'undefined' &&\n typeof import.meta.env !== 'undefined' &&\n import.meta.env.DEV === true\n ) {\n console.warn(\n `[WalletPayButton] buttonStyle=\"white-outline\" is not a Google Pay ` +\n `brand type — falling back to \"white\". Google's branding has no ` +\n `outline button; use \"black\" or \"white\".`,\n );\n }\n }\n\n const providerRoot = PROVIDER_I18N_ROOT[provider];\n // The default label always carries the wallet brand name as part of\n // the i18n string (\"Pay with Apple Pay\"), so SR users hear the\n // wallet name. `label` overrides only the verb phrase.\n const resolvedLabel =\n label ?? t(`walletPayButton.${providerRoot}.${labelType}`);\n\n const isMarkOnly = labelType === 'plain' && label == null;\n const isInteractive = !loading && !disabled;\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>): void => {\n // Forward the native onClick first (analytics, logging) — it sees\n // every click, including suppressed ones, mirroring WhatsAppButton.\n onClick?.(event);\n if (!isInteractive) return;\n onActivate?.(event);\n };\n\n const composedClassName = [\n rootVariants({\n provider,\n buttonStyle: resolvedButtonStyle,\n size,\n fullWidth,\n }),\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <button\n ref={ref}\n type={type}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n data-component=\"wallet-pay-button\"\n data-provider={provider}\n data-button-style={resolvedButtonStyle}\n className={composedClassName}\n onClick={handleClick}\n {...rest}\n >\n {loading ? (\n <Spinner size={size ?? 'md'} variant=\"pulse\" />\n ) : (\n <WalletMark provider={provider} />\n )}\n {/*\n Label rendering:\n - mark-only (`plain`): sr-only accessible name, no visible\n text. The bare mark still resolves a button name.\n - otherwise: visible label. When loading, the label stays\n visible (aria-busy carries the \"in flight\" semantics) so\n the accessible name never collapses to nothing.\n */}\n {isMarkOnly ? (\n <span className=\"ds:sr-only\">{resolvedLabel}</span>\n ) : (\n // `<bdi>` isolates the label from the surrounding bidi context so\n // the Latin brand wordmark (\"Apple Pay\" / \"Google Pay\") embedded\n // in an RTL verb phrase keeps its own direction — matching the\n // i18n note. Trailing-Latin placement already renders correctly\n // under the UBA today; the isolation also keeps a future\n // mid-phrase wordmark from reordering.\n <bdi>{resolvedLabel}</bdi>\n )}\n </button>\n );\n },\n);\n\nWalletPayButton.displayName = 'WalletPayButton';\n"],"names":["ApplePayMark","className","jsxs","jsx","GooglePayMark","PROVIDER_I18N_ROOT","rootVariants","cva","WalletMark","provider","WalletPayButton","forwardRef","buttonStyle","labelType","size","fullWidth","onActivate","loading","disabled","label","type","onClick","rest","ref","t","useTranslation","resolvedButtonStyle","providerRoot","resolvedLabel","isMarkOnly","isInteractive","handleClick","event","composedClassName","Spinner"],"mappings":"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"}
|