@seekalfred/primitives 0.4.0 → 0.5.1
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/accordion/accordion.d.ts +5 -5
- package/dist/accordion/accordion.js +13 -8
- package/dist/accordion/accordion.js.map +1 -1
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +4 -2
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +1 -1
- package/dist/alert-dialog/alert-dialog.js +4 -2
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/avatar/avatar.d.ts +1 -1
- package/dist/avatar/avatar.js +6 -4
- package/dist/avatar/avatar.js.map +1 -1
- package/dist/badge/badge.d.ts +2 -2
- package/dist/badge/badge.js +4 -2
- package/dist/badge/badge.js.map +1 -1
- package/dist/breadcrumb/breadcrumb.d.ts +3 -3
- package/dist/breadcrumb/breadcrumb.js +7 -4
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/button/button.d.ts +3 -3
- package/dist/button/button.js +4 -2
- package/dist/button/button.js.map +1 -1
- package/dist/calendar/calendar.d.ts +2 -2
- package/dist/calendar/calendar.js +7 -5
- package/dist/calendar/calendar.js.map +1 -1
- package/dist/card/card.d.ts +6 -6
- package/dist/card/card.js +19 -12
- package/dist/card/card.js.map +1 -1
- package/dist/checkbox/checkbox.d.ts +2 -2
- package/dist/checkbox/checkbox.js +4 -2
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/dialog/dialog.d.ts +3 -3
- package/dist/dialog/dialog.js +7 -4
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/input/input.d.ts +2 -2
- package/dist/input/input.js +4 -2
- package/dist/input/input.js.map +1 -1
- package/dist/label/label.d.ts +1 -1
- package/dist/label/label.js +4 -2
- package/dist/label/label.js.map +1 -1
- package/dist/lib/cn.d.ts +55 -5
- package/dist/lib/cn.js +64 -6
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/merge-refs.js +22 -0
- package/dist/lib/merge-refs.js.map +1 -0
- package/dist/menu/menu.d.ts +5 -5
- package/dist/menu/menu.js +10 -6
- package/dist/menu/menu.js.map +1 -1
- package/dist/popover/popover.d.ts +2 -2
- package/dist/popover/popover.js +4 -2
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +2 -2
- package/dist/progress/progress.js +7 -5
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/radio-group.d.ts +3 -3
- package/dist/radio-group/radio-group.js +9 -6
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/scroll-area/scroll-area.d.ts +7 -6
- package/dist/scroll-area/scroll-area.js +6 -4
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/select/select.d.ts +3 -3
- package/dist/select/select.js +9 -6
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/separator/separator.js +4 -2
- package/dist/separator/separator.js.map +1 -1
- package/dist/sheet/sheet.d.ts +1 -1
- package/dist/sheet/sheet.js +7 -4
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/skeleton/skeleton.d.ts +1 -1
- package/dist/skeleton/skeleton.js +4 -2
- package/dist/skeleton/skeleton.js.map +1 -1
- package/dist/slider/slider.d.ts +2 -2
- package/dist/slider/slider.js +6 -4
- package/dist/slider/slider.js.map +1 -1
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +4 -2
- package/dist/spinner/spinner.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/switch/switch.d.ts +2 -2
- package/dist/switch/switch.js +4 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/table.d.ts +4 -4
- package/dist/table/table.js +18 -17
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.d.ts +4 -4
- package/dist/tabs/tabs.js +13 -8
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/textarea/textarea.d.ts +1 -1
- package/dist/textarea/textarea.js +4 -2
- package/dist/textarea/textarea.js.map +1 -1
- package/dist/toast/toast.d.ts +2 -2
- package/dist/toast/toast.js +4 -2
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +2 -2
- package/dist/tooltip/tooltip.js +4 -2
- package/dist/tooltip/tooltip.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumb.js","names":["Breadcrumbs","AriaBreadcrumbs","AriaBreadcrumb","AriaLink"],"sources":["../../src/breadcrumb/breadcrumb.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Breadcrumb as AriaBreadcrumb,\n Breadcrumbs as AriaBreadcrumbs,\n Link as AriaLink,\n type BreadcrumbProps as AriaBreadcrumbProps,\n type BreadcrumbsProps as AriaBreadcrumbsProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Breadcrumbs, ported from the legacy shadcn/Radix component onto React\n * Aria's Breadcrumbs collection.\n *\n * SIX LEGACY PARTS COLLAPSE TO TWO, and not for tidiness -- React Aria's\n * `Breadcrumbs` is a real collection, so it knows which item is last:\n *\n * | legacy | uses | fate |\n * | ------------------- | ---- | ------------------------------------------- |\n * | Breadcrumb | 2 | -> `Breadcrumbs` (nav + ol) |\n * | BreadcrumbList | 2 | dropped -- `Breadcrumbs` IS the `<ol>` |\n * | BreadcrumbItem | 6 | -> `BreadcrumbItem` |\n * | BreadcrumbLink | 4 | merged into `BreadcrumbItem` as `href` |\n * | BreadcrumbPage | 2 | dropped -- the last crumb is auto-current |\n * | BreadcrumbSeparator | 4 | dropped -- every non-last crumb draws one |\n * | BreadcrumbEllipsis | 0 | dropped -- zero call sites |\n *\n * The collection is also why the separator CANNOT stay a component: RAC\n * builds its item list from the children, so a `<BreadcrumbSeparator/>`\n * sibling would be a non-item node inside a collection and there is nowhere\n * legal to put it.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted -- the component would render unstyled with no\n * error anywhere.\n */\nexport const breadcrumbsVariants = cva([\n \"flex flex-wrap items-center gap-2\",\n\n // The user-agent `<ol>`: decimal markers, a 40px inline padding and a 1em\n // block margin. The reset in @seekalfred/styles is OPT-IN, so a consumer\n // may load none -- unreset, the trail renders as a numbered, indented\n // list. The Accordion shipped its panel in the browser's serif for exactly\n // this family of reason.\n \"m-0 list-none p-0\",\n\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"font-sans text-sm leading-(--leading-normal) text-muted-foreground\",\n\n // box-border: content-box would add any padding a consumer sets on top of\n // a constrained width. The Alert shipped overflowing by 46px that way.\n \"box-border break-words\",\n]);\n\nexport const breadcrumbItemVariants = cva([\n // `group` is load-bearing: the separator hides itself from the current\n // crumb via group-data-[current], and RAC puts data-current on the <li>,\n // which CSS cannot look upward to from the span.\n \"group inline-flex items-center gap-2\",\n]);\n\nexport const breadcrumbLinkVariants = cva([\n \"inline-flex items-center gap-1.5\",\n\n // The user-agent `<a>`: -webkit-link blue and a permanent underline. Left\n // alone, every crumb is blue, and the hover underline below stops being a\n // state cue at all because it is already on.\n \"font-sans text-muted-foreground no-underline\",\n \"rounded-sm cursor-pointer\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n\n // Hover is NOT colour-only. The legacy component moved muted-foreground to\n // foreground and nothing else; under forced-colors the user's palette\n // overrides both and the cue vanishes entirely (WCAG 1.4.1, and\n // ARCHITECTURE section 5 rule 6). The underline survives any colour mode.\n \"data-[hovered]:text-foreground data-[hovered]:underline\",\n\n // outline-solid is load-bearing, not decoration. `outline-none` sets BOTH\n // outline-style:none and --tw-outline-style:none, and the width utility\n // resolves its style from that same variable -- so a focus variant can\n // apply a width and a colour while faithfully re-applying style:none and\n // painting nothing. Button shipped exactly that: keyboard-focusable with\n // no visible ring, and every gate stayed green because axe has no\n // focus-indicator rule.\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n\n // The current crumb. RAC forces isDisabled on it, so React Aria renders a\n // <span role=\"link\" aria-disabled=\"true\" aria-current=\"page\"> with no\n // tabIndex -- byte for byte the legacy BreadcrumbPage, minus the chance of\n // hand-writing it onto the wrong item.\n \"data-[current]:text-foreground data-[current]:font-normal data-[current]:cursor-default\",\n\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n]);\n\n/**\n * A navigation trail.\n *\n * The last child is the current page: give it no `href`, and React Aria\n * marks it `aria-current=\"page\"` and takes it out of the tab order.\n *\n * @example\n * <Breadcrumbs>\n * <BreadcrumbItem href=\"/\">Home</BreadcrumbItem>\n * <BreadcrumbItem href=\"/design-thinking\">Design Thinking</BreadcrumbItem>\n * <BreadcrumbItem>User Personas</BreadcrumbItem>\n * </Breadcrumbs>\n */\nexport interface BreadcrumbsProps\n extends Omit<\n AriaBreadcrumbsProps<object>,\n \"className\" | \"style\" | \"children\" | \"items\" | \"dependencies\"\n > {\n /**\n * `BreadcrumbItem` elements. Static children only: `items` and the render\n * function are not exposed, because no call site builds a trail from data\n * and the generic they require leaks into every consumer's types.\n */\n children?: ReactNode;\n /**\n * Extra classes, merged last so they win conflicts with ours.\n *\n * They land on the `<ol>`, where our own classes are, so `tailwind-merge`\n * can actually resolve a conflict. The `<nav>` is an unstyled landmark\n * wrapper.\n */\n className?: string;\n /**\n * Accessible name for the navigation landmark. Only worth changing when a\n * page has more than one trail.\n *\n * @default \"Breadcrumb\"\n */\n \"aria-label\"?: string;\n}\n\nexport function Breadcrumbs({\n className,\n children,\n \"aria-label\": ariaLabel = \"Breadcrumb\",\n ...props\n}: BreadcrumbsProps) {\n return (\n // RAC renders the <ol> and puts its aria-label there; it does NOT render\n // a landmark. Dropping the <nav> would silently remove the thing screen\n // reader users actually navigate by, so it is added back here. Both\n // carry the same name deliberately -- RAC falls back to a localized\n // \"Breadcrumbs\" when aria-label is absent, so the <ol> is named either\n // way and letting the two names diverge would be the worse outcome.\n <nav aria-label={ariaLabel} data-slot=\"breadcrumb\">\n <AriaBreadcrumbs\n {...props}\n aria-label={ariaLabel}\n data-slot=\"breadcrumb-list\"\n className={cn(breadcrumbsVariants(), className)}\n >\n {children}\n </AriaBreadcrumbs>\n </nav>\n );\n}\n\n/**\n * One crumb.\n *\n * @example\n * <BreadcrumbItem href=\"/reports\">Reports</BreadcrumbItem>\n */\nexport interface BreadcrumbItemProps\n extends Omit<AriaBreadcrumbProps, \"className\" | \"style\" | \"children\"> {\n /**\n * The crumb's label.\n *\n * An icon-only crumb has no accessible name, which is an axe `link-name`\n * violation -- both legacy call sites ship one. Pass `aria-label` when the\n * only child is a glyph.\n */\n children?: ReactNode;\n /** Extra classes for the `<li>`, merged last. */\n className?: string;\n /** Destination. Omit it on the last crumb: that one is the current page. */\n href?: string;\n /** Anchor target, forwarded when `href` is set. */\n target?: string;\n /** Anchor rel, forwarded when `href` is set. */\n rel?: string;\n}\n\nexport function BreadcrumbItem({\n className,\n children,\n href,\n target,\n rel,\n \"aria-label\": ariaLabel,\n ...props\n}: BreadcrumbItemProps) {\n return (\n <AriaBreadcrumb\n {...props}\n data-slot=\"breadcrumb-item\"\n className={cn(breadcrumbItemVariants(), className)}\n >\n <AriaLink\n href={href}\n target={target}\n rel={rel}\n aria-label={ariaLabel}\n data-slot=\"breadcrumb-link\"\n className={breadcrumbLinkVariants()}\n >\n {children}\n </AriaLink>\n {/*\n A real element, not a ::after. Generated content is announced by some\n screen readers, and it cannot be read back from the DOM at all -- a\n separator that stopped rendering would be invisible to the suite.\n\n \"/\" rather than the legacy lucide ChevronRight: this library ships no\n icon dependency, all four legacy separators are the default, and a\n chevron glyph does not mirror in RTL while a slash needs no mirroring\n at all.\n */}\n <span\n aria-hidden=\"true\"\n data-slot=\"breadcrumb-separator\"\n className=\"shrink-0 select-none group-data-[current]:hidden\"\n >\n /\n </span>\n </AriaBreadcrumb>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,sBAAsB,IAAI;CACrC;CAOA;CAIA;CAIA;AACF,CAAC;AAED,MAAa,yBAAyB,IAAI,CAIxC,sCACF,CAAC;AAED,MAAa,yBAAyB,IAAI;CACxC;CAKA;CACA;CACA;CAMA;CASA;CACA;CACA;CACA;CACA;CAMA;CAEA;CACA;AACF,CAAC;AA2CD,SAAgBA,cAAY,EAC1B,WACA,UACA,cAAc,YAAY,cAC1B,GAAG,SACgB;CACnB,OAOE,oBAAC,OAAD;EAAK,cAAY;EAAW,aAAU;EACpC,UAAA,oBAACC,aAAD;GACE,GAAI;GACJ,cAAY;GACZ,aAAU;GACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;GAE7C;EACc,CAAA;CACd,CAAA;AAET;AA4BA,SAAgB,eAAe,EAC7B,WACA,UACA,MACA,QACA,KACA,cAAc,WACd,GAAG,SACmB;CACtB,OACE,qBAACC,YAAD;EACE,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,uBAAuB,GAAG,SAAS;EAHnD,UAAA,CAKE,oBAACC,MAAD;GACQ;GACE;GACH;GACL,cAAY;GACZ,aAAU;GACV,WAAW,uBAAuB;GAEjC;EACO,CAAA,GAWV,oBAAC,QAAD;GACE,eAAY;GACZ,aAAU;GACV,WAAU;GACX,UAAA;EAEK,CAAA,CACQ;;AAEpB"}
|
|
1
|
+
{"version":3,"file":"breadcrumb.js","names":["Breadcrumbs","AriaBreadcrumbs","AriaBreadcrumb","AriaLink"],"sources":["../../src/breadcrumb/breadcrumb.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Breadcrumb as AriaBreadcrumb,\n Breadcrumbs as AriaBreadcrumbs,\n Link as AriaLink,\n type BreadcrumbProps as AriaBreadcrumbProps,\n type BreadcrumbsProps as AriaBreadcrumbsProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Breadcrumbs, ported from the legacy shadcn/Radix component onto React\n * Aria's Breadcrumbs collection.\n *\n * SIX LEGACY PARTS COLLAPSE TO TWO, and not for tidiness -- React Aria's\n * `Breadcrumbs` is a real collection, so it knows which item is last:\n *\n * | legacy | uses | fate |\n * | ------------------- | ---- | ------------------------------------------- |\n * | Breadcrumb | 2 | -> `Breadcrumbs` (nav + ol) |\n * | BreadcrumbList | 2 | dropped -- `Breadcrumbs` IS the `<ol>` |\n * | BreadcrumbItem | 6 | -> `BreadcrumbItem` |\n * | BreadcrumbLink | 4 | merged into `BreadcrumbItem` as `href` |\n * | BreadcrumbPage | 2 | dropped -- the last crumb is auto-current |\n * | BreadcrumbSeparator | 4 | dropped -- every non-last crumb draws one |\n * | BreadcrumbEllipsis | 0 | dropped -- zero call sites |\n *\n * The collection is also why the separator CANNOT stay a component: RAC\n * builds its item list from the children, so a `<BreadcrumbSeparator/>`\n * sibling would be a non-item node inside a collection and there is nowhere\n * legal to put it.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted -- the component would render unstyled with no\n * error anywhere.\n */\nexport const breadcrumbsVariants = cva([\n \"flex flex-wrap items-center gap-2\",\n\n // The user-agent `<ol>`: decimal markers, a 40px inline padding and a 1em\n // block margin. The reset in @seekalfred/styles is OPT-IN, so a consumer\n // may load none -- unreset, the trail renders as a numbered, indented\n // list. The Accordion shipped its panel in the browser's serif for exactly\n // this family of reason.\n \"m-0 list-none p-0\",\n\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"font-sans text-sm leading-(--leading-normal) text-muted-foreground\",\n\n // box-border: content-box would add any padding a consumer sets on top of\n // a constrained width. The Alert shipped overflowing by 46px that way.\n \"box-border break-words\",\n]);\n\nexport const breadcrumbItemVariants = cva([\n // `group` is load-bearing: the separator hides itself from the current\n // crumb via group-data-[current], and RAC puts data-current on the <li>,\n // which CSS cannot look upward to from the span.\n \"group inline-flex items-center gap-2\",\n]);\n\nexport const breadcrumbLinkVariants = cva([\n \"inline-flex items-center gap-1.5\",\n\n // The user-agent `<a>`: -webkit-link blue and a permanent underline. Left\n // alone, every crumb is blue, and the hover underline below stops being a\n // state cue at all because it is already on.\n \"font-sans text-muted-foreground no-underline\",\n \"rounded-sm cursor-pointer\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n\n // Hover is NOT colour-only. The legacy component moved muted-foreground to\n // foreground and nothing else; under forced-colors the user's palette\n // overrides both and the cue vanishes entirely (WCAG 1.4.1, and\n // ARCHITECTURE section 5 rule 6). The underline survives any colour mode.\n \"data-[hovered]:text-foreground data-[hovered]:underline\",\n\n // outline-solid is load-bearing, not decoration. `outline-none` sets BOTH\n // outline-style:none and --tw-outline-style:none, and the width utility\n // resolves its style from that same variable -- so a focus variant can\n // apply a width and a colour while faithfully re-applying style:none and\n // painting nothing. Button shipped exactly that: keyboard-focusable with\n // no visible ring, and every gate stayed green because axe has no\n // focus-indicator rule.\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n\n // The current crumb. RAC forces isDisabled on it, so React Aria renders a\n // <span role=\"link\" aria-disabled=\"true\" aria-current=\"page\"> with no\n // tabIndex -- byte for byte the legacy BreadcrumbPage, minus the chance of\n // hand-writing it onto the wrong item.\n \"data-[current]:text-foreground data-[current]:font-normal data-[current]:cursor-default\",\n\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n]);\n\n/**\n * A navigation trail.\n *\n * The last child is the current page: give it no `href`, and React Aria\n * marks it `aria-current=\"page\"` and takes it out of the tab order.\n *\n * @example\n * <Breadcrumbs>\n * <BreadcrumbItem href=\"/\">Home</BreadcrumbItem>\n * <BreadcrumbItem href=\"/design-thinking\">Design Thinking</BreadcrumbItem>\n * <BreadcrumbItem>User Personas</BreadcrumbItem>\n * </Breadcrumbs>\n */\nexport interface BreadcrumbsProps\n extends Omit<\n AriaBreadcrumbsProps<object>,\n \"className\" | \"style\" | \"children\" | \"items\" | \"dependencies\"\n > {\n /**\n * `BreadcrumbItem` elements. Static children only: `items` and the render\n * function are not exposed, because no call site builds a trail from data\n * and the generic they require leaks into every consumer's types.\n */\n children?: ReactNode;\n /**\n * Extra classes, merged last so they win conflicts with ours.\n *\n * They land on the `<ol>`, where our own classes are, so `tailwind-merge`\n * can actually resolve a conflict. The `<nav>` is an unstyled landmark\n * wrapper.\n */\n className?: string;\n /**\n * Accessible name for the navigation landmark. Only worth changing when a\n * page has more than one trail.\n *\n * @default \"Breadcrumb\"\n */\n \"aria-label\"?: string;\n}\n\nexport const Breadcrumbs = forwardRef<HTMLOListElement, BreadcrumbsProps>(function Breadcrumbs(\n { className, children, \"aria-label\": ariaLabel = \"Breadcrumb\", ...props },\n ref,\n) {\n return (\n // RAC renders the <ol> and puts its aria-label there; it does NOT render\n // a landmark. Dropping the <nav> would silently remove the thing screen\n // reader users actually navigate by, so it is added back here. Both\n // carry the same name deliberately -- RAC falls back to a localized\n // \"Breadcrumbs\" when aria-label is absent, so the <ol> is named either\n // way and letting the two names diverge would be the worse outcome.\n <nav aria-label={ariaLabel} data-slot=\"breadcrumb\">\n <AriaBreadcrumbs\n // The ref goes on the <ol>, not the <nav>: the nav is this\n // component's OWN added landmark wrapper, not something React Aria\n // renders, so the real list element is one level in.\n ref={ref}\n {...props}\n aria-label={ariaLabel}\n data-slot=\"breadcrumb-list\"\n className={cn(breadcrumbsVariants(), className)}\n >\n {children}\n </AriaBreadcrumbs>\n </nav>\n );\n});\n\n/**\n * One crumb.\n *\n * @example\n * <BreadcrumbItem href=\"/reports\">Reports</BreadcrumbItem>\n */\nexport interface BreadcrumbItemProps\n extends Omit<AriaBreadcrumbProps, \"className\" | \"style\" | \"children\"> {\n /**\n * The crumb's label.\n *\n * An icon-only crumb has no accessible name, which is an axe `link-name`\n * violation -- both legacy call sites ship one. Pass `aria-label` when the\n * only child is a glyph.\n */\n children?: ReactNode;\n /** Extra classes for the `<li>`, merged last. */\n className?: string;\n /** Destination. Omit it on the last crumb: that one is the current page. */\n href?: string;\n /** Anchor target, forwarded when `href` is set. */\n target?: string;\n /** Anchor rel, forwarded when `href` is set. */\n rel?: string;\n}\n\nexport const BreadcrumbItem = forwardRef<HTMLAnchorElement, BreadcrumbItemProps>(\n function BreadcrumbItem(\n { className, children, href, target, rel, \"aria-label\": ariaLabel, ...props },\n ref,\n ) {\n return (\n <AriaBreadcrumb\n {...props}\n data-slot=\"breadcrumb-item\"\n className={cn(breadcrumbItemVariants(), className)}\n >\n <AriaLink\n // The ref goes on the anchor, not the <li> RAC wraps it in: the list\n // item is the collection's own item container, not the interactive\n // element -- the link is what a call site actually needs to focus.\n ref={ref}\n href={href}\n target={target}\n rel={rel}\n aria-label={ariaLabel}\n data-slot=\"breadcrumb-link\"\n className={breadcrumbLinkVariants()}\n >\n {children}\n </AriaLink>\n {/*\n A real element, not a ::after. Generated content is announced by some\n screen readers, and it cannot be read back from the DOM at all -- a\n separator that stopped rendering would be invisible to the suite.\n\n \"/\" rather than the legacy lucide ChevronRight: this library ships no\n icon dependency, all four legacy separators are the default, and a\n chevron glyph does not mirror in RTL while a slash needs no mirroring\n at all.\n */}\n <span\n aria-hidden=\"true\"\n data-slot=\"breadcrumb-separator\"\n className=\"shrink-0 select-none group-data-[current]:hidden\"\n >\n /\n </span>\n </AriaBreadcrumb>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,sBAAsB,IAAI;CACrC;CAOA;CAIA;CAIA;AACF,CAAC;AAED,MAAa,yBAAyB,IAAI,CAIxC,sCACF,CAAC;AAED,MAAa,yBAAyB,IAAI;CACxC;CAKA;CACA;CACA;CAMA;CASA;CACA;CACA;CACA;CACA;CAMA;CAEA;CACA;AACF,CAAC;AA2CD,MAAaA,gBAAc,WAA+C,SAASA,cACjF,EAAE,WAAW,UAAU,cAAc,YAAY,cAAc,GAAG,SAClE,KACA;CACA,OAOE,oBAAC,OAAD;EAAK,cAAY;EAAW,aAAU;EACpC,UAAA,oBAACC,aAAD;GAIO;GACL,GAAI;GACJ,cAAY;GACZ,aAAU;GACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;GAE7C;EACc,CAAA;CACd,CAAA;AAET,CAAC;AA4BD,MAAa,iBAAiB,WAC5B,SAAS,eACP,EAAE,WAAW,UAAU,MAAM,QAAQ,KAAK,cAAc,WAAW,GAAG,SACtE,KACA;CACA,OACE,qBAACC,YAAD;EACE,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,uBAAuB,GAAG,SAAS;EAHnD,UAAA,CAKE,oBAACC,MAAD;GAIO;GACC;GACE;GACH;GACL,cAAY;GACZ,aAAU;GACV,WAAW,uBAAuB;GAEjC;EACO,CAAA,GAWV,oBAAC,QAAD;GACE,eAAY;GACZ,aAAU;GACV,WAAU;GACX,UAAA;EAEK,CAAA,CACQ;;AAEpB,CACF"}
|
package/dist/button/button.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { VariantProps } from "class-variance-authority";
|
|
2
|
-
import { ButtonProps } from "react-aria-components";
|
|
3
2
|
import { ReactNode } from "react";
|
|
3
|
+
import { ButtonProps } from "react-aria-components";
|
|
4
4
|
//#region src/button/button.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* Variant classes, exported so a consumer can reuse the exact classes we ship
|
|
@@ -21,7 +21,7 @@ import { ReactNode } from "react";
|
|
|
21
21
|
* touch and keyboard uniformly. The original had no pressed state at all.
|
|
22
22
|
*/
|
|
23
23
|
export declare const buttonVariants: (props?: ({
|
|
24
|
-
variant?: "
|
|
24
|
+
variant?: "primary" | "outline" | "link" | "secondary" | "outlineNeutral" | "ghost" | "destructive" | null | undefined;
|
|
25
25
|
size?: "sm" | "md" | "lg" | "icon" | null | undefined;
|
|
26
26
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
27
27
|
/**
|
|
@@ -73,7 +73,7 @@ interface ButtonProps$1 extends Omit<ButtonProps, "className" | "style">, Varian
|
|
|
73
73
|
* <Button endIcon={<ChevronIcon aria-hidden />}>Continue</Button>
|
|
74
74
|
* <Button variant="outline" size="icon" aria-label="Close"><XIcon /></Button>
|
|
75
75
|
*/
|
|
76
|
-
declare
|
|
76
|
+
declare const Button$1: import("react").ForwardRefExoticComponent<ButtonProps$1 & import("react").RefAttributes<HTMLButtonElement>>;
|
|
77
77
|
//#endregion
|
|
78
78
|
export { Button$1 as Button, ButtonProps$1 as ButtonProps };
|
|
79
79
|
//# sourceMappingURL=button.d.ts.map
|
package/dist/button/button.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
+
import { forwardRef } from "react";
|
|
4
5
|
import { Button } from "react-aria-components";
|
|
5
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region src/button/button.tsx
|
|
@@ -106,8 +107,9 @@ const buttonVariants = cva([
|
|
|
106
107
|
* <Button endIcon={<ChevronIcon aria-hidden />}>Continue</Button>
|
|
107
108
|
* <Button variant="outline" size="icon" aria-label="Close"><XIcon /></Button>
|
|
108
109
|
*/
|
|
109
|
-
|
|
110
|
+
const Button$1 = forwardRef(function Button$2({ className, variant, size, startIcon, endIcon, children, ...props }, ref) {
|
|
110
111
|
return /* @__PURE__ */ jsx(Button, {
|
|
112
|
+
ref,
|
|
111
113
|
...props,
|
|
112
114
|
"data-slot": "button",
|
|
113
115
|
className: cn(buttonVariants({
|
|
@@ -120,7 +122,7 @@ function Button$1({ className, variant, size, startIcon, endIcon, children, ...p
|
|
|
120
122
|
endIcon
|
|
121
123
|
] })
|
|
122
124
|
});
|
|
123
|
-
}
|
|
125
|
+
});
|
|
124
126
|
//#endregion
|
|
125
127
|
export { Button$1 as Button, buttonVariants };
|
|
126
128
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":["Button","AriaButton"],"sources":["../../src/button/button.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport { Button as AriaButton, type ButtonProps as AriaButtonProps } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Variant classes, exported so a consumer can reuse the exact classes we ship\n * rather than guessing at them. This is also the supported answer for the\n * link-shaped cases React Aria's Button cannot cover: it never renders an\n * anchor, so `<a className={buttonVariants({ variant: \"outline\" })}>` is how a\n * real navigation target gets button styling until the Link primitive lands.\n *\n * Ported from the legacy shadcn Button. The variant and size vocabulary is\n * deliberately identical so existing call sites migrate by rename, with six\n * corrections that the Alfred contracts require -- each marked below.\n *\n * Every string here is a STATIC literal. Tailwind scans source text, so a class\n * assembled at runtime is invisible to it and would simply never be emitted.\n *\n * State comes from React Aria data attributes rather than pseudo-classes:\n * data-[focus-visible] is keyboard focus only, and data-[pressed] covers mouse,\n * touch and keyboard uniformly. The original had no pressed state at all.\n */\nexport const buttonVariants = cva(\n [\n \"inline-flex items-center justify-center gap-(--density-control-gap) shrink-0 whitespace-nowrap\",\n // No leading-* here on purpose. tailwind-merge treats a later text-* class\n // as owning line-height (a Tailwind v3 assumption, where text-base carried\n // one), so a leading-* in the base string is silently STRIPPED by cn() once\n // a size variant adds text-base. It is not needed either: the control has a\n // fixed height and centres with flex, so line-height cannot overflow it.\n // A component with auto height must put leading-* AFTER its text-* class.\n \"rounded-md font-sans font-medium\",\n \"cursor-pointer select-none\",\n\n // font-sans above is part of the same problem: a <button> does not inherit\n // the page font, so without a reset it renders in the user-agent face\n // (Arial in Chromium) rather than the design system's. The same applies to\n // bg-transparent: an unstyled <button> paints the user-agent ButtonFace\n // grey -- which is what surfaces the moment a fill class fails to resolve,\n // and is why the paint stories compare against tokens rather than checking\n // for transparency.\n //\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may never load\n // one. A bare <button> then keeps its user-agent border (2px, black, in\n // Chromium) and inline padding, which is a black box around every variant\n // that does not set a border of its own. The original inherited a zeroed\n // border from Tailwind Preflight in the app; a library component cannot\n // assume the consumer supplied one.\n \"border-(length:--border-width) border-transparent bg-transparent\",\n\n // Icon handling, ported verbatim: an svg child is sized unless the caller\n // already said otherwise, and never swallows the press.\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n\n // CORRECTION 1 -- the original focused with `ring-[3px]`, which is a\n // box-shadow. Box-shadows are discarded under forced-colors, taking the\n // only focus indication with them, and axe has no rule that would catch it\n // (ARCHITECTURE section 5, rule 6). A real outline survives.\n //\n // outline-solid on the focus variant is load-bearing, not decoration.\n // `outline-none` sets BOTH outline-style:none and --tw-outline-style:none,\n // and Tailwind's outline-width utility resolves its style from that same\n // variable -- so the focus variant applied a width and a colour while\n // faithfully re-applying style:none, and painted nothing. The button was\n // keyboard-focusable with no visible ring. axe has no focus-indicator\n // rule, so every automated gate stayed green; the KeyboardFocus story now\n // asserts outlineStyle directly.\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n\n // CORRECTION 2 -- `disabled:pointer-events-none` also kills the cursor, so\n // the control reads as inert rather than as unavailable. React Aria already\n // blocks activation; this only has to say so.\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n\n // React Aria's own pending state. The original had no equivalent.\n \"data-[pending]:cursor-progress\",\n ],\n {\n variants: {\n variant: {\n // CORRECTION 3 -- the legacy button filled this variant with the brand\n // gradient. It is flat here, because the brand system caps\n // --gradient-brand at one intentional element per view and keeps that\n // budget for the logo and progress bars: \"a gradient on every button\n // spends that budget everywhere. Ours is the flat primary.\"\n //\n // CORRECTION 4 -- the label is ink, not white. Brand orange #FF8431\n // carries white at 2.44:1; ink clears it at 8.35:1, which is the brand\n // system's own --text-on-orange rule. Its Button ships white only as a\n // dated, individually signed-off exception, and this library's contrast\n // contract is build-failing with no exemption mechanism to inherit it.\n //\n // CORRECTION 7 -- hover and pressed are their own TOKENS, not opacity\n // modifiers on the fill. `bg-primary/90` lightened the button toward the\n // page, which reads as receding rather than interactive and was nearly\n // invisible; it also made the label pair unmeasurable, because an alpha\n // composite depends on whatever happens to sit behind it. The brand system\n // steps DOWN its ramp instead -- 500 rest, 600 hover, 700 pressed -- and\n // every one of those fills is now in the contrast contract.\n primary: [\n \"bg-primary text-primary-foreground shadow-surface\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-primary-hover\",\n \"data-[pressed]:bg-primary-pressed\",\n ],\n secondary: [\n \"bg-secondary text-secondary-foreground shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-secondary-hover\",\n \"data-[pressed]:bg-secondary-pressed\",\n ],\n // primary-strong, never primary: the brand orange is 2.44:1 on the page\n // and cannot legally be a label OR a border. That is the whole reason\n // the token exists.\n outline: [\n \"bg-background text-primary-strong border-(length:--border-width) border-primary-strong shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-muted\",\n \"data-[pressed]:bg-muted data-[pressed]:border-foreground\",\n ],\n /*\n * The NEUTRAL outline, and the product's single most-used button:\n * 99 call sites against 32 for the brand-coloured one.\n *\n * It exists because `outline` above is brand-toned -- orange border,\n * orange label -- which is what the product calls `secondaryOutline`.\n * Its plain `outline` is this: a grey border and ordinary body text,\n * for a button that must not compete with the primary action next to\n * it. One name could not cover both, and renaming `outline` would have\n * silently repainted every existing call site of ours instead.\n *\n * `border` rather than `primary-strong`: --color-border is the token\n * already held to the 3:1 non-text floor of WCAG 1.4.11, which is what\n * a border separating a control from the page has to clear.\n */\n outlineNeutral: [\n \"bg-background text-foreground border-(length:--border-width) border-border shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-muted\",\n \"data-[pressed]:bg-muted data-[pressed]:border-foreground\",\n ],\n ghost: [\n \"bg-transparent text-primary-strong\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-muted\",\n // CORRECTION 5 -- pressed must not be background-only. A ghost button\n // has no border at rest, so the pressed border is the non-colour cue\n // that survives forced-colors (rule 6 again).\n \"data-[pressed]:bg-muted data-[pressed]:border-primary-strong\",\n ],\n destructive: [\n \"bg-destructive text-destructive-foreground shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-destructive-hover\",\n \"data-[pressed]:bg-destructive-pressed\",\n ],\n link: [\n \"bg-transparent text-primary-strong underline-offset-(--underline-offset)\",\n \"data-[hovered]:underline\",\n \"data-[pressed]:underline\",\n ],\n },\n size: {\n // Height, padding and label size all come from the control scale, so\n // data-density moves them together. The icon-only variant is square at\n // the md height. The legacy shadcn rule that tightened padding when a\n // child was an icon is gone: it needed three more magic numbers, the\n // brand system has no equivalent, and the gap token now owns icon\n // spacing.\n sm: \"h-(--density-control-h-sm) px-(--density-control-pad-sm) text-sm\",\n md: \"h-(--density-control-h-md) px-(--density-control-pad-md) text-base\",\n lg: \"h-(--density-control-h-lg) px-(--density-control-pad-lg) text-lg\",\n // text-base even though there is no visible label: without it the\n // user-agent default (13.33px in Chromium) leaks in and sizes any\n // em-based child against the wrong root.\n icon: \"size-(--density-control-h-md) px-0 text-base\",\n },\n },\n defaultVariants: {\n variant: \"primary\",\n size: \"md\",\n },\n },\n);\n\n/**\n * Props.\n *\n * CORRECTION 6 -- the legacy Button was a plain `<button>`, so it took\n * `onClick`, `disabled` and Radix's `asChild`. React Aria's Button has none of\n * those: `ButtonProps` explicitly omits `onClick` and `href`. The replacements\n * are `onPress`, `isDisabled`, and `buttonVariants()` on the consumer's own\n * element. See the migration table in the package README.\n *\n * Deliberately NOT intersected with ComponentPropsWithoutRef<\"button\">: that\n * would advertise disabled, tabIndex and role, which React Aria does not\n * implement and silently ignores. React Aria already forwards the native event\n * surface, so nothing is lost.\n */\nexport interface ButtonProps\n extends Omit<AriaButtonProps, \"className\" | \"style\">, VariantProps<typeof buttonVariants> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Icon rendered before the label.\n *\n * Mark the glyph `aria-hidden` -- the label is the accessible name, and a\n * second name source only makes the button announce twice. It is sized by the\n * base `[&_svg]` rule at `--icon-sm` unless it carries a `size-*` class of\n * its own, and spaced by `--density-control-gap`.\n *\n * Two named slots rather than one `icon` plus a position prop: a button can\n * legitimately carry a leading glyph AND a trailing affordance, which one\n * icon and a position cannot express.\n *\n * NOT the route for `size=\"icon\"`. An icon-only button passes its glyph as\n * `children`, which is what gives it its single centred child.\n */\n startIcon?: ReactNode;\n /** Icon rendered after the label. Same rules as {@link ButtonProps.startIcon}. */\n endIcon?: ReactNode;\n}\n\n/**\n * A button.\n *\n * `size=\"icon\"` renders a square button with no visible text, so it **must**\n * carry an `aria-label` -- otherwise it has no accessible name and fails axe's\n * `button-name` rule. The IconOnly story is the guard on that.\n *\n * @example\n * <Button onPress={save}>Save changes</Button>\n * <Button startIcon={<SaveIcon aria-hidden />}>Save changes</Button>\n * <Button endIcon={<ChevronIcon aria-hidden />}>Continue</Button>\n * <Button variant=\"outline\" size=\"icon\" aria-label=\"Close\"><XIcon /></Button>\n */\nexport function Button({\n className,\n variant,\n size,\n startIcon,\n endIcon,\n children,\n ...props\n}: ButtonProps) {\n return (\n <AriaButton\n {...props}\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size }), className)}\n >\n {/*\n * children passes through UNTOUCHED when neither slot is used, so a\n * button with no icon renders byte-identical DOM to before this prop\n * existed. That is what keeps this an additive change rather than a\n * structural one -- and the childOrder assertions in the stories are what\n * hold it true, since they read the button's direct children.\n *\n * The slot branch has to be a render function because React Aria's\n * children are ChildrenOrFunction<ButtonRenderProps>: a consumer may pass\n * a function to read isPressed or isPending, and wrapping the node\n * directly would silently render that function as nothing. The fragment\n * adds no element of its own.\n */}\n {startIcon === undefined && endIcon === undefined\n ? children\n : (renderProps) => (\n <>\n {startIcon}\n {typeof children === \"function\" ? children(renderProps) : children}\n {endIcon}\n </>\n )}\n </AriaButton>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,iBAAiB,IAC5B;CACE;CAOA;CACA;CAgBA;CAIA;CACA;CAeA;CACA;CACA;CACA;CACA;CAKA;CAGA;AACF,GACA;CACE,UAAU;EACR,SAAS;GAoBP,SAAS;IACP;IACA;IACA;IACA;GACF;GACA,WAAW;IACT;IACA;IACA;IACA;GACF;GAIA,SAAS;IACP;IACA;IACA;IACA;GACF;GAgBA,gBAAgB;IACd;IACA;IACA;IACA;GACF;GACA,OAAO;IACL;IACA;IACA;IAIA;GACF;GACA,aAAa;IACX;IACA;IACA;IACA;GACF;GACA,MAAM;IACJ;IACA;IACA;GACF;EACF;EACA,MAAM;GAOJ,IAAI;GACJ,IAAI;GACJ,IAAI;GAIJ,MAAM;EACR;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CACF;;;;;;;;;;;;;;AAqDA,SAAgBA,SAAO,EACrB,WACA,SACA,MACA,WACA,SACA,UACA,GAAG,SACW;CACd,OACE,oBAACC,QAAD;EACE,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,eAAe;GAAE;GAAS;EAAK,CAAC,GAAG,SAAS;EAezD,UAAA,cAAc,KAAA,KAAa,YAAY,KAAA,IACpC,YACC,gBACC,qBAAA,UAAA,EAAA,UAAA;GACG;GACA,OAAO,aAAa,aAAa,SAAS,WAAW,IAAI;GACzD;EACD,EAAA,CAAA;CAEE,CAAA;AAEhB"}
|
|
1
|
+
{"version":3,"file":"button.js","names":["Button","AriaButton"],"sources":["../../src/button/button.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport { Button as AriaButton, type ButtonProps as AriaButtonProps } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Variant classes, exported so a consumer can reuse the exact classes we ship\n * rather than guessing at them. This is also the supported answer for the\n * link-shaped cases React Aria's Button cannot cover: it never renders an\n * anchor, so `<a className={buttonVariants({ variant: \"outline\" })}>` is how a\n * real navigation target gets button styling until the Link primitive lands.\n *\n * Ported from the legacy shadcn Button. The variant and size vocabulary is\n * deliberately identical so existing call sites migrate by rename, with six\n * corrections that the Alfred contracts require -- each marked below.\n *\n * Every string here is a STATIC literal. Tailwind scans source text, so a class\n * assembled at runtime is invisible to it and would simply never be emitted.\n *\n * State comes from React Aria data attributes rather than pseudo-classes:\n * data-[focus-visible] is keyboard focus only, and data-[pressed] covers mouse,\n * touch and keyboard uniformly. The original had no pressed state at all.\n */\nexport const buttonVariants = cva(\n [\n \"inline-flex items-center justify-center gap-(--density-control-gap) shrink-0 whitespace-nowrap\",\n // No leading-* here on purpose. tailwind-merge treats a later text-* class\n // as owning line-height (a Tailwind v3 assumption, where text-base carried\n // one), so a leading-* in the base string is silently STRIPPED by cn() once\n // a size variant adds text-base. It is not needed either: the control has a\n // fixed height and centres with flex, so line-height cannot overflow it.\n // A component with auto height must put leading-* AFTER its text-* class.\n \"rounded-md font-sans font-medium\",\n \"cursor-pointer select-none\",\n\n // font-sans above is part of the same problem: a <button> does not inherit\n // the page font, so without a reset it renders in the user-agent face\n // (Arial in Chromium) rather than the design system's. The same applies to\n // bg-transparent: an unstyled <button> paints the user-agent ButtonFace\n // grey -- which is what surfaces the moment a fill class fails to resolve,\n // and is why the paint stories compare against tokens rather than checking\n // for transparency.\n //\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may never load\n // one. A bare <button> then keeps its user-agent border (2px, black, in\n // Chromium) and inline padding, which is a black box around every variant\n // that does not set a border of its own. The original inherited a zeroed\n // border from Tailwind Preflight in the app; a library component cannot\n // assume the consumer supplied one.\n \"border-(length:--border-width) border-transparent bg-transparent\",\n\n // Icon handling, ported verbatim: an svg child is sized unless the caller\n // already said otherwise, and never swallows the press.\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n\n // CORRECTION 1 -- the original focused with `ring-[3px]`, which is a\n // box-shadow. Box-shadows are discarded under forced-colors, taking the\n // only focus indication with them, and axe has no rule that would catch it\n // (ARCHITECTURE section 5, rule 6). A real outline survives.\n //\n // outline-solid on the focus variant is load-bearing, not decoration.\n // `outline-none` sets BOTH outline-style:none and --tw-outline-style:none,\n // and Tailwind's outline-width utility resolves its style from that same\n // variable -- so the focus variant applied a width and a colour while\n // faithfully re-applying style:none, and painted nothing. The button was\n // keyboard-focusable with no visible ring. axe has no focus-indicator\n // rule, so every automated gate stayed green; the KeyboardFocus story now\n // asserts outlineStyle directly.\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n\n // CORRECTION 2 -- `disabled:pointer-events-none` also kills the cursor, so\n // the control reads as inert rather than as unavailable. React Aria already\n // blocks activation; this only has to say so.\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n\n // React Aria's own pending state. The original had no equivalent.\n \"data-[pending]:cursor-progress\",\n ],\n {\n variants: {\n variant: {\n // CORRECTION 3 -- the legacy button filled this variant with the brand\n // gradient. It is flat here, because the brand system caps\n // --gradient-brand at one intentional element per view and keeps that\n // budget for the logo and progress bars: \"a gradient on every button\n // spends that budget everywhere. Ours is the flat primary.\"\n //\n // CORRECTION 4 -- the label is ink, not white. Brand orange #FF8431\n // carries white at 2.44:1; ink clears it at 8.35:1, which is the brand\n // system's own --text-on-orange rule. Its Button ships white only as a\n // dated, individually signed-off exception, and this library's contrast\n // contract is build-failing with no exemption mechanism to inherit it.\n //\n // CORRECTION 7 -- hover and pressed are their own TOKENS, not opacity\n // modifiers on the fill. `bg-primary/90` lightened the button toward the\n // page, which reads as receding rather than interactive and was nearly\n // invisible; it also made the label pair unmeasurable, because an alpha\n // composite depends on whatever happens to sit behind it. The brand system\n // steps DOWN its ramp instead -- 500 rest, 600 hover, 700 pressed -- and\n // every one of those fills is now in the contrast contract.\n primary: [\n \"bg-primary text-primary-foreground shadow-surface\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-primary-hover\",\n \"data-[pressed]:bg-primary-pressed\",\n ],\n secondary: [\n \"bg-secondary text-secondary-foreground shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-secondary-hover\",\n \"data-[pressed]:bg-secondary-pressed\",\n ],\n // primary-strong, never primary: the brand orange is 2.44:1 on the page\n // and cannot legally be a label OR a border. That is the whole reason\n // the token exists.\n outline: [\n \"bg-background text-primary-strong border-(length:--border-width) border-primary-strong shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-muted\",\n \"data-[pressed]:bg-muted data-[pressed]:border-foreground\",\n ],\n /*\n * The NEUTRAL outline, and the product's single most-used button:\n * 99 call sites against 32 for the brand-coloured one.\n *\n * It exists because `outline` above is brand-toned -- orange border,\n * orange label -- which is what the product calls `secondaryOutline`.\n * Its plain `outline` is this: a grey border and ordinary body text,\n * for a button that must not compete with the primary action next to\n * it. One name could not cover both, and renaming `outline` would have\n * silently repainted every existing call site of ours instead.\n *\n * `border` rather than `primary-strong`: --color-border is the token\n * already held to the 3:1 non-text floor of WCAG 1.4.11, which is what\n * a border separating a control from the page has to clear.\n */\n outlineNeutral: [\n \"bg-background text-foreground border-(length:--border-width) border-border shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-muted\",\n \"data-[pressed]:bg-muted data-[pressed]:border-foreground\",\n ],\n ghost: [\n \"bg-transparent text-primary-strong\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-muted\",\n // CORRECTION 5 -- pressed must not be background-only. A ghost button\n // has no border at rest, so the pressed border is the non-colour cue\n // that survives forced-colors (rule 6 again).\n \"data-[pressed]:bg-muted data-[pressed]:border-primary-strong\",\n ],\n destructive: [\n \"bg-destructive text-destructive-foreground shadow-surface\",\n \"motion-safe:transition-colors\",\n \"data-[hovered]:bg-destructive-hover\",\n \"data-[pressed]:bg-destructive-pressed\",\n ],\n link: [\n \"bg-transparent text-primary-strong underline-offset-(--underline-offset)\",\n \"data-[hovered]:underline\",\n \"data-[pressed]:underline\",\n ],\n },\n size: {\n // Height, padding and label size all come from the control scale, so\n // data-density moves them together. The icon-only variant is square at\n // the md height. The legacy shadcn rule that tightened padding when a\n // child was an icon is gone: it needed three more magic numbers, the\n // brand system has no equivalent, and the gap token now owns icon\n // spacing.\n sm: \"h-(--density-control-h-sm) px-(--density-control-pad-sm) text-sm\",\n md: \"h-(--density-control-h-md) px-(--density-control-pad-md) text-base\",\n lg: \"h-(--density-control-h-lg) px-(--density-control-pad-lg) text-lg\",\n // text-base even though there is no visible label: without it the\n // user-agent default (13.33px in Chromium) leaks in and sizes any\n // em-based child against the wrong root.\n icon: \"size-(--density-control-h-md) px-0 text-base\",\n },\n },\n defaultVariants: {\n variant: \"primary\",\n size: \"md\",\n },\n },\n);\n\n/**\n * Props.\n *\n * CORRECTION 6 -- the legacy Button was a plain `<button>`, so it took\n * `onClick`, `disabled` and Radix's `asChild`. React Aria's Button has none of\n * those: `ButtonProps` explicitly omits `onClick` and `href`. The replacements\n * are `onPress`, `isDisabled`, and `buttonVariants()` on the consumer's own\n * element. See the migration table in the package README.\n *\n * Deliberately NOT intersected with ComponentPropsWithoutRef<\"button\">: that\n * would advertise disabled, tabIndex and role, which React Aria does not\n * implement and silently ignores. React Aria already forwards the native event\n * surface, so nothing is lost.\n */\nexport interface ButtonProps\n extends Omit<AriaButtonProps, \"className\" | \"style\">, VariantProps<typeof buttonVariants> {\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n /**\n * Icon rendered before the label.\n *\n * Mark the glyph `aria-hidden` -- the label is the accessible name, and a\n * second name source only makes the button announce twice. It is sized by the\n * base `[&_svg]` rule at `--icon-sm` unless it carries a `size-*` class of\n * its own, and spaced by `--density-control-gap`.\n *\n * Two named slots rather than one `icon` plus a position prop: a button can\n * legitimately carry a leading glyph AND a trailing affordance, which one\n * icon and a position cannot express.\n *\n * NOT the route for `size=\"icon\"`. An icon-only button passes its glyph as\n * `children`, which is what gives it its single centred child.\n */\n startIcon?: ReactNode;\n /** Icon rendered after the label. Same rules as {@link ButtonProps.startIcon}. */\n endIcon?: ReactNode;\n}\n\n/**\n * A button.\n *\n * `size=\"icon\"` renders a square button with no visible text, so it **must**\n * carry an `aria-label` -- otherwise it has no accessible name and fails axe's\n * `button-name` rule. The IconOnly story is the guard on that.\n *\n * @example\n * <Button onPress={save}>Save changes</Button>\n * <Button startIcon={<SaveIcon aria-hidden />}>Save changes</Button>\n * <Button endIcon={<ChevronIcon aria-hidden />}>Continue</Button>\n * <Button variant=\"outline\" size=\"icon\" aria-label=\"Close\"><XIcon /></Button>\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n { className, variant, size, startIcon, endIcon, children, ...props },\n ref,\n) {\n return (\n <AriaButton\n ref={ref}\n {...props}\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size }), className)}\n >\n {/*\n * children passes through UNTOUCHED when neither slot is used, so a\n * button with no icon renders byte-identical DOM to before this prop\n * existed. That is what keeps this an additive change rather than a\n * structural one -- and the childOrder assertions in the stories are what\n * hold it true, since they read the button's direct children.\n *\n * The slot branch has to be a render function because React Aria's\n * children are ChildrenOrFunction<ButtonRenderProps>: a consumer may pass\n * a function to read isPressed or isPending, and wrapping the node\n * directly would silently render that function as nothing. The fragment\n * adds no element of its own.\n */}\n {startIcon === undefined && endIcon === undefined\n ? children\n : (renderProps) => (\n <>\n {startIcon}\n {typeof children === \"function\" ? children(renderProps) : children}\n {endIcon}\n </>\n )}\n </AriaButton>\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,iBAAiB,IAC5B;CACE;CAOA;CACA;CAgBA;CAIA;CACA;CAeA;CACA;CACA;CACA;CACA;CAKA;CAGA;AACF,GACA;CACE,UAAU;EACR,SAAS;GAoBP,SAAS;IACP;IACA;IACA;IACA;GACF;GACA,WAAW;IACT;IACA;IACA;IACA;GACF;GAIA,SAAS;IACP;IACA;IACA;IACA;GACF;GAgBA,gBAAgB;IACd;IACA;IACA;IACA;GACF;GACA,OAAO;IACL;IACA;IACA;IAIA;GACF;GACA,aAAa;IACX;IACA;IACA;IACA;GACF;GACA,MAAM;IACJ;IACA;IACA;GACF;EACF;EACA,MAAM;GAOJ,IAAI;GACJ,IAAI;GACJ,IAAI;GAIJ,MAAM;EACR;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;CACR;AACF,CACF;;;;;;;;;;;;;;AAqDA,MAAaA,WAAS,WAA2C,SAASA,SACxE,EAAE,WAAW,SAAS,MAAM,WAAW,SAAS,UAAU,GAAG,SAC7D,KACA;CACA,OACE,oBAACC,QAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,eAAe;GAAE;GAAS;EAAK,CAAC,GAAG,SAAS;EAezD,UAAA,cAAc,KAAA,KAAa,YAAY,KAAA,IACpC,YACC,gBACC,qBAAA,UAAA,EAAA,UAAA;GACG;GACA,OAAO,aAAa,aAAa,SAAS,WAAW,IAAI;GACzD;EACD,EAAA,CAAA;CAEE,CAAA;AAEhB,CAAC"}
|
|
@@ -138,7 +138,7 @@ export interface CalendarRangeProps extends Omit<RangeCalendarProps<DateValue>,
|
|
|
138
138
|
* @example
|
|
139
139
|
* <Calendar label="Report date" value={date} onChange={setDate} clearable />
|
|
140
140
|
*/
|
|
141
|
-
declare
|
|
141
|
+
declare const Calendar$1: import("react").ForwardRefExoticComponent<CalendarProps$1 & import("react").RefAttributes<HTMLDivElement>>;
|
|
142
142
|
/**
|
|
143
143
|
* A calendar for picking a contiguous date range.
|
|
144
144
|
*
|
|
@@ -148,7 +148,7 @@ declare function Calendar$1({ className, label, labelHidden, visibleMonths, clea
|
|
|
148
148
|
* <CalendarRange label="Period" visibleMonths={3}
|
|
149
149
|
* value={range} onChange={setRange} clearable />
|
|
150
150
|
*/
|
|
151
|
-
export declare
|
|
151
|
+
export declare const CalendarRange: import("react").ForwardRefExoticComponent<CalendarRangeProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
152
152
|
//#endregion
|
|
153
153
|
export { Calendar$1 as Calendar, CalendarProps$1 as CalendarProps };
|
|
154
154
|
//# sourceMappingURL=calendar.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
+
import { forwardRef, useContext } from "react";
|
|
4
5
|
import { Button, Calendar, CalendarCell, CalendarGrid, CalendarGridBody, CalendarGridHeader, CalendarHeaderCell, CalendarHeading, CalendarStateContext, RangeCalendar, RangeCalendarStateContext } from "react-aria-components";
|
|
5
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { useContext } from "react";
|
|
7
7
|
//#region src/calendar/calendar.tsx
|
|
8
8
|
/**
|
|
9
9
|
* Calendar, built on React Aria's Calendar and RangeCalendar.
|
|
@@ -225,8 +225,9 @@ function CalendarBody({ visibleMonths, clearable, clearLabel, previousLabel, nex
|
|
|
225
225
|
* @example
|
|
226
226
|
* <Calendar label="Report date" value={date} onChange={setDate} clearable />
|
|
227
227
|
*/
|
|
228
|
-
|
|
228
|
+
const Calendar$1 = forwardRef(function Calendar$2({ className, label, labelHidden = false, visibleMonths = 1, clearable = false, clearLabel = "Clear", previousLabel = "Previous month", nextLabel = "Next month", ...props }, ref) {
|
|
229
229
|
return /* @__PURE__ */ jsxs(Calendar, {
|
|
230
|
+
ref,
|
|
230
231
|
...props,
|
|
231
232
|
"aria-label": label,
|
|
232
233
|
visibleDuration: { months: visibleMonths },
|
|
@@ -245,7 +246,7 @@ function Calendar$1({ className, label, labelHidden = false, visibleMonths = 1,
|
|
|
245
246
|
selection: "single"
|
|
246
247
|
})]
|
|
247
248
|
});
|
|
248
|
-
}
|
|
249
|
+
});
|
|
249
250
|
/**
|
|
250
251
|
* A calendar for picking a contiguous date range.
|
|
251
252
|
*
|
|
@@ -255,8 +256,9 @@ function Calendar$1({ className, label, labelHidden = false, visibleMonths = 1,
|
|
|
255
256
|
* <CalendarRange label="Period" visibleMonths={3}
|
|
256
257
|
* value={range} onChange={setRange} clearable />
|
|
257
258
|
*/
|
|
258
|
-
function CalendarRange({ className, label, labelHidden = false, visibleMonths = 1, clearable = false, clearLabel = "Clear", previousLabel = "Previous month", nextLabel = "Next month", ...props }) {
|
|
259
|
+
const CalendarRange = forwardRef(function CalendarRange({ className, label, labelHidden = false, visibleMonths = 1, clearable = false, clearLabel = "Clear", previousLabel = "Previous month", nextLabel = "Next month", ...props }, ref) {
|
|
259
260
|
return /* @__PURE__ */ jsxs(RangeCalendar, {
|
|
261
|
+
ref,
|
|
260
262
|
...props,
|
|
261
263
|
"aria-label": label,
|
|
262
264
|
visibleDuration: { months: visibleMonths },
|
|
@@ -275,7 +277,7 @@ function CalendarRange({ className, label, labelHidden = false, visibleMonths =
|
|
|
275
277
|
selection: "range"
|
|
276
278
|
})]
|
|
277
279
|
});
|
|
278
|
-
}
|
|
280
|
+
});
|
|
279
281
|
//#endregion
|
|
280
282
|
export { Calendar$1 as Calendar, CalendarRange, calendarCellVariants, calendarClearVariants, calendarGridVariants, calendarHeaderCellVariants, calendarHeaderVariants, calendarHeadingVariants, calendarLabelVariants, calendarMonthsVariants, calendarNavVariants, calendarVariants };
|
|
281
283
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.js","names":["AriaButton","AriaCalendarHeading","AriaCalendarGrid","AriaCalendarGridHeader","AriaCalendarHeaderCell","AriaCalendarGridBody","AriaCalendarCell","Calendar","AriaCalendar","AriaRangeCalendar"],"sources":["../../src/calendar/calendar.tsx"],"sourcesContent":["\"use client\";\n\nimport type { DateValue } from \"@internationalized/date\";\nimport { cva } from \"class-variance-authority\";\nimport { useContext } from \"react\";\nimport {\n Button as AriaButton,\n Calendar as AriaCalendar,\n CalendarCell as AriaCalendarCell,\n CalendarGrid as AriaCalendarGrid,\n CalendarGridBody as AriaCalendarGridBody,\n CalendarGridHeader as AriaCalendarGridHeader,\n CalendarHeaderCell as AriaCalendarHeaderCell,\n CalendarHeading as AriaCalendarHeading,\n CalendarStateContext,\n RangeCalendar as AriaRangeCalendar,\n RangeCalendarStateContext,\n type CalendarProps as AriaCalendarProps,\n type RangeCalendarProps as AriaRangeCalendarProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Calendar, built on React Aria's Calendar and RangeCalendar.\n *\n * Values are `DateValue` objects from `@internationalized/date`, NOT JS\n * `Date`. ADR 0006 names that package the one allowed public type dependency\n * precisely here: a calendar day has no time zone, so modelling it as a\n * timestamp is what costs you a day at a DST boundary. The import is\n * `import type` only — nothing at runtime needs a constructor from it, so the\n * package contributes zero bytes to the bundle.\n *\n * Every string below is a STATIC literal. Tailwind scans source text, so a\n * class assembled at runtime is invisible to it and would simply never be\n * emitted — the component would render unstyled, with no error anywhere.\n *\n * State comes from React Aria's data attributes rather than pseudo-classes:\n * `data-[focus-visible]` is keyboard focus only, and `data-[hovered]` /\n * `data-[pressed]` cover pointer, touch and keyboard uniformly.\n */\nexport const calendarVariants = cva([\n \"flex w-fit flex-col gap-(--density-gap) box-border\",\n \"font-sans text-sm leading-(--leading-normal) text-foreground\",\n]);\n\nexport const calendarLabelVariants = cva([\"text-sm font-medium text-foreground\"]);\n\nexport const calendarHeaderVariants = cva([\n \"flex items-center justify-between gap-(--density-control-gap)\",\n]);\n\n/**\n * The month/prev/next buttons.\n *\n * Deliberately NOT `buttonVariants` from ../button. That would put a\n * cross-component import in the bundle for one caller and six classes, and\n * DRY extracts at two callers, not one.\n */\nexport const calendarNavVariants = cva([\n \"inline-flex shrink-0 items-center justify-center\",\n \"size-(--density-control-h-sm) rounded-md\",\n // A library consumer may load no reset at all, so a bare <button> keeps its\n // user-agent border and ButtonFace grey without these two.\n \"border-(length:--border-width) border-transparent bg-transparent\",\n \"cursor-pointer select-none text-foreground outline-none\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n // The glyph is directional where the BEHAVIOUR is logical: React Aria maps\n // slot=\"previous\" to the correct month either way, so only the arrow flips.\n \"[&_svg]:rtl:-scale-x-100\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-muted data-[pressed]:bg-muted\",\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\",\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n]);\n\nexport const calendarMonthsVariants = cva([\n \"flex flex-col gap-(--density-gap) sm:flex-row sm:items-start\",\n]);\n\nexport const calendarHeadingVariants = cva([\n \"mb-1 text-center text-sm font-semibold text-foreground\",\n]);\n\nexport const calendarGridVariants = cva([\n // Without this a bare <table> falls back to user-agent border spacing and\n // the range band shows gaps between cells.\n \"border-collapse\",\n]);\n\nexport const calendarHeaderCellVariants = cva([\n \"w-(--density-control-h-sm) pb-1\",\n \"text-center text-2xs font-medium text-muted-foreground\",\n]);\n\nexport const calendarClearVariants = cva([\n \"self-end rounded-md px-2 py-1\",\n \"border-(length:--border-width) border-transparent bg-transparent\",\n \"cursor-pointer select-none text-sm text-muted-foreground outline-none\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-muted data-[hovered]:text-foreground\",\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\",\n]);\n\n/**\n * A day.\n *\n * Sized from `--density-control-h-sm` rather than a new\n * `--density-calendar-cell`: the density contract test requires every scope to\n * declare every token name, so a new one is three file edits — and this one is\n * already 30/36/42px, all clear of the 24px floor that WCAG 2.2 SC 2.5.8 sets.\n *\n * ARCHITECTURE section 5 rule 6 — no state is carried by colour alone, because\n * every author colour is replaced under forced-colors and the distinction\n * would vanish. Selected adds weight, the range ends add SHAPE, today is an\n * underline (not an outline, which would fight the focus ring) and unavailable\n * is a strikethrough.\n */\nexport const calendarCellVariants = cva(\n [\n \"flex items-center justify-center box-border\",\n \"size-(--density-control-h-sm) rounded-md\",\n \"text-sm leading-(--leading-normal) text-foreground\",\n \"cursor-pointer select-none outline-none\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-muted\",\n \"data-[today]:font-semibold data-[today]:underline data-[today]:underline-offset-4\",\n \"data-[outside-month]:text-muted-foreground data-[outside-month]:opacity-(--opacity-disabled)\",\n // Unavailable dates stay FOCUSABLE, so ADR 0007 holds them to 4.5:1 — this\n // dims to muted-foreground, which the contrast contract grades, never below.\n \"data-[unavailable]:cursor-not-allowed data-[unavailable]:text-muted-foreground\",\n \"data-[unavailable]:line-through\",\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\",\n ],\n {\n variants: {\n /**\n * A variant axis rather than a boolean prop, because the two modes are\n * mutually exclusive visual treatments of the same element — which is\n * exactly what OCP asks a variant to model.\n */\n selection: {\n single: [\n \"data-[selected]:bg-primary data-[selected]:text-primary-foreground\",\n \"data-[selected]:font-semibold\",\n ],\n range: [\n // The band is `muted` — a pair the contrast contract already grades\n // at 4.5:1, so the range needs no new contrast-pairs.json entry.\n \"data-[selected]:rounded-none data-[selected]:bg-muted data-[selected]:text-foreground\",\n \"data-[selection-start]:rounded-s-md data-[selection-start]:bg-primary\",\n \"data-[selection-start]:font-semibold data-[selection-start]:text-primary-foreground\",\n \"data-[selection-end]:rounded-e-md data-[selection-end]:bg-primary\",\n \"data-[selection-end]:font-semibold data-[selection-end]:text-primary-foreground\",\n ],\n },\n },\n defaultVariants: { selection: \"single\" },\n },\n);\n\nfunction ChevronPrevious() {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\">\n <path d=\"m15 18-6-6 6-6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\nfunction ChevronNext() {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\n/**\n * Resets the selection to null.\n *\n * It reads the calendar state from React Aria's context and calls\n * `setValue(null)`, which runs through `useControlledState` — so it fires\n * `onChange` for a controlled parent AND updates uncontrolled state, with no\n * mirrored state here. That is the whole reason there is no `onClear` prop:\n * from inside the provider tree the reset is free, and from outside it it\n * would force every consumer to own the value.\n *\n * The two states are narrowed separately rather than through a union, because\n * `setValue` has a different parameter type on each and the union call is not\n * assignable to either.\n *\n * `slot={null}` opts out of the Button context: inside a Calendar that context\n * carries the previous/next slots, and an unslotted Button would claim one.\n */\nfunction ClearButton({ label }: { label: string }) {\n const calendarState = useContext(CalendarStateContext);\n const rangeState = useContext(RangeCalendarStateContext);\n\n /*\n * Inherit the calendar's own inert states.\n *\n * Without this the Clear button stayed focusable and announced as enabled\n * inside an `isDisabled` calendar — every cell and both nav buttons were\n * inert, and one control in the middle of them was not. `isReadOnly` is the\n * same argument: \"the value is immutable\" has to include clearing it.\n */\n const state = calendarState ?? rangeState;\n const isInert = Boolean(state?.isDisabled ?? false) || Boolean(state?.isReadOnly ?? false);\n\n return (\n <AriaButton\n slot={null}\n data-slot=\"calendar-clear\"\n isDisabled={isInert}\n className={calendarClearVariants()}\n // Stays enabled when there is nothing to clear. Disabling it on press\n // moves focus to <body>, which silently strands a keyboard user mid-grid.\n onPress={() => {\n if (calendarState) calendarState.setValue(null);\n else rangeState?.setValue(null);\n }}\n >\n {label}\n </AriaButton>\n );\n}\n\ninterface CalendarBodyProps {\n visibleMonths: number;\n clearable: boolean;\n clearLabel: string;\n previousLabel: string;\n nextLabel: string;\n selection: \"single\" | \"range\";\n}\n\n/** Header, grids and Clear — identical under both calendar roots. */\nfunction CalendarBody({\n visibleMonths,\n clearable,\n clearLabel,\n previousLabel,\n nextLabel,\n selection,\n}: CalendarBodyProps) {\n return (\n <>\n <header data-slot=\"calendar-header\" className={calendarHeaderVariants()}>\n <AriaButton slot=\"previous\" aria-label={previousLabel} className={calendarNavVariants()}>\n <ChevronPrevious />\n </AriaButton>\n <AriaButton slot=\"next\" aria-label={nextLabel} className={calendarNavVariants()}>\n <ChevronNext />\n </AriaButton>\n </header>\n\n <div data-slot=\"calendar-months\" className={calendarMonthsVariants()}>\n {Array.from({ length: visibleMonths }, (_, offset) => (\n <div key={offset} data-slot=\"calendar-month\">\n {/* Per grid, so three months are three named columns rather than\n one combined range nobody can map back to a column. */}\n <AriaCalendarHeading\n offset={{ months: offset }}\n className={calendarHeadingVariants()}\n />\n <AriaCalendarGrid offset={{ months: offset }} className={calendarGridVariants()}>\n <AriaCalendarGridHeader>\n {(weekday) => (\n <AriaCalendarHeaderCell className={calendarHeaderCellVariants()}>\n {weekday}\n </AriaCalendarHeaderCell>\n )}\n </AriaCalendarGridHeader>\n <AriaCalendarGridBody>\n {(date) => (\n <AriaCalendarCell\n date={date}\n data-slot=\"calendar-cell\"\n className={calendarCellVariants({ selection })}\n />\n )}\n </AriaCalendarGridBody>\n </AriaCalendarGrid>\n </div>\n ))}\n </div>\n\n {clearable ? <ClearButton label={clearLabel} /> : null}\n </>\n );\n}\n\ninterface CalendarSharedProps {\n /**\n * Accessible name for the calendar.\n *\n * REQUIRED. React Aria renders the root as `role=\"application\"`, which has\n * no implicit name, so without this every calendar announces as an unnamed\n * application — the same finding that made `label` required on Select,\n * Progress, Slider and ScrollArea.\n */\n label: string;\n /**\n * Hide the label visually while leaving it on the accessibility tree. Pass\n * this when the surrounding UI already names the control.\n *\n * @default false\n */\n labelHidden?: boolean;\n /**\n * Number of months displayed side by side. Maps to React Aria's\n * `visibleDuration`, which is omitted from the public surface so the two\n * cannot disagree.\n *\n * @default 1\n */\n visibleMonths?: number;\n /**\n * Render a Clear button that resets the selection to `null`.\n *\n * Works controlled and uncontrolled: the button calls React Aria's\n * `setValue(null)` through context, so `onChange` fires either way and there\n * is no `onClear` for a consumer to wire up.\n *\n * @default false\n */\n clearable?: boolean;\n /**\n * Visible text of the Clear button.\n *\n * A prop rather than a literal because `ARCHITECTURE.md` §7 rule 9 forbids\n * hard-coded user-visible strings and says it cannot be retrofitted.\n * `useMessages()` does not exist yet, so this follows Dialog's `closeLabel`.\n *\n * @default \"Clear\"\n */\n clearLabel?: string;\n /**\n * Accessible name for the previous-month button, which is icon-only. Same\n * reasoning as {@link CalendarSharedProps.clearLabel}.\n *\n * @default \"Previous month\"\n */\n previousLabel?: string;\n /**\n * Accessible name for the next-month button. Same reasoning as\n * {@link CalendarSharedProps.clearLabel}.\n *\n * @default \"Next month\"\n */\n nextLabel?: string;\n /** Extra classes. Merged last through `cn()`, so the consumer wins. */\n className?: string;\n}\n\n/**\n * Props for {@link Calendar}.\n *\n * Extends the React Aria type via `Omit` rather than\n * `ComponentPropsWithoutRef`, matching every other React Aria-backed component\n * here and recorded in ADR 0008. Intersecting would advertise DOM props that\n * React Aria ignores silently.\n */\nexport interface CalendarProps\n extends Omit<\n AriaCalendarProps<DateValue>,\n \"className\" | \"style\" | \"children\" | \"visibleDuration\"\n >,\n CalendarSharedProps {}\n\n/** Props for {@link CalendarRange}. */\nexport interface CalendarRangeProps\n extends Omit<\n AriaRangeCalendarProps<DateValue>,\n \"className\" | \"style\" | \"children\" | \"visibleDuration\"\n >,\n CalendarSharedProps {}\n\n/**\n * A calendar for picking a single date.\n *\n * Maturity: **experimental**. `ARCHITECTURE.md` §7 rule 8's metadata mechanism\n * does not exist yet, and ADR 0006 blocks `stable` until a WebKit test\n * instance lands regardless.\n *\n * `onChange` hands back a `DateValue`; `String(value)` is its `YYYY-MM-DD`\n * form, which is what an API filter or query parameter wants.\n *\n * @example\n * <Calendar label=\"Report date\" value={date} onChange={setDate} clearable />\n */\nexport function Calendar({\n className,\n label,\n labelHidden = false,\n visibleMonths = 1,\n clearable = false,\n clearLabel = \"Clear\",\n previousLabel = \"Previous month\",\n nextLabel = \"Next month\",\n ...props\n}: CalendarProps) {\n return (\n <AriaCalendar\n {...props}\n aria-label={label}\n visibleDuration={{ months: visibleMonths }}\n data-slot=\"calendar\"\n className={cn(calendarVariants(), className)}\n >\n {labelHidden ? null : (\n <span data-slot=\"calendar-label\" className={calendarLabelVariants()}>\n {label}\n </span>\n )}\n <CalendarBody\n visibleMonths={visibleMonths}\n clearable={clearable}\n clearLabel={clearLabel}\n previousLabel={previousLabel}\n nextLabel={nextLabel}\n selection=\"single\"\n />\n </AriaCalendar>\n );\n}\n\n/**\n * A calendar for picking a contiguous date range.\n *\n * `onChange` hands back `{ start, end }`, both `DateValue`.\n *\n * @example\n * <CalendarRange label=\"Period\" visibleMonths={3}\n * value={range} onChange={setRange} clearable />\n */\nexport function CalendarRange({\n className,\n label,\n labelHidden = false,\n visibleMonths = 1,\n clearable = false,\n clearLabel = \"Clear\",\n previousLabel = \"Previous month\",\n nextLabel = \"Next month\",\n ...props\n}: CalendarRangeProps) {\n return (\n <AriaRangeCalendar\n {...props}\n aria-label={label}\n visibleDuration={{ months: visibleMonths }}\n data-slot=\"calendar\"\n className={cn(calendarVariants(), className)}\n >\n {labelHidden ? null : (\n <span data-slot=\"calendar-label\" className={calendarLabelVariants()}>\n {label}\n </span>\n )}\n <CalendarBody\n visibleMonths={visibleMonths}\n clearable={clearable}\n clearLabel={clearLabel}\n previousLabel={previousLabel}\n nextLabel={nextLabel}\n selection=\"range\"\n />\n </AriaRangeCalendar>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"calendar.js","names":["AriaButton","AriaCalendarHeading","AriaCalendarGrid","AriaCalendarGridHeader","AriaCalendarHeaderCell","AriaCalendarGridBody","AriaCalendarCell","Calendar","AriaCalendar","AriaRangeCalendar"],"sources":["../../src/calendar/calendar.tsx"],"sourcesContent":["\"use client\";\n\nimport type { DateValue } from \"@internationalized/date\";\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useContext } from \"react\";\nimport {\n Button as AriaButton,\n Calendar as AriaCalendar,\n CalendarCell as AriaCalendarCell,\n CalendarGrid as AriaCalendarGrid,\n CalendarGridBody as AriaCalendarGridBody,\n CalendarGridHeader as AriaCalendarGridHeader,\n CalendarHeaderCell as AriaCalendarHeaderCell,\n CalendarHeading as AriaCalendarHeading,\n CalendarStateContext,\n RangeCalendar as AriaRangeCalendar,\n RangeCalendarStateContext,\n type CalendarProps as AriaCalendarProps,\n type RangeCalendarProps as AriaRangeCalendarProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Calendar, built on React Aria's Calendar and RangeCalendar.\n *\n * Values are `DateValue` objects from `@internationalized/date`, NOT JS\n * `Date`. ADR 0006 names that package the one allowed public type dependency\n * precisely here: a calendar day has no time zone, so modelling it as a\n * timestamp is what costs you a day at a DST boundary. The import is\n * `import type` only — nothing at runtime needs a constructor from it, so the\n * package contributes zero bytes to the bundle.\n *\n * Every string below is a STATIC literal. Tailwind scans source text, so a\n * class assembled at runtime is invisible to it and would simply never be\n * emitted — the component would render unstyled, with no error anywhere.\n *\n * State comes from React Aria's data attributes rather than pseudo-classes:\n * `data-[focus-visible]` is keyboard focus only, and `data-[hovered]` /\n * `data-[pressed]` cover pointer, touch and keyboard uniformly.\n */\nexport const calendarVariants = cva([\n \"flex w-fit flex-col gap-(--density-gap) box-border\",\n \"font-sans text-sm leading-(--leading-normal) text-foreground\",\n]);\n\nexport const calendarLabelVariants = cva([\"text-sm font-medium text-foreground\"]);\n\nexport const calendarHeaderVariants = cva([\n \"flex items-center justify-between gap-(--density-control-gap)\",\n]);\n\n/**\n * The month/prev/next buttons.\n *\n * Deliberately NOT `buttonVariants` from ../button. That would put a\n * cross-component import in the bundle for one caller and six classes, and\n * DRY extracts at two callers, not one.\n */\nexport const calendarNavVariants = cva([\n \"inline-flex shrink-0 items-center justify-center\",\n \"size-(--density-control-h-sm) rounded-md\",\n // A library consumer may load no reset at all, so a bare <button> keeps its\n // user-agent border and ButtonFace grey without these two.\n \"border-(length:--border-width) border-transparent bg-transparent\",\n \"cursor-pointer select-none text-foreground outline-none\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n // The glyph is directional where the BEHAVIOUR is logical: React Aria maps\n // slot=\"previous\" to the correct month either way, so only the arrow flips.\n \"[&_svg]:rtl:-scale-x-100\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-muted data-[pressed]:bg-muted\",\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\",\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n]);\n\nexport const calendarMonthsVariants = cva([\n \"flex flex-col gap-(--density-gap) sm:flex-row sm:items-start\",\n]);\n\nexport const calendarHeadingVariants = cva([\n \"mb-1 text-center text-sm font-semibold text-foreground\",\n]);\n\nexport const calendarGridVariants = cva([\n // Without this a bare <table> falls back to user-agent border spacing and\n // the range band shows gaps between cells.\n \"border-collapse\",\n]);\n\nexport const calendarHeaderCellVariants = cva([\n \"w-(--density-control-h-sm) pb-1\",\n \"text-center text-2xs font-medium text-muted-foreground\",\n]);\n\nexport const calendarClearVariants = cva([\n \"self-end rounded-md px-2 py-1\",\n \"border-(length:--border-width) border-transparent bg-transparent\",\n \"cursor-pointer select-none text-sm text-muted-foreground outline-none\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-muted data-[hovered]:text-foreground\",\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\",\n]);\n\n/**\n * A day.\n *\n * Sized from `--density-control-h-sm` rather than a new\n * `--density-calendar-cell`: the density contract test requires every scope to\n * declare every token name, so a new one is three file edits — and this one is\n * already 30/36/42px, all clear of the 24px floor that WCAG 2.2 SC 2.5.8 sets.\n *\n * ARCHITECTURE section 5 rule 6 — no state is carried by colour alone, because\n * every author colour is replaced under forced-colors and the distinction\n * would vanish. Selected adds weight, the range ends add SHAPE, today is an\n * underline (not an outline, which would fight the focus ring) and unavailable\n * is a strikethrough.\n */\nexport const calendarCellVariants = cva(\n [\n \"flex items-center justify-center box-border\",\n \"size-(--density-control-h-sm) rounded-md\",\n \"text-sm leading-(--leading-normal) text-foreground\",\n \"cursor-pointer select-none outline-none\",\n \"motion-safe:transition-colors motion-safe:duration-(--duration-fast) motion-safe:ease-standard\",\n \"data-[hovered]:bg-muted\",\n \"data-[today]:font-semibold data-[today]:underline data-[today]:underline-offset-4\",\n \"data-[outside-month]:text-muted-foreground data-[outside-month]:opacity-(--opacity-disabled)\",\n // Unavailable dates stay FOCUSABLE, so ADR 0007 holds them to 4.5:1 — this\n // dims to muted-foreground, which the contrast contract grades, never below.\n \"data-[unavailable]:cursor-not-allowed data-[unavailable]:text-muted-foreground\",\n \"data-[unavailable]:line-through\",\n \"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring)\",\n ],\n {\n variants: {\n /**\n * A variant axis rather than a boolean prop, because the two modes are\n * mutually exclusive visual treatments of the same element — which is\n * exactly what OCP asks a variant to model.\n */\n selection: {\n single: [\n \"data-[selected]:bg-primary data-[selected]:text-primary-foreground\",\n \"data-[selected]:font-semibold\",\n ],\n range: [\n // The band is `muted` — a pair the contrast contract already grades\n // at 4.5:1, so the range needs no new contrast-pairs.json entry.\n \"data-[selected]:rounded-none data-[selected]:bg-muted data-[selected]:text-foreground\",\n \"data-[selection-start]:rounded-s-md data-[selection-start]:bg-primary\",\n \"data-[selection-start]:font-semibold data-[selection-start]:text-primary-foreground\",\n \"data-[selection-end]:rounded-e-md data-[selection-end]:bg-primary\",\n \"data-[selection-end]:font-semibold data-[selection-end]:text-primary-foreground\",\n ],\n },\n },\n defaultVariants: { selection: \"single\" },\n },\n);\n\nfunction ChevronPrevious() {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\">\n <path d=\"m15 18-6-6 6-6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\nfunction ChevronNext() {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden=\"true\">\n <path d=\"m9 18 6-6-6-6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n );\n}\n\n/**\n * Resets the selection to null.\n *\n * It reads the calendar state from React Aria's context and calls\n * `setValue(null)`, which runs through `useControlledState` — so it fires\n * `onChange` for a controlled parent AND updates uncontrolled state, with no\n * mirrored state here. That is the whole reason there is no `onClear` prop:\n * from inside the provider tree the reset is free, and from outside it it\n * would force every consumer to own the value.\n *\n * The two states are narrowed separately rather than through a union, because\n * `setValue` has a different parameter type on each and the union call is not\n * assignable to either.\n *\n * `slot={null}` opts out of the Button context: inside a Calendar that context\n * carries the previous/next slots, and an unslotted Button would claim one.\n */\nfunction ClearButton({ label }: { label: string }) {\n const calendarState = useContext(CalendarStateContext);\n const rangeState = useContext(RangeCalendarStateContext);\n\n /*\n * Inherit the calendar's own inert states.\n *\n * Without this the Clear button stayed focusable and announced as enabled\n * inside an `isDisabled` calendar — every cell and both nav buttons were\n * inert, and one control in the middle of them was not. `isReadOnly` is the\n * same argument: \"the value is immutable\" has to include clearing it.\n */\n const state = calendarState ?? rangeState;\n const isInert = Boolean(state?.isDisabled ?? false) || Boolean(state?.isReadOnly ?? false);\n\n return (\n <AriaButton\n slot={null}\n data-slot=\"calendar-clear\"\n isDisabled={isInert}\n className={calendarClearVariants()}\n // Stays enabled when there is nothing to clear. Disabling it on press\n // moves focus to <body>, which silently strands a keyboard user mid-grid.\n onPress={() => {\n if (calendarState) calendarState.setValue(null);\n else rangeState?.setValue(null);\n }}\n >\n {label}\n </AriaButton>\n );\n}\n\ninterface CalendarBodyProps {\n visibleMonths: number;\n clearable: boolean;\n clearLabel: string;\n previousLabel: string;\n nextLabel: string;\n selection: \"single\" | \"range\";\n}\n\n/** Header, grids and Clear — identical under both calendar roots. */\nfunction CalendarBody({\n visibleMonths,\n clearable,\n clearLabel,\n previousLabel,\n nextLabel,\n selection,\n}: CalendarBodyProps) {\n return (\n <>\n <header data-slot=\"calendar-header\" className={calendarHeaderVariants()}>\n <AriaButton slot=\"previous\" aria-label={previousLabel} className={calendarNavVariants()}>\n <ChevronPrevious />\n </AriaButton>\n <AriaButton slot=\"next\" aria-label={nextLabel} className={calendarNavVariants()}>\n <ChevronNext />\n </AriaButton>\n </header>\n\n <div data-slot=\"calendar-months\" className={calendarMonthsVariants()}>\n {Array.from({ length: visibleMonths }, (_, offset) => (\n <div key={offset} data-slot=\"calendar-month\">\n {/* Per grid, so three months are three named columns rather than\n one combined range nobody can map back to a column. */}\n <AriaCalendarHeading\n offset={{ months: offset }}\n className={calendarHeadingVariants()}\n />\n <AriaCalendarGrid offset={{ months: offset }} className={calendarGridVariants()}>\n <AriaCalendarGridHeader>\n {(weekday) => (\n <AriaCalendarHeaderCell className={calendarHeaderCellVariants()}>\n {weekday}\n </AriaCalendarHeaderCell>\n )}\n </AriaCalendarGridHeader>\n <AriaCalendarGridBody>\n {(date) => (\n <AriaCalendarCell\n date={date}\n data-slot=\"calendar-cell\"\n className={calendarCellVariants({ selection })}\n />\n )}\n </AriaCalendarGridBody>\n </AriaCalendarGrid>\n </div>\n ))}\n </div>\n\n {clearable ? <ClearButton label={clearLabel} /> : null}\n </>\n );\n}\n\ninterface CalendarSharedProps {\n /**\n * Accessible name for the calendar.\n *\n * REQUIRED. React Aria renders the root as `role=\"application\"`, which has\n * no implicit name, so without this every calendar announces as an unnamed\n * application — the same finding that made `label` required on Select,\n * Progress, Slider and ScrollArea.\n */\n label: string;\n /**\n * Hide the label visually while leaving it on the accessibility tree. Pass\n * this when the surrounding UI already names the control.\n *\n * @default false\n */\n labelHidden?: boolean;\n /**\n * Number of months displayed side by side. Maps to React Aria's\n * `visibleDuration`, which is omitted from the public surface so the two\n * cannot disagree.\n *\n * @default 1\n */\n visibleMonths?: number;\n /**\n * Render a Clear button that resets the selection to `null`.\n *\n * Works controlled and uncontrolled: the button calls React Aria's\n * `setValue(null)` through context, so `onChange` fires either way and there\n * is no `onClear` for a consumer to wire up.\n *\n * @default false\n */\n clearable?: boolean;\n /**\n * Visible text of the Clear button.\n *\n * A prop rather than a literal because `ARCHITECTURE.md` §7 rule 9 forbids\n * hard-coded user-visible strings and says it cannot be retrofitted.\n * `useMessages()` does not exist yet, so this follows Dialog's `closeLabel`.\n *\n * @default \"Clear\"\n */\n clearLabel?: string;\n /**\n * Accessible name for the previous-month button, which is icon-only. Same\n * reasoning as {@link CalendarSharedProps.clearLabel}.\n *\n * @default \"Previous month\"\n */\n previousLabel?: string;\n /**\n * Accessible name for the next-month button. Same reasoning as\n * {@link CalendarSharedProps.clearLabel}.\n *\n * @default \"Next month\"\n */\n nextLabel?: string;\n /** Extra classes. Merged last through `cn()`, so the consumer wins. */\n className?: string;\n}\n\n/**\n * Props for {@link Calendar}.\n *\n * Extends the React Aria type via `Omit` rather than\n * `ComponentPropsWithoutRef`, matching every other React Aria-backed component\n * here and recorded in ADR 0008. Intersecting would advertise DOM props that\n * React Aria ignores silently.\n */\nexport interface CalendarProps\n extends Omit<\n AriaCalendarProps<DateValue>,\n \"className\" | \"style\" | \"children\" | \"visibleDuration\"\n >,\n CalendarSharedProps {}\n\n/** Props for {@link CalendarRange}. */\nexport interface CalendarRangeProps\n extends Omit<\n AriaRangeCalendarProps<DateValue>,\n \"className\" | \"style\" | \"children\" | \"visibleDuration\"\n >,\n CalendarSharedProps {}\n\n/**\n * A calendar for picking a single date.\n *\n * Maturity: **experimental**. `ARCHITECTURE.md` §7 rule 8's metadata mechanism\n * does not exist yet, and ADR 0006 blocks `stable` until a WebKit test\n * instance lands regardless.\n *\n * `onChange` hands back a `DateValue`; `String(value)` is its `YYYY-MM-DD`\n * form, which is what an API filter or query parameter wants.\n *\n * @example\n * <Calendar label=\"Report date\" value={date} onChange={setDate} clearable />\n */\nexport const Calendar = forwardRef<HTMLDivElement, CalendarProps>(function Calendar(\n {\n className,\n label,\n labelHidden = false,\n visibleMonths = 1,\n clearable = false,\n clearLabel = \"Clear\",\n previousLabel = \"Previous month\",\n nextLabel = \"Next month\",\n ...props\n },\n ref,\n) {\n return (\n <AriaCalendar\n ref={ref}\n {...props}\n aria-label={label}\n visibleDuration={{ months: visibleMonths }}\n data-slot=\"calendar\"\n className={cn(calendarVariants(), className)}\n >\n {labelHidden ? null : (\n <span data-slot=\"calendar-label\" className={calendarLabelVariants()}>\n {label}\n </span>\n )}\n <CalendarBody\n visibleMonths={visibleMonths}\n clearable={clearable}\n clearLabel={clearLabel}\n previousLabel={previousLabel}\n nextLabel={nextLabel}\n selection=\"single\"\n />\n </AriaCalendar>\n );\n});\n\n/**\n * A calendar for picking a contiguous date range.\n *\n * `onChange` hands back `{ start, end }`, both `DateValue`.\n *\n * @example\n * <CalendarRange label=\"Period\" visibleMonths={3}\n * value={range} onChange={setRange} clearable />\n */\nexport const CalendarRange = forwardRef<HTMLDivElement, CalendarRangeProps>(function CalendarRange(\n {\n className,\n label,\n labelHidden = false,\n visibleMonths = 1,\n clearable = false,\n clearLabel = \"Clear\",\n previousLabel = \"Previous month\",\n nextLabel = \"Next month\",\n ...props\n },\n ref,\n) {\n return (\n <AriaRangeCalendar\n ref={ref}\n {...props}\n aria-label={label}\n visibleDuration={{ months: visibleMonths }}\n data-slot=\"calendar\"\n className={cn(calendarVariants(), className)}\n >\n {labelHidden ? null : (\n <span data-slot=\"calendar-label\" className={calendarLabelVariants()}>\n {label}\n </span>\n )}\n <CalendarBody\n visibleMonths={visibleMonths}\n clearable={clearable}\n clearLabel={clearLabel}\n previousLabel={previousLabel}\n nextLabel={nextLabel}\n selection=\"range\"\n />\n </AriaRangeCalendar>\n );\n});\n"],"mappings":"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"}
|
package/dist/card/card.d.ts
CHANGED
|
@@ -54,10 +54,10 @@ export declare const cardFooterVariants: (props?: import("class-variance-authori
|
|
|
54
54
|
* </Card>
|
|
55
55
|
*/
|
|
56
56
|
export type CardProps = ComponentPropsWithoutRef<"div">;
|
|
57
|
-
export declare
|
|
57
|
+
export declare const Card: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
58
58
|
/** Title and description, stacked. */
|
|
59
59
|
export type CardHeaderProps = ComponentPropsWithoutRef<"div">;
|
|
60
|
-
export declare
|
|
60
|
+
export declare const CardHeader: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
61
61
|
/**
|
|
62
62
|
* The card's heading.
|
|
63
63
|
*
|
|
@@ -79,15 +79,15 @@ export interface CardTitleProps extends ComponentPropsWithoutRef<"h3"> {
|
|
|
79
79
|
*/
|
|
80
80
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
81
81
|
}
|
|
82
|
-
export declare
|
|
82
|
+
export declare const CardTitle: import("react").ForwardRefExoticComponent<CardTitleProps & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
83
83
|
/** Secondary text under the title. */
|
|
84
84
|
export type CardDescriptionProps = ComponentPropsWithoutRef<"div">;
|
|
85
|
-
export declare
|
|
85
|
+
export declare const CardDescription: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
86
86
|
/** The card's body. */
|
|
87
87
|
export type CardContentProps = ComponentPropsWithoutRef<"div">;
|
|
88
|
-
export declare
|
|
88
|
+
export declare const CardContent: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
89
89
|
/** Actions or metadata at the foot of the card. */
|
|
90
90
|
export type CardFooterProps = ComponentPropsWithoutRef<"div">;
|
|
91
|
-
export declare
|
|
91
|
+
export declare const CardFooter: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
92
92
|
//#endregion
|
|
93
93
|
//# sourceMappingURL=card.d.ts.map
|
package/dist/card/card.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
|
+
import { forwardRef } from "react";
|
|
4
5
|
import { jsx } from "react/jsx-runtime";
|
|
5
6
|
//#region src/card/card.tsx
|
|
6
7
|
/**
|
|
@@ -49,21 +50,23 @@ const cardTitleVariants = cva(["text-base leading-(--leading-none) font-semibold
|
|
|
49
50
|
const cardDescriptionVariants = cva(["text-sm leading-(--leading-normal) text-muted-foreground"]);
|
|
50
51
|
const cardContentVariants = cva("px-6");
|
|
51
52
|
const cardFooterVariants = cva("flex items-center px-6");
|
|
52
|
-
function Card({ className, ...props }) {
|
|
53
|
+
const Card = forwardRef(function Card({ className, ...props }, ref) {
|
|
53
54
|
return /* @__PURE__ */ jsx("div", {
|
|
55
|
+
ref,
|
|
54
56
|
...props,
|
|
55
57
|
"data-slot": "card",
|
|
56
58
|
className: cn(cardVariants(), className)
|
|
57
59
|
});
|
|
58
|
-
}
|
|
59
|
-
function CardHeader({ className, ...props }) {
|
|
60
|
+
});
|
|
61
|
+
const CardHeader = forwardRef(function CardHeader({ className, ...props }, ref) {
|
|
60
62
|
return /* @__PURE__ */ jsx("div", {
|
|
63
|
+
ref,
|
|
61
64
|
...props,
|
|
62
65
|
"data-slot": "card-header",
|
|
63
66
|
className: cn(cardHeaderVariants(), className)
|
|
64
67
|
});
|
|
65
|
-
}
|
|
66
|
-
function CardTitle({ className, level = 3, ...props }) {
|
|
68
|
+
});
|
|
69
|
+
const CardTitle = forwardRef(function CardTitle({ className, level = 3, ...props }, ref) {
|
|
67
70
|
const Heading = {
|
|
68
71
|
1: "h1",
|
|
69
72
|
2: "h2",
|
|
@@ -73,32 +76,36 @@ function CardTitle({ className, level = 3, ...props }) {
|
|
|
73
76
|
6: "h6"
|
|
74
77
|
}[level];
|
|
75
78
|
return /* @__PURE__ */ jsx(Heading, {
|
|
79
|
+
ref,
|
|
76
80
|
...props,
|
|
77
81
|
"data-slot": "card-title",
|
|
78
82
|
className: cn(cardTitleVariants(), className)
|
|
79
83
|
});
|
|
80
|
-
}
|
|
81
|
-
function CardDescription({ className, ...props }) {
|
|
84
|
+
});
|
|
85
|
+
const CardDescription = forwardRef(function CardDescription({ className, ...props }, ref) {
|
|
82
86
|
return /* @__PURE__ */ jsx("div", {
|
|
87
|
+
ref,
|
|
83
88
|
...props,
|
|
84
89
|
"data-slot": "card-description",
|
|
85
90
|
className: cn(cardDescriptionVariants(), className)
|
|
86
91
|
});
|
|
87
|
-
}
|
|
88
|
-
function CardContent({ className, ...props }) {
|
|
92
|
+
});
|
|
93
|
+
const CardContent = forwardRef(function CardContent({ className, ...props }, ref) {
|
|
89
94
|
return /* @__PURE__ */ jsx("div", {
|
|
95
|
+
ref,
|
|
90
96
|
...props,
|
|
91
97
|
"data-slot": "card-content",
|
|
92
98
|
className: cn(cardContentVariants(), className)
|
|
93
99
|
});
|
|
94
|
-
}
|
|
95
|
-
function CardFooter({ className, ...props }) {
|
|
100
|
+
});
|
|
101
|
+
const CardFooter = forwardRef(function CardFooter({ className, ...props }, ref) {
|
|
96
102
|
return /* @__PURE__ */ jsx("div", {
|
|
103
|
+
ref,
|
|
97
104
|
...props,
|
|
98
105
|
"data-slot": "card-footer",
|
|
99
106
|
className: cn(cardFooterVariants(), className)
|
|
100
107
|
});
|
|
101
|
-
}
|
|
108
|
+
});
|
|
102
109
|
//#endregion
|
|
103
110
|
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, cardContentVariants, cardDescriptionVariants, cardFooterVariants, cardHeaderVariants, cardTitleVariants, cardVariants };
|
|
104
111
|
|
package/dist/card/card.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type
|
|
1
|
+
{"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Card, ported from the legacy shadcn component.\n *\n * Presentational, so there is no React Aria base -- React Aria ships no card,\n * and a container that is not focusable, selectable or interactive needs no\n * behaviour to inherit.\n *\n * Five parts, from six. The census (24 consumer files, the most-used\n * container in the app) reads: Card 55, CardContent 40, CardHeader 21,\n * CardTitle 19, CardDescription 8, CardFooter 2, **CardAction 0**.\n *\n * Dropping `CardAction` takes three things with it, which is most of the\n * legacy header's complexity: the `grid`, the `grid-rows-[auto_auto]`, and\n * the `has-data-[slot=card-action]:grid-cols-[1fr_auto]` rule that existed\n * only to reserve a column for it. With no action to place, a header is a\n * stack.\n *\n * Two more legacy rules are gone. `[.border-b]:pb-6` and `[.border-t]:pt-6`\n * added padding when the CONSUMER happened to add a border class -- clever,\n * but action at a distance through a class name we do not own, and the census\n * found zero application call sites relying on it (one call site passes\n * `border-b-0`, which removes a border it never had). `@container/card-header`\n * named a container nothing queried.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const cardVariants = cva([\n \"flex flex-col gap-6\",\n \"rounded-xl border-(length:--border-width) border-border py-6\",\n \"bg-card text-card-foreground shadow-surface\",\n\n // box-border and font-sans are not boilerplate. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none: content-box\n // would add the padding to any width they set (the Alert shipped\n // overflowing its container by 46px), and an unstyled document renders\n // text in a serif (the Accordion shipped its panel that way). Declaring\n // the family HERE rather than on each part means one declaration covers\n // the whole subtree by inheritance.\n \"box-border font-sans\",\n]);\n\n/**\n * Padding is inline-only on the sections and block-only on the shell.\n *\n * That split is load-bearing rather than stylistic: it is what lets a\n * consumer run a divider or a full-bleed image edge to edge, which a single\n * `p-6` on the shell would make impossible.\n */\nexport const cardHeaderVariants = cva(\"flex flex-col gap-1.5 px-6\");\n\nexport const cardTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-base leading-(--leading-none) font-semibold\",\n // The user-agent <h3> ships a 1em block margin and font-size 1.17em. The\n // legacy component was a <div> and never met either.\n \"m-0\",\n]);\n\nexport const cardDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n]);\n\nexport const cardContentVariants = cva(\"px-6\");\n\nexport const cardFooterVariants = cva(\"flex items-center px-6\");\n\n/**\n * A content container.\n *\n * @example\n * <Card>\n * <CardHeader>\n * <CardTitle>Learning Progress</CardTitle>\n * <CardDescription>Across all active courses.</CardDescription>\n * </CardHeader>\n * <CardContent>Twelve of eighteen modules complete.</CardContent>\n * </Card>\n */\nexport type CardProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { className, ...props },\n ref,\n) {\n return (\n <div ref={ref} {...props} data-slot=\"card\" className={cn(cardVariants(), className)} />\n );\n});\n\n/** Title and description, stacked. */\nexport type CardHeaderProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(function CardHeader(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-header\"\n className={cn(cardHeaderVariants(), className)}\n />\n );\n});\n\n/**\n * The card's heading.\n *\n * A REAL heading, unlike the legacy `<div class=\"font-semibold\">`. None of\n * the 19 legacy call sites wrapped one, so 19 card titles were invisible to\n * screen-reader heading navigation -- the same shape of finding as the Avatar\n * port's missing `alt`, and the migration is when it gets answered.\n */\nexport interface CardTitleProps extends ComponentPropsWithoutRef<\"h3\"> {\n /**\n * Heading level.\n *\n * `3` by default, because a card almost always sits under a section\n * heading. It is a prop rather than a constant because the correct level\n * is a fact about the PAGE, and a fixed one would be wrong somewhere; the\n * level changes semantics only, never the rendered size.\n *\n * @default 3\n */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(function CardTitle(\n { className, level = 3, ...props },\n ref,\n) {\n // Static union, not a template literal: `h${level}` is a string React\n // accepts but TypeScript cannot narrow to an intrinsic element, and the\n // cast that silences it would also accept `h7`.\n const Heading = ({ 1: \"h1\", 2: \"h2\", 3: \"h3\", 4: \"h4\", 5: \"h5\", 6: \"h6\" } as const)[level];\n\n return (\n <Heading\n ref={ref}\n {...props}\n data-slot=\"card-title\"\n className={cn(cardTitleVariants(), className)}\n />\n );\n});\n\n/** Secondary text under the title. */\nexport type CardDescriptionProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardDescription = forwardRef<HTMLDivElement, CardDescriptionProps>(\n function CardDescription({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-description\"\n className={cn(cardDescriptionVariants(), className)}\n />\n );\n },\n);\n\n/** The card's body. */\nexport type CardContentProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardContent = forwardRef<HTMLDivElement, CardContentProps>(function CardContent(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-content\"\n className={cn(cardContentVariants(), className)}\n />\n );\n});\n\n/** Actions or metadata at the foot of the card. */\nexport type CardFooterProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(function CardFooter(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-footer\"\n className={cn(cardFooterVariants(), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,eAAe,IAAI;CAC9B;CACA;CACA;CASA;AACF,CAAC;;;;;;;;AASD,MAAa,qBAAqB,IAAI,4BAA4B;AAElE,MAAa,oBAAoB,IAAI,CAGnC,oDAGA,KACF,CAAC;AAED,MAAa,0BAA0B,IAAI,CACzC,0DACF,CAAC;AAED,MAAa,sBAAsB,IAAI,MAAM;AAE7C,MAAa,qBAAqB,IAAI,wBAAwB;AAgB9D,MAAa,OAAO,WAAsC,SAAS,KACjE,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EAAU;EAAK,GAAI;EAAO,aAAU;EAAO,WAAW,GAAG,aAAa,GAAG,SAAS;CAAI,CAAA;AAE1F,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAwBD,MAAa,YAAY,WAA+C,SAAS,UAC/E,EAAE,WAAW,QAAQ,GAAG,GAAG,SAC3B,KACA;CAIA,MAAM,UAAW;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,EAAY;CAEpF,OACE,oBAAC,SAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,kBAAkB,GAAG,SAAS;CAC7C,CAAA;AAEL,CAAC;AAKD,MAAa,kBAAkB,WAC7B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,wBAAwB,GAAG,SAAS;CACnD,CAAA;AAEL,CACF;AAKA,MAAa,cAAc,WAA6C,SAAS,YAC/E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;CAC/C,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CheckboxProps } from "react-aria-components";
|
|
2
1
|
import { ReactNode } from "react";
|
|
2
|
+
import { CheckboxProps } from "react-aria-components";
|
|
3
3
|
//#region src/checkbox/checkbox.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Checkbox, rebuilt on React Aria.
|
|
@@ -57,7 +57,7 @@ interface CheckboxProps$1 extends Omit<CheckboxProps, "className" | "style" | "c
|
|
|
57
57
|
/** Extra classes for the label. Merged last, so they win conflicts. */
|
|
58
58
|
className?: string;
|
|
59
59
|
}
|
|
60
|
-
declare
|
|
60
|
+
declare const Checkbox$1: import("react").ForwardRefExoticComponent<CheckboxProps$1 & import("react").RefAttributes<HTMLInputElement>>;
|
|
61
61
|
//#endregion
|
|
62
62
|
export { Checkbox$1 as Checkbox, CheckboxProps$1 as CheckboxProps };
|
|
63
63
|
//# sourceMappingURL=checkbox.d.ts.map
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { selectionLabelVariants, selectionRingVariants } from "../lib/selection-control.js";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
|
+
import { forwardRef } from "react";
|
|
5
6
|
import { Checkbox } from "react-aria-components";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
8
|
//#region src/checkbox/checkbox.tsx
|
|
@@ -53,9 +54,10 @@ const checkboxBoxVariants = cva([
|
|
|
53
54
|
"group-data-[indeterminate]:text-primary-foreground",
|
|
54
55
|
selectionRingVariants()
|
|
55
56
|
]);
|
|
56
|
-
|
|
57
|
+
const Checkbox$1 = forwardRef(function Checkbox$2({ className, children, ...props }, ref) {
|
|
57
58
|
return /* @__PURE__ */ jsx(Checkbox, {
|
|
58
59
|
...props,
|
|
60
|
+
inputRef: ref,
|
|
59
61
|
"data-slot": "checkbox",
|
|
60
62
|
className: cn(checkboxVariants(), className),
|
|
61
63
|
children: ({ isSelected, isIndeterminate }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
@@ -87,7 +89,7 @@ function Checkbox$1({ className, children, ...props }) {
|
|
|
87
89
|
children
|
|
88
90
|
})] })
|
|
89
91
|
});
|
|
90
|
-
}
|
|
92
|
+
});
|
|
91
93
|
//#endregion
|
|
92
94
|
export { Checkbox$1 as Checkbox, checkboxBoxVariants, checkboxVariants };
|
|
93
95
|
|