@alfadocs/ui-kit-debug 0.30.2 → 0.30.3
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/{agenda-card-CznB7K8e.js → agenda-card-DO9_yogb.js} +2 -2
- package/dist/_chunks/{agenda-card-CznB7K8e.js.map → agenda-card-DO9_yogb.js.map} +1 -1
- package/dist/_chunks/{agenda-tray-C_bha7oK.js → agenda-tray-WLbfMip6.js} +3 -3
- package/dist/_chunks/{agenda-tray-C_bha7oK.js.map → agenda-tray-WLbfMip6.js.map} +1 -1
- package/dist/_chunks/{ai-prompt-input-Bka3jX3i.js → ai-prompt-input-BXlwNnWe.js} +2 -2
- package/dist/_chunks/{ai-prompt-input-Bka3jX3i.js.map → ai-prompt-input-BXlwNnWe.js.map} +1 -1
- package/dist/_chunks/{alia-sidebar-CnvXq95r.js → alia-sidebar-C-232sD7.js} +4 -4
- package/dist/_chunks/{alia-sidebar-CnvXq95r.js.map → alia-sidebar-C-232sD7.js.map} +1 -1
- package/dist/_chunks/{audio-recorder-OB6IV9B1.js → audio-recorder-DL800PE_.js} +2 -2
- package/dist/_chunks/{audio-recorder-OB6IV9B1.js.map → audio-recorder-DL800PE_.js.map} +1 -1
- package/dist/_chunks/{badge-cLYVGlMh.js → badge-CIjQ1Us1.js} +2 -2
- package/dist/_chunks/{badge-cLYVGlMh.js.map → badge-CIjQ1Us1.js.map} +1 -1
- package/dist/_chunks/{benefit-card-CjYM8vNr.js → benefit-card-CLPNpiQE.js} +14 -3
- package/dist/_chunks/{benefit-card-CjYM8vNr.js.map → benefit-card-CLPNpiQE.js.map} +1 -1
- package/dist/_chunks/{booking-CBaSujVP.js → booking-DiyuLsQV.js} +4 -4
- package/dist/_chunks/{booking-CBaSujVP.js.map → booking-DiyuLsQV.js.map} +1 -1
- package/dist/_chunks/{carousel.agent-C9swH-Uf.js → carousel.agent-C2Icc9_T.js} +2 -2
- package/dist/_chunks/{carousel.agent-C9swH-Uf.js.map → carousel.agent-C2Icc9_T.js.map} +1 -1
- package/dist/_chunks/{chat-input-BfDcz1Yi.js → chat-input-cyl_y9bh.js} +2 -2
- package/dist/_chunks/{chat-input-BfDcz1Yi.js.map → chat-input-cyl_y9bh.js.map} +1 -1
- package/dist/_chunks/{checkbox-mwbrPZDY.js → checkbox-Dgp_cfUr.js} +2 -2
- package/dist/_chunks/{checkbox-mwbrPZDY.js.map → checkbox-Dgp_cfUr.js.map} +1 -1
- package/dist/_chunks/{checkbox-group-Bwmt1ovQ.js → checkbox-group-eVowqKcT.js} +2 -2
- package/dist/_chunks/{checkbox-group-Bwmt1ovQ.js.map → checkbox-group-eVowqKcT.js.map} +1 -1
- package/dist/_chunks/{description-list-yl3y3QKg.js → description-list-CWjnaDGn.js} +2 -2
- package/dist/_chunks/{description-list-yl3y3QKg.js.map → description-list-CWjnaDGn.js.map} +1 -1
- package/dist/_chunks/{dialog-BBXqpZIt.js → dialog-C-lTGVkB.js} +2 -2
- package/dist/_chunks/{dialog-BBXqpZIt.js.map → dialog-C-lTGVkB.js.map} +1 -1
- package/dist/_chunks/{editable-currency-cell-renderer-DSReCae7.js → editable-currency-cell-renderer-CztRy_21.js} +8 -8
- package/dist/_chunks/{editable-currency-cell-renderer-DSReCae7.js.map → editable-currency-cell-renderer-CztRy_21.js.map} +1 -1
- package/dist/_chunks/{header-CkMb1TZS.js → header-DDj7uGD-.js} +2 -2
- package/dist/_chunks/{header-CkMb1TZS.js.map → header-DDj7uGD-.js.map} +1 -1
- package/dist/_chunks/{icon-button-BRHSSFmZ.js → icon-button-DPEqBKBQ.js} +2 -2
- package/dist/_chunks/{icon-button-BRHSSFmZ.js.map → icon-button-DPEqBKBQ.js.map} +1 -1
- package/dist/_chunks/{key-value-pair-CqeKiP__.js → key-value-pair-BvDY3zuc.js} +2 -2
- package/dist/_chunks/{key-value-pair-CqeKiP__.js.map → key-value-pair-BvDY3zuc.js.map} +1 -1
- package/dist/_chunks/{locale-picker-BHxbTNmR.js → locale-picker-CY89MChR.js} +3 -3
- package/dist/_chunks/{locale-picker-BHxbTNmR.js.map → locale-picker-CY89MChR.js.map} +1 -1
- package/dist/_chunks/{message-card-DjRtA8GG.js → message-card-D6iyPacd.js} +2 -2
- package/dist/_chunks/{message-card-DjRtA8GG.js.map → message-card-D6iyPacd.js.map} +1 -1
- package/dist/_chunks/{message-tray-DfsAMncP.js → message-tray-Bh-0ra-Y.js} +3 -3
- package/dist/_chunks/{message-tray-DfsAMncP.js.map → message-tray-Bh-0ra-Y.js.map} +1 -1
- package/dist/_chunks/{multi-select-CyspR5ZF.js → multi-select-dSe6Xtc4.js} +2 -2
- package/dist/_chunks/{multi-select-CyspR5ZF.js.map → multi-select-dSe6Xtc4.js.map} +1 -1
- package/dist/_chunks/{notification-card-B_847w5g.js → notification-card-vaMaA7Wa.js} +2 -2
- package/dist/_chunks/{notification-card-B_847w5g.js.map → notification-card-vaMaA7Wa.js.map} +1 -1
- package/dist/_chunks/{notification-tray-6f7smmT1.js → notification-tray-Cnum6LwM.js} +3 -3
- package/dist/_chunks/{notification-tray-6f7smmT1.js.map → notification-tray-Cnum6LwM.js.map} +1 -1
- package/dist/_chunks/{patient-shell-CAXYzbRw.js → patient-shell-CYaNkbA1.js} +4 -4
- package/dist/_chunks/{patient-shell-CAXYzbRw.js.map → patient-shell-CYaNkbA1.js.map} +1 -1
- package/dist/_chunks/{popover-D0slaBB9.js → popover-BfHSBEKq.js} +2 -2
- package/dist/_chunks/{popover-D0slaBB9.js.map → popover-BfHSBEKq.js.map} +1 -1
- package/dist/_chunks/{privacy-lock-BI4vi9Ud.js → privacy-lock-C6Ra5m3p.js} +2 -2
- package/dist/_chunks/{privacy-lock-BI4vi9Ud.js.map → privacy-lock-C6Ra5m3p.js.map} +1 -1
- package/dist/_chunks/{public-header.agent-5U3S9QiC.js → public-header.agent-B-B0WDAi.js} +3 -3
- package/dist/_chunks/{public-header.agent-5U3S9QiC.js.map → public-header.agent-B-B0WDAi.js.map} +1 -1
- package/dist/_chunks/{radio-group-BHZOxrIK.js → radio-group-CTlGc36r.js} +2 -2
- package/dist/_chunks/{radio-group-BHZOxrIK.js.map → radio-group-CTlGc36r.js.map} +1 -1
- package/dist/_chunks/{radio-DvF59ThA.js → radio-i4ogu3cq.js} +2 -2
- package/dist/_chunks/{radio-DvF59ThA.js.map → radio-i4ogu3cq.js.map} +1 -1
- package/dist/_chunks/{reviews-panel-D-zjPKFL.js → reviews-panel-bKEUKSic.js} +2 -2
- package/dist/_chunks/{reviews-panel-D-zjPKFL.js.map → reviews-panel-bKEUKSic.js.map} +1 -1
- package/dist/_chunks/{sheet-D8M8hf8B.js → sheet-D8Yl0nKR.js} +2 -2
- package/dist/_chunks/{sheet-D8M8hf8B.js.map → sheet-D8Yl0nKR.js.map} +1 -1
- package/dist/_chunks/{slot-grid-B2zprPcv.js → slot-grid-Q94gVmhn.js} +2 -2
- package/dist/_chunks/{slot-grid-B2zprPcv.js.map → slot-grid-Q94gVmhn.js.map} +1 -1
- package/dist/_chunks/{tag-CQmHRM4Y.js → tag-bBPAvXyh.js} +2 -2
- package/dist/_chunks/{tag-CQmHRM4Y.js.map → tag-bBPAvXyh.js.map} +1 -1
- package/dist/_chunks/{task-tray-Bcmrrs8m.js → task-tray-6NlryfMJ.js} +2 -2
- package/dist/_chunks/{task-tray-Bcmrrs8m.js.map → task-tray-6NlryfMJ.js.map} +1 -1
- package/dist/_chunks/{theme-toggle-nPzb378f.js → theme-toggle-JoeMs_ws.js} +2 -2
- package/dist/_chunks/{theme-toggle-nPzb378f.js.map → theme-toggle-JoeMs_ws.js.map} +1 -1
- package/dist/_chunks/{toast.agent-CTF6nIj5.js → toast.agent-DVpKt38p.js} +2 -2
- package/dist/_chunks/{toast.agent-CTF6nIj5.js.map → toast.agent-DVpKt38p.js.map} +1 -1
- package/dist/_chunks/{tooltip-DHik5yRI.js → tooltip-ClJd0ciy.js} +31 -19
- package/dist/_chunks/tooltip-ClJd0ciy.js.map +1 -0
- package/dist/_chunks/{workflow-map-BKsKdYvZ.js → workflow-map-DlWBJJBt.js} +4 -4
- package/dist/_chunks/{workflow-map-BKsKdYvZ.js.map → workflow-map-DlWBJJBt.js.map} +1 -1
- package/dist/agent-catalog.json +1 -1
- package/dist/components/agenda-card/index.js +1 -1
- package/dist/components/agenda-tray/index.js +1 -1
- package/dist/components/ai-prompt-input/index.js +1 -1
- package/dist/components/audio-recorder/index.js +1 -1
- package/dist/components/badge/index.js +1 -1
- package/dist/components/benefit-card/benefit-card.d.ts.map +1 -1
- package/dist/components/benefit-card/index.js +1 -1
- package/dist/components/booking/index.js +1 -1
- package/dist/components/button/index.js +1 -1
- package/dist/components/carousel/index.js +1 -1
- package/dist/components/chat-input/index.js +1 -1
- package/dist/components/checkbox/index.js +1 -1
- package/dist/components/checkbox-group/index.js +1 -1
- package/dist/components/data-table/index.js +1 -1
- package/dist/components/description-list/index.js +1 -1
- package/dist/components/dialog/index.js +1 -1
- package/dist/components/header/index.js +1 -1
- package/dist/components/header-settings/index.js +2 -2
- package/dist/components/icon-button/index.js +1 -1
- package/dist/components/key-value-pair/index.js +1 -1
- package/dist/components/locale-picker/index.js +1 -1
- package/dist/components/message-card/index.js +1 -1
- package/dist/components/message-tray/index.js +1 -1
- package/dist/components/multi-select/index.js +1 -1
- package/dist/components/notification-card/index.js +1 -1
- package/dist/components/notification-tray/index.js +1 -1
- package/dist/components/popover/index.js +1 -1
- package/dist/components/privacy-lock/index.js +1 -1
- package/dist/components/public-header/index.js +1 -1
- package/dist/components/radio/index.js +1 -1
- package/dist/components/radio-group/index.js +2 -2
- package/dist/components/reviews-panel/index.js +1 -1
- package/dist/components/sheet/index.js +1 -1
- package/dist/components/slot-grid/index.js +1 -1
- package/dist/components/tag/index.js +1 -1
- package/dist/components/task-tray/index.js +1 -1
- package/dist/components/theme-toggle/index.js +1 -1
- package/dist/components/toast/index.js +1 -1
- package/dist/components/tooltip/index.js +1 -1
- package/dist/components/tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/workflow/index.js +1 -1
- package/dist/index.js +38 -38
- package/dist/patterns/alia-assistant/index.js +1 -1
- package/dist/patterns/patient-shell/index.js +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/tooltip-DHik5yRI.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviews-panel-D-zjPKFL.js","sources":["../../src/components/reviews-panel/reviews-panel.agent.ts","../../src/components/reviews-panel/reviews-panel.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — ReviewsPanel. */\n/* */\n/* Replaces the legacy patient-reviews Carousel with a single panel that */\n/* surfaces an aggregate score, a star-bucket filter, and a vertical list */\n/* of compact review cards. The adapter exposes the active filter and the */\n/* visible review count so a host agent can drive the panel and observe */\n/* what the user actually sees after the filter is applied. The reviews */\n/* themselves remain owned by the consumer — they never flow through the */\n/* adapter (PHI: pseudonyms + free-text comments stay on the page). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { ReviewFilter, ReviewsPanelHandle } from './reviews-panel';\n\nexport const reviewsPanelAgent: AgentAdapter<ReviewsPanelHandle> = {\n id: 'reviews-panel',\n capabilities: ['filter'],\n state: {\n filter: {\n type: 'string',\n descriptionKey: 'ui.agent.reviewsPanel.state.filter',\n description:\n 'Active star-bucket filter — `\"all\"` or an integer 1..5. Reads the controlled `filter` prop.',\n read: (handle) => handle.getFilter(),\n },\n visibleCount: {\n type: 'number',\n descriptionKey: 'ui.agent.reviewsPanel.state.visibleCount',\n description:\n 'Number of reviews currently rendered after the filter is applied.',\n read: (handle) => handle.getVisibleCount(),\n },\n },\n actions: {\n set_filter: {\n safety: 'read',\n argsType: '{ filter: \"all\" | 1 | 2 | 3 | 4 | 5 }',\n descriptionKey: 'ui.agent.reviewsPanel.actions.setFilter',\n description:\n 'Narrow the visible review list to a star bucket, or `\"all\"` to clear the filter.',\n invoke: (handle, args: { filter: ReviewFilter }) => {\n handle.setFilter(args.filter);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'reviews-panel',\n description: 'Marks the ReviewsPanel root region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { ChevronDown } from 'lucide-react';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { reviewsPanelAgent } from './reviews-panel.agent';\nimport { Rating } from '../rating/rating';\nimport { Card } from '../card/card';\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '../collapsible/collapsible';\nimport { DescriptionList } from '../description-list/description-list';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { Button } from '../button/button';\n\n/* -------------------------------------------------------------------- */\n/* ReviewsPanel */\n/* */\n/* Replaces the legacy patient-reviews Carousel with a two-column panel */\n/* — a star-bucket filter sidebar + a vertical list of compact review */\n/* cards — that surfaces the aggregate score, the volume, and (via a */\n/* per-card Collapsible) the rich per-axis breakdown alfaDocs already */\n/* collects but the carousel buried. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\nexport interface ReviewItem {\n id: string;\n /** When the review was authored (ISO 8601). */\n authoredAt: string;\n /** Anonymised author display (\"Marco T.\", \"Anna_M\"). Falls back to a localised \"Anonymous\". */\n pseudonym?: string;\n /** 0..max — drives the visible star summary on the compact card. */\n overallScore: number;\n /** Short free-text comment shown on the compact card. */\n comment?: string;\n /** Practice reply (revealed in the expanded panel only). */\n reply?: string;\n /** Optional per-axis breakdown — when present, the card grows an\n * \"Expand\" affordance that reveals these in a DescriptionList. */\n breakdown?: {\n appointmentScore?: number;\n communicationScore?: number;\n staffScore?: number;\n overallScore?: number;\n };\n /** Optional categorical follow-ups, surfaced inside the breakdown. */\n futureVisit?: 'yes' | 'no' | 'unsure';\n recommendation?: 'yes' | 'no' | 'unsure';\n}\n\nexport interface ReviewsAggregate {\n /** Total review count across all star buckets (may exceed the loaded set when paginated). */\n count: number;\n /** Average score across the full population (drives the header `Rating`). */\n averageScore: number;\n /**\n * Optional histogram — per-bucket count keyed by integer star value 1..5.\n * When omitted, the panel derives buckets from the loaded `reviews` array\n * (best-effort: paginated consumers SHOULD provide this explicitly).\n */\n histogram?: Record<1 | 2 | 3 | 4 | 5, number>;\n}\n\nexport type ReviewFilter = 'all' | 1 | 2 | 3 | 4 | 5;\n\nexport interface ReviewsPanelHandle {\n getFilter: () => ReviewFilter;\n setFilter: (next: ReviewFilter) => void;\n /** Total visible after filter is applied. */\n getVisibleCount: () => number;\n}\n\nexport interface ReviewsPanelProps extends Omit<\n ComponentPropsWithoutRef<'section'>,\n 'aria-label' | 'children'\n> {\n reviews: ReviewItem[];\n aggregate: ReviewsAggregate;\n\n /**\n * Active star filter. Controlled — consumer owns it for shareability via\n * URL query strings. Omit for uncontrolled (panel-managed) filtering.\n */\n filter?: ReviewFilter;\n onFilterChange?: (next: ReviewFilter) => void;\n\n /**\n * Optional intro callout shown above the list (e.g. \"Riepilogo delle\n * opinioni — i pazienti raccontano…\"). Render-prop slot so the consumer\n * can drop in `<Alert>` / `<Callout>` / plain `<p>`.\n */\n introSlot?: ReactNode;\n\n /** Optional header right-end action (e.g. a \"Leave a review\" button). */\n headerActionSlot?: ReactNode;\n\n /** Override the default region aria-label. */\n 'aria-label'?: string;\n\n /** Agent-readiness id. */\n id?: string;\n\n className?: string;\n\n /**\n * Hide the filter sidebar — useful when the panel mounts under ~640px\n * width or inside a narrow column. Defaults to false.\n */\n hideFilter?: boolean;\n\n /**\n * @default 'flat'\n * `'elevated'` wraps the panel in the equivalent of `Card variant=\"elevated\"`\n * — same tokens (`--card`, `--card-border`, `--shadow-card`,\n * `--radius-lg`), same padding. Use to drop the consumer-side\n * `<Card><Card.Body>` wrap.\n */\n surface?: 'flat' | 'elevated';\n\n /** Render skeletons for the header and the list. */\n loading?: boolean;\n}\n\n/* -------------------------------------------------------------------- */\n/* Constants & helpers */\n/* -------------------------------------------------------------------- */\n\nconst FILTER_VALUES: readonly ReviewFilter[] = ['all', 5, 4, 3, 2, 1] as const;\n\nfunction bucketOf(score: number): 1 | 2 | 3 | 4 | 5 {\n const n = Math.min(5, Math.max(1, Math.floor(score)));\n return n as 1 | 2 | 3 | 4 | 5;\n}\n\nfunction deriveHistogram(\n reviews: ReviewItem[],\n): Record<1 | 2 | 3 | 4 | 5, number> {\n const out: Record<1 | 2 | 3 | 4 | 5, number> = {\n 1: 0,\n 2: 0,\n 3: 0,\n 4: 0,\n 5: 0,\n };\n for (const r of reviews) out[bucketOf(r.overallScore)] += 1;\n return out;\n}\n\n/* -------------------------------------------------------------------- */\n/* FilterSidebar — hand-rolled radiogroup with roving tabindex */\n/* */\n/* Mirrors `ServiceGrid` in `booking.tsx`: the WAI-ARIA RadioGroup */\n/* pattern with full-row click target + icon column. NOT Radix */\n/* `RadioGroup` because we need a row layout with a non-text leading */\n/* glyph (the star ramp) and a trailing count. */\n/* */\n/* Activation is automatic — Arrow keys move BOTH focus and selection, */\n/* so a keyboard user feels the same responsiveness as a mouse user. */\n/* That's the right call here because filter activation is cheap (re- */\n/* renders the visible list); contrast with the booking ServiceGrid */\n/* where activation cascades through downstream selection state. */\n/* -------------------------------------------------------------------- */\n\nconst filterRowVariants = cva(\n [\n 'ds:relative ds:flex ds:w-full ds:items-center ds:justify-between',\n 'ds:gap-[var(--spacing-sm)]',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)]',\n 'ds:pt-[var(--spacing-2xs)] ds:pb-[var(--spacing-2xs)]',\n 'ds:text-start',\n 'ds:transition-[background-color,color]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:cursor-pointer',\n ].join(' '),\n {\n variants: {\n state: {\n idle: 'ds:hover:bg-[var(--muted)]/30 ds:text-[var(--foreground)]',\n selected: [\n 'ds:bg-[var(--primary)]',\n 'ds:text-[var(--primary-foreground)]',\n 'ds:font-[var(--font-weight-semibold)]',\n ].join(' '),\n },\n },\n defaultVariants: { state: 'idle' },\n },\n);\n\ninterface FilterSidebarProps {\n filter: ReviewFilter;\n histogram: Record<1 | 2 | 3 | 4 | 5, number>;\n totalCount: number;\n onChange: (next: ReviewFilter) => void;\n}\n\nfunction FilterSidebar({\n filter,\n histogram,\n totalCount,\n onChange,\n}: FilterSidebarProps) {\n const { t } = useTranslation();\n const labelId = useId();\n\n // Roving tabindex tracks where keyboard focus is. We use AUTOMATIC\n // activation: arrow keys move focus AND fire onChange (see the rationale\n // above the component). To keep the visible aria-checked row and the\n // tab-stop aligned when the parent changes `filter` externally (e.g.\n // from a `?filter=4` URL parameter on page load), sync focusIndex to\n // the current filter via the effect below — without it, the tab stop\n // could land on `All` while the highlight shows `4`.\n const [focusIndex, setFocusIndex] = useState<number>(() =>\n Math.max(0, FILTER_VALUES.indexOf(filter)),\n );\n useEffect(() => {\n const i = FILTER_VALUES.indexOf(filter);\n if (i >= 0) setFocusIndex(i);\n }, [filter]);\n\n const rowRefs = useRef<Map<number, HTMLButtonElement>>(new Map());\n const focusAt = useCallback((index: number) => {\n rowRefs.current.get(index)?.focus();\n }, []);\n\n const total = FILTER_VALUES.length;\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight': {\n event.preventDefault();\n const next = (index + 1) % total;\n setFocusIndex(next);\n focusAt(next);\n onChange(FILTER_VALUES[next]);\n break;\n }\n case 'ArrowUp':\n case 'ArrowLeft': {\n event.preventDefault();\n const prev = (index - 1 + total) % total;\n setFocusIndex(prev);\n focusAt(prev);\n onChange(FILTER_VALUES[prev]);\n break;\n }\n case 'Home': {\n event.preventDefault();\n setFocusIndex(0);\n focusAt(0);\n onChange(FILTER_VALUES[0]);\n break;\n }\n case 'End': {\n event.preventDefault();\n const last = total - 1;\n setFocusIndex(last);\n focusAt(last);\n onChange(FILTER_VALUES[last]);\n break;\n }\n case 'Enter':\n case ' ': {\n event.preventDefault();\n onChange(FILTER_VALUES[index]);\n break;\n }\n default:\n break;\n }\n },\n [total, focusAt, onChange],\n );\n\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <h3\n id={labelId}\n className=\"type-label ds:font-[var(--font-weight-semibold)] ds:text-[var(--foreground)] ds:m-0\"\n >\n {t('reviewsPanel.filterTitle')}\n </h3>\n <div\n role=\"radiogroup\"\n aria-labelledby={labelId}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n {FILTER_VALUES.map((val, index) => {\n const isAll = val === 'all';\n const stars = isAll ? 0 : (val as 1 | 2 | 3 | 4 | 5);\n const count = isAll\n ? totalCount\n : histogram[stars as 1 | 2 | 3 | 4 | 5];\n const isSelected = val === filter;\n const isFocused = focusIndex === index;\n const ariaLabel = isAll\n ? t('reviewsPanel.filter.allLabel', { count })\n : t('reviewsPanel.filter.starsLabel', { stars, count });\n\n return (\n <button\n key={String(val)}\n ref={(node) => {\n if (node) rowRefs.current.set(index, node);\n else rowRefs.current.delete(index);\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n aria-label={ariaLabel}\n tabIndex={isFocused ? 0 : -1}\n onClick={() => {\n setFocusIndex(index);\n onChange(val);\n }}\n onFocus={() => setFocusIndex(index)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n data-filter-bucket={String(val)}\n className={filterRowVariants({\n state: isSelected ? 'selected' : 'idle',\n })}\n >\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n {isAll ? (\n <span className=\"type-label\">\n {t('reviewsPanel.filter.allShort')}\n </span>\n ) : (\n <Rating value={stars} size=\"sm\" decorative />\n )}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"type-meta ds:text-[var(--muted-foreground)]\"\n >\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* ReviewCard — compact card with optional Collapsible breakdown */\n/* -------------------------------------------------------------------- */\n\ninterface ReviewCardProps {\n review: ReviewItem;\n locale: string;\n}\n\nfunction ReviewCard({ review, locale }: ReviewCardProps) {\n const { t } = useTranslation();\n const triggerId = useId();\n const contentId = useId();\n const [open, setOpen] = useState(false);\n\n const breakdownHasValues = Boolean(\n review.breakdown && Object.values(review.breakdown).some((v) => v != null),\n );\n const hasDetails =\n Boolean(review.reply) ||\n breakdownHasValues ||\n Boolean(review.futureVisit) ||\n Boolean(review.recommendation);\n\n const formattedDate = useMemo(() => {\n if (!review.authoredAt) return '';\n try {\n const parsed = new Date(review.authoredAt);\n if (Number.isNaN(parsed.getTime())) return review.authoredAt;\n return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(\n parsed,\n );\n } catch {\n return review.authoredAt;\n }\n }, [review.authoredAt, locale]);\n\n const pseudonym = review.pseudonym ?? t('reviewsPanel.anonymous');\n\n // Compose the full Card body. The Collapsible wraps the footer + the\n // hidden content so both share the same Radix Root and the Trigger's\n // `data-state` reaches the Content automatically (used for the height\n // animation). The Footer + Content are direct Card children so the\n // Card root's flex-col places them in order.\n const footerAndDetails = (\n <Collapsible\n open={hasDetails ? open : false}\n onOpenChange={hasDetails ? setOpen : undefined}\n >\n <Card.Footer>\n <span className=\"type-meta ds:text-[var(--muted-foreground)]\">\n {formattedDate}\n </span>\n {hasDetails ? (\n <CollapsibleTrigger asChild>\n <button\n id={triggerId}\n type=\"button\"\n data-review-trigger\n className=\"ds:ms-auto ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)] ds:rounded-[var(--radius-sm)] ds:ps-[var(--spacing-2xs)] ds:pe-[var(--spacing-2xs)] ds:pt-[var(--spacing-2xs)] ds:pb-[var(--spacing-2xs)] ds:bg-transparent ds:border-0 ds:text-[length:var(--font-size-sm)] ds:text-[var(--primary)] ds:hover:underline ds:cursor-pointer ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]\"\n >\n {open\n ? t('reviewsPanel.card.collapse')\n : t('reviewsPanel.card.expand')}\n <ChevronDown\n aria-hidden=\"true\"\n className={[\n 'ds:size-4 ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n open ? 'ds:rotate-180' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n />\n </button>\n </CollapsibleTrigger>\n ) : null}\n </Card.Footer>\n {hasDetails ? (\n <CollapsibleContent id={contentId} aria-labelledby={triggerId}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\">\n {breakdownHasValues ||\n review.futureVisit ||\n review.recommendation ? (\n <DescriptionList layout=\"inline\" density=\"compact\">\n {review.breakdown?.appointmentScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.appointment')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating\n value={review.breakdown.appointmentScore}\n size=\"sm\"\n />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.communicationScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.communication')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating\n value={review.breakdown.communicationScore}\n size=\"sm\"\n />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.staffScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.staff')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating value={review.breakdown.staffScore} size=\"sm\" />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.overallScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.overall')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating value={review.breakdown.overallScore} size=\"sm\" />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.futureVisit ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.futureVisit')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n {t(`reviewsPanel.answer.${review.futureVisit}`)}\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.recommendation ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.recommendation')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n {t(`reviewsPanel.answer.${review.recommendation}`)}\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n </DescriptionList>\n ) : null}\n {review.reply ? (\n <div className=\"ds:rounded-[var(--radius-sm)] ds:bg-[var(--muted)]/40 ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]\">\n <p className=\"type-label ds:m-0 ds:mb-[var(--spacing-2xs)] ds:text-[var(--muted-foreground)] ds:font-[var(--font-weight-semibold)]\">\n {t('reviewsPanel.replyTitle')}\n </p>\n <p className=\"type-body-sm ds:m-0 ds:text-[var(--foreground)] ds:[white-space:pre-line]\">\n {review.reply}\n </p>\n </div>\n ) : null}\n </div>\n </CollapsibleContent>\n ) : null}\n </Collapsible>\n );\n\n return (\n <Card variant=\"default\" className=\"ds:shadow-[var(--shadow-sm)]\">\n <Card.Header>\n <div className=\"ds:flex ds:items-start ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <h4 className=\"type-title-item ds:m-0 ds:text-[var(--foreground)]\">\n {pseudonym}\n </h4>\n <Rating value={review.overallScore} size=\"sm\" />\n </div>\n </Card.Header>\n {review.comment ? (\n <Card.Body>\n <p className=\"type-body ds:m-0 ds:text-[var(--foreground)] ds:line-clamp-2\">\n {review.comment}\n </p>\n </Card.Body>\n ) : null}\n {footerAndDetails}\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* PanelSkeleton — loading state */\n/* -------------------------------------------------------------------- */\n\nfunction PanelSkeleton({ hideFilter }: { hideFilter: boolean }) {\n // The parent <section> already carries `aria-busy=\"true\"`, which is the\n // canonical SR signal for \"this region is loading\". Don't add `role=status`\n // + `aria-live=polite` here too — pairing both would double-announce on\n // mount. Skeleton itself is `aria-hidden`.\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <Skeleton variant=\"text\" width=\"40%\" />\n <Skeleton variant=\"text\" width=\"20%\" />\n </div>\n <div\n className={\n hideFilter\n ? 'ds:flex ds:flex-col ds:gap-[var(--spacing-md)]'\n : 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:md:grid-cols-[minmax(0,30%)_minmax(0,1fr)]'\n }\n >\n {!hideFilter ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {Array.from({ length: 6 }, (_, i) => (\n <Skeleton key={`rp-fs-${i}`} variant=\"rounded\" height=\"2.5rem\" />\n ))}\n </div>\n ) : null}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {Array.from({ length: 3 }, (_, i) => (\n <Skeleton key={`rp-rs-${i}`} variant=\"rounded\" height=\"6rem\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* Root variants */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n 'ds:flex ds:w-full ds:flex-col ds:gap-[var(--spacing-md)] ds:text-[var(--foreground)]',\n {\n variants: {\n // `surface=\"elevated\"` matches `Card variant=\"elevated\"` — same\n // tokens, same padding, same boundary behaviour — so consumers can\n // drop their own Card wraps without producing a double-card look.\n // Default `'flat'` preserves 0.30.x rendering (no wrapping).\n surface: {\n flat: '',\n elevated: [\n 'ds:rounded-[var(--radius-lg)]',\n 'ds:bg-[var(--card)] ds:text-[var(--card-foreground)]',\n 'ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-card)]',\n 'ds:p-[var(--spacing-md)]',\n ].join(' '),\n },\n },\n defaultVariants: { surface: 'flat' },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* ReviewsPanel */\n/* -------------------------------------------------------------------- */\n\nexport const ReviewsPanel = forwardRef<HTMLElement, ReviewsPanelProps>(\n (\n {\n reviews,\n aggregate,\n filter: filterProp,\n onFilterChange,\n introSlot,\n headerActionSlot,\n 'aria-label': ariaLabel,\n id,\n className,\n hideFilter = false,\n surface = 'flat',\n loading = false,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const locale = i18n.language ?? 'en';\n\n // Controlled / uncontrolled filter handling. When `filterProp` is\n // undefined the panel manages the filter internally — the consumer can\n // mount the panel and never wire `filter` / `onFilterChange` for the\n // common case where the URL doesn't carry the filter state. When the\n // prop IS provided, the consumer owns the truth.\n const [internalFilter, setInternalFilter] = useState<ReviewFilter>('all');\n const filter = filterProp ?? internalFilter;\n\n const handleFilterChange = useCallback(\n (next: ReviewFilter) => {\n if (filterProp === undefined) setInternalFilter(next);\n onFilterChange?.(next);\n },\n [filterProp, onFilterChange],\n );\n\n // Prefer the consumer-provided histogram; fall back to deriving from the\n // loaded reviews. Pagination consumers SHOULD pass an explicit histogram\n // so the per-bucket counts reflect server-side totals rather than only\n // what's loaded on this page.\n const histogram = useMemo(\n () => aggregate.histogram ?? deriveHistogram(reviews),\n [aggregate.histogram, reviews],\n );\n\n const visibleReviews = useMemo(() => {\n if (filter === 'all') return reviews;\n return reviews.filter((r) => bucketOf(r.overallScore) === filter);\n }, [reviews, filter]);\n\n /* Agent registration — exposes filter + visible count via refs so the\n imperative handle returns the latest values without re-creating the\n handle on every render. */\n const latestFilterRef = useRef(filter);\n latestFilterRef.current = filter;\n const latestVisibleCountRef = useRef(visibleReviews.length);\n latestVisibleCountRef.current = visibleReviews.length;\n\n const agentHandle = useMemo<ReviewsPanelHandle>(\n () => ({\n getFilter: () => latestFilterRef.current,\n setFilter: (next) => handleFilterChange(next),\n getVisibleCount: () => latestVisibleCountRef.current,\n }),\n [handleFilterChange],\n );\n\n const rootRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLElement, []);\n useAgentRegistration(reviewsPanelAgent, agentHandle, id);\n\n const resolvedAriaLabel = ariaLabel ?? t('reviewsPanel.regionLabel');\n const dataFilter = String(filter);\n\n /* ---------------------------- Loading ---------------------------- */\n if (loading) {\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n aria-busy=\"true\"\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n data-state=\"loading\"\n className={rootVariants({ surface, className })}\n {...rest}\n >\n <PanelSkeleton hideFilter={hideFilter} />\n </section>\n );\n }\n\n /* ----------------------- Zero-total empty ------------------------ */\n // No header, no filter — just the EmptyState. The region landmark is\n // still emitted so an agent can locate the empty panel by its\n // data-component marker.\n if (aggregate.count === 0 && reviews.length === 0) {\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n data-state=\"empty\"\n className={rootVariants({ surface, className })}\n {...rest}\n >\n <EmptyState\n variant=\"no-results\"\n title={t('reviewsPanel.empty.title')}\n description={t('reviewsPanel.empty.description')}\n />\n </section>\n );\n }\n\n const showFilterSidebar = !hideFilter;\n\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n className={rootVariants({ surface, className })}\n {...rest}\n >\n {/* Header row — plain <div>, not <header>: the panel root is a\n region landmark with an accessible name, so a nested <header>\n would emit a banner-inside-region pair that axe's\n landmark-banner-is-top-level rule flags. */}\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <h2 className=\"type-title-section ds:m-0 ds:text-[var(--foreground)]\">\n {t('reviewsPanel.title')}\n </h2>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-md)]\">\n {headerActionSlot}\n <Rating\n value={aggregate.averageScore}\n reviews={{ count: aggregate.count }}\n size=\"md\"\n />\n </div>\n </div>\n\n <div\n className={\n showFilterSidebar\n ? 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:md:grid-cols-[minmax(0,30%)_minmax(0,1fr)]'\n : 'ds:flex ds:flex-col ds:gap-[var(--spacing-md)]'\n }\n >\n {showFilterSidebar ? (\n <FilterSidebar\n filter={filter}\n histogram={histogram}\n totalCount={aggregate.count}\n onChange={handleFilterChange}\n />\n ) : null}\n\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:min-w-0\">\n {introSlot ? (\n // Plain div, not <aside>: axe's\n // `landmark-complementary-is-top-level` rule fires when an\n // `<aside>` lands inside another landmark (this panel's\n // <section role=\"region\">), and the consumer's introSlot\n // already supplies its own semantics (e.g. an Alert with\n // role=\"status\").\n <div className=\"ds:rounded-[var(--radius-md)] ds:bg-[color-mix(in_srgb,var(--info)_8%,transparent)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\">\n {introSlot}\n </div>\n ) : null}\n\n {visibleReviews.length === 0 ? (\n // `role=\"status\"` already implies `aria-live=\"polite\"` —\n // declaring both would queue two announcements when the\n // filter changes (the radio's aria-checked flip + this\n // container's text). Keeping `role` only.\n <div\n role=\"status\"\n data-state=\"no-match\"\n className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)] ds:rounded-[var(--radius-md)] ds:border ds:border-dashed ds:border-[var(--border)] ds:p-[var(--spacing-lg)] ds:text-center\"\n >\n <p className=\"type-body ds:m-0 ds:text-[var(--muted-foreground)]\">\n {filter === 'all'\n ? t('reviewsPanel.empty.title')\n : t('reviewsPanel.empty.filterDescription', {\n stars: filter,\n })}\n </p>\n {filter !== 'all' ? (\n <Button\n intent=\"secondary\"\n size=\"sm\"\n onClick={() => handleFilterChange('all')}\n >\n {t('reviewsPanel.empty.resetFilter')}\n </Button>\n ) : null}\n </div>\n ) : (\n <ol\n aria-label={t('reviewsPanel.listLabel')}\n className=\"ds:list-none ds:m-0 ds:p-0 ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {visibleReviews.map((review) => (\n <li key={review.id} data-review-id={review.id}>\n <ReviewCard review={review} locale={locale} />\n </li>\n ))}\n </ol>\n )}\n </div>\n </div>\n </section>\n );\n },\n);\n\nReviewsPanel.displayName = 'ReviewsPanel';\n"],"names":["reviewsPanelAgent","handle","args","FILTER_VALUES","bucketOf","score","deriveHistogram","reviews","out","r","filterRowVariants","cva","FilterSidebar","filter","histogram","totalCount","onChange","t","useTranslation","labelId","useId","focusIndex","setFocusIndex","useState","useEffect","i","rowRefs","useRef","focusAt","useCallback","index","_a","total","handleKeyDown","event","next","prev","last","jsxs","jsx","val","isAll","stars","count","isSelected","isFocused","ariaLabel","node","e","Rating","ReviewCard","review","locale","triggerId","contentId","open","setOpen","breakdownHasValues","hasDetails","formattedDate","useMemo","parsed","pseudonym","footerAndDetails","Collapsible","Card","CollapsibleTrigger","ChevronDown","CollapsibleContent","DescriptionList","_b","_c","_d","PanelSkeleton","hideFilter","Skeleton","_","rootVariants","ReviewsPanel","forwardRef","aggregate","filterProp","onFilterChange","introSlot","headerActionSlot","id","className","surface","loading","rest","ref","i18n","internalFilter","setInternalFilter","handleFilterChange","visibleReviews","latestFilterRef","latestVisibleCountRef","agentHandle","rootRef","useImperativeHandle","useAgentRegistration","resolvedAriaLabel","dataFilter","EmptyState","showFilterSidebar","Button"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"reviews-panel-bKEUKSic.js","sources":["../../src/components/reviews-panel/reviews-panel.agent.ts","../../src/components/reviews-panel/reviews-panel.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — ReviewsPanel. */\n/* */\n/* Replaces the legacy patient-reviews Carousel with a single panel that */\n/* surfaces an aggregate score, a star-bucket filter, and a vertical list */\n/* of compact review cards. The adapter exposes the active filter and the */\n/* visible review count so a host agent can drive the panel and observe */\n/* what the user actually sees after the filter is applied. The reviews */\n/* themselves remain owned by the consumer — they never flow through the */\n/* adapter (PHI: pseudonyms + free-text comments stay on the page). */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { ReviewFilter, ReviewsPanelHandle } from './reviews-panel';\n\nexport const reviewsPanelAgent: AgentAdapter<ReviewsPanelHandle> = {\n id: 'reviews-panel',\n capabilities: ['filter'],\n state: {\n filter: {\n type: 'string',\n descriptionKey: 'ui.agent.reviewsPanel.state.filter',\n description:\n 'Active star-bucket filter — `\"all\"` or an integer 1..5. Reads the controlled `filter` prop.',\n read: (handle) => handle.getFilter(),\n },\n visibleCount: {\n type: 'number',\n descriptionKey: 'ui.agent.reviewsPanel.state.visibleCount',\n description:\n 'Number of reviews currently rendered after the filter is applied.',\n read: (handle) => handle.getVisibleCount(),\n },\n },\n actions: {\n set_filter: {\n safety: 'read',\n argsType: '{ filter: \"all\" | 1 | 2 | 3 | 4 | 5 }',\n descriptionKey: 'ui.agent.reviewsPanel.actions.setFilter',\n description:\n 'Narrow the visible review list to a star bucket, or `\"all\"` to clear the filter.',\n invoke: (handle, args: { filter: ReviewFilter }) => {\n handle.setFilter(args.filter);\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'reviews-panel',\n description: 'Marks the ReviewsPanel root region.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { ChevronDown } from 'lucide-react';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { reviewsPanelAgent } from './reviews-panel.agent';\nimport { Rating } from '../rating/rating';\nimport { Card } from '../card/card';\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '../collapsible/collapsible';\nimport { DescriptionList } from '../description-list/description-list';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { Skeleton } from '../skeleton/skeleton';\nimport { Button } from '../button/button';\n\n/* -------------------------------------------------------------------- */\n/* ReviewsPanel */\n/* */\n/* Replaces the legacy patient-reviews Carousel with a two-column panel */\n/* — a star-bucket filter sidebar + a vertical list of compact review */\n/* cards — that surfaces the aggregate score, the volume, and (via a */\n/* per-card Collapsible) the rich per-axis breakdown alfaDocs already */\n/* collects but the carousel buried. */\n/* -------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------- */\n/* Public types */\n/* -------------------------------------------------------------------- */\n\nexport interface ReviewItem {\n id: string;\n /** When the review was authored (ISO 8601). */\n authoredAt: string;\n /** Anonymised author display (\"Marco T.\", \"Anna_M\"). Falls back to a localised \"Anonymous\". */\n pseudonym?: string;\n /** 0..max — drives the visible star summary on the compact card. */\n overallScore: number;\n /** Short free-text comment shown on the compact card. */\n comment?: string;\n /** Practice reply (revealed in the expanded panel only). */\n reply?: string;\n /** Optional per-axis breakdown — when present, the card grows an\n * \"Expand\" affordance that reveals these in a DescriptionList. */\n breakdown?: {\n appointmentScore?: number;\n communicationScore?: number;\n staffScore?: number;\n overallScore?: number;\n };\n /** Optional categorical follow-ups, surfaced inside the breakdown. */\n futureVisit?: 'yes' | 'no' | 'unsure';\n recommendation?: 'yes' | 'no' | 'unsure';\n}\n\nexport interface ReviewsAggregate {\n /** Total review count across all star buckets (may exceed the loaded set when paginated). */\n count: number;\n /** Average score across the full population (drives the header `Rating`). */\n averageScore: number;\n /**\n * Optional histogram — per-bucket count keyed by integer star value 1..5.\n * When omitted, the panel derives buckets from the loaded `reviews` array\n * (best-effort: paginated consumers SHOULD provide this explicitly).\n */\n histogram?: Record<1 | 2 | 3 | 4 | 5, number>;\n}\n\nexport type ReviewFilter = 'all' | 1 | 2 | 3 | 4 | 5;\n\nexport interface ReviewsPanelHandle {\n getFilter: () => ReviewFilter;\n setFilter: (next: ReviewFilter) => void;\n /** Total visible after filter is applied. */\n getVisibleCount: () => number;\n}\n\nexport interface ReviewsPanelProps extends Omit<\n ComponentPropsWithoutRef<'section'>,\n 'aria-label' | 'children'\n> {\n reviews: ReviewItem[];\n aggregate: ReviewsAggregate;\n\n /**\n * Active star filter. Controlled — consumer owns it for shareability via\n * URL query strings. Omit for uncontrolled (panel-managed) filtering.\n */\n filter?: ReviewFilter;\n onFilterChange?: (next: ReviewFilter) => void;\n\n /**\n * Optional intro callout shown above the list (e.g. \"Riepilogo delle\n * opinioni — i pazienti raccontano…\"). Render-prop slot so the consumer\n * can drop in `<Alert>` / `<Callout>` / plain `<p>`.\n */\n introSlot?: ReactNode;\n\n /** Optional header right-end action (e.g. a \"Leave a review\" button). */\n headerActionSlot?: ReactNode;\n\n /** Override the default region aria-label. */\n 'aria-label'?: string;\n\n /** Agent-readiness id. */\n id?: string;\n\n className?: string;\n\n /**\n * Hide the filter sidebar — useful when the panel mounts under ~640px\n * width or inside a narrow column. Defaults to false.\n */\n hideFilter?: boolean;\n\n /**\n * @default 'flat'\n * `'elevated'` wraps the panel in the equivalent of `Card variant=\"elevated\"`\n * — same tokens (`--card`, `--card-border`, `--shadow-card`,\n * `--radius-lg`), same padding. Use to drop the consumer-side\n * `<Card><Card.Body>` wrap.\n */\n surface?: 'flat' | 'elevated';\n\n /** Render skeletons for the header and the list. */\n loading?: boolean;\n}\n\n/* -------------------------------------------------------------------- */\n/* Constants & helpers */\n/* -------------------------------------------------------------------- */\n\nconst FILTER_VALUES: readonly ReviewFilter[] = ['all', 5, 4, 3, 2, 1] as const;\n\nfunction bucketOf(score: number): 1 | 2 | 3 | 4 | 5 {\n const n = Math.min(5, Math.max(1, Math.floor(score)));\n return n as 1 | 2 | 3 | 4 | 5;\n}\n\nfunction deriveHistogram(\n reviews: ReviewItem[],\n): Record<1 | 2 | 3 | 4 | 5, number> {\n const out: Record<1 | 2 | 3 | 4 | 5, number> = {\n 1: 0,\n 2: 0,\n 3: 0,\n 4: 0,\n 5: 0,\n };\n for (const r of reviews) out[bucketOf(r.overallScore)] += 1;\n return out;\n}\n\n/* -------------------------------------------------------------------- */\n/* FilterSidebar — hand-rolled radiogroup with roving tabindex */\n/* */\n/* Mirrors `ServiceGrid` in `booking.tsx`: the WAI-ARIA RadioGroup */\n/* pattern with full-row click target + icon column. NOT Radix */\n/* `RadioGroup` because we need a row layout with a non-text leading */\n/* glyph (the star ramp) and a trailing count. */\n/* */\n/* Activation is automatic — Arrow keys move BOTH focus and selection, */\n/* so a keyboard user feels the same responsiveness as a mouse user. */\n/* That's the right call here because filter activation is cheap (re- */\n/* renders the visible list); contrast with the booking ServiceGrid */\n/* where activation cascades through downstream selection state. */\n/* -------------------------------------------------------------------- */\n\nconst filterRowVariants = cva(\n [\n 'ds:relative ds:flex ds:w-full ds:items-center ds:justify-between',\n 'ds:gap-[var(--spacing-sm)]',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)]',\n 'ds:pt-[var(--spacing-2xs)] ds:pb-[var(--spacing-2xs)]',\n 'ds:text-start',\n 'ds:transition-[background-color,color]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:cursor-pointer',\n ].join(' '),\n {\n variants: {\n state: {\n idle: 'ds:hover:bg-[var(--muted)]/30 ds:text-[var(--foreground)]',\n selected: [\n 'ds:bg-[var(--primary)]',\n 'ds:text-[var(--primary-foreground)]',\n 'ds:font-[var(--font-weight-semibold)]',\n ].join(' '),\n },\n },\n defaultVariants: { state: 'idle' },\n },\n);\n\ninterface FilterSidebarProps {\n filter: ReviewFilter;\n histogram: Record<1 | 2 | 3 | 4 | 5, number>;\n totalCount: number;\n onChange: (next: ReviewFilter) => void;\n}\n\nfunction FilterSidebar({\n filter,\n histogram,\n totalCount,\n onChange,\n}: FilterSidebarProps) {\n const { t } = useTranslation();\n const labelId = useId();\n\n // Roving tabindex tracks where keyboard focus is. We use AUTOMATIC\n // activation: arrow keys move focus AND fire onChange (see the rationale\n // above the component). To keep the visible aria-checked row and the\n // tab-stop aligned when the parent changes `filter` externally (e.g.\n // from a `?filter=4` URL parameter on page load), sync focusIndex to\n // the current filter via the effect below — without it, the tab stop\n // could land on `All` while the highlight shows `4`.\n const [focusIndex, setFocusIndex] = useState<number>(() =>\n Math.max(0, FILTER_VALUES.indexOf(filter)),\n );\n useEffect(() => {\n const i = FILTER_VALUES.indexOf(filter);\n if (i >= 0) setFocusIndex(i);\n }, [filter]);\n\n const rowRefs = useRef<Map<number, HTMLButtonElement>>(new Map());\n const focusAt = useCallback((index: number) => {\n rowRefs.current.get(index)?.focus();\n }, []);\n\n const total = FILTER_VALUES.length;\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>, index: number) => {\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowRight': {\n event.preventDefault();\n const next = (index + 1) % total;\n setFocusIndex(next);\n focusAt(next);\n onChange(FILTER_VALUES[next]);\n break;\n }\n case 'ArrowUp':\n case 'ArrowLeft': {\n event.preventDefault();\n const prev = (index - 1 + total) % total;\n setFocusIndex(prev);\n focusAt(prev);\n onChange(FILTER_VALUES[prev]);\n break;\n }\n case 'Home': {\n event.preventDefault();\n setFocusIndex(0);\n focusAt(0);\n onChange(FILTER_VALUES[0]);\n break;\n }\n case 'End': {\n event.preventDefault();\n const last = total - 1;\n setFocusIndex(last);\n focusAt(last);\n onChange(FILTER_VALUES[last]);\n break;\n }\n case 'Enter':\n case ' ': {\n event.preventDefault();\n onChange(FILTER_VALUES[index]);\n break;\n }\n default:\n break;\n }\n },\n [total, focusAt, onChange],\n );\n\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n <h3\n id={labelId}\n className=\"type-label ds:font-[var(--font-weight-semibold)] ds:text-[var(--foreground)] ds:m-0\"\n >\n {t('reviewsPanel.filterTitle')}\n </h3>\n <div\n role=\"radiogroup\"\n aria-labelledby={labelId}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n {FILTER_VALUES.map((val, index) => {\n const isAll = val === 'all';\n const stars = isAll ? 0 : (val as 1 | 2 | 3 | 4 | 5);\n const count = isAll\n ? totalCount\n : histogram[stars as 1 | 2 | 3 | 4 | 5];\n const isSelected = val === filter;\n const isFocused = focusIndex === index;\n const ariaLabel = isAll\n ? t('reviewsPanel.filter.allLabel', { count })\n : t('reviewsPanel.filter.starsLabel', { stars, count });\n\n return (\n <button\n key={String(val)}\n ref={(node) => {\n if (node) rowRefs.current.set(index, node);\n else rowRefs.current.delete(index);\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n aria-label={ariaLabel}\n tabIndex={isFocused ? 0 : -1}\n onClick={() => {\n setFocusIndex(index);\n onChange(val);\n }}\n onFocus={() => setFocusIndex(index)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n data-filter-bucket={String(val)}\n className={filterRowVariants({\n state: isSelected ? 'selected' : 'idle',\n })}\n >\n <span\n aria-hidden=\"true\"\n className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\"\n >\n {isAll ? (\n <span className=\"type-label\">\n {t('reviewsPanel.filter.allShort')}\n </span>\n ) : (\n <Rating value={stars} size=\"sm\" decorative />\n )}\n </span>\n <span\n aria-hidden=\"true\"\n className=\"type-meta ds:text-[var(--muted-foreground)]\"\n >\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* ReviewCard — compact card with optional Collapsible breakdown */\n/* -------------------------------------------------------------------- */\n\ninterface ReviewCardProps {\n review: ReviewItem;\n locale: string;\n}\n\nfunction ReviewCard({ review, locale }: ReviewCardProps) {\n const { t } = useTranslation();\n const triggerId = useId();\n const contentId = useId();\n const [open, setOpen] = useState(false);\n\n const breakdownHasValues = Boolean(\n review.breakdown && Object.values(review.breakdown).some((v) => v != null),\n );\n const hasDetails =\n Boolean(review.reply) ||\n breakdownHasValues ||\n Boolean(review.futureVisit) ||\n Boolean(review.recommendation);\n\n const formattedDate = useMemo(() => {\n if (!review.authoredAt) return '';\n try {\n const parsed = new Date(review.authoredAt);\n if (Number.isNaN(parsed.getTime())) return review.authoredAt;\n return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(\n parsed,\n );\n } catch {\n return review.authoredAt;\n }\n }, [review.authoredAt, locale]);\n\n const pseudonym = review.pseudonym ?? t('reviewsPanel.anonymous');\n\n // Compose the full Card body. The Collapsible wraps the footer + the\n // hidden content so both share the same Radix Root and the Trigger's\n // `data-state` reaches the Content automatically (used for the height\n // animation). The Footer + Content are direct Card children so the\n // Card root's flex-col places them in order.\n const footerAndDetails = (\n <Collapsible\n open={hasDetails ? open : false}\n onOpenChange={hasDetails ? setOpen : undefined}\n >\n <Card.Footer>\n <span className=\"type-meta ds:text-[var(--muted-foreground)]\">\n {formattedDate}\n </span>\n {hasDetails ? (\n <CollapsibleTrigger asChild>\n <button\n id={triggerId}\n type=\"button\"\n data-review-trigger\n className=\"ds:ms-auto ds:inline-flex ds:items-center ds:gap-[var(--spacing-2xs)] ds:rounded-[var(--radius-sm)] ds:ps-[var(--spacing-2xs)] ds:pe-[var(--spacing-2xs)] ds:pt-[var(--spacing-2xs)] ds:pb-[var(--spacing-2xs)] ds:bg-transparent ds:border-0 ds:text-[length:var(--font-size-sm)] ds:text-[var(--primary)] ds:hover:underline ds:cursor-pointer ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)] ds:forced-colors:focus-visible:outline-[CanvasText]\"\n >\n {open\n ? t('reviewsPanel.card.collapse')\n : t('reviewsPanel.card.expand')}\n <ChevronDown\n aria-hidden=\"true\"\n className={[\n 'ds:size-4 ds:transition-transform ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n open ? 'ds:rotate-180' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n />\n </button>\n </CollapsibleTrigger>\n ) : null}\n </Card.Footer>\n {hasDetails ? (\n <CollapsibleContent id={contentId} aria-labelledby={triggerId}>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\">\n {breakdownHasValues ||\n review.futureVisit ||\n review.recommendation ? (\n <DescriptionList layout=\"inline\" density=\"compact\">\n {review.breakdown?.appointmentScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.appointment')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating\n value={review.breakdown.appointmentScore}\n size=\"sm\"\n />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.communicationScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.communication')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating\n value={review.breakdown.communicationScore}\n size=\"sm\"\n />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.staffScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.staff')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating value={review.breakdown.staffScore} size=\"sm\" />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.breakdown?.overallScore != null ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.overall')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n <Rating value={review.breakdown.overallScore} size=\"sm\" />\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.futureVisit ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.futureVisit')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n {t(`reviewsPanel.answer.${review.futureVisit}`)}\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n {review.recommendation ? (\n <DescriptionList.Row>\n <DescriptionList.Term>\n {t('reviewsPanel.breakdown.recommendation')}\n </DescriptionList.Term>\n <DescriptionList.Detail>\n {t(`reviewsPanel.answer.${review.recommendation}`)}\n </DescriptionList.Detail>\n </DescriptionList.Row>\n ) : null}\n </DescriptionList>\n ) : null}\n {review.reply ? (\n <div className=\"ds:rounded-[var(--radius-sm)] ds:bg-[var(--muted)]/40 ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]\">\n <p className=\"type-label ds:m-0 ds:mb-[var(--spacing-2xs)] ds:text-[var(--muted-foreground)] ds:font-[var(--font-weight-semibold)]\">\n {t('reviewsPanel.replyTitle')}\n </p>\n <p className=\"type-body-sm ds:m-0 ds:text-[var(--foreground)] ds:[white-space:pre-line]\">\n {review.reply}\n </p>\n </div>\n ) : null}\n </div>\n </CollapsibleContent>\n ) : null}\n </Collapsible>\n );\n\n return (\n <Card variant=\"default\" className=\"ds:shadow-[var(--shadow-sm)]\">\n <Card.Header>\n <div className=\"ds:flex ds:items-start ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <h4 className=\"type-title-item ds:m-0 ds:text-[var(--foreground)]\">\n {pseudonym}\n </h4>\n <Rating value={review.overallScore} size=\"sm\" />\n </div>\n </Card.Header>\n {review.comment ? (\n <Card.Body>\n <p className=\"type-body ds:m-0 ds:text-[var(--foreground)] ds:line-clamp-2\">\n {review.comment}\n </p>\n </Card.Body>\n ) : null}\n {footerAndDetails}\n </Card>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* PanelSkeleton — loading state */\n/* -------------------------------------------------------------------- */\n\nfunction PanelSkeleton({ hideFilter }: { hideFilter: boolean }) {\n // The parent <section> already carries `aria-busy=\"true\"`, which is the\n // canonical SR signal for \"this region is loading\". Don't add `role=status`\n // + `aria-live=polite` here too — pairing both would double-announce on\n // mount. Skeleton itself is `aria-hidden`.\n return (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n <div className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <Skeleton variant=\"text\" width=\"40%\" />\n <Skeleton variant=\"text\" width=\"20%\" />\n </div>\n <div\n className={\n hideFilter\n ? 'ds:flex ds:flex-col ds:gap-[var(--spacing-md)]'\n : 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:md:grid-cols-[minmax(0,30%)_minmax(0,1fr)]'\n }\n >\n {!hideFilter ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n {Array.from({ length: 6 }, (_, i) => (\n <Skeleton key={`rp-fs-${i}`} variant=\"rounded\" height=\"2.5rem\" />\n ))}\n </div>\n ) : null}\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {Array.from({ length: 3 }, (_, i) => (\n <Skeleton key={`rp-rs-${i}`} variant=\"rounded\" height=\"6rem\" />\n ))}\n </div>\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------- */\n/* Root variants */\n/* -------------------------------------------------------------------- */\n\nconst rootVariants = cva(\n 'ds:flex ds:w-full ds:flex-col ds:gap-[var(--spacing-md)] ds:text-[var(--foreground)]',\n {\n variants: {\n // `surface=\"elevated\"` matches `Card variant=\"elevated\"` — same\n // tokens, same padding, same boundary behaviour — so consumers can\n // drop their own Card wraps without producing a double-card look.\n // Default `'flat'` preserves 0.30.x rendering (no wrapping).\n surface: {\n flat: '',\n elevated: [\n 'ds:rounded-[var(--radius-lg)]',\n 'ds:bg-[var(--card)] ds:text-[var(--card-foreground)]',\n 'ds:border ds:border-[color:var(--card-border)]',\n 'ds:shadow-[var(--shadow-card)]',\n 'ds:p-[var(--spacing-md)]',\n ].join(' '),\n },\n },\n defaultVariants: { surface: 'flat' },\n },\n);\n\n/* -------------------------------------------------------------------- */\n/* ReviewsPanel */\n/* -------------------------------------------------------------------- */\n\nexport const ReviewsPanel = forwardRef<HTMLElement, ReviewsPanelProps>(\n (\n {\n reviews,\n aggregate,\n filter: filterProp,\n onFilterChange,\n introSlot,\n headerActionSlot,\n 'aria-label': ariaLabel,\n id,\n className,\n hideFilter = false,\n surface = 'flat',\n loading = false,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const locale = i18n.language ?? 'en';\n\n // Controlled / uncontrolled filter handling. When `filterProp` is\n // undefined the panel manages the filter internally — the consumer can\n // mount the panel and never wire `filter` / `onFilterChange` for the\n // common case where the URL doesn't carry the filter state. When the\n // prop IS provided, the consumer owns the truth.\n const [internalFilter, setInternalFilter] = useState<ReviewFilter>('all');\n const filter = filterProp ?? internalFilter;\n\n const handleFilterChange = useCallback(\n (next: ReviewFilter) => {\n if (filterProp === undefined) setInternalFilter(next);\n onFilterChange?.(next);\n },\n [filterProp, onFilterChange],\n );\n\n // Prefer the consumer-provided histogram; fall back to deriving from the\n // loaded reviews. Pagination consumers SHOULD pass an explicit histogram\n // so the per-bucket counts reflect server-side totals rather than only\n // what's loaded on this page.\n const histogram = useMemo(\n () => aggregate.histogram ?? deriveHistogram(reviews),\n [aggregate.histogram, reviews],\n );\n\n const visibleReviews = useMemo(() => {\n if (filter === 'all') return reviews;\n return reviews.filter((r) => bucketOf(r.overallScore) === filter);\n }, [reviews, filter]);\n\n /* Agent registration — exposes filter + visible count via refs so the\n imperative handle returns the latest values without re-creating the\n handle on every render. */\n const latestFilterRef = useRef(filter);\n latestFilterRef.current = filter;\n const latestVisibleCountRef = useRef(visibleReviews.length);\n latestVisibleCountRef.current = visibleReviews.length;\n\n const agentHandle = useMemo<ReviewsPanelHandle>(\n () => ({\n getFilter: () => latestFilterRef.current,\n setFilter: (next) => handleFilterChange(next),\n getVisibleCount: () => latestVisibleCountRef.current,\n }),\n [handleFilterChange],\n );\n\n const rootRef = useRef<HTMLElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLElement, []);\n useAgentRegistration(reviewsPanelAgent, agentHandle, id);\n\n const resolvedAriaLabel = ariaLabel ?? t('reviewsPanel.regionLabel');\n const dataFilter = String(filter);\n\n /* ---------------------------- Loading ---------------------------- */\n if (loading) {\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n aria-busy=\"true\"\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n data-state=\"loading\"\n className={rootVariants({ surface, className })}\n {...rest}\n >\n <PanelSkeleton hideFilter={hideFilter} />\n </section>\n );\n }\n\n /* ----------------------- Zero-total empty ------------------------ */\n // No header, no filter — just the EmptyState. The region landmark is\n // still emitted so an agent can locate the empty panel by its\n // data-component marker.\n if (aggregate.count === 0 && reviews.length === 0) {\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n data-state=\"empty\"\n className={rootVariants({ surface, className })}\n {...rest}\n >\n <EmptyState\n variant=\"no-results\"\n title={t('reviewsPanel.empty.title')}\n description={t('reviewsPanel.empty.description')}\n />\n </section>\n );\n }\n\n const showFilterSidebar = !hideFilter;\n\n return (\n <section\n ref={rootRef as React.Ref<HTMLElement>}\n aria-label={resolvedAriaLabel}\n id={id}\n data-component=\"reviews-panel\"\n data-component-id={id}\n data-filter={dataFilter}\n className={rootVariants({ surface, className })}\n {...rest}\n >\n {/* Header row — plain <div>, not <header>: the panel root is a\n region landmark with an accessible name, so a nested <header>\n would emit a banner-inside-region pair that axe's\n landmark-banner-is-top-level rule flags. */}\n <div className=\"ds:flex ds:flex-wrap ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <h2 className=\"type-title-section ds:m-0 ds:text-[var(--foreground)]\">\n {t('reviewsPanel.title')}\n </h2>\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-md)]\">\n {headerActionSlot}\n <Rating\n value={aggregate.averageScore}\n reviews={{ count: aggregate.count }}\n size=\"md\"\n />\n </div>\n </div>\n\n <div\n className={\n showFilterSidebar\n ? 'ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-md)] ds:md:grid-cols-[minmax(0,30%)_minmax(0,1fr)]'\n : 'ds:flex ds:flex-col ds:gap-[var(--spacing-md)]'\n }\n >\n {showFilterSidebar ? (\n <FilterSidebar\n filter={filter}\n histogram={histogram}\n totalCount={aggregate.count}\n onChange={handleFilterChange}\n />\n ) : null}\n\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)] ds:min-w-0\">\n {introSlot ? (\n // Plain div, not <aside>: axe's\n // `landmark-complementary-is-top-level` rule fires when an\n // `<aside>` lands inside another landmark (this panel's\n // <section role=\"region\">), and the consumer's introSlot\n // already supplies its own semantics (e.g. an Alert with\n // role=\"status\").\n <div className=\"ds:rounded-[var(--radius-md)] ds:bg-[color-mix(in_srgb,var(--info)_8%,transparent)] ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:pt-[var(--spacing-md)] ds:pb-[var(--spacing-md)]\">\n {introSlot}\n </div>\n ) : null}\n\n {visibleReviews.length === 0 ? (\n // `role=\"status\"` already implies `aria-live=\"polite\"` —\n // declaring both would queue two announcements when the\n // filter changes (the radio's aria-checked flip + this\n // container's text). Keeping `role` only.\n <div\n role=\"status\"\n data-state=\"no-match\"\n className=\"ds:flex ds:flex-col ds:items-center ds:gap-[var(--spacing-sm)] ds:rounded-[var(--radius-md)] ds:border ds:border-dashed ds:border-[var(--border)] ds:p-[var(--spacing-lg)] ds:text-center\"\n >\n <p className=\"type-body ds:m-0 ds:text-[var(--muted-foreground)]\">\n {filter === 'all'\n ? t('reviewsPanel.empty.title')\n : t('reviewsPanel.empty.filterDescription', {\n stars: filter,\n })}\n </p>\n {filter !== 'all' ? (\n <Button\n intent=\"secondary\"\n size=\"sm\"\n onClick={() => handleFilterChange('all')}\n >\n {t('reviewsPanel.empty.resetFilter')}\n </Button>\n ) : null}\n </div>\n ) : (\n <ol\n aria-label={t('reviewsPanel.listLabel')}\n className=\"ds:list-none ds:m-0 ds:p-0 ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {visibleReviews.map((review) => (\n <li key={review.id} data-review-id={review.id}>\n <ReviewCard review={review} locale={locale} />\n </li>\n ))}\n </ol>\n )}\n </div>\n </div>\n </section>\n );\n },\n);\n\nReviewsPanel.displayName = 'ReviewsPanel';\n"],"names":["reviewsPanelAgent","handle","args","FILTER_VALUES","bucketOf","score","deriveHistogram","reviews","out","r","filterRowVariants","cva","FilterSidebar","filter","histogram","totalCount","onChange","t","useTranslation","labelId","useId","focusIndex","setFocusIndex","useState","useEffect","i","rowRefs","useRef","focusAt","useCallback","index","_a","total","handleKeyDown","event","next","prev","last","jsxs","jsx","val","isAll","stars","count","isSelected","isFocused","ariaLabel","node","e","Rating","ReviewCard","review","locale","triggerId","contentId","open","setOpen","breakdownHasValues","hasDetails","formattedDate","useMemo","parsed","pseudonym","footerAndDetails","Collapsible","Card","CollapsibleTrigger","ChevronDown","CollapsibleContent","DescriptionList","_b","_c","_d","PanelSkeleton","hideFilter","Skeleton","_","rootVariants","ReviewsPanel","forwardRef","aggregate","filterProp","onFilterChange","introSlot","headerActionSlot","id","className","surface","loading","rest","ref","i18n","internalFilter","setInternalFilter","handleFilterChange","visibleReviews","latestFilterRef","latestVisibleCountRef","agentHandle","rootRef","useImperativeHandle","useAgentRegistration","resolvedAriaLabel","dataFilter","EmptyState","showFilterSidebar","Button"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { useState as R, useRef as B, useEffect as T, useCallback as I, useMemo a
|
|
|
3
3
|
import * as d from "@radix-ui/react-dialog";
|
|
4
4
|
import { c as H } from "./index-D2ZczOXr.js";
|
|
5
5
|
import { useTranslation as E } from "react-i18next";
|
|
6
|
-
import { I as F } from "./icon-button-
|
|
6
|
+
import { I as F } from "./icon-button-DPEqBKBQ.js";
|
|
7
7
|
import { u as L } from "./registry-C9nwlNyL.js";
|
|
8
8
|
import { X as V } from "./x-CCcI3eJp.js";
|
|
9
9
|
const P = {
|
|
@@ -268,4 +268,4 @@ export {
|
|
|
268
268
|
W as S,
|
|
269
269
|
P as s
|
|
270
270
|
};
|
|
271
|
-
//# sourceMappingURL=sheet-
|
|
271
|
+
//# sourceMappingURL=sheet-D8Yl0nKR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet-D8M8hf8B.js","sources":["../../src/components/sheet/sheet.agent.ts","../../src/components/sheet/sheet.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { SheetHandle } from './sheet';\n\nexport const sheetAgent: AgentAdapter<SheetHandle> = {\n id: 'sheet',\n capabilities: ['open', 'close', 'dismiss'],\n state: {\n isOpen: {\n type: 'boolean',\n description: 'True when the sheet is currently open.',\n read: (handle) => handle.getIsOpen(),\n },\n },\n actions: {\n open: {\n safety: 'read',\n description: 'Open the sheet.',\n invoke: (handle) => {\n handle.open();\n },\n },\n close: {\n safety: 'write',\n description: 'Close the sheet. Reversible by reopening.',\n invoke: (handle) => {\n handle.close();\n },\n },\n dismiss: {\n safety: 'destructive',\n description:\n 'Dismiss the sheet. Loses any in-progress input that has not been committed.',\n invoke: (handle) => {\n handle.close();\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'sheet' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop on Sheet.Root.',\n },\n },\n};\n","import {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport * as RadixDialog from '@radix-ui/react-dialog';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { X } from 'lucide-react';\nimport { IconButton } from '../button/icon-button';\nimport { useAgentRegistration } from '../../agent';\nimport { sheetAgent } from './sheet.agent';\n\nconst OVERLAY_CLASSES = [\n 'ds:fixed ds:inset-0 ds:z-[var(--z-modal-backdrop)]',\n 'ds:bg-[var(--background)]/[var(--opacity-70)]',\n 'ds:backdrop-blur-sm',\n 'ds:data-[state=open]:animate-in ds:data-[state=open]:fade-in-0',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:fade-out-0',\n 'ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n].join(' ');\n\nconst contentVariants = cva(\n [\n 'ds:fixed ds:z-[var(--z-modal)] ds:flex ds:flex-col',\n 'ds:bg-[var(--popover)] ds:text-[var(--popover-foreground)]',\n 'ds:shadow-[var(--shadow-xl)]',\n 'ds:focus-visible:outline-none',\n 'ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n side: {\n start: [\n 'ds:start-0 ds:inset-y-0',\n 'ds:border-e ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:ltr:data-[state=open]:slide-in-from-left-full ds:rtl:data-[state=open]:slide-in-from-right-full',\n 'ds:data-[state=closed]:animate-out ds:ltr:data-[state=closed]:slide-out-to-left-full ds:rtl:data-[state=closed]:slide-out-to-right-full',\n 'ds:max-sm:[inline-size:100dvw] ds:max-sm:[block-size:100dvh]',\n ].join(' '),\n end: [\n 'ds:end-0 ds:inset-y-0',\n 'ds:border-s ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:ltr:data-[state=open]:slide-in-from-right-full ds:rtl:data-[state=open]:slide-in-from-left-full',\n 'ds:data-[state=closed]:animate-out ds:ltr:data-[state=closed]:slide-out-to-right-full ds:rtl:data-[state=closed]:slide-out-to-left-full',\n 'ds:max-sm:[inline-size:100dvw] ds:max-sm:[block-size:100dvh]',\n ].join(' '),\n top: [\n 'ds:top-0 ds:inset-x-0',\n 'ds:border-b ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:data-[state=open]:slide-in-from-top-full',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:slide-out-to-top-full',\n ].join(' '),\n bottom: [\n 'ds:bottom-0 ds:inset-x-0',\n 'ds:border-t ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:data-[state=open]:slide-in-from-bottom-full',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:slide-out-to-bottom-full',\n ].join(' '),\n },\n },\n defaultVariants: { side: 'end' },\n },\n);\n\nfunction getSizeClass(\n side: 'start' | 'end' | 'top' | 'bottom',\n size: 'sm' | 'md' | 'lg',\n): string {\n if (side === 'start' || side === 'end') {\n const map = {\n sm: '[inline-size:360px]',\n md: '[inline-size:480px]',\n lg: '[inline-size:640px]',\n } as const;\n return map[size];\n }\n\n const map = {\n sm: '[block-size:40dvh]',\n md: '[block-size:60dvh]',\n lg: '[block-size:80dvh]',\n } as const;\n return map[size];\n}\n\n// Curated agent-readiness handle — see sheet.agent.ts.\nexport interface SheetHandle {\n getIsOpen: () => boolean;\n open: () => void;\n close: () => void;\n}\n\nexport interface SheetRootProps extends ComponentPropsWithoutRef<\n typeof RadixDialog.Root\n> {\n /** Opaque instance id — forwarded to Sheet.Content as `data-component-id`. */\n id?: string;\n}\n\nconst SheetIdContext = createContext<string | undefined>(undefined);\n\nconst SheetRoot = ({\n children,\n id,\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}: SheetRootProps) => {\n const isControlled = openProp !== undefined;\n const [internalOpen, setInternalOpen] = useState<boolean>(\n defaultOpen ?? false,\n );\n const open = isControlled ? Boolean(openProp) : internalOpen;\n\n const openRef = useRef(open);\n useEffect(() => {\n openRef.current = open;\n }, [open]);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const handle = useMemo<SheetHandle>(\n () => ({\n getIsOpen: () => openRef.current,\n open: () => setOpen(true),\n close: () => setOpen(false),\n }),\n [setOpen],\n );\n useAgentRegistration(sheetAgent, handle, id);\n\n return (\n <SheetIdContext.Provider value={id}>\n <RadixDialog.Root open={open} onOpenChange={setOpen} {...props}>\n {children}\n </RadixDialog.Root>\n </SheetIdContext.Provider>\n );\n};\nSheetRoot.displayName = 'Sheet.Root';\n\nconst SheetTrigger = forwardRef<\n HTMLButtonElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Trigger>\n>(({ children, ...props }, ref) => (\n <RadixDialog.Trigger ref={ref} {...props}>\n {children}\n </RadixDialog.Trigger>\n));\nSheetTrigger.displayName = 'Sheet.Trigger';\n\nconst SheetClose = forwardRef<\n HTMLButtonElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Close>\n>(({ children, ...props }, ref) => (\n <RadixDialog.Close ref={ref} {...props}>\n {children}\n </RadixDialog.Close>\n));\nSheetClose.displayName = 'Sheet.Close';\n\nexport interface SheetContentProps\n extends\n Omit<ComponentPropsWithoutRef<typeof RadixDialog.Content>, 'className'>,\n VariantProps<typeof contentVariants> {\n side?: 'start' | 'end' | 'top' | 'bottom';\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n children: ReactNode;\n}\n\nconst SheetContent = forwardRef<HTMLDivElement, SheetContentProps>(\n ({ side = 'end', size = 'md', className, children, ...props }, ref) => {\n const instanceId = useContext(SheetIdContext);\n return (\n <RadixDialog.Portal>\n <RadixDialog.Overlay\n data-component=\"sheet-overlay\"\n className={OVERLAY_CLASSES}\n />\n <RadixDialog.Content\n ref={ref}\n className={[\n contentVariants({ side }),\n getSizeClass(side, size),\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n data-component=\"sheet\"\n data-component-id={instanceId}\n {...props}\n >\n {children}\n </RadixDialog.Content>\n </RadixDialog.Portal>\n );\n },\n);\nSheetContent.displayName = 'Sheet.Content';\n\nconst SheetHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => {\n const { t } = useTranslation();\n\n return (\n <div\n ref={ref}\n className={[\n 'ds:sticky ds:top-0 ds:z-10 ds:flex ds:items-center ds:justify-between',\n 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:py-[var(--spacing-md)]',\n 'ds:border-b ds:border-[color:var(--border)] ds:bg-[var(--popover)]',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n >\n {children}\n <SheetClose asChild>\n <IconButton\n icon={<X />}\n intent=\"ghost\"\n size=\"sm\"\n aria-label={t('common.close', 'Close')}\n />\n </SheetClose>\n </div>\n );\n },\n);\nSheetHeader.displayName = 'Sheet.Header';\n\nconst SheetBody = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={[\n 'ds:overflow-y-auto ds:overflow-x-hidden ds:flex-1',\n 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:py-[var(--spacing-lg)]',\n 'ds:break-words',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n ),\n);\nSheetBody.displayName = 'Sheet.Body';\n\nconst SheetFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={[\n 'ds:sticky ds:bottom-0 ds:z-10 ds:flex ds:items-center ds:justify-end ds:gap-[var(--spacing-sm)]',\n 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:py-[var(--spacing-md)]',\n 'ds:border-t ds:border-[color:var(--border)] ds:bg-[var(--popover)]',\n 'ds:max-sm:flex-col ds:max-sm:[&>*]:w-full',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n ),\n);\nSheetFooter.displayName = 'Sheet.Footer';\n\nconst SheetTitle = forwardRef<\n HTMLHeadingElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Title>\n>(({ className, ...props }, ref) => (\n <RadixDialog.Title\n ref={ref}\n className={['type-title-card ds:break-words', className]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n));\nSheetTitle.displayName = 'Sheet.Title';\n\nconst SheetDescription = forwardRef<\n HTMLParagraphElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Description>\n>(({ className, ...props }, ref) => (\n <RadixDialog.Description\n ref={ref}\n className={['type-body-sm ds:text-[var(--muted-foreground)]', className]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n));\nSheetDescription.displayName = 'Sheet.Description';\n\nexport const Sheet = Object.assign(SheetRoot, {\n Root: SheetRoot,\n Trigger: SheetTrigger,\n Content: SheetContent,\n Header: SheetHeader,\n Body: SheetBody,\n Footer: SheetFooter,\n Title: SheetTitle,\n Description: SheetDescription,\n Close: SheetClose,\n});\n\nexport type SheetHeaderProps = HTMLAttributes<HTMLDivElement>;\nexport type SheetBodyProps = HTMLAttributes<HTMLDivElement>;\nexport type SheetFooterProps = HTMLAttributes<HTMLDivElement>;\n"],"names":["sheetAgent","handle","OVERLAY_CLASSES","contentVariants","cva","getSizeClass","side","size","SheetIdContext","createContext","SheetRoot","children","id","openProp","defaultOpen","onOpenChange","props","isControlled","internalOpen","setInternalOpen","useState","open","openRef","useRef","useEffect","setOpen","useCallback","next","useMemo","useAgentRegistration","jsx","RadixDialog","SheetTrigger","forwardRef","ref","SheetClose","SheetContent","className","instanceId","useContext","jsxs","SheetHeader","t","useTranslation","IconButton","X","SheetBody","SheetFooter","SheetTitle","SheetDescription","Sheet"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sheet-D8Yl0nKR.js","sources":["../../src/components/sheet/sheet.agent.ts","../../src/components/sheet/sheet.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { SheetHandle } from './sheet';\n\nexport const sheetAgent: AgentAdapter<SheetHandle> = {\n id: 'sheet',\n capabilities: ['open', 'close', 'dismiss'],\n state: {\n isOpen: {\n type: 'boolean',\n description: 'True when the sheet is currently open.',\n read: (handle) => handle.getIsOpen(),\n },\n },\n actions: {\n open: {\n safety: 'read',\n description: 'Open the sheet.',\n invoke: (handle) => {\n handle.open();\n },\n },\n close: {\n safety: 'write',\n description: 'Close the sheet. Reversible by reopening.',\n invoke: (handle) => {\n handle.close();\n },\n },\n dismiss: {\n safety: 'destructive',\n description:\n 'Dismiss the sheet. Loses any in-progress input that has not been committed.',\n invoke: (handle) => {\n handle.close();\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'sheet' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop on Sheet.Root.',\n },\n },\n};\n","import {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport * as RadixDialog from '@radix-ui/react-dialog';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { X } from 'lucide-react';\nimport { IconButton } from '../button/icon-button';\nimport { useAgentRegistration } from '../../agent';\nimport { sheetAgent } from './sheet.agent';\n\nconst OVERLAY_CLASSES = [\n 'ds:fixed ds:inset-0 ds:z-[var(--z-modal-backdrop)]',\n 'ds:bg-[var(--background)]/[var(--opacity-70)]',\n 'ds:backdrop-blur-sm',\n 'ds:data-[state=open]:animate-in ds:data-[state=open]:fade-in-0',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:fade-out-0',\n 'ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n].join(' ');\n\nconst contentVariants = cva(\n [\n 'ds:fixed ds:z-[var(--z-modal)] ds:flex ds:flex-col',\n 'ds:bg-[var(--popover)] ds:text-[var(--popover-foreground)]',\n 'ds:shadow-[var(--shadow-xl)]',\n 'ds:focus-visible:outline-none',\n 'ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n side: {\n start: [\n 'ds:start-0 ds:inset-y-0',\n 'ds:border-e ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:ltr:data-[state=open]:slide-in-from-left-full ds:rtl:data-[state=open]:slide-in-from-right-full',\n 'ds:data-[state=closed]:animate-out ds:ltr:data-[state=closed]:slide-out-to-left-full ds:rtl:data-[state=closed]:slide-out-to-right-full',\n 'ds:max-sm:[inline-size:100dvw] ds:max-sm:[block-size:100dvh]',\n ].join(' '),\n end: [\n 'ds:end-0 ds:inset-y-0',\n 'ds:border-s ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:ltr:data-[state=open]:slide-in-from-right-full ds:rtl:data-[state=open]:slide-in-from-left-full',\n 'ds:data-[state=closed]:animate-out ds:ltr:data-[state=closed]:slide-out-to-right-full ds:rtl:data-[state=closed]:slide-out-to-left-full',\n 'ds:max-sm:[inline-size:100dvw] ds:max-sm:[block-size:100dvh]',\n ].join(' '),\n top: [\n 'ds:top-0 ds:inset-x-0',\n 'ds:border-b ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:data-[state=open]:slide-in-from-top-full',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:slide-out-to-top-full',\n ].join(' '),\n bottom: [\n 'ds:bottom-0 ds:inset-x-0',\n 'ds:border-t ds:border-[color:var(--border)]',\n 'ds:data-[state=open]:animate-in ds:data-[state=open]:slide-in-from-bottom-full',\n 'ds:data-[state=closed]:animate-out ds:data-[state=closed]:slide-out-to-bottom-full',\n ].join(' '),\n },\n },\n defaultVariants: { side: 'end' },\n },\n);\n\nfunction getSizeClass(\n side: 'start' | 'end' | 'top' | 'bottom',\n size: 'sm' | 'md' | 'lg',\n): string {\n if (side === 'start' || side === 'end') {\n const map = {\n sm: '[inline-size:360px]',\n md: '[inline-size:480px]',\n lg: '[inline-size:640px]',\n } as const;\n return map[size];\n }\n\n const map = {\n sm: '[block-size:40dvh]',\n md: '[block-size:60dvh]',\n lg: '[block-size:80dvh]',\n } as const;\n return map[size];\n}\n\n// Curated agent-readiness handle — see sheet.agent.ts.\nexport interface SheetHandle {\n getIsOpen: () => boolean;\n open: () => void;\n close: () => void;\n}\n\nexport interface SheetRootProps extends ComponentPropsWithoutRef<\n typeof RadixDialog.Root\n> {\n /** Opaque instance id — forwarded to Sheet.Content as `data-component-id`. */\n id?: string;\n}\n\nconst SheetIdContext = createContext<string | undefined>(undefined);\n\nconst SheetRoot = ({\n children,\n id,\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}: SheetRootProps) => {\n const isControlled = openProp !== undefined;\n const [internalOpen, setInternalOpen] = useState<boolean>(\n defaultOpen ?? false,\n );\n const open = isControlled ? Boolean(openProp) : internalOpen;\n\n const openRef = useRef(open);\n useEffect(() => {\n openRef.current = open;\n }, [open]);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (!isControlled) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [isControlled, onOpenChange],\n );\n\n const handle = useMemo<SheetHandle>(\n () => ({\n getIsOpen: () => openRef.current,\n open: () => setOpen(true),\n close: () => setOpen(false),\n }),\n [setOpen],\n );\n useAgentRegistration(sheetAgent, handle, id);\n\n return (\n <SheetIdContext.Provider value={id}>\n <RadixDialog.Root open={open} onOpenChange={setOpen} {...props}>\n {children}\n </RadixDialog.Root>\n </SheetIdContext.Provider>\n );\n};\nSheetRoot.displayName = 'Sheet.Root';\n\nconst SheetTrigger = forwardRef<\n HTMLButtonElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Trigger>\n>(({ children, ...props }, ref) => (\n <RadixDialog.Trigger ref={ref} {...props}>\n {children}\n </RadixDialog.Trigger>\n));\nSheetTrigger.displayName = 'Sheet.Trigger';\n\nconst SheetClose = forwardRef<\n HTMLButtonElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Close>\n>(({ children, ...props }, ref) => (\n <RadixDialog.Close ref={ref} {...props}>\n {children}\n </RadixDialog.Close>\n));\nSheetClose.displayName = 'Sheet.Close';\n\nexport interface SheetContentProps\n extends\n Omit<ComponentPropsWithoutRef<typeof RadixDialog.Content>, 'className'>,\n VariantProps<typeof contentVariants> {\n side?: 'start' | 'end' | 'top' | 'bottom';\n size?: 'sm' | 'md' | 'lg';\n className?: string;\n children: ReactNode;\n}\n\nconst SheetContent = forwardRef<HTMLDivElement, SheetContentProps>(\n ({ side = 'end', size = 'md', className, children, ...props }, ref) => {\n const instanceId = useContext(SheetIdContext);\n return (\n <RadixDialog.Portal>\n <RadixDialog.Overlay\n data-component=\"sheet-overlay\"\n className={OVERLAY_CLASSES}\n />\n <RadixDialog.Content\n ref={ref}\n className={[\n contentVariants({ side }),\n getSizeClass(side, size),\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n data-component=\"sheet\"\n data-component-id={instanceId}\n {...props}\n >\n {children}\n </RadixDialog.Content>\n </RadixDialog.Portal>\n );\n },\n);\nSheetContent.displayName = 'Sheet.Content';\n\nconst SheetHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, children, ...props }, ref) => {\n const { t } = useTranslation();\n\n return (\n <div\n ref={ref}\n className={[\n 'ds:sticky ds:top-0 ds:z-10 ds:flex ds:items-center ds:justify-between',\n 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:py-[var(--spacing-md)]',\n 'ds:border-b ds:border-[color:var(--border)] ds:bg-[var(--popover)]',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n >\n {children}\n <SheetClose asChild>\n <IconButton\n icon={<X />}\n intent=\"ghost\"\n size=\"sm\"\n aria-label={t('common.close', 'Close')}\n />\n </SheetClose>\n </div>\n );\n },\n);\nSheetHeader.displayName = 'Sheet.Header';\n\nconst SheetBody = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={[\n 'ds:overflow-y-auto ds:overflow-x-hidden ds:flex-1',\n 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:py-[var(--spacing-lg)]',\n 'ds:break-words',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n ),\n);\nSheetBody.displayName = 'Sheet.Body';\n\nconst SheetFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={[\n 'ds:sticky ds:bottom-0 ds:z-10 ds:flex ds:items-center ds:justify-end ds:gap-[var(--spacing-sm)]',\n 'ds:ps-[var(--spacing-lg)] ds:pe-[var(--spacing-lg)] ds:py-[var(--spacing-md)]',\n 'ds:border-t ds:border-[color:var(--border)] ds:bg-[var(--popover)]',\n 'ds:max-sm:flex-col ds:max-sm:[&>*]:w-full',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n ),\n);\nSheetFooter.displayName = 'Sheet.Footer';\n\nconst SheetTitle = forwardRef<\n HTMLHeadingElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Title>\n>(({ className, ...props }, ref) => (\n <RadixDialog.Title\n ref={ref}\n className={['type-title-card ds:break-words', className]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n));\nSheetTitle.displayName = 'Sheet.Title';\n\nconst SheetDescription = forwardRef<\n HTMLParagraphElement,\n ComponentPropsWithoutRef<typeof RadixDialog.Description>\n>(({ className, ...props }, ref) => (\n <RadixDialog.Description\n ref={ref}\n className={['type-body-sm ds:text-[var(--muted-foreground)]', className]\n .filter(Boolean)\n .join(' ')}\n {...props}\n />\n));\nSheetDescription.displayName = 'Sheet.Description';\n\nexport const Sheet = Object.assign(SheetRoot, {\n Root: SheetRoot,\n Trigger: SheetTrigger,\n Content: SheetContent,\n Header: SheetHeader,\n Body: SheetBody,\n Footer: SheetFooter,\n Title: SheetTitle,\n Description: SheetDescription,\n Close: SheetClose,\n});\n\nexport type SheetHeaderProps = HTMLAttributes<HTMLDivElement>;\nexport type SheetBodyProps = HTMLAttributes<HTMLDivElement>;\nexport type SheetFooterProps = HTMLAttributes<HTMLDivElement>;\n"],"names":["sheetAgent","handle","OVERLAY_CLASSES","contentVariants","cva","getSizeClass","side","size","SheetIdContext","createContext","SheetRoot","children","id","openProp","defaultOpen","onOpenChange","props","isControlled","internalOpen","setInternalOpen","useState","open","openRef","useRef","useEffect","setOpen","useCallback","next","useMemo","useAgentRegistration","jsx","RadixDialog","SheetTrigger","forwardRef","ref","SheetClose","SheetContent","className","instanceId","useContext","jsxs","SheetHeader","t","useTranslation","IconButton","X","SheetBody","SheetFooter","SheetTitle","SheetDescription","Sheet"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { forwardRef as os, useId as ls, useState as cs, useCallback as L } from
|
|
|
3
3
|
import { c as f } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as ms } from "react-i18next";
|
|
5
5
|
import { B as $ } from "./button-DD_0Xdmr.js";
|
|
6
|
-
import { I as A } from "./icon-button-
|
|
6
|
+
import { I as A } from "./icon-button-DPEqBKBQ.js";
|
|
7
7
|
import { E as us } from "./empty-state-BLy7tigq.js";
|
|
8
8
|
import { S as B } from "./skeleton-CZbwyJAA.js";
|
|
9
9
|
import { i as fs } from "./isSameDay-DHG8Xade.js";
|
|
@@ -384,4 +384,4 @@ export {
|
|
|
384
384
|
Ts as f,
|
|
385
385
|
Rs as i
|
|
386
386
|
};
|
|
387
|
-
//# sourceMappingURL=slot-grid-
|
|
387
|
+
//# sourceMappingURL=slot-grid-Q94gVmhn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-grid-B2zprPcv.js","sources":["../../src/components/slot-grid/slot-grid.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* SlotGrid — multi-day time-slot picker for patient booking. */\n/* */\n/* Renders N day columns side-by-side. Each column shows a day header */\n/* (formatted via `Intl.DateTimeFormat(locale)`) plus a vertical list */\n/* of slot buttons. Long days collapse to `maxInitialSlotsPerDay` and */\n/* reveal the rest via a \"Show more\" button (progressive disclosure). */\n/* */\n/* Navigation arrows (`onPreviousDays` / `onNextDays`) let the consumer */\n/* page the window of days; the optional \"Next available\" CTA jumps to */\n/* the first day with an open slot. */\n/* */\n/* Purely presentational — no business logic, no data fetching. Date */\n/* comparisons use `date-fns` (never moment.js) and the rendered labels */\n/* come exclusively from `Intl.DateTimeFormat` or the `ui.slotGrid` */\n/* namespace via `useTranslation()`. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useId,\n useState,\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 { isSameDay, parseISO } from 'date-fns';\nimport { Button } from '../button/button';\nimport { IconButton } from '../button/icon-button';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { Skeleton } from '../skeleton/skeleton';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport type SlotGridState = 'default' | 'loading' | 'disabled' | 'empty';\n\nexport type SlotGridSize = 'sm' | 'md' | 'lg';\n\nexport interface SlotGridSlot {\n /** Stable identity — used by consumers to resolve selection. */\n id: string;\n /** Slot start — ISO string or `Date`. Rendered via `Intl.DateTimeFormat`. */\n start: string | Date;\n /** Optional slot end — if omitted only the start time is announced. */\n end?: string | Date;\n /** Defaults to `true`. Unavailable slots render disabled and are not focusable. */\n available?: boolean;\n /** Override the default `Intl`-formatted time label. */\n label?: string;\n /** Override the accessible name of the slot button. */\n ariaLabel?: string;\n}\n\nexport interface SlotGridDay {\n /** The calendar day the column represents. */\n date: string | Date;\n /** Slots for this day, in chronological order. Empty array ⇒ empty day. */\n slots: SlotGridSlot[];\n /** Override the default `Intl`-formatted day header. */\n label?: string;\n}\n\nexport interface SlotGridProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'aria-label' | 'onSelect'>,\n VariantProps<typeof slotGridVariants> {\n /** Days to render, one column per entry. */\n days: SlotGridDay[];\n /** Visual / interactive state. Defaults to `'default'`. */\n state?: SlotGridState;\n /** Density of the slot buttons and day headers. Defaults to `'md'`. */\n size?: SlotGridSize;\n /** Id of the currently selected slot (controlled). */\n selectedSlotId?: string;\n /**\n * Initial number of slots to render per day before the \"Show more\"\n * progressive-disclosure control collapses the rest. Defaults to `6`.\n */\n maxInitialSlotsPerDay?: number;\n /**\n * BCP-47 locale tag for `Intl.DateTimeFormat`. Defaults to the active\n * i18next language.\n */\n locale?: string;\n /** IANA time zone for `Intl.DateTimeFormat`. Optional. */\n timeZone?: string;\n /** Whether the \"previous days\" navigation arrow should be enabled. */\n hasPrevious?: boolean;\n /** Whether the \"next days\" navigation arrow should be enabled. */\n hasNext?: boolean;\n /**\n * When `true`, renders the \"Next available\" CTA at the end of the\n * toolbar. Only emits a callback — the consumer owns the navigation.\n */\n showNextAvailable?: boolean;\n /** Accessible label for the outer grid region. */\n ariaLabel?: string;\n /** Fires when the user selects an available slot. */\n onSlotSelect?: (slot: SlotGridSlot, day: SlotGridDay) => void;\n /** Fires when the user clicks the \"previous days\" arrow. */\n onPreviousDays?: () => void;\n /** Fires when the user clicks the \"next days\" arrow. */\n onNextDays?: () => void;\n /** Fires when the user clicks a day's \"Show more\" button. */\n onShowMore?: (day: SlotGridDay) => void;\n /** Fires when the user clicks the \"Next available\" CTA. */\n onJumpToNextAvailable?: () => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst slotGridVariants = cva(\n [\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:text-[var(--foreground)]',\n 'ds:w-full',\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 toolbarVariants = cva(\n 'ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)] ds:w-full',\n);\n\n/**\n * Column-count is dynamic (1–12), so it is declared as a full CVA variant —\n * this keeps every `grid-cols-N` string statically visible to Tailwind's\n * JIT and keeps the variant table inside CVA rather than hand-rolled.\n */\nconst COLS_MAX = 12;\n\nconst columnsVariants = cva(\n 'ds:grid ds:gap-[var(--spacing-sm)] ds:w-full ds:auto-rows-min ds:items-start',\n {\n variants: {\n cols: {\n 1: 'ds:grid-cols-1',\n 2: 'ds:grid-cols-2',\n 3: 'ds:grid-cols-3',\n 4: 'ds:grid-cols-4',\n 5: 'ds:grid-cols-5',\n 6: 'ds:grid-cols-6',\n 7: 'ds:grid-cols-7',\n 8: 'ds:grid-cols-8',\n 9: 'ds:grid-cols-9',\n 10: 'ds:grid-cols-10',\n 11: 'ds:grid-cols-11',\n 12: 'ds:grid-cols-12',\n },\n },\n defaultVariants: { cols: 1 },\n },\n);\n\ntype ColumnsCount = NonNullable<VariantProps<typeof columnsVariants>['cols']>;\n\nfunction clampCols(count: number): ColumnsCount {\n return Math.max(1, Math.min(count, COLS_MAX)) as ColumnsCount;\n}\n\nconst columnVariants = cva(\n [\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)]',\n 'ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:bg-[var(--card)]',\n 'ds:min-w-0',\n ].join(' '),\n);\n\nconst dayHeaderVariants = cva(\n [\n 'ds:font-[var(--font-weight-semibold)]',\n 'ds:text-[var(--foreground)]',\n 'ds:text-start',\n 'ds:truncate',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:text-[length:var(--font-size-xs)]',\n md: 'ds:text-[length:var(--font-size-sm)]',\n lg: 'ds:text-[length:var(--font-size-base)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst slotListVariants = cva(\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:w-full ds:m-0 ds:p-0 ds:list-none',\n);\n\nconst slotButtonVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:w-full',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:font-[var(--font-weight-medium)]',\n 'ds:bg-[var(--secondary)]',\n 'ds:shadow-[var(--shadow-sm)]',\n 'ds:text-[var(--foreground)]',\n 'ds:transition-[background-color,color,box-shadow]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:focus-visible:outline-[var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[var(--focus-ring-offset)]',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed ds:disabled:shadow-none',\n 'ds:aria-disabled:opacity-50 ds:aria-disabled:cursor-not-allowed ds:aria-disabled:shadow-none',\n 'ds:hover:enabled:shadow-[var(--shadow-md)]',\n 'ds:aria-pressed:bg-[var(--primary)] ds:aria-pressed:text-[var(--primary-foreground)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:text-[length:var(--font-size-xs)] ds:h-8',\n md: 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:text-[length:var(--font-size-sm)] ds:h-10',\n lg: 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:text-[length:var(--font-size-base)] ds:h-12',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction toDate(value: string | Date): Date {\n return value instanceof Date ? value : parseISO(value);\n}\n\n/** Is `a` the same calendar day as `b`? Uses `date-fns`, locale-agnostic. */\nexport function isSameDate(a: string | Date, b: string | Date): boolean {\n return isSameDay(toDate(a), toDate(b));\n}\n\n/** Find the first day in a list that has at least one available slot. */\nexport function findFirstAvailable(\n days: SlotGridDay[],\n): { day: SlotGridDay; slot: SlotGridSlot } | undefined {\n for (const day of days) {\n const slot = day.slots.find((s) => s.available !== false);\n if (slot) return { day, slot };\n }\n return undefined;\n}\n\nfunction formatDayHeader(\n date: Date,\n locale: string,\n timeZone?: string,\n): string {\n try {\n return new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n timeZone,\n }).format(date);\n } catch {\n return new Intl.DateTimeFormat('en', {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n }).format(date);\n }\n}\n\nfunction formatSlotTime(\n slot: SlotGridSlot,\n locale: string,\n timeZone?: string,\n): string {\n if (slot.label !== undefined) return slot.label;\n const start = toDate(slot.start);\n try {\n const fmt = new Intl.DateTimeFormat(locale, {\n hour: 'numeric',\n minute: '2-digit',\n timeZone,\n });\n if (slot.end) {\n const end = toDate(slot.end);\n if (typeof fmt.formatRange === 'function') {\n try {\n return fmt.formatRange(start, end);\n } catch {\n /* fall through */\n }\n }\n return `${fmt.format(start)} – ${fmt.format(end)}`;\n }\n return fmt.format(start);\n } catch {\n return start.toISOString();\n }\n}\n\nfunction formatSlotAriaLabel(\n slot: SlotGridSlot,\n day: SlotGridDay,\n locale: string,\n timeZone: string | undefined,\n template: (opts: { time: string; day: string }) => string,\n): string {\n if (slot.ariaLabel !== undefined) return slot.ariaLabel;\n const time = formatSlotTime(slot, locale, timeZone);\n const dayLabel =\n day.label !== undefined\n ? day.label\n : formatDayHeader(toDate(day.date), locale, timeZone);\n return template({ time, day: dayLabel });\n}\n\n/* ------------------------------------------------------------------ */\n/* Glyphs — logical-direction arrows */\n/* ------------------------------------------------------------------ */\n\nfunction ChevronStartIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"ds:size-4\"\n >\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n );\n}\n\nfunction ChevronEndIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"ds:size-4\"\n >\n <path d=\"M9 6l6 6-6 6\" />\n </svg>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const SlotGrid = forwardRef<HTMLDivElement, SlotGridProps>(\n (\n {\n days,\n state = 'default',\n size = 'md',\n selectedSlotId,\n maxInitialSlotsPerDay = 6,\n locale: localeProp,\n timeZone,\n hasPrevious = false,\n hasNext = false,\n showNextAvailable = false,\n ariaLabel,\n onSlotSelect,\n onPreviousDays,\n onNextDays,\n onShowMore,\n onJumpToNextAvailable,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const locale = localeProp ?? i18n.language ?? 'en';\n const idBase = useId();\n\n const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n\n const isLoading = state === 'loading';\n const isDisabled = state === 'disabled';\n const isInteractionSuppressed = isLoading || isDisabled;\n const isEmpty =\n state === 'empty' ||\n days.length === 0 ||\n days.every((day) => day.slots.length === 0);\n\n const resolvedAriaLabel = ariaLabel ?? t('slotGrid.ariaLabel');\n\n const handleSlotClick = useCallback(\n (slot: SlotGridSlot, day: SlotGridDay) => {\n if (isInteractionSuppressed) return;\n if (slot.available === false) return;\n onSlotSelect?.(slot, day);\n },\n [isInteractionSuppressed, onSlotSelect],\n );\n\n const handleShowMore = useCallback(\n (day: SlotGridDay, key: string) => {\n setExpanded((prev) => ({ ...prev, [key]: true }));\n onShowMore?.(day);\n },\n [onShowMore],\n );\n\n const handleSlotKeyDown = useCallback(\n (\n event: KeyboardEvent<HTMLButtonElement>,\n slot: SlotGridSlot,\n day: SlotGridDay,\n ) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleSlotClick(slot, day);\n }\n },\n [handleSlotClick],\n );\n\n const columnCount = clampCols(days.length || 1);\n const columnsClass = columnsVariants({ cols: columnCount });\n\n /* ---- Rendered content ------------------------------------------ */\n\n let body: ReactNode;\n if (isLoading) {\n body = (\n <div\n className={columnsClass}\n data-state=\"loading\"\n role=\"status\"\n aria-live=\"polite\"\n aria-label={t('common.loading')}\n >\n {Array.from({ length: columnCount }, (_, colIdx) => (\n <div key={`loading-col-${colIdx}`} className={columnVariants()}>\n <Skeleton variant=\"text\" size={size} width=\"60%\" />\n <div className={slotListVariants()}>\n {Array.from(\n { length: Math.min(maxInitialSlotsPerDay, 4) },\n (_, rowIdx) => (\n <Skeleton\n key={`loading-slot-${colIdx}-${rowIdx}`}\n variant=\"rounded\"\n size={size}\n height=\"40px\"\n />\n ),\n )}\n </div>\n </div>\n ))}\n </div>\n );\n } else if (isEmpty) {\n body = (\n <div data-state=\"empty\">\n <EmptyState variant=\"no-results\" title={t('slotGrid.noSlots')} />\n </div>\n );\n } else {\n body = (\n <div\n className={columnsClass}\n data-state={isDisabled ? 'disabled' : 'default'}\n >\n {days.map((day, dayIdx) => {\n const dayKey = `${toDate(day.date).toISOString()}-${dayIdx}`;\n const headerLabel =\n day.label ?? formatDayHeader(toDate(day.date), locale, timeZone);\n const headerId = `${idBase}-day-${dayIdx}-header`;\n const isExpanded = expanded[dayKey] ?? false;\n const visibleSlots = isExpanded\n ? day.slots\n : day.slots.slice(0, maxInitialSlotsPerDay);\n const hiddenCount = day.slots.length - visibleSlots.length;\n\n const isDayEmpty = day.slots.length === 0;\n\n return (\n // Use a plain <div role=\"group\"> rather than <section> — a\n // <section> with aria-labelledby is a landmark, and nesting many\n // landmarks inside the outer slot-grid landmark fails axe's\n // landmark-unique rule.\n <div\n key={dayKey}\n role=\"group\"\n className={columnVariants()}\n aria-labelledby={headerId}\n data-empty={isDayEmpty || undefined}\n >\n <h3 id={headerId} className={dayHeaderVariants({ size })}>\n {headerLabel}\n </h3>\n {isDayEmpty ? (\n <p className=\"type-body-sm ds:text-[var(--muted-foreground)] ds:m-0\">\n {t('slotGrid.noSlots')}\n </p>\n ) : null}\n <ul className={slotListVariants()}>\n {visibleSlots.map((slot) => {\n const ariaLabelSlot = formatSlotAriaLabel(\n slot,\n day,\n locale,\n timeZone,\n ({ time, day: dayLabel }) =>\n t('slotGrid.slotLabel', { time, day: dayLabel }),\n );\n const isSelected = selectedSlotId === slot.id;\n const isSlotDisabled =\n slot.available === false || isInteractionSuppressed;\n return (\n <li key={slot.id}>\n <button\n type=\"button\"\n className={slotButtonVariants({ size })}\n aria-label={ariaLabelSlot}\n aria-pressed={isSelected}\n disabled={isSlotDisabled}\n aria-disabled={isSlotDisabled || undefined}\n data-slot-id={slot.id}\n onClick={() => handleSlotClick(slot, day)}\n onKeyDown={(event) =>\n handleSlotKeyDown(event, slot, day)\n }\n >\n {formatSlotTime(slot, locale, timeZone)}\n </button>\n </li>\n );\n })}\n </ul>\n {hiddenCount > 0 ? (\n <Button\n intent=\"ghost\"\n size={size === 'lg' ? 'md' : 'sm'}\n onClick={() => handleShowMore(day, dayKey)}\n disabled={isInteractionSuppressed}\n >\n {t('slotGrid.showMore', { count: hiddenCount })}\n </Button>\n ) : null}\n </div>\n );\n })}\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={resolvedAriaLabel}\n className={slotGridVariants({ size, className })}\n data-component=\"slot-grid\"\n data-state={state}\n {...rest}\n >\n <div className={toolbarVariants()}>\n <IconButton\n icon={<ChevronStartIcon />}\n aria-label={t('slotGrid.previousDays')}\n size={size === 'lg' ? 'md' : 'sm'}\n intent=\"ghost\"\n flipIconInRtl\n disabled={!hasPrevious || isInteractionSuppressed}\n onClick={onPreviousDays}\n />\n {showNextAvailable ? (\n <Button\n intent=\"secondary\"\n size={size === 'lg' ? 'md' : 'sm'}\n disabled={isInteractionSuppressed}\n onClick={onJumpToNextAvailable}\n >\n {t('slotGrid.nextAvailable')}\n </Button>\n ) : (\n <span aria-hidden=\"true\" />\n )}\n <IconButton\n icon={<ChevronEndIcon />}\n aria-label={t('slotGrid.nextDays')}\n size={size === 'lg' ? 'md' : 'sm'}\n intent=\"ghost\"\n flipIconInRtl\n disabled={!hasNext || isInteractionSuppressed}\n onClick={onNextDays}\n />\n </div>\n\n {body}\n </div>\n );\n },\n);\n\nSlotGrid.displayName = 'SlotGrid';\n"],"names":["slotGridVariants","cva","toolbarVariants","COLS_MAX","columnsVariants","clampCols","count","columnVariants","dayHeaderVariants","slotListVariants","slotButtonVariants","toDate","value","parseISO","isSameDate","a","b","isSameDay","findFirstAvailable","days","day","slot","s","formatDayHeader","date","locale","timeZone","formatSlotTime","start","fmt","end","formatSlotAriaLabel","template","time","dayLabel","ChevronStartIcon","jsx","ChevronEndIcon","SlotGrid","forwardRef","state","size","selectedSlotId","maxInitialSlotsPerDay","localeProp","hasPrevious","hasNext","showNextAvailable","ariaLabel","onSlotSelect","onPreviousDays","onNextDays","onShowMore","onJumpToNextAvailable","className","rest","ref","t","i18n","useTranslation","idBase","useId","expanded","setExpanded","useState","isLoading","isDisabled","isInteractionSuppressed","isEmpty","resolvedAriaLabel","handleSlotClick","useCallback","handleShowMore","key","prev","handleSlotKeyDown","event","columnCount","columnsClass","body","_","colIdx","jsxs","Skeleton","rowIdx","EmptyState","dayIdx","dayKey","headerLabel","headerId","visibleSlots","hiddenCount","isDayEmpty","ariaLabelSlot","isSelected","isSlotDisabled","Button","IconButton"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slot-grid-Q94gVmhn.js","sources":["../../src/components/slot-grid/slot-grid.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* SlotGrid — multi-day time-slot picker for patient booking. */\n/* */\n/* Renders N day columns side-by-side. Each column shows a day header */\n/* (formatted via `Intl.DateTimeFormat(locale)`) plus a vertical list */\n/* of slot buttons. Long days collapse to `maxInitialSlotsPerDay` and */\n/* reveal the rest via a \"Show more\" button (progressive disclosure). */\n/* */\n/* Navigation arrows (`onPreviousDays` / `onNextDays`) let the consumer */\n/* page the window of days; the optional \"Next available\" CTA jumps to */\n/* the first day with an open slot. */\n/* */\n/* Purely presentational — no business logic, no data fetching. Date */\n/* comparisons use `date-fns` (never moment.js) and the rendered labels */\n/* come exclusively from `Intl.DateTimeFormat` or the `ui.slotGrid` */\n/* namespace via `useTranslation()`. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useId,\n useState,\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 { isSameDay, parseISO } from 'date-fns';\nimport { Button } from '../button/button';\nimport { IconButton } from '../button/icon-button';\nimport { EmptyState } from '../empty-state/empty-state';\nimport { Skeleton } from '../skeleton/skeleton';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport type SlotGridState = 'default' | 'loading' | 'disabled' | 'empty';\n\nexport type SlotGridSize = 'sm' | 'md' | 'lg';\n\nexport interface SlotGridSlot {\n /** Stable identity — used by consumers to resolve selection. */\n id: string;\n /** Slot start — ISO string or `Date`. Rendered via `Intl.DateTimeFormat`. */\n start: string | Date;\n /** Optional slot end — if omitted only the start time is announced. */\n end?: string | Date;\n /** Defaults to `true`. Unavailable slots render disabled and are not focusable. */\n available?: boolean;\n /** Override the default `Intl`-formatted time label. */\n label?: string;\n /** Override the accessible name of the slot button. */\n ariaLabel?: string;\n}\n\nexport interface SlotGridDay {\n /** The calendar day the column represents. */\n date: string | Date;\n /** Slots for this day, in chronological order. Empty array ⇒ empty day. */\n slots: SlotGridSlot[];\n /** Override the default `Intl`-formatted day header. */\n label?: string;\n}\n\nexport interface SlotGridProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'aria-label' | 'onSelect'>,\n VariantProps<typeof slotGridVariants> {\n /** Days to render, one column per entry. */\n days: SlotGridDay[];\n /** Visual / interactive state. Defaults to `'default'`. */\n state?: SlotGridState;\n /** Density of the slot buttons and day headers. Defaults to `'md'`. */\n size?: SlotGridSize;\n /** Id of the currently selected slot (controlled). */\n selectedSlotId?: string;\n /**\n * Initial number of slots to render per day before the \"Show more\"\n * progressive-disclosure control collapses the rest. Defaults to `6`.\n */\n maxInitialSlotsPerDay?: number;\n /**\n * BCP-47 locale tag for `Intl.DateTimeFormat`. Defaults to the active\n * i18next language.\n */\n locale?: string;\n /** IANA time zone for `Intl.DateTimeFormat`. Optional. */\n timeZone?: string;\n /** Whether the \"previous days\" navigation arrow should be enabled. */\n hasPrevious?: boolean;\n /** Whether the \"next days\" navigation arrow should be enabled. */\n hasNext?: boolean;\n /**\n * When `true`, renders the \"Next available\" CTA at the end of the\n * toolbar. Only emits a callback — the consumer owns the navigation.\n */\n showNextAvailable?: boolean;\n /** Accessible label for the outer grid region. */\n ariaLabel?: string;\n /** Fires when the user selects an available slot. */\n onSlotSelect?: (slot: SlotGridSlot, day: SlotGridDay) => void;\n /** Fires when the user clicks the \"previous days\" arrow. */\n onPreviousDays?: () => void;\n /** Fires when the user clicks the \"next days\" arrow. */\n onNextDays?: () => void;\n /** Fires when the user clicks a day's \"Show more\" button. */\n onShowMore?: (day: SlotGridDay) => void;\n /** Fires when the user clicks the \"Next available\" CTA. */\n onJumpToNextAvailable?: () => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst slotGridVariants = cva(\n [\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]',\n 'ds:text-[var(--foreground)]',\n 'ds:w-full',\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 toolbarVariants = cva(\n 'ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)] ds:w-full',\n);\n\n/**\n * Column-count is dynamic (1–12), so it is declared as a full CVA variant —\n * this keeps every `grid-cols-N` string statically visible to Tailwind's\n * JIT and keeps the variant table inside CVA rather than hand-rolled.\n */\nconst COLS_MAX = 12;\n\nconst columnsVariants = cva(\n 'ds:grid ds:gap-[var(--spacing-sm)] ds:w-full ds:auto-rows-min ds:items-start',\n {\n variants: {\n cols: {\n 1: 'ds:grid-cols-1',\n 2: 'ds:grid-cols-2',\n 3: 'ds:grid-cols-3',\n 4: 'ds:grid-cols-4',\n 5: 'ds:grid-cols-5',\n 6: 'ds:grid-cols-6',\n 7: 'ds:grid-cols-7',\n 8: 'ds:grid-cols-8',\n 9: 'ds:grid-cols-9',\n 10: 'ds:grid-cols-10',\n 11: 'ds:grid-cols-11',\n 12: 'ds:grid-cols-12',\n },\n },\n defaultVariants: { cols: 1 },\n },\n);\n\ntype ColumnsCount = NonNullable<VariantProps<typeof columnsVariants>['cols']>;\n\nfunction clampCols(count: number): ColumnsCount {\n return Math.max(1, Math.min(count, COLS_MAX)) as ColumnsCount;\n}\n\nconst columnVariants = cva(\n [\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)]',\n 'ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'ds:rounded-[var(--radius-md)]',\n 'ds:bg-[var(--card)]',\n 'ds:min-w-0',\n ].join(' '),\n);\n\nconst dayHeaderVariants = cva(\n [\n 'ds:font-[var(--font-weight-semibold)]',\n 'ds:text-[var(--foreground)]',\n 'ds:text-start',\n 'ds:truncate',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:text-[length:var(--font-size-xs)]',\n md: 'ds:text-[length:var(--font-size-sm)]',\n lg: 'ds:text-[length:var(--font-size-base)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst slotListVariants = cva(\n 'ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:w-full ds:m-0 ds:p-0 ds:list-none',\n);\n\nconst slotButtonVariants = cva(\n [\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:w-full',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:font-[var(--font-weight-medium)]',\n 'ds:bg-[var(--secondary)]',\n 'ds:shadow-[var(--shadow-sm)]',\n 'ds:text-[var(--foreground)]',\n 'ds:transition-[background-color,color,box-shadow]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n 'ds:focus-visible:outline-[var(--focus-ring-width)]',\n 'ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[var(--ring)]',\n 'ds:focus-visible:outline-offset-[var(--focus-ring-offset)]',\n 'ds:min-h-[var(--min-target-size)]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed ds:disabled:shadow-none',\n 'ds:aria-disabled:opacity-50 ds:aria-disabled:cursor-not-allowed ds:aria-disabled:shadow-none',\n 'ds:hover:enabled:shadow-[var(--shadow-md)]',\n 'ds:aria-pressed:bg-[var(--primary)] ds:aria-pressed:text-[var(--primary-foreground)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:text-[length:var(--font-size-xs)] ds:h-8',\n md: 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:text-[length:var(--font-size-sm)] ds:h-10',\n lg: 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:text-[length:var(--font-size-base)] ds:h-12',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction toDate(value: string | Date): Date {\n return value instanceof Date ? value : parseISO(value);\n}\n\n/** Is `a` the same calendar day as `b`? Uses `date-fns`, locale-agnostic. */\nexport function isSameDate(a: string | Date, b: string | Date): boolean {\n return isSameDay(toDate(a), toDate(b));\n}\n\n/** Find the first day in a list that has at least one available slot. */\nexport function findFirstAvailable(\n days: SlotGridDay[],\n): { day: SlotGridDay; slot: SlotGridSlot } | undefined {\n for (const day of days) {\n const slot = day.slots.find((s) => s.available !== false);\n if (slot) return { day, slot };\n }\n return undefined;\n}\n\nfunction formatDayHeader(\n date: Date,\n locale: string,\n timeZone?: string,\n): string {\n try {\n return new Intl.DateTimeFormat(locale, {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n timeZone,\n }).format(date);\n } catch {\n return new Intl.DateTimeFormat('en', {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n }).format(date);\n }\n}\n\nfunction formatSlotTime(\n slot: SlotGridSlot,\n locale: string,\n timeZone?: string,\n): string {\n if (slot.label !== undefined) return slot.label;\n const start = toDate(slot.start);\n try {\n const fmt = new Intl.DateTimeFormat(locale, {\n hour: 'numeric',\n minute: '2-digit',\n timeZone,\n });\n if (slot.end) {\n const end = toDate(slot.end);\n if (typeof fmt.formatRange === 'function') {\n try {\n return fmt.formatRange(start, end);\n } catch {\n /* fall through */\n }\n }\n return `${fmt.format(start)} – ${fmt.format(end)}`;\n }\n return fmt.format(start);\n } catch {\n return start.toISOString();\n }\n}\n\nfunction formatSlotAriaLabel(\n slot: SlotGridSlot,\n day: SlotGridDay,\n locale: string,\n timeZone: string | undefined,\n template: (opts: { time: string; day: string }) => string,\n): string {\n if (slot.ariaLabel !== undefined) return slot.ariaLabel;\n const time = formatSlotTime(slot, locale, timeZone);\n const dayLabel =\n day.label !== undefined\n ? day.label\n : formatDayHeader(toDate(day.date), locale, timeZone);\n return template({ time, day: dayLabel });\n}\n\n/* ------------------------------------------------------------------ */\n/* Glyphs — logical-direction arrows */\n/* ------------------------------------------------------------------ */\n\nfunction ChevronStartIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"ds:size-4\"\n >\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n );\n}\n\nfunction ChevronEndIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"ds:size-4\"\n >\n <path d=\"M9 6l6 6-6 6\" />\n </svg>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport const SlotGrid = forwardRef<HTMLDivElement, SlotGridProps>(\n (\n {\n days,\n state = 'default',\n size = 'md',\n selectedSlotId,\n maxInitialSlotsPerDay = 6,\n locale: localeProp,\n timeZone,\n hasPrevious = false,\n hasNext = false,\n showNextAvailable = false,\n ariaLabel,\n onSlotSelect,\n onPreviousDays,\n onNextDays,\n onShowMore,\n onJumpToNextAvailable,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const locale = localeProp ?? i18n.language ?? 'en';\n const idBase = useId();\n\n const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n\n const isLoading = state === 'loading';\n const isDisabled = state === 'disabled';\n const isInteractionSuppressed = isLoading || isDisabled;\n const isEmpty =\n state === 'empty' ||\n days.length === 0 ||\n days.every((day) => day.slots.length === 0);\n\n const resolvedAriaLabel = ariaLabel ?? t('slotGrid.ariaLabel');\n\n const handleSlotClick = useCallback(\n (slot: SlotGridSlot, day: SlotGridDay) => {\n if (isInteractionSuppressed) return;\n if (slot.available === false) return;\n onSlotSelect?.(slot, day);\n },\n [isInteractionSuppressed, onSlotSelect],\n );\n\n const handleShowMore = useCallback(\n (day: SlotGridDay, key: string) => {\n setExpanded((prev) => ({ ...prev, [key]: true }));\n onShowMore?.(day);\n },\n [onShowMore],\n );\n\n const handleSlotKeyDown = useCallback(\n (\n event: KeyboardEvent<HTMLButtonElement>,\n slot: SlotGridSlot,\n day: SlotGridDay,\n ) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleSlotClick(slot, day);\n }\n },\n [handleSlotClick],\n );\n\n const columnCount = clampCols(days.length || 1);\n const columnsClass = columnsVariants({ cols: columnCount });\n\n /* ---- Rendered content ------------------------------------------ */\n\n let body: ReactNode;\n if (isLoading) {\n body = (\n <div\n className={columnsClass}\n data-state=\"loading\"\n role=\"status\"\n aria-live=\"polite\"\n aria-label={t('common.loading')}\n >\n {Array.from({ length: columnCount }, (_, colIdx) => (\n <div key={`loading-col-${colIdx}`} className={columnVariants()}>\n <Skeleton variant=\"text\" size={size} width=\"60%\" />\n <div className={slotListVariants()}>\n {Array.from(\n { length: Math.min(maxInitialSlotsPerDay, 4) },\n (_, rowIdx) => (\n <Skeleton\n key={`loading-slot-${colIdx}-${rowIdx}`}\n variant=\"rounded\"\n size={size}\n height=\"40px\"\n />\n ),\n )}\n </div>\n </div>\n ))}\n </div>\n );\n } else if (isEmpty) {\n body = (\n <div data-state=\"empty\">\n <EmptyState variant=\"no-results\" title={t('slotGrid.noSlots')} />\n </div>\n );\n } else {\n body = (\n <div\n className={columnsClass}\n data-state={isDisabled ? 'disabled' : 'default'}\n >\n {days.map((day, dayIdx) => {\n const dayKey = `${toDate(day.date).toISOString()}-${dayIdx}`;\n const headerLabel =\n day.label ?? formatDayHeader(toDate(day.date), locale, timeZone);\n const headerId = `${idBase}-day-${dayIdx}-header`;\n const isExpanded = expanded[dayKey] ?? false;\n const visibleSlots = isExpanded\n ? day.slots\n : day.slots.slice(0, maxInitialSlotsPerDay);\n const hiddenCount = day.slots.length - visibleSlots.length;\n\n const isDayEmpty = day.slots.length === 0;\n\n return (\n // Use a plain <div role=\"group\"> rather than <section> — a\n // <section> with aria-labelledby is a landmark, and nesting many\n // landmarks inside the outer slot-grid landmark fails axe's\n // landmark-unique rule.\n <div\n key={dayKey}\n role=\"group\"\n className={columnVariants()}\n aria-labelledby={headerId}\n data-empty={isDayEmpty || undefined}\n >\n <h3 id={headerId} className={dayHeaderVariants({ size })}>\n {headerLabel}\n </h3>\n {isDayEmpty ? (\n <p className=\"type-body-sm ds:text-[var(--muted-foreground)] ds:m-0\">\n {t('slotGrid.noSlots')}\n </p>\n ) : null}\n <ul className={slotListVariants()}>\n {visibleSlots.map((slot) => {\n const ariaLabelSlot = formatSlotAriaLabel(\n slot,\n day,\n locale,\n timeZone,\n ({ time, day: dayLabel }) =>\n t('slotGrid.slotLabel', { time, day: dayLabel }),\n );\n const isSelected = selectedSlotId === slot.id;\n const isSlotDisabled =\n slot.available === false || isInteractionSuppressed;\n return (\n <li key={slot.id}>\n <button\n type=\"button\"\n className={slotButtonVariants({ size })}\n aria-label={ariaLabelSlot}\n aria-pressed={isSelected}\n disabled={isSlotDisabled}\n aria-disabled={isSlotDisabled || undefined}\n data-slot-id={slot.id}\n onClick={() => handleSlotClick(slot, day)}\n onKeyDown={(event) =>\n handleSlotKeyDown(event, slot, day)\n }\n >\n {formatSlotTime(slot, locale, timeZone)}\n </button>\n </li>\n );\n })}\n </ul>\n {hiddenCount > 0 ? (\n <Button\n intent=\"ghost\"\n size={size === 'lg' ? 'md' : 'sm'}\n onClick={() => handleShowMore(day, dayKey)}\n disabled={isInteractionSuppressed}\n >\n {t('slotGrid.showMore', { count: hiddenCount })}\n </Button>\n ) : null}\n </div>\n );\n })}\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={resolvedAriaLabel}\n className={slotGridVariants({ size, className })}\n data-component=\"slot-grid\"\n data-state={state}\n {...rest}\n >\n <div className={toolbarVariants()}>\n <IconButton\n icon={<ChevronStartIcon />}\n aria-label={t('slotGrid.previousDays')}\n size={size === 'lg' ? 'md' : 'sm'}\n intent=\"ghost\"\n flipIconInRtl\n disabled={!hasPrevious || isInteractionSuppressed}\n onClick={onPreviousDays}\n />\n {showNextAvailable ? (\n <Button\n intent=\"secondary\"\n size={size === 'lg' ? 'md' : 'sm'}\n disabled={isInteractionSuppressed}\n onClick={onJumpToNextAvailable}\n >\n {t('slotGrid.nextAvailable')}\n </Button>\n ) : (\n <span aria-hidden=\"true\" />\n )}\n <IconButton\n icon={<ChevronEndIcon />}\n aria-label={t('slotGrid.nextDays')}\n size={size === 'lg' ? 'md' : 'sm'}\n intent=\"ghost\"\n flipIconInRtl\n disabled={!hasNext || isInteractionSuppressed}\n onClick={onNextDays}\n />\n </div>\n\n {body}\n </div>\n );\n },\n);\n\nSlotGrid.displayName = 'SlotGrid';\n"],"names":["slotGridVariants","cva","toolbarVariants","COLS_MAX","columnsVariants","clampCols","count","columnVariants","dayHeaderVariants","slotListVariants","slotButtonVariants","toDate","value","parseISO","isSameDate","a","b","isSameDay","findFirstAvailable","days","day","slot","s","formatDayHeader","date","locale","timeZone","formatSlotTime","start","fmt","end","formatSlotAriaLabel","template","time","dayLabel","ChevronStartIcon","jsx","ChevronEndIcon","SlotGrid","forwardRef","state","size","selectedSlotId","maxInitialSlotsPerDay","localeProp","hasPrevious","hasNext","showNextAvailable","ariaLabel","onSlotSelect","onPreviousDays","onNextDays","onShowMore","onJumpToNextAvailable","className","rest","ref","t","i18n","useTranslation","idBase","useId","expanded","setExpanded","useState","isLoading","isDisabled","isInteractionSuppressed","isEmpty","resolvedAriaLabel","handleSlotClick","useCallback","handleShowMore","key","prev","handleSlotKeyDown","event","columnCount","columnsClass","body","_","colIdx","jsxs","Skeleton","rowIdx","EmptyState","dayIdx","dayKey","headerLabel","headerId","visibleSlots","hiddenCount","isDayEmpty","ariaLabelSlot","isSelected","isSlotDisabled","Button","IconButton"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { jsx as r, jsxs as f } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef as q, useRef as F, useState as E, useEffect as G } from "react";
|
|
3
3
|
import { c as B } from "./index-D2ZczOXr.js";
|
|
4
4
|
import { useTranslation as H } from "react-i18next";
|
|
5
|
-
import { T as v } from "./tooltip-
|
|
5
|
+
import { T as v } from "./tooltip-ClJd0ciy.js";
|
|
6
6
|
import { X as J } from "./x-CCcI3eJp.js";
|
|
7
7
|
const V = B(
|
|
8
8
|
[
|
|
@@ -252,4 +252,4 @@ M.displayName = "Tag";
|
|
|
252
252
|
export {
|
|
253
253
|
M as T
|
|
254
254
|
};
|
|
255
|
-
//# sourceMappingURL=tag-
|
|
255
|
+
//# sourceMappingURL=tag-bBPAvXyh.js.map
|