@mediquo/medicore 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/advisor-contact/advisor-contact.js +1 -1
- package/dist/components/advisor-contact/advisor-contact.js.map +1 -1
- package/dist/components/bottom-bar/bottom-bar.js +1 -1
- package/dist/components/bottom-bar/bottom-bar.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +1 -1
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/button/button.d.ts +2 -1
- package/dist/components/button/button.js +8 -4
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/card/card.d.ts +2 -1
- package/dist/components/card/card.js +10 -3
- package/dist/components/card/card.js.map +1 -1
- package/dist/components/card-article/card-article.js +6 -2
- package/dist/components/card-article/card-article.js.map +1 -1
- package/dist/components/card-professional-mini/card-professional-mini.js +1 -1
- package/dist/components/card-professional-mini/card-professional-mini.js.map +1 -1
- package/dist/components/card-program/card-program.js +9 -3
- package/dist/components/card-program/card-program.js.map +1 -1
- package/dist/components/chat/chat-composer.js +1 -1
- package/dist/components/chat/chat-composer.js.map +1 -1
- package/dist/components/chat/chat-conversation-row.js +1 -1
- package/dist/components/chat/chat-conversation-row.js.map +1 -1
- package/dist/components/checkbox/checkbox.js +2 -1
- package/dist/components/checkbox/checkbox.js.map +1 -1
- package/dist/components/chip/chip.js +2 -2
- package/dist/components/chip/chip.js.map +1 -1
- package/dist/components/day-cell/day-cell.js +2 -1
- package/dist/components/day-cell/day-cell.js.map +1 -1
- package/dist/components/hover-arrow/hover-arrow.d.ts +4 -0
- package/dist/components/hover-arrow/hover-arrow.js +18 -0
- package/dist/components/hover-arrow/hover-arrow.js.map +1 -0
- package/dist/components/hover-arrow/index.d.ts +1 -0
- package/dist/components/input/control-styles.d.ts +2 -2
- package/dist/components/input/control-styles.js +2 -2
- package/dist/components/input/control-styles.js.map +1 -1
- package/dist/components/link/link.d.ts +2 -1
- package/dist/components/link/link.js +8 -5
- package/dist/components/link/link.js.map +1 -1
- package/dist/components/list-row/list-row.js +1 -1
- package/dist/components/list-row/list-row.js.map +1 -1
- package/dist/components/menu/menu.js +1 -1
- package/dist/components/menu/menu.js.map +1 -1
- package/dist/components/modal/modal.js +1 -1
- package/dist/components/modal/modal.js.map +1 -1
- package/dist/components/nav-item/nav-item.js +2 -2
- package/dist/components/nav-item/nav-item.js.map +1 -1
- package/dist/components/otp-input/otp-input.js +3 -2
- package/dist/components/otp-input/otp-input.js.map +1 -1
- package/dist/components/radio/radio.js +2 -1
- package/dist/components/radio/radio.js.map +1 -1
- package/dist/components/segmented-control/segmented-control.js +1 -1
- package/dist/components/segmented-control/segmented-control.js.map +1 -1
- package/dist/components/select/select.js +1 -1
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/selection-card/selection-card.js +2 -1
- package/dist/components/selection-card/selection-card.js.map +1 -1
- package/dist/components/switch/switch.js +3 -2
- package/dist/components/switch/switch.js.map +1 -1
- package/dist/components/table/table.js +3 -3
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/tabs/tabs.js +3 -3
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/components/time-slot/time-slot.js +2 -1
- package/dist/components/time-slot/time-slot.js.map +1 -1
- package/dist/components/toast/toast.js +1 -1
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/tooltip/tooltip.js +1 -1
- package/dist/components/tooltip/tooltip.js.map +1 -1
- package/dist/components/video/video-control-button.js +1 -1
- package/dist/components/video/video-control-button.js.map +1 -1
- package/dist/icons/create-icon.d.ts +1 -0
- package/dist/icons/create-icon.js +7 -1
- package/dist/icons/create-icon.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/styles/tokens.css +3 -0
- package/dist/styles/utilities.css +121 -0
- package/package.json +1 -1
|
@@ -61,7 +61,7 @@ function AdvisorContact({
|
|
|
61
61
|
className: cn(
|
|
62
62
|
"relative flex min-w-0 items-center rounded-lg border px-3 py-2",
|
|
63
63
|
hero ? "gap-4 border-border-inverse" : "gap-3 border-border-subtle bg-background",
|
|
64
|
-
isLink && "
|
|
64
|
+
isLink && "interactive hover:bg-surface",
|
|
65
65
|
className
|
|
66
66
|
),
|
|
67
67
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"advisor-contact.js","sources":["../../../src/components/advisor-contact/advisor-contact.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Avatar, type AvatarSize } from \"../avatar\";\nimport { CardLink } from \"../card\";\n\nexport type AdvisorContactVariant = \"hero\" | \"bar\" | \"callout\";\n\nexport interface AdvisorContactProps\n extends Omit<ComponentProps<\"div\">, \"title\"> {\n /**\n * `hero` (default): on the home greeting (dark background) · `bar`: in the header of\n * the programme pages, the whole bar opens the chat (`href`) · `callout`: inside a\n * programme page, after the options. The callout stacks by itself (Figma\n * `Stacked`) when its column is narrower than 560px.\n */\n variant?: AdvisorContactVariant;\n /** Hero/Bar: \"Víctor Martínez · Tu asesor de salud\". Callout: \"¿No sabes cuál elegir?\". */\n title: ReactNode;\n /** \"Hola, Marta. Cuéntame en qué te ayudo.\" / \"¿Te ayudo a elegir?\". */\n message: ReactNode;\n /** The advisor's photo. */\n avatarSrc?: string;\n /**\n * Available right now: the green dot, named \"Disponible ahora\"\n * (`advisorAvailable` label). If not, leave it out and say in `message` when they\n * answer.\n */\n available?: boolean;\n /**\n * Hero: an `inverse-ghost` small `Button` \"Escribir\" with `MessageIcon`. Callout: a\n * secondary `Button` \"Escribir a Víctor\". It opens the chat with the advisor.\n */\n action?: ReactNode;\n /** Bar: the chat with the advisor. The whole bar is this link. */\n href?: string;\n /** Bar: router link, `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n}\n\nconst avatarSizes: Record<AdvisorContactVariant, AvatarSize> = {\n hero: 40,\n bar: 32,\n callout: 48,\n};\n\n/**\n * Access to the patient's health advisor: a real person from the team, not a\n * doctor. Presented as \"tu asesor de salud\", never \"asesor comercial\": they help to\n * choose, they don't sell, and they never give medical advice.\n */\nexport function AdvisorContact({\n variant = \"hero\",\n title,\n message,\n avatarSrc,\n available = false,\n action,\n href,\n render,\n className,\n ...props\n}: AdvisorContactProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const avatar = available ? (\n <Avatar\n size={avatarSizes[variant]}\n src={avatarSrc}\n status=\"online\"\n statusLabel={labels.advisorAvailable}\n />\n ) : (\n <Avatar size={avatarSizes[variant]} src={avatarSrc} />\n );\n\n if (variant === \"callout\") {\n return (\n <div\n data-slot=\"advisor-contact\"\n data-variant=\"callout\"\n className={cn(\"@container rounded-xl bg-brand-subtle\", className)}\n {...props}\n >\n <div className=\"flex flex-col gap-4 px-6 py-4 @min-[560px]:flex-row @min-[560px]:items-center\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3 @min-[560px]:gap-4\">\n {avatar}\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <p className=\"text-label-strong text-text-primary\">{title}</p>\n <p className=\"text-body-small text-text-primary\">{message}</p>\n </div>\n </div>\n {action && (\n <div className=\"flex shrink-0 *:flex-1 @min-[560px]:*:flex-none\">\n {action}\n </div>\n )}\n </div>\n </div>\n );\n }\n\n const hero = variant === \"hero\";\n const isLink = !hero && Boolean(href || render);\n\n return (\n <div\n data-slot=\"advisor-contact\"\n data-variant={variant}\n className={cn(\n \"relative flex min-w-0 items-center rounded-lg border px-3 py-2\",\n hero\n ? \"gap-4 border-border-inverse\"\n : \"gap-3 border-border-subtle bg-background\",\n isLink && \"
|
|
1
|
+
{"version":3,"file":"advisor-contact.js","sources":["../../../src/components/advisor-contact/advisor-contact.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Avatar, type AvatarSize } from \"../avatar\";\nimport { CardLink } from \"../card\";\n\nexport type AdvisorContactVariant = \"hero\" | \"bar\" | \"callout\";\n\nexport interface AdvisorContactProps\n extends Omit<ComponentProps<\"div\">, \"title\"> {\n /**\n * `hero` (default): on the home greeting (dark background) · `bar`: in the header of\n * the programme pages, the whole bar opens the chat (`href`) · `callout`: inside a\n * programme page, after the options. The callout stacks by itself (Figma\n * `Stacked`) when its column is narrower than 560px.\n */\n variant?: AdvisorContactVariant;\n /** Hero/Bar: \"Víctor Martínez · Tu asesor de salud\". Callout: \"¿No sabes cuál elegir?\". */\n title: ReactNode;\n /** \"Hola, Marta. Cuéntame en qué te ayudo.\" / \"¿Te ayudo a elegir?\". */\n message: ReactNode;\n /** The advisor's photo. */\n avatarSrc?: string;\n /**\n * Available right now: the green dot, named \"Disponible ahora\"\n * (`advisorAvailable` label). If not, leave it out and say in `message` when they\n * answer.\n */\n available?: boolean;\n /**\n * Hero: an `inverse-ghost` small `Button` \"Escribir\" with `MessageIcon`. Callout: a\n * secondary `Button` \"Escribir a Víctor\". It opens the chat with the advisor.\n */\n action?: ReactNode;\n /** Bar: the chat with the advisor. The whole bar is this link. */\n href?: string;\n /** Bar: router link, `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n}\n\nconst avatarSizes: Record<AdvisorContactVariant, AvatarSize> = {\n hero: 40,\n bar: 32,\n callout: 48,\n};\n\n/**\n * Access to the patient's health advisor: a real person from the team, not a\n * doctor. Presented as \"tu asesor de salud\", never \"asesor comercial\": they help to\n * choose, they don't sell, and they never give medical advice.\n */\nexport function AdvisorContact({\n variant = \"hero\",\n title,\n message,\n avatarSrc,\n available = false,\n action,\n href,\n render,\n className,\n ...props\n}: AdvisorContactProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const avatar = available ? (\n <Avatar\n size={avatarSizes[variant]}\n src={avatarSrc}\n status=\"online\"\n statusLabel={labels.advisorAvailable}\n />\n ) : (\n <Avatar size={avatarSizes[variant]} src={avatarSrc} />\n );\n\n if (variant === \"callout\") {\n return (\n <div\n data-slot=\"advisor-contact\"\n data-variant=\"callout\"\n className={cn(\"@container rounded-xl bg-brand-subtle\", className)}\n {...props}\n >\n <div className=\"flex flex-col gap-4 px-6 py-4 @min-[560px]:flex-row @min-[560px]:items-center\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3 @min-[560px]:gap-4\">\n {avatar}\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <p className=\"text-label-strong text-text-primary\">{title}</p>\n <p className=\"text-body-small text-text-primary\">{message}</p>\n </div>\n </div>\n {action && (\n <div className=\"flex shrink-0 *:flex-1 @min-[560px]:*:flex-none\">\n {action}\n </div>\n )}\n </div>\n </div>\n );\n }\n\n const hero = variant === \"hero\";\n const isLink = !hero && Boolean(href || render);\n\n return (\n <div\n data-slot=\"advisor-contact\"\n data-variant={variant}\n className={cn(\n \"relative flex min-w-0 items-center rounded-lg border px-3 py-2\",\n hero\n ? \"gap-4 border-border-inverse\"\n : \"gap-3 border-border-subtle bg-background\",\n isLink && \"interactive hover:bg-surface\",\n className,\n )}\n {...props}\n >\n {avatar}\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <p\n className={\n hero\n ? \"text-label-small text-text-inverse-secondary\"\n : \"text-label-strong text-text-primary\"\n }\n >\n {isLink ? (\n <CardLink href={href} render={render} className=\"after:rounded-lg\">\n {title}\n </CardLink>\n ) : (\n title\n )}\n </p>\n <p\n className={\n hero\n ? \"text-label-strong text-text-inverse\"\n : \"text-body-small text-text-secondary\"\n }\n >\n {message}\n </p>\n </div>\n {action && <div className=\"relative z-10 flex shrink-0\">{action}</div>}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;AA0CA,MAAM,cAAyD;AAAA,EAC7D,MAAM;AAAA,EACN,KAAK;AAAA,EACL,SAAS;AACX;AAOO,SAAS,eAAe;AAAA,EAC7B,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,QAAM,SAAS,kBAAA;AACf,QAAM,SAAS,YACb;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAM,YAAY,OAAO;AAAA,MACzB,KAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa,OAAO;AAAA,IAAA;AAAA,EAAA,wBAGrB,QAAA,EAAO,MAAM,YAAY,OAAO,GAAG,KAAK,WAAW;AAGtD,MAAI,YAAY,WAAW;AACzB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,aAAU;AAAA,QACV,gBAAa;AAAA,QACb,WAAW,GAAG,yCAAyC,SAAS;AAAA,QAC/D,GAAG;AAAA,QAEJ,UAAA,qBAAC,OAAA,EAAI,WAAU,iFACb,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,6DACZ,UAAA;AAAA,YAAA;AAAA,YACD,qBAAC,OAAA,EAAI,WAAU,wCACb,UAAA;AAAA,cAAA,oBAAC,KAAA,EAAE,WAAU,uCAAuC,UAAA,OAAM;AAAA,cAC1D,oBAAC,KAAA,EAAE,WAAU,qCAAqC,UAAA,QAAA,CAAQ;AAAA,YAAA,EAAA,CAC5D;AAAA,UAAA,GACF;AAAA,UACC,UACC,oBAAC,OAAA,EAAI,WAAU,mDACZ,UAAA,OAAA,CACH;AAAA,QAAA,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AAEA,QAAM,OAAO,YAAY;AACzB,QAAM,SAAS,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAE9C,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA,OACI,gCACA;AAAA,QACJ,UAAU;AAAA,QACV;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA;AAAA,QACD,qBAAC,OAAA,EAAI,WAAU,wCACb,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WACE,OACI,iDACA;AAAA,cAGL,UAAA,6BACE,UAAA,EAAS,MAAY,QAAgB,WAAU,oBAC7C,iBACH,IAEA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WACE,OACI,wCACA;AAAA,cAGL,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH,GACF;AAAA,QACC,UAAU,oBAAC,OAAA,EAAI,WAAU,+BAA+B,UAAA,OAAA,CAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGtE;"}
|
|
@@ -27,7 +27,7 @@ function BottomBar({
|
|
|
27
27
|
}
|
|
28
28
|
const bottomBarItemVariants = cva([
|
|
29
29
|
"group/tab relative flex min-w-0 flex-1 cursor-pointer select-none flex-col items-center justify-center gap-1 rounded-md",
|
|
30
|
-
"text-label-small text-text-secondary no-underline
|
|
30
|
+
"text-label-small text-text-secondary no-underline interactive",
|
|
31
31
|
"[&_svg]:size-5.5 [&_svg]:shrink-0",
|
|
32
32
|
"not-aria-[current=page]:hover:text-text-primary",
|
|
33
33
|
"aria-[current=page]:text-text-brand",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottom-bar.js","sources":["../../../src/components/bottom-bar/bottom-bar.tsx"],"sourcesContent":["import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva } from \"class-variance-authority\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport interface BottomBarProps extends ComponentProps<\"nav\"> {}\n\n/**\n * Mobile tab bar: a `<nav>` named \"Navegación principal\" with a list of links, the\n * current one with `aria-current=\"page\"`. Tabs share the width equally (five at 75px\n * on a 375 phone) and it adds the iPhone's `safe-area-inset-bottom` under its 77px.\n *\n * - Up to five tabs, the main sections. Everything else must stay reachable from the\n * menu in `MobileHeader` (see `AppShell`).\n * - A tab may use the short form of the section's name (\"Documentos\" for \"Mis\n * documentos\"), never a different name. If a label doesn't fit, shorten the word;\n * don't lower the 12px.\n * - `AppShell` keeps it at the bottom of the screen below the desktop breakpoint.\n */\nexport function BottomBar({\n \"aria-label\": ariaLabel,\n className,\n children,\n ...props\n}: BottomBarProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n\n return (\n <nav\n data-slot=\"bottom-bar\"\n aria-label={ariaLabel ?? labels.mainNavigation}\n className={cn(\n \"border-border border-t bg-surface pb-[env(safe-area-inset-bottom)]\",\n className,\n )}\n {...props}\n >\n <ul className=\"flex h-19.25\">{children}</ul>\n </nav>\n );\n}\n\nexport const bottomBarItemVariants = cva([\n \"group/tab relative flex min-w-0 flex-1 cursor-pointer select-none flex-col items-center justify-center gap-1 rounded-md\",\n \"text-label-small text-text-secondary no-underline
|
|
1
|
+
{"version":3,"file":"bottom-bar.js","sources":["../../../src/components/bottom-bar/bottom-bar.tsx"],"sourcesContent":["import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva } from \"class-variance-authority\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport interface BottomBarProps extends ComponentProps<\"nav\"> {}\n\n/**\n * Mobile tab bar: a `<nav>` named \"Navegación principal\" with a list of links, the\n * current one with `aria-current=\"page\"`. Tabs share the width equally (five at 75px\n * on a 375 phone) and it adds the iPhone's `safe-area-inset-bottom` under its 77px.\n *\n * - Up to five tabs, the main sections. Everything else must stay reachable from the\n * menu in `MobileHeader` (see `AppShell`).\n * - A tab may use the short form of the section's name (\"Documentos\" for \"Mis\n * documentos\"), never a different name. If a label doesn't fit, shorten the word;\n * don't lower the 12px.\n * - `AppShell` keeps it at the bottom of the screen below the desktop breakpoint.\n */\nexport function BottomBar({\n \"aria-label\": ariaLabel,\n className,\n children,\n ...props\n}: BottomBarProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n\n return (\n <nav\n data-slot=\"bottom-bar\"\n aria-label={ariaLabel ?? labels.mainNavigation}\n className={cn(\n \"border-border border-t bg-surface pb-[env(safe-area-inset-bottom)]\",\n className,\n )}\n {...props}\n >\n <ul className=\"flex h-19.25\">{children}</ul>\n </nav>\n );\n}\n\nexport const bottomBarItemVariants = cva([\n \"group/tab relative flex min-w-0 flex-1 cursor-pointer select-none flex-col items-center justify-center gap-1 rounded-md\",\n \"text-label-small text-text-secondary no-underline interactive\",\n \"[&_svg]:size-5.5 [&_svg]:shrink-0\",\n \"not-aria-[current=page]:hover:text-text-primary\",\n \"aria-[current=page]:text-text-brand\",\n // Inside the tab: the tabs touch each other, there is no room outside.\n \"focus-ring focus-visible:-outline-offset-2\",\n]);\n\nexport interface BottomBarItemProps\n extends Omit<useRender.ComponentProps<\"a\">, \"className\" | \"children\"> {\n /** A MediCore icon, drawn at 22px. */\n icon: ReactNode;\n /** 12px label (Label/Small). Always visible: an icon-only tab bar is a guessing game. */\n children: ReactNode;\n /**\n * The tab of the current screen: `aria-current=\"page\"`, brand colour and a 3px bar\n * on top (the non-colour cue). Leave it out if the router link sets\n * `aria-current=\"page\"` itself.\n */\n active?: boolean;\n /**\n * The central action of the product (\"Pedir cita\"): the icon sits in a\n * `brand-subtle` circle. At most one per bar.\n */\n featured?: boolean;\n className?: string;\n}\n\n/**\n * One tab (Figma `Nav/TabItem`). Renders an `<a>` inside an `<li>`; pass the router's\n * link with `render={<Link to=\"/agenda\" />}`.\n */\nexport function BottomBarItem({\n icon,\n children,\n active = false,\n featured = false,\n className,\n render,\n ...props\n}: BottomBarItemProps): React.JSX.Element {\n const ownProps: useRender.ElementProps<\"a\"> & { \"data-slot\": string } = {\n \"data-slot\": \"bottom-bar-link\",\n className: cn(bottomBarItemVariants(), className),\n \"aria-current\": active ? \"page\" : undefined,\n children: (\n <>\n <span\n aria-hidden\n data-slot=\"bottom-bar-indicator\"\n className=\"absolute inset-x-0 top-0 hidden h-0.75 rounded-b-xs bg-text-brand group-aria-[current=page]/tab:block\"\n />\n {featured ? (\n <span className=\"flex size-9.5 items-center justify-center rounded-full bg-brand-subtle\">\n {icon}\n </span>\n ) : (\n icon\n )}\n <span className=\"max-w-full truncate\">{children}</span>\n </>\n ),\n };\n\n const element = useRender({\n defaultTagName: \"a\",\n render,\n state: { active, featured },\n props: mergeProps<\"a\">(ownProps, props),\n });\n\n return (\n <li data-slot=\"bottom-bar-item\" className=\"flex min-w-0 flex-1\">\n {element}\n </li>\n );\n}\n"],"names":[],"mappings":";;;;;;AAsBO,SAAS,UAAU;AAAA,EACxB,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsC;AACpC,QAAM,SAAS,kBAAA;AAEf,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,aAAa,OAAO;AAAA,MAChC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA,oBAAC,MAAA,EAAG,WAAU,gBAAgB,SAAA,CAAS;AAAA,IAAA;AAAA,EAAA;AAG7C;AAEO,MAAM,wBAAwB,IAAI;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AACF,CAAC;AA0BM,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA0C;AACxC,QAAM,WAAkE;AAAA,IACtE,aAAa;AAAA,IACb,WAAW,GAAG,sBAAA,GAAyB,SAAS;AAAA,IAChD,gBAAgB,SAAS,SAAS;AAAA,IAClC,UACE,qBAAA,UAAA,EACE,UAAA;AAAA,MAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,eAAW;AAAA,UACX,aAAU;AAAA,UACV,WAAU;AAAA,QAAA;AAAA,MAAA;AAAA,MAEX,WACC,oBAAC,QAAA,EAAK,WAAU,0EACb,gBACH,IAEA;AAAA,MAEF,oBAAC,QAAA,EAAK,WAAU,uBAAuB,SAAA,CAAS;AAAA,IAAA,EAAA,CAClD;AAAA,EAAA;AAIJ,QAAM,UAAU,UAAU;AAAA,IACxB,gBAAgB;AAAA,IAChB;AAAA,IACA,OAAO,EAAE,QAAQ,SAAA;AAAA,IACjB,OAAO,WAAgB,UAAU,KAAK;AAAA,EAAA,CACvC;AAED,6BACG,MAAA,EAAG,aAAU,mBAAkB,WAAU,uBACvC,UAAA,SACH;AAEJ;"}
|
|
@@ -46,7 +46,7 @@ function BreadcrumbItem({
|
|
|
46
46
|
props: {
|
|
47
47
|
"data-slot": "breadcrumb-link",
|
|
48
48
|
className: cn(
|
|
49
|
-
"focus-ring touch-target inline-flex items-center gap-1 rounded-xs text-label-default text-text-secondary
|
|
49
|
+
"focus-ring touch-target inline-flex items-center gap-1 rounded-xs text-label-default text-text-secondary interactive hover:text-text-brand",
|
|
50
50
|
className
|
|
51
51
|
),
|
|
52
52
|
children,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breadcrumb.js","sources":["../../../src/components/breadcrumb/breadcrumb.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { ChevronLeftIcon, ChevronRightIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport interface BreadcrumbProps extends ComponentProps<\"nav\"> {\n /** `BreadcrumbItem`s for the levels above, then one `BreadcrumbPage` for the current one. */\n children: ReactNode;\n}\n\n/**\n * Breadcrumb for pages two or three levels deep (a programme inside \"Programas\n * especializados\"). A `<nav>` named \"Migas de pan\" (`breadcrumb` label) with an\n * `<ol>`: the levels above are links, the last one is plain text with\n * `aria-current=\"page\"`.\n *\n * ```tsx\n * <Breadcrumb>\n * <BreadcrumbItem render={<Link to=\"/programas\" />}>Programas especializados</BreadcrumbItem>\n * <BreadcrumbPage>Pérdida de peso</BreadcrumbPage>\n * </Breadcrumb>\n * ```\n *\n * The levels above are quiet (Label/Default in text-secondary, brand on hover), with\n * a chevron in border-strong between them, so the trail never competes with the\n * page title under it. The links are not underlined: inside the breadcrumb landmark\n * and its list, their position already tells they are links (WCAG 1.4.1 applies to\n * links in running text).\n *\n * Below the `lg` breakpoint only the level right above shows, as a way back with an\n * arrow (\"‹ Programas especializados\").\n */\nexport function Breadcrumb({\n \"aria-label\": ariaLabel,\n className,\n children,\n ...props\n}: BreadcrumbProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n\n return (\n <nav\n data-slot=\"breadcrumb\"\n aria-label={ariaLabel ?? labels.breadcrumb}\n className={className}\n {...props}\n >\n <ol\n className={cn(\n \"flex flex-wrap items-center gap-2\",\n // Mobile: only the parent level (the second-to-last item) stays.\n \"max-lg:[&>li:not(:nth-last-child(2))]:hidden\",\n )}\n >\n {children}\n </ol>\n </nav>\n );\n}\n\nexport interface BreadcrumbItemProps extends useRender.ComponentProps<\"a\"> {\n /** The level's name, the same as the section or page it links to. */\n children: ReactNode;\n}\n\n/**\n * A level above the current page: a link (`href`, or the router's with `render`)\n * followed by a chevron. On mobile, the parent level gets a back arrow in front\n * instead.\n */\nexport function BreadcrumbItem({\n render,\n ref,\n className,\n children,\n ...props\n}: BreadcrumbItemProps): React.JSX.Element {\n const link = useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"data-slot\": \"breadcrumb-link\",\n className: cn(\n \"focus-ring touch-target inline-flex items-center gap-1 rounded-xs text-label-default text-text-secondary
|
|
1
|
+
{"version":3,"file":"breadcrumb.js","sources":["../../../src/components/breadcrumb/breadcrumb.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { ChevronLeftIcon, ChevronRightIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport interface BreadcrumbProps extends ComponentProps<\"nav\"> {\n /** `BreadcrumbItem`s for the levels above, then one `BreadcrumbPage` for the current one. */\n children: ReactNode;\n}\n\n/**\n * Breadcrumb for pages two or three levels deep (a programme inside \"Programas\n * especializados\"). A `<nav>` named \"Migas de pan\" (`breadcrumb` label) with an\n * `<ol>`: the levels above are links, the last one is plain text with\n * `aria-current=\"page\"`.\n *\n * ```tsx\n * <Breadcrumb>\n * <BreadcrumbItem render={<Link to=\"/programas\" />}>Programas especializados</BreadcrumbItem>\n * <BreadcrumbPage>Pérdida de peso</BreadcrumbPage>\n * </Breadcrumb>\n * ```\n *\n * The levels above are quiet (Label/Default in text-secondary, brand on hover), with\n * a chevron in border-strong between them, so the trail never competes with the\n * page title under it. The links are not underlined: inside the breadcrumb landmark\n * and its list, their position already tells they are links (WCAG 1.4.1 applies to\n * links in running text).\n *\n * Below the `lg` breakpoint only the level right above shows, as a way back with an\n * arrow (\"‹ Programas especializados\").\n */\nexport function Breadcrumb({\n \"aria-label\": ariaLabel,\n className,\n children,\n ...props\n}: BreadcrumbProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n\n return (\n <nav\n data-slot=\"breadcrumb\"\n aria-label={ariaLabel ?? labels.breadcrumb}\n className={className}\n {...props}\n >\n <ol\n className={cn(\n \"flex flex-wrap items-center gap-2\",\n // Mobile: only the parent level (the second-to-last item) stays.\n \"max-lg:[&>li:not(:nth-last-child(2))]:hidden\",\n )}\n >\n {children}\n </ol>\n </nav>\n );\n}\n\nexport interface BreadcrumbItemProps extends useRender.ComponentProps<\"a\"> {\n /** The level's name, the same as the section or page it links to. */\n children: ReactNode;\n}\n\n/**\n * A level above the current page: a link (`href`, or the router's with `render`)\n * followed by a chevron. On mobile, the parent level gets a back arrow in front\n * instead.\n */\nexport function BreadcrumbItem({\n render,\n ref,\n className,\n children,\n ...props\n}: BreadcrumbItemProps): React.JSX.Element {\n const link = useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"data-slot\": \"breadcrumb-link\",\n className: cn(\n \"focus-ring touch-target inline-flex items-center gap-1 rounded-xs text-label-default text-text-secondary interactive hover:text-text-brand\",\n className,\n ),\n children,\n ...props,\n },\n });\n\n return (\n <li data-slot=\"breadcrumb-item\" className=\"flex items-center gap-2\">\n <ChevronLeftIcon size=\"sm\" className=\"text-text-secondary lg:hidden\" />\n {link}\n <ChevronRightIcon\n size=\"sm\"\n className=\"text-border-strong max-lg:hidden\"\n />\n </li>\n );\n}\n\nexport interface BreadcrumbPageProps extends ComponentProps<\"span\"> {}\n\n/** The current level: text, not a link, with `aria-current=\"page\"`. Hidden on mobile. */\nexport function BreadcrumbPage({\n className,\n ...props\n}: BreadcrumbPageProps): React.JSX.Element {\n return (\n <li data-slot=\"breadcrumb-page\" className=\"flex items-center\">\n <span\n aria-current=\"page\"\n className={cn(\"text-label-strong text-text-primary\", className)}\n {...props}\n />\n </li>\n );\n}\n"],"names":[],"mappings":";;;;;;AAkCO,SAAS,WAAW;AAAA,EACzB,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAuC;AACrC,QAAM,SAAS,kBAAA;AAEf,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,aAAa,OAAO;AAAA,MAChC;AAAA,MACC,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA;AAAA,YAEA;AAAA,UAAA;AAAA,UAGD;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN;AAYO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,QAAM,OAAO,UAAU;AAAA,IACrB;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,OAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAEF;AAAA,MACA,GAAG;AAAA,IAAA;AAAA,EACL,CACD;AAED,SACE,qBAAC,MAAA,EAAG,aAAU,mBAAkB,WAAU,2BACxC,UAAA;AAAA,IAAA,oBAAC,iBAAA,EAAgB,MAAK,MAAK,WAAU,iCAAgC;AAAA,IACpE;AAAA,IACD;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EACZ,GACF;AAEJ;AAKO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,SACE,oBAAC,MAAA,EAAG,aAAU,mBAAkB,WAAU,qBACxC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,gBAAa;AAAA,MACb,WAAW,GAAG,uCAAuC,SAAS;AAAA,MAC7D,GAAG;AAAA,IAAA;AAAA,EAAA,GAER;AAEJ;"}
|
|
@@ -12,6 +12,7 @@ export interface ButtonProps extends Omit<BaseButton.Props, "className">, Button
|
|
|
12
12
|
loading?: boolean;
|
|
13
13
|
leadingIcon?: ReactNode;
|
|
14
14
|
trailingIcon?: ReactNode;
|
|
15
|
+
hoverArrow?: boolean;
|
|
15
16
|
className?: string;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
@@ -25,5 +26,5 @@ export interface ButtonProps extends Omit<BaseButton.Props, "className">, Button
|
|
|
25
26
|
* The drawing is 40px (36 small) but the hit area is always 44px. Full width: pass
|
|
26
27
|
* `className="w-full"`.
|
|
27
28
|
*/
|
|
28
|
-
export declare function Button({ variant, size, disabled, loading, leadingIcon, trailingIcon, className, children, type, ...props }: ButtonProps): React.JSX.Element;
|
|
29
|
+
export declare function Button({ variant, size, disabled, loading, leadingIcon, trailingIcon, hoverArrow, className, children, type, ...props }: ButtonProps): React.JSX.Element;
|
|
29
30
|
export {};
|
|
@@ -3,10 +3,12 @@ import { Button as Button$1 } from "@base-ui/react/button";
|
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
import { cn } from "../../lib/cn.js";
|
|
5
5
|
import { Spinner } from "../spinner/spinner.js";
|
|
6
|
+
import { HoverArrow } from "../hover-arrow/hover-arrow.js";
|
|
6
7
|
const buttonVariants = cva(
|
|
7
8
|
[
|
|
8
9
|
"inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap",
|
|
9
|
-
"rounded-md border border-transparent text-label-strong
|
|
10
|
+
"hover-trigger rounded-md border border-transparent text-label-strong interactive pressable",
|
|
11
|
+
"[--hover-arrow-gap:--spacing(2)] [--hover-arrow-size:var(--spacing-icon-sm)] [--hover-arrow-space:--spacing(2)]",
|
|
10
12
|
"focus-ring touch-target",
|
|
11
13
|
"[&_svg]:shrink-0 [&_svg]:size-icon-sm",
|
|
12
14
|
"data-disabled:cursor-not-allowed data-disabled:shadow-none aria-busy:cursor-progress"
|
|
@@ -14,11 +16,11 @@ const buttonVariants = cva(
|
|
|
14
16
|
{
|
|
15
17
|
variants: {
|
|
16
18
|
variant: {
|
|
17
|
-
primary: "bg-brand text-text-inverse shadow-xs not-data-disabled:hover:bg-brand-strong",
|
|
19
|
+
primary: "bg-brand text-text-inverse shadow-xs not-data-disabled:hover:bg-brand-strong not-data-disabled:hover:shadow-md",
|
|
18
20
|
secondary: "border-text-brand bg-brand-subtle text-text-brand not-data-disabled:hover:bg-background",
|
|
19
21
|
outline: "border-border bg-background text-text-primary shadow-xs not-data-disabled:hover:border-border-strong not-data-disabled:hover:bg-surface",
|
|
20
22
|
ghost: "bg-transparent text-text-primary not-data-disabled:hover:bg-surface",
|
|
21
|
-
destructive: "bg-error-strong text-text-inverse shadow-xs",
|
|
23
|
+
destructive: "bg-error-strong text-text-inverse shadow-xs not-data-disabled:hover:brightness-90",
|
|
22
24
|
inverse: "bg-background text-text-brand shadow-xs not-data-disabled:hover:bg-surface",
|
|
23
25
|
"inverse-ghost": "bg-transparent text-text-inverse not-data-disabled:hover:bg-overlay-light"
|
|
24
26
|
},
|
|
@@ -74,6 +76,7 @@ function Button({
|
|
|
74
76
|
loading = false,
|
|
75
77
|
leadingIcon,
|
|
76
78
|
trailingIcon,
|
|
79
|
+
hoverArrow = false,
|
|
77
80
|
className,
|
|
78
81
|
children,
|
|
79
82
|
type = "button",
|
|
@@ -92,7 +95,8 @@ function Button({
|
|
|
92
95
|
children: [
|
|
93
96
|
loading ? /* @__PURE__ */ jsx(Spinner, { size: 16, label: null, className: "text-current" }) : leadingIcon,
|
|
94
97
|
children,
|
|
95
|
-
!loading && trailingIcon
|
|
98
|
+
!loading && trailingIcon,
|
|
99
|
+
hoverArrow && !loading && !disabled && /* @__PURE__ */ jsx(HoverArrow, {})
|
|
96
100
|
]
|
|
97
101
|
}
|
|
98
102
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Spinner } from \"../spinner\";\n\nexport const buttonVariants = cva(\n [\n \"inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap\",\n \"rounded-md border border-transparent text-label-strong
|
|
1
|
+
{"version":3,"file":"button.js","sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { HoverArrow } from \"../hover-arrow\";\nimport { Spinner } from \"../spinner\";\n\nexport const buttonVariants = cva(\n [\n \"inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap\",\n \"hover-trigger rounded-md border border-transparent text-label-strong interactive pressable\",\n \"[--hover-arrow-gap:--spacing(2)] [--hover-arrow-size:var(--spacing-icon-sm)] [--hover-arrow-space:--spacing(2)]\",\n \"focus-ring touch-target\",\n \"[&_svg]:shrink-0 [&_svg]:size-icon-sm\",\n \"data-disabled:cursor-not-allowed data-disabled:shadow-none aria-busy:cursor-progress\",\n ],\n {\n variants: {\n variant: {\n primary:\n \"bg-brand text-text-inverse shadow-xs not-data-disabled:hover:bg-brand-strong not-data-disabled:hover:shadow-md\",\n secondary:\n \"border-text-brand bg-brand-subtle text-text-brand not-data-disabled:hover:bg-background\",\n outline:\n \"border-border bg-background text-text-primary shadow-xs not-data-disabled:hover:border-border-strong not-data-disabled:hover:bg-surface\",\n ghost:\n \"bg-transparent text-text-primary not-data-disabled:hover:bg-surface\",\n destructive:\n \"bg-error-strong text-text-inverse shadow-xs not-data-disabled:hover:brightness-90\",\n inverse:\n \"bg-background text-text-brand shadow-xs not-data-disabled:hover:bg-surface\",\n \"inverse-ghost\":\n \"bg-transparent text-text-inverse not-data-disabled:hover:bg-overlay-light\",\n },\n size: {\n default: \"h-control-md px-4\",\n small: \"h-control-sm px-3\",\n },\n disabled: {\n true: \"\",\n false: \"\",\n },\n },\n // Disabled uses explicit colours, not opacity: an opacity can't be a token and it\n // also dims the border.\n compoundVariants: [\n {\n variant: [\"primary\", \"destructive\"],\n disabled: true,\n className: \"bg-surface-raised text-text-disabled\",\n },\n {\n variant: [\"secondary\", \"outline\"],\n disabled: true,\n className: \"border-border-subtle bg-surface text-text-disabled\",\n },\n {\n variant: \"ghost\",\n disabled: true,\n className: \"text-text-disabled\",\n },\n {\n variant: \"inverse\",\n disabled: true,\n className: \"bg-border-inverse text-text-inverse-secondary\",\n },\n {\n variant: \"inverse-ghost\",\n disabled: true,\n className: \"text-text-inverse-secondary\",\n },\n ],\n defaultVariants: {\n variant: \"primary\",\n size: \"default\",\n disabled: false,\n },\n },\n);\n\ntype ButtonVariantProps = Omit<VariantProps<typeof buttonVariants>, \"disabled\">;\n\nexport interface ButtonProps\n extends Omit<BaseButton.Props, \"className\">,\n ButtonVariantProps {\n /** Keeps the variant colour, shows a spinner and ignores clicks without losing focus. */\n loading?: boolean;\n leadingIcon?: ReactNode;\n trailingIcon?: ReactNode;\n hoverArrow?: boolean;\n className?: string;\n}\n\n/**\n * Action button. A button ACTS; to navigate use `Link` (or `render={<a href />}` when\n * it has to look like a button).\n *\n * Hierarchy, from most to least weight: primary → secondary → outline → ghost.\n * `destructive` is a warning, not a level. `inverse` and `inverse-ghost` are only\n * for dark surfaces (`bg-brand-dark`). One primary action per block.\n *\n * The drawing is 40px (36 small) but the hit area is always 44px. Full width: pass\n * `className=\"w-full\"`.\n */\nexport function Button({\n variant,\n size,\n disabled = false,\n loading = false,\n leadingIcon,\n trailingIcon,\n hoverArrow = false,\n className,\n children,\n type = \"button\",\n ...props\n}: ButtonProps): React.JSX.Element {\n return (\n <BaseButton\n data-slot=\"button\"\n type={type}\n disabled={disabled || loading}\n focusableWhenDisabled={loading}\n aria-busy={loading || undefined}\n className={cn(buttonVariants({ variant, size, disabled }), className)}\n {...props}\n >\n {loading ? (\n <Spinner size={16} label={null} className=\"text-current\" />\n ) : (\n leadingIcon\n )}\n {children}\n {!loading && trailingIcon}\n {hoverArrow && !loading && !disabled && <HoverArrow />}\n </BaseButton>\n );\n}\n"],"names":["BaseButton"],"mappings":";;;;;;AAQO,MAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SACE;AAAA,QACF,WACE;AAAA,QACF,SACE;AAAA,QACF,OACE;AAAA,QACF,aACE;AAAA,QACF,SACE;AAAA,QACF,iBACE;AAAA,MAAA;AAAA,MAEJ,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAAA,MAET,UAAU;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,MAAA;AAAA,IACT;AAAA;AAAA;AAAA,IAIF,kBAAkB;AAAA,MAChB;AAAA,QACE,SAAS,CAAC,WAAW,aAAa;AAAA,QAClC,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS,CAAC,aAAa,SAAS;AAAA,QAChC,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,IACb;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,MACN,UAAU;AAAA,IAAA;AAAA,EACZ;AAEJ;AA0BO,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,GAAG;AACL,GAAmC;AACjC,SACE;AAAA,IAACA;AAAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,uBAAuB;AAAA,MACvB,aAAW,WAAW;AAAA,MACtB,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,SAAA,CAAU,GAAG,SAAS;AAAA,MACnE,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,UACC,oBAAC,WAAQ,MAAM,IAAI,OAAO,MAAM,WAAU,gBAAe,IAEzD;AAAA,QAED;AAAA,QACA,CAAC,WAAW;AAAA,QACZ,cAAc,CAAC,WAAW,CAAC,gCAAa,YAAA,CAAA,CAAW;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG1D;"}
|
|
@@ -49,6 +49,7 @@ export interface CardFooterProps extends ComponentProps<"div"> {
|
|
|
49
49
|
/** Where the card's single primary action goes. Its children share the width. */
|
|
50
50
|
export declare function CardFooter({ className, ...props }: CardFooterProps): React.JSX.Element;
|
|
51
51
|
export interface CardLinkProps extends useRender.ComponentProps<"a"> {
|
|
52
|
+
hoverArrow?: boolean;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* The link that makes a whole card navigable. It goes inside `CardHeader`'s title
|
|
@@ -56,7 +57,7 @@ export interface CardLinkProps extends useRender.ComponentProps<"a"> {
|
|
|
56
57
|
* opened in a new tab and read as one link. Router links: `render={<Link to="…" />}`.
|
|
57
58
|
* Other controls in the card (the "⋯" menu, the footer) stay on top of it.
|
|
58
59
|
*/
|
|
59
|
-
export declare function CardLink({ render, ref, className, ...props }: CardLinkProps): React.JSX.Element;
|
|
60
|
+
export declare function CardLink({ render, ref, className, hoverArrow, children, ...props }: CardLinkProps): React.JSX.Element;
|
|
60
61
|
export interface CardListProps extends ComponentProps<"ul"> {
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRender } from "@base-ui/react/use-render";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
import { InfoCircleIcon } from "../../icons/generated/info-circle.js";
|
|
5
5
|
import { cn } from "../../lib/cn.js";
|
|
6
|
+
import { HoverArrow } from "../hover-arrow/hover-arrow.js";
|
|
6
7
|
const cardVariants = cva(
|
|
7
|
-
"relative flex min-w-0 flex-col gap-4 rounded-xl",
|
|
8
|
+
"relative flex min-w-0 flex-col gap-4 rounded-xl interactive hover-lift",
|
|
8
9
|
{
|
|
9
10
|
variants: {
|
|
10
11
|
elevation: {
|
|
@@ -80,6 +81,8 @@ function CardLink({
|
|
|
80
81
|
render,
|
|
81
82
|
ref,
|
|
82
83
|
className,
|
|
84
|
+
hoverArrow = false,
|
|
85
|
+
children,
|
|
83
86
|
...props
|
|
84
87
|
}) {
|
|
85
88
|
return useRender({
|
|
@@ -89,10 +92,14 @@ function CardLink({
|
|
|
89
92
|
props: {
|
|
90
93
|
"data-slot": "card-link",
|
|
91
94
|
className: cn(
|
|
92
|
-
"outline-none after:absolute after:inset-0 after:rounded-xl",
|
|
95
|
+
"hover-trigger outline-none interactive hover:text-text-brand after:absolute after:inset-0 after:rounded-xl",
|
|
93
96
|
"focus-visible:after:outline-2 focus-visible:after:outline-border-focus focus-visible:after:outline-offset-2",
|
|
94
97
|
className
|
|
95
98
|
),
|
|
99
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
100
|
+
children,
|
|
101
|
+
hoverArrow && /* @__PURE__ */ jsx(HoverArrow, {})
|
|
102
|
+
] }),
|
|
96
103
|
...props
|
|
97
104
|
}
|
|
98
105
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","sources":["../../../src/components/card/card.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { InfoCircleIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\n\nexport const cardVariants = cva(\n \"relative flex min-w-0 flex-col gap-4 rounded-xl\",\n {\n variants: {\n elevation: {\n flat: \"border border-border-subtle bg-background\",\n raised: \"bg-background shadow-s\",\n filled: \"bg-surface\",\n },\n padding: {\n default: \"p-4\",\n large: \"p-6\",\n },\n },\n defaultVariants: {\n elevation: \"flat\",\n padding: \"default\",\n },\n },\n);\n\ntype CardVariantProps = VariantProps<typeof cardVariants>;\n\nexport type CardElevation = NonNullable<CardVariantProps[\"elevation\"]>;\n\nexport type CardPadding = NonNullable<CardVariantProps[\"padding\"]>;\n\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\nexport interface CardProps extends ComponentProps<\"div\">, CardVariantProps {}\n\n/**\n * The container the Card family is built on (radius-xl, padding 16 or 24, one of\n * three elevations). Header, content and footer are parts, not props:\n * `<Card><CardHeader /> …children… <CardFooter /></Card>`.\n *\n * **Pick the elevation by what it sits on — pick wrong and the card disappears:**\n * - `flat` (default): white with a 1px border, on a light `surface`. Without the\n * border, white on surface is 1.07:1.\n * - `filled`: `surface` fill, no border, on a white background.\n * - `raised`: white with `shadow-s`, only for what really floats (a dropdown, a\n * draggable card). Eight shadowed cards in a list rank nothing, they're just noise.\n *\n * A Card is not interactive. If the whole card navigates, put a `CardLink` in the\n * title (a real link stretched over the card), never an `onClick` on the `<div>`.\n *\n * Not for full-bleed images: the padding lives in the container. Don't zero it.\n */\nexport function Card({\n elevation,\n padding,\n className,\n ...props\n}: CardProps): React.JSX.Element {\n return (\n <div\n data-slot=\"card\"\n className={cn(cardVariants({ elevation, padding }), className)}\n {...props}\n />\n );\n}\n\nexport interface CardHeaderProps extends Omit<ComponentProps<\"div\">, \"title\"> {\n /** Heading/h4 look. Put a `CardLink` inside when the whole card navigates. */\n title: ReactNode;\n /**\n * Heading level for the outline (h2–h6). The look doesn't change; the level comes\n * from where the card sits in the page. Default 3.\n */\n headingLevel?: CardHeadingLevel;\n /**\n * The \"⋯\" overflow menu (a `Menu` with a ghost small icon trigger). One primary\n * action per block: the primary goes in `CardFooter`, everything else here.\n */\n action?: ReactNode;\n}\n\nexport function CardHeader({\n title,\n headingLevel = 3,\n action,\n className,\n ...props\n}: CardHeaderProps): React.JSX.Element {\n const Heading = `h${headingLevel}` as const;\n\n return (\n <div\n data-slot=\"card-header\"\n className={cn(\"flex min-w-0 items-center gap-2\", className)}\n {...props}\n >\n <Heading className=\"min-w-0 flex-1 text-heading-h4 text-text-primary\">\n {title}\n </Heading>\n {action && (\n // Above a stretched CardLink so it stays clickable.\n <div className=\"relative z-10 -my-2 flex shrink-0\">{action}</div>\n )}\n </div>\n );\n}\n\nexport interface CardFooterProps extends ComponentProps<\"div\"> {}\n\n/** Where the card's single primary action goes. Its children share the width. */\nexport function CardFooter({\n className,\n ...props\n}: CardFooterProps): React.JSX.Element {\n return (\n <div\n data-slot=\"card-footer\"\n className={cn(\n \"relative z-10 flex items-center gap-2 *:flex-1\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface CardLinkProps extends useRender.ComponentProps<\"a\"> {}\n\n/**\n * The link that makes a whole card navigable. It goes inside `CardHeader`'s title\n * and stretches over the card with a pseudo-element, so it can still be copied,\n * opened in a new tab and read as one link. Router links: `render={<Link to=\"…\" />}`.\n * Other controls in the card (the \"⋯\" menu, the footer) stay on top of it.\n */\nexport function CardLink({\n render,\n ref,\n className,\n ...props\n}: CardLinkProps): React.JSX.Element {\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"data-slot\": \"card-link\",\n className: cn(\n \"outline-none after:absolute after:inset-0 after:rounded-xl\",\n \"focus-visible:after:outline-2 focus-visible:after:outline-border-focus focus-visible:after:outline-offset-2\",\n className,\n ),\n ...props,\n },\n });\n}\n\nexport interface CardListProps extends ComponentProps<\"ul\"> {}\n\n/**\n * Card content for a short list: 1 to 4 `ListRow`s (a consultation's documents, the\n * next appointments). With more than four, it isn't a card: use a list or a page.\n */\nexport function CardList({\n className,\n ...props\n}: CardListProps): React.JSX.Element {\n return (\n <ul\n data-slot=\"card-list\"\n className={cn(\"flex flex-col\", className)}\n {...props}\n />\n );\n}\n\nexport interface CardCalloutProps\n extends Omit<ComponentProps<\"div\">, \"children\">,\n Pick<CardVariantProps, \"elevation\"> {\n /** One short sentence (\"Usa el chat para enviar el motivo de consulta…\"). */\n message: ReactNode;\n /** Decorative, in `text-secondary`: the text carries the message. Default `info-circle`. */\n icon?: ReactNode;\n /** One `Button`, full width (\"Chatear con la profesional\"). */\n action?: ReactNode;\n}\n\n/**\n * A card with a short hint and ONE action, full width: waiting room, end of the\n * video call (\"Puedes chatear con tu profesional hasta 48 horas después de la cita\").\n *\n * It is not a Banner: a Banner reports a state (error, warning); this invites you to\n * do something. Raised by default, as in Figma; follow the Card elevation rule if it\n * sits on white.\n */\nexport function CardCallout({\n message,\n icon = <InfoCircleIcon />,\n action,\n elevation = \"raised\",\n className,\n ...props\n}: CardCalloutProps): React.JSX.Element {\n return (\n <div\n data-slot=\"card-callout\"\n className={cn(\n cardVariants({ elevation, padding: \"default\" }),\n \"gap-3\",\n className,\n )}\n {...props}\n >\n <div className=\"flex items-start gap-2\">\n <span\n aria-hidden\n className=\"flex h-5 shrink-0 items-center text-text-secondary [&_svg]:size-icon-md\"\n >\n {icon}\n </span>\n <p className=\"min-w-0 flex-1 text-body-small text-text-secondary\">\n {message}\n </p>\n </div>\n {action && <div className=\"flex *:flex-1\">{action}</div>}\n </div>\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"card.js","sources":["../../../src/components/card/card.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { InfoCircleIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { HoverArrow } from \"../hover-arrow\";\n\nexport const cardVariants = cva(\n \"relative flex min-w-0 flex-col gap-4 rounded-xl interactive hover-lift\",\n {\n variants: {\n elevation: {\n flat: \"border border-border-subtle bg-background\",\n raised: \"bg-background shadow-s\",\n filled: \"bg-surface\",\n },\n padding: {\n default: \"p-4\",\n large: \"p-6\",\n },\n },\n defaultVariants: {\n elevation: \"flat\",\n padding: \"default\",\n },\n },\n);\n\ntype CardVariantProps = VariantProps<typeof cardVariants>;\n\nexport type CardElevation = NonNullable<CardVariantProps[\"elevation\"]>;\n\nexport type CardPadding = NonNullable<CardVariantProps[\"padding\"]>;\n\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\n\nexport interface CardProps extends ComponentProps<\"div\">, CardVariantProps {}\n\n/**\n * The container the Card family is built on (radius-xl, padding 16 or 24, one of\n * three elevations). Header, content and footer are parts, not props:\n * `<Card><CardHeader /> …children… <CardFooter /></Card>`.\n *\n * **Pick the elevation by what it sits on — pick wrong and the card disappears:**\n * - `flat` (default): white with a 1px border, on a light `surface`. Without the\n * border, white on surface is 1.07:1.\n * - `filled`: `surface` fill, no border, on a white background.\n * - `raised`: white with `shadow-s`, only for what really floats (a dropdown, a\n * draggable card). Eight shadowed cards in a list rank nothing, they're just noise.\n *\n * A Card is not interactive. If the whole card navigates, put a `CardLink` in the\n * title (a real link stretched over the card), never an `onClick` on the `<div>`.\n *\n * Not for full-bleed images: the padding lives in the container. Don't zero it.\n */\nexport function Card({\n elevation,\n padding,\n className,\n ...props\n}: CardProps): React.JSX.Element {\n return (\n <div\n data-slot=\"card\"\n className={cn(cardVariants({ elevation, padding }), className)}\n {...props}\n />\n );\n}\n\nexport interface CardHeaderProps extends Omit<ComponentProps<\"div\">, \"title\"> {\n /** Heading/h4 look. Put a `CardLink` inside when the whole card navigates. */\n title: ReactNode;\n /**\n * Heading level for the outline (h2–h6). The look doesn't change; the level comes\n * from where the card sits in the page. Default 3.\n */\n headingLevel?: CardHeadingLevel;\n /**\n * The \"⋯\" overflow menu (a `Menu` with a ghost small icon trigger). One primary\n * action per block: the primary goes in `CardFooter`, everything else here.\n */\n action?: ReactNode;\n}\n\nexport function CardHeader({\n title,\n headingLevel = 3,\n action,\n className,\n ...props\n}: CardHeaderProps): React.JSX.Element {\n const Heading = `h${headingLevel}` as const;\n\n return (\n <div\n data-slot=\"card-header\"\n className={cn(\"flex min-w-0 items-center gap-2\", className)}\n {...props}\n >\n <Heading className=\"min-w-0 flex-1 text-heading-h4 text-text-primary\">\n {title}\n </Heading>\n {action && (\n // Above a stretched CardLink so it stays clickable.\n <div className=\"relative z-10 -my-2 flex shrink-0\">{action}</div>\n )}\n </div>\n );\n}\n\nexport interface CardFooterProps extends ComponentProps<\"div\"> {}\n\n/** Where the card's single primary action goes. Its children share the width. */\nexport function CardFooter({\n className,\n ...props\n}: CardFooterProps): React.JSX.Element {\n return (\n <div\n data-slot=\"card-footer\"\n className={cn(\n \"relative z-10 flex items-center gap-2 *:flex-1\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport interface CardLinkProps extends useRender.ComponentProps<\"a\"> {\n hoverArrow?: boolean;\n}\n\n/**\n * The link that makes a whole card navigable. It goes inside `CardHeader`'s title\n * and stretches over the card with a pseudo-element, so it can still be copied,\n * opened in a new tab and read as one link. Router links: `render={<Link to=\"…\" />}`.\n * Other controls in the card (the \"⋯\" menu, the footer) stay on top of it.\n */\nexport function CardLink({\n render,\n ref,\n className,\n hoverArrow = false,\n children,\n ...props\n}: CardLinkProps): React.JSX.Element {\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"data-slot\": \"card-link\",\n className: cn(\n \"hover-trigger outline-none interactive hover:text-text-brand after:absolute after:inset-0 after:rounded-xl\",\n \"focus-visible:after:outline-2 focus-visible:after:outline-border-focus focus-visible:after:outline-offset-2\",\n className,\n ),\n children: (\n <>\n {children}\n {hoverArrow && <HoverArrow />}\n </>\n ),\n ...props,\n },\n });\n}\n\nexport interface CardListProps extends ComponentProps<\"ul\"> {}\n\n/**\n * Card content for a short list: 1 to 4 `ListRow`s (a consultation's documents, the\n * next appointments). With more than four, it isn't a card: use a list or a page.\n */\nexport function CardList({\n className,\n ...props\n}: CardListProps): React.JSX.Element {\n return (\n <ul\n data-slot=\"card-list\"\n className={cn(\"flex flex-col\", className)}\n {...props}\n />\n );\n}\n\nexport interface CardCalloutProps\n extends Omit<ComponentProps<\"div\">, \"children\">,\n Pick<CardVariantProps, \"elevation\"> {\n /** One short sentence (\"Usa el chat para enviar el motivo de consulta…\"). */\n message: ReactNode;\n /** Decorative, in `text-secondary`: the text carries the message. Default `info-circle`. */\n icon?: ReactNode;\n /** One `Button`, full width (\"Chatear con la profesional\"). */\n action?: ReactNode;\n}\n\n/**\n * A card with a short hint and ONE action, full width: waiting room, end of the\n * video call (\"Puedes chatear con tu profesional hasta 48 horas después de la cita\").\n *\n * It is not a Banner: a Banner reports a state (error, warning); this invites you to\n * do something. Raised by default, as in Figma; follow the Card elevation rule if it\n * sits on white.\n */\nexport function CardCallout({\n message,\n icon = <InfoCircleIcon />,\n action,\n elevation = \"raised\",\n className,\n ...props\n}: CardCalloutProps): React.JSX.Element {\n return (\n <div\n data-slot=\"card-callout\"\n className={cn(\n cardVariants({ elevation, padding: \"default\" }),\n \"gap-3\",\n className,\n )}\n {...props}\n >\n <div className=\"flex items-start gap-2\">\n <span\n aria-hidden\n className=\"flex h-5 shrink-0 items-center text-text-secondary [&_svg]:size-icon-md\"\n >\n {icon}\n </span>\n <p className=\"min-w-0 flex-1 text-body-small text-text-secondary\">\n {message}\n </p>\n </div>\n {action && <div className=\"flex *:flex-1\">{action}</div>}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { cn } from "../../lib/cn.js";
|
|
|
3
3
|
import { useMediCoreLabels } from "../../provider/medicore-provider.js";
|
|
4
4
|
import { Badge } from "../badge/badge.js";
|
|
5
5
|
import { CardLink } from "../card/card.js";
|
|
6
|
+
import { HoverArrow } from "../hover-arrow/hover-arrow.js";
|
|
6
7
|
import { linkVariants } from "../link/link.js";
|
|
7
8
|
function CardArticle({
|
|
8
9
|
title,
|
|
@@ -22,7 +23,7 @@ function CardArticle({
|
|
|
22
23
|
{
|
|
23
24
|
"data-slot": "card-article",
|
|
24
25
|
className: cn(
|
|
25
|
-
"relative flex min-w-0 flex-col rounded-xl bg-background shadow-s",
|
|
26
|
+
"relative flex min-w-0 flex-col rounded-xl bg-background shadow-s interactive hover-lift hover-zoom-media",
|
|
26
27
|
className
|
|
27
28
|
),
|
|
28
29
|
...props,
|
|
@@ -34,7 +35,10 @@ function CardArticle({
|
|
|
34
35
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-2 p-4", children: [
|
|
35
36
|
/* @__PURE__ */ jsx(Heading, { className: "text-heading-h4 text-text-primary", children: href || render ? /* @__PURE__ */ jsx(CardLink, { href, render, children: title }) : title }),
|
|
36
37
|
excerpt && /* @__PURE__ */ jsx("p", { className: "text-body-small text-text-secondary", children: excerpt }),
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
+
/* @__PURE__ */ jsxs("span", { "aria-hidden": true, className: linkVariants(), children: [
|
|
39
|
+
labels.articleRead,
|
|
40
|
+
/* @__PURE__ */ jsx(HoverArrow, {})
|
|
41
|
+
] })
|
|
38
42
|
] })
|
|
39
43
|
]
|
|
40
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-article.js","sources":["../../../src/components/card-article/card-article.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Badge } from \"../badge\";\nimport { type CardHeadingLevel, CardLink } from \"../card\";\nimport { linkVariants } from \"../link\";\n\nexport interface CardArticleProps\n extends Omit<ComponentProps<\"article\">, \"title\"> {\n /** Informative, with no promise of results. Heading/h4 look; the link's name. */\n title: ReactNode;\n /** Two or three lines. */\n excerpt?: ReactNode;\n /** The article's category as a brand Badge on the image (\"Pérdida de peso\"). */\n category?: ReactNode;\n /** `<img src=\"…\" alt=\"\" />`, cropped to fill 200px. */\n image?: ReactNode;\n /** The article. The whole card is this link. */\n href?: string;\n /** Router link: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n /** Level of the title's heading. Default 3. */\n headingLevel?: CardHeadingLevel;\n}\n\n/**\n * The featured article of \"Te puede interesar\". The other articles go as\n * `ListRow`s with a thumbnail and the category as overline.\n *\n * The whole card links (a `CardLink` in the title). \"Leer artículo\"\n * (`articleRead` label) is only the visual cue: it looks like a link but it isn't a\n * second one.\n */\nexport function CardArticle({\n title,\n excerpt,\n category,\n image,\n href,\n render,\n headingLevel = 3,\n className,\n ...props\n}: CardArticleProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const Heading = `h${headingLevel}` as const;\n\n return (\n <article\n data-slot=\"card-article\"\n className={cn(\n \"relative flex min-w-0 flex-col rounded-xl bg-background shadow-s\",\n className,\n )}\n {...props}\n >\n <div className=\"relative h-50 shrink-0 overflow-hidden rounded-t-xl bg-brand-subtle [&>img]:size-full [&>img]:object-cover\">\n {image}\n {category && (\n <Badge variant=\"brand\" className=\"absolute top-3 left-3\">\n {category}\n </Badge>\n )}\n </div>\n <div className=\"flex flex-col items-start gap-2 p-4\">\n <Heading className=\"text-heading-h4 text-text-primary\">\n {href || render ? (\n <CardLink href={href} render={render}>\n {title}\n </CardLink>\n ) : (\n title\n )}\n </Heading>\n {excerpt && (\n <p className=\"text-body-small text-text-secondary\">{excerpt}</p>\n )}\n <span aria-hidden className={linkVariants()}>\n {labels.articleRead}\n </span>\n </div>\n </article>\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"card-article.js","sources":["../../../src/components/card-article/card-article.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Badge } from \"../badge\";\nimport { type CardHeadingLevel, CardLink } from \"../card\";\nimport { HoverArrow } from \"../hover-arrow\";\nimport { linkVariants } from \"../link\";\n\nexport interface CardArticleProps\n extends Omit<ComponentProps<\"article\">, \"title\"> {\n /** Informative, with no promise of results. Heading/h4 look; the link's name. */\n title: ReactNode;\n /** Two or three lines. */\n excerpt?: ReactNode;\n /** The article's category as a brand Badge on the image (\"Pérdida de peso\"). */\n category?: ReactNode;\n /** `<img src=\"…\" alt=\"\" />`, cropped to fill 200px. */\n image?: ReactNode;\n /** The article. The whole card is this link. */\n href?: string;\n /** Router link: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n /** Level of the title's heading. Default 3. */\n headingLevel?: CardHeadingLevel;\n}\n\n/**\n * The featured article of \"Te puede interesar\". The other articles go as\n * `ListRow`s with a thumbnail and the category as overline.\n *\n * The whole card links (a `CardLink` in the title). \"Leer artículo\"\n * (`articleRead` label) is only the visual cue: it looks like a link but it isn't a\n * second one.\n */\nexport function CardArticle({\n title,\n excerpt,\n category,\n image,\n href,\n render,\n headingLevel = 3,\n className,\n ...props\n}: CardArticleProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const Heading = `h${headingLevel}` as const;\n\n return (\n <article\n data-slot=\"card-article\"\n className={cn(\n \"relative flex min-w-0 flex-col rounded-xl bg-background shadow-s interactive hover-lift hover-zoom-media\",\n className,\n )}\n {...props}\n >\n <div className=\"relative h-50 shrink-0 overflow-hidden rounded-t-xl bg-brand-subtle [&>img]:size-full [&>img]:object-cover\">\n {image}\n {category && (\n <Badge variant=\"brand\" className=\"absolute top-3 left-3\">\n {category}\n </Badge>\n )}\n </div>\n <div className=\"flex flex-col items-start gap-2 p-4\">\n <Heading className=\"text-heading-h4 text-text-primary\">\n {href || render ? (\n <CardLink href={href} render={render}>\n {title}\n </CardLink>\n ) : (\n title\n )}\n </Heading>\n {excerpt && (\n <p className=\"text-body-small text-text-secondary\">{excerpt}</p>\n )}\n <span aria-hidden className={linkVariants()}>\n {labels.articleRead}\n <HoverArrow />\n </span>\n </div>\n </article>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAoCO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA,GAAG;AACL,GAAwC;AACtC,QAAM,SAAS,kBAAA;AACf,QAAM,UAAU,IAAI,YAAY;AAEhC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,8GACZ,UAAA;AAAA,UAAA;AAAA,UACA,YACC,oBAAC,OAAA,EAAM,SAAQ,SAAQ,WAAU,yBAC9B,UAAA,SAAA,CACH;AAAA,QAAA,GAEJ;AAAA,QACA,qBAAC,OAAA,EAAI,WAAU,uCACb,UAAA;AAAA,UAAA,oBAAC,SAAA,EAAQ,WAAU,qCAChB,UAAA,QAAQ,SACP,oBAAC,UAAA,EAAS,MAAY,QACnB,UAAA,MAAA,CACH,IAEA,OAEJ;AAAA,UACC,WACC,oBAAC,KAAA,EAAE,WAAU,uCAAuC,UAAA,SAAQ;AAAA,+BAE7D,QAAA,EAAK,eAAW,MAAC,WAAW,gBAC1B,UAAA;AAAA,YAAA,OAAO;AAAA,gCACP,YAAA,CAAA,CAAW;AAAA,UAAA,EAAA,CACd;AAAA,QAAA,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
|
@@ -19,7 +19,7 @@ function CardProfessionalMini({
|
|
|
19
19
|
"data-slot": "card-professional-mini",
|
|
20
20
|
className: cn(
|
|
21
21
|
"relative flex min-w-0 flex-col items-center gap-2 rounded-xl bg-background p-4 text-center shadow-s",
|
|
22
|
-
href || render ? "
|
|
22
|
+
href || render ? "interactive hover-lift" : void 0,
|
|
23
23
|
className
|
|
24
24
|
),
|
|
25
25
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-professional-mini.js","sources":["../../../src/components/card-professional-mini/card-professional-mini.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Avatar } from \"../avatar\";\nimport { type CardHeadingLevel, CardLink } from \"../card\";\n\nexport interface CardProfessionalMiniProps\n extends Omit<ComponentProps<\"article\">, \"title\"> {\n /** \"Dra. Paula García\". The card's heading. */\n name: ReactNode;\n /** \"Nº de colegiado 282873573\": their own, never a repeated placeholder number. */\n license: ReactNode;\n /** A real photo, always: never initials or an icon for a professional. */\n avatarSrc?: string;\n /** Their full profile (`CardProfessional`) when they have an agenda. The whole card links. */\n href?: string;\n /** Router link: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n /** Level of the name's heading. Default 3. */\n headingLevel?: CardHeadingLevel;\n}\n\n/**\n * A professional of a programme's medical team: photo + name + licence number, the\n * first trust signal.\n */\nexport function CardProfessionalMini({\n name,\n license,\n avatarSrc,\n href,\n render,\n headingLevel = 3,\n className,\n ...props\n}: CardProfessionalMiniProps): React.JSX.Element {\n const Heading = `h${headingLevel}` as const;\n\n return (\n <article\n data-slot=\"card-professional-mini\"\n className={cn(\n \"relative flex min-w-0 flex-col items-center gap-2 rounded-xl bg-background p-4 text-center shadow-s\",\n href || render ? \"
|
|
1
|
+
{"version":3,"file":"card-professional-mini.js","sources":["../../../src/components/card-professional-mini/card-professional-mini.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Avatar } from \"../avatar\";\nimport { type CardHeadingLevel, CardLink } from \"../card\";\n\nexport interface CardProfessionalMiniProps\n extends Omit<ComponentProps<\"article\">, \"title\"> {\n /** \"Dra. Paula García\". The card's heading. */\n name: ReactNode;\n /** \"Nº de colegiado 282873573\": their own, never a repeated placeholder number. */\n license: ReactNode;\n /** A real photo, always: never initials or an icon for a professional. */\n avatarSrc?: string;\n /** Their full profile (`CardProfessional`) when they have an agenda. The whole card links. */\n href?: string;\n /** Router link: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n /** Level of the name's heading. Default 3. */\n headingLevel?: CardHeadingLevel;\n}\n\n/**\n * A professional of a programme's medical team: photo + name + licence number, the\n * first trust signal.\n */\nexport function CardProfessionalMini({\n name,\n license,\n avatarSrc,\n href,\n render,\n headingLevel = 3,\n className,\n ...props\n}: CardProfessionalMiniProps): React.JSX.Element {\n const Heading = `h${headingLevel}` as const;\n\n return (\n <article\n data-slot=\"card-professional-mini\"\n className={cn(\n \"relative flex min-w-0 flex-col items-center gap-2 rounded-xl bg-background p-4 text-center shadow-s\",\n href || render ? \"interactive hover-lift\" : undefined,\n className,\n )}\n {...props}\n >\n <Avatar size={56} src={avatarSrc} />\n <Heading className=\"text-label-strong text-text-primary\">\n {href || render ? (\n <CardLink href={href} render={render}>\n {name}\n </CardLink>\n ) : (\n name\n )}\n </Heading>\n <p className=\"text-label-small text-text-secondary\">{license}</p>\n </article>\n );\n}\n"],"names":[],"mappings":";;;;AA2BO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA,GAAG;AACL,GAAiD;AAC/C,QAAM,UAAU,IAAI,YAAY;AAEhC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA,QAAQ,SAAS,2BAA2B;AAAA,QAC5C;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAO,MAAM,IAAI,KAAK,WAAW;AAAA,QAClC,oBAAC,SAAA,EAAQ,WAAU,uCAChB,UAAA,QAAQ,SACP,oBAAC,UAAA,EAAS,MAAY,QACnB,UAAA,KAAA,CACH,IAEA,MAEJ;AAAA,QACA,oBAAC,KAAA,EAAE,WAAU,wCAAwC,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGnE;"}
|
|
@@ -63,7 +63,7 @@ function CardProgram({
|
|
|
63
63
|
"data-slot": "card-program",
|
|
64
64
|
"data-layout": "featured",
|
|
65
65
|
className: cn(
|
|
66
|
-
"@container relative min-w-0 rounded-xl bg-background shadow-s",
|
|
66
|
+
"@container relative min-w-0 rounded-xl bg-background shadow-s interactive hover-lift hover-zoom-media",
|
|
67
67
|
className
|
|
68
68
|
),
|
|
69
69
|
...props,
|
|
@@ -97,7 +97,7 @@ function CardProgram({
|
|
|
97
97
|
"data-slot": "card-program",
|
|
98
98
|
"data-layout": "grid",
|
|
99
99
|
className: cn(
|
|
100
|
-
"@container relative flex min-w-0 flex-col rounded-xl bg-background shadow-s",
|
|
100
|
+
"@container relative flex min-w-0 flex-col rounded-xl bg-background shadow-s interactive hover-lift hover-zoom-media",
|
|
101
101
|
className
|
|
102
102
|
),
|
|
103
103
|
...props,
|
|
@@ -127,7 +127,13 @@ function CardProgram({
|
|
|
127
127
|
{
|
|
128
128
|
"aria-hidden": true,
|
|
129
129
|
className: "hidden size-7 shrink-0 items-center justify-center rounded-full bg-brand text-text-inverse @min-[240px]:flex",
|
|
130
|
-
children: /* @__PURE__ */ jsx(
|
|
130
|
+
children: /* @__PURE__ */ jsx(
|
|
131
|
+
ArrowNarrowRightIcon,
|
|
132
|
+
{
|
|
133
|
+
size: "sm",
|
|
134
|
+
className: "interactive motion-safe:hover-triggered:translate-x-0.5"
|
|
135
|
+
}
|
|
136
|
+
)
|
|
131
137
|
}
|
|
132
138
|
)
|
|
133
139
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-program.js","sources":["../../../src/components/card-program/card-program.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { ArrowNarrowRightIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { Badge } from \"../badge\";\nimport { type CardHeadingLevel, CardLink } from \"../card\";\n\nexport type CardProgramLayout = \"grid\" | \"featured\";\n\nexport interface CardProgramProps\n extends Omit<ComponentProps<\"article\">, \"title\"> {\n /** \"Pérdida de peso\". The card's heading and the link's name. */\n title: ReactNode;\n /** One or two lines. Hidden in the compact (Mobile) card. */\n description?: ReactNode;\n /**\n * The live price of THIS clinic's page, es-ES format with a space: \"Desde 30 €\",\n * \"29,99 €\". Never another clinic's price.\n */\n price: ReactNode;\n /** \"Videoconsulta\". Shown after the price with a \"·\". Hidden in the compact card. */\n channel?: ReactNode;\n /** A real photo from mediQuo's image line: `<img src=\"…\" alt=\"\" />` if it adds nothing. */\n image?: ReactNode;\n /** \"Tratamiento destacado\": a brand Badge (on the image in Grid, above the title in Featured). */\n badge?: ReactNode;\n /** Featured only: a metric Badge next to `badge` (\"4,6/5 valoración\"). */\n metric?: ReactNode;\n /** The programme's page. The whole card is this link. */\n href?: string;\n /** Router link: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n /** Featured only: the one action, a primary `Button` \"Reservar consulta\" (never \"Empezar tratamiento\"). */\n action?: ReactNode;\n /**\n * `grid` (default): the three-column grid; below 240px wide it becomes the compact\n * Mobile card (two-column grid on a phone) by itself. `featured`: the highlighted\n * programme, horizontal from 640px wide.\n */\n layout?: CardProgramLayout;\n /** Level of the title's heading. Default 3. */\n headingLevel?: CardHeadingLevel;\n}\n\nfunction ImageSlot({\n image,\n className,\n children,\n}: {\n image?: ReactNode;\n className?: string;\n children?: ReactNode;\n}): React.JSX.Element {\n return (\n <div\n className={cn(\n \"relative shrink-0 overflow-hidden bg-brand-subtle [&>img]:size-full [&>img]:object-cover\",\n className,\n )}\n >\n {image}\n {children}\n </div>\n );\n}\n\n/**\n * A specialised programme (Clínic). The whole card is the link to the programme's\n * page (a `CardLink` in the title); the arrow is decorative. One action.\n *\n * Figma `Layout=Mobile` is not a value: the `grid` card turns compact (no\n * description, no channel, no arrow) when its column is narrower than 240px.\n */\nexport function CardProgram({\n title,\n description,\n price,\n channel,\n image,\n badge,\n metric,\n href,\n render,\n action,\n layout = \"grid\",\n headingLevel = 3,\n className,\n ...props\n}: CardProgramProps): React.JSX.Element {\n const Heading = `h${headingLevel}` as const;\n const heading =\n href || render ? (\n <CardLink href={href} render={render}>\n {title}\n </CardLink>\n ) : (\n title\n );\n\n const priceRow = (\n <p className=\"flex min-w-0 flex-wrap items-baseline gap-1\">\n <span className=\"text-label-strong text-text-primary\">{price}</span>\n {channel && (\n <span\n className={cn(\n \"text-body-small text-text-secondary\",\n layout === \"grid\" && \"hidden @min-[240px]:inline\",\n )}\n >\n · {channel}\n </span>\n )}\n </p>\n );\n\n if (layout === \"featured\") {\n return (\n <article\n data-slot=\"card-program\"\n data-layout=\"featured\"\n className={cn(\n \"@container relative min-w-0 rounded-xl bg-background shadow-s\",\n className,\n )}\n {...props}\n >\n <div className=\"flex flex-col @min-[640px]:flex-row\">\n <ImageSlot\n image={image}\n className=\"h-40 rounded-t-xl @min-[640px]:h-auto @min-[640px]:min-h-60 @min-[640px]:w-1/2 @min-[640px]:rounded-tr-none @min-[640px]:rounded-l-xl\"\n />\n <div className=\"flex min-w-0 flex-1 flex-col justify-center gap-4 p-6 @min-[640px]:p-8\">\n {(badge || metric) && (\n <div className=\"flex flex-wrap gap-2\">\n {badge && <Badge variant=\"brand\">{badge}</Badge>}\n {metric && <Badge>{metric}</Badge>}\n </div>\n )}\n <Heading className=\"text-heading-h2 text-text-primary\">\n {heading}\n </Heading>\n {description && (\n <p className=\"text-body-small text-text-secondary\">\n {description}\n </p>\n )}\n <div className=\"flex flex-wrap items-center justify-between gap-4\">\n {priceRow}\n {action && <div className=\"relative z-10 flex\">{action}</div>}\n </div>\n </div>\n </div>\n </article>\n );\n }\n\n return (\n <article\n data-slot=\"card-program\"\n data-layout=\"grid\"\n className={cn(\n \"@container relative flex min-w-0 flex-col rounded-xl bg-background shadow-s\",\n className,\n )}\n {...props}\n >\n <ImageSlot\n image={image}\n className=\"h-27.5 rounded-t-xl @min-[240px]:h-40\"\n >\n {badge && (\n <Badge\n variant=\"brand\"\n className=\"absolute top-3 left-3 hidden @min-[240px]:inline-flex\"\n >\n {badge}\n </Badge>\n )}\n </ImageSlot>\n <div className=\"flex flex-1 flex-col gap-2 p-3 @min-[240px]:p-4\">\n <Heading className=\"text-label-strong text-text-primary @min-[240px]:text-heading-h4\">\n {heading}\n </Heading>\n {description && (\n <p className=\"line-clamp-3 hidden text-body-small text-text-secondary @min-[240px]:block\">\n {description}\n </p>\n )}\n <div className=\"mt-auto flex items-center gap-2\">\n <div className=\"min-w-0 flex-1\">{priceRow}</div>\n <span\n aria-hidden\n className=\"hidden size-7 shrink-0 items-center justify-center rounded-full bg-brand text-text-inverse @min-[240px]:flex\"\n >\n <ArrowNarrowRightIcon
|
|
1
|
+
{"version":3,"file":"card-program.js","sources":["../../../src/components/card-program/card-program.tsx"],"sourcesContent":["import type { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { ArrowNarrowRightIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { Badge } from \"../badge\";\nimport { type CardHeadingLevel, CardLink } from \"../card\";\n\nexport type CardProgramLayout = \"grid\" | \"featured\";\n\nexport interface CardProgramProps\n extends Omit<ComponentProps<\"article\">, \"title\"> {\n /** \"Pérdida de peso\". The card's heading and the link's name. */\n title: ReactNode;\n /** One or two lines. Hidden in the compact (Mobile) card. */\n description?: ReactNode;\n /**\n * The live price of THIS clinic's page, es-ES format with a space: \"Desde 30 €\",\n * \"29,99 €\". Never another clinic's price.\n */\n price: ReactNode;\n /** \"Videoconsulta\". Shown after the price with a \"·\". Hidden in the compact card. */\n channel?: ReactNode;\n /** A real photo from mediQuo's image line: `<img src=\"…\" alt=\"\" />` if it adds nothing. */\n image?: ReactNode;\n /** \"Tratamiento destacado\": a brand Badge (on the image in Grid, above the title in Featured). */\n badge?: ReactNode;\n /** Featured only: a metric Badge next to `badge` (\"4,6/5 valoración\"). */\n metric?: ReactNode;\n /** The programme's page. The whole card is this link. */\n href?: string;\n /** Router link: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n /** Featured only: the one action, a primary `Button` \"Reservar consulta\" (never \"Empezar tratamiento\"). */\n action?: ReactNode;\n /**\n * `grid` (default): the three-column grid; below 240px wide it becomes the compact\n * Mobile card (two-column grid on a phone) by itself. `featured`: the highlighted\n * programme, horizontal from 640px wide.\n */\n layout?: CardProgramLayout;\n /** Level of the title's heading. Default 3. */\n headingLevel?: CardHeadingLevel;\n}\n\nfunction ImageSlot({\n image,\n className,\n children,\n}: {\n image?: ReactNode;\n className?: string;\n children?: ReactNode;\n}): React.JSX.Element {\n return (\n <div\n className={cn(\n \"relative shrink-0 overflow-hidden bg-brand-subtle [&>img]:size-full [&>img]:object-cover\",\n className,\n )}\n >\n {image}\n {children}\n </div>\n );\n}\n\n/**\n * A specialised programme (Clínic). The whole card is the link to the programme's\n * page (a `CardLink` in the title); the arrow is decorative. One action.\n *\n * Figma `Layout=Mobile` is not a value: the `grid` card turns compact (no\n * description, no channel, no arrow) when its column is narrower than 240px.\n */\nexport function CardProgram({\n title,\n description,\n price,\n channel,\n image,\n badge,\n metric,\n href,\n render,\n action,\n layout = \"grid\",\n headingLevel = 3,\n className,\n ...props\n}: CardProgramProps): React.JSX.Element {\n const Heading = `h${headingLevel}` as const;\n const heading =\n href || render ? (\n <CardLink href={href} render={render}>\n {title}\n </CardLink>\n ) : (\n title\n );\n\n const priceRow = (\n <p className=\"flex min-w-0 flex-wrap items-baseline gap-1\">\n <span className=\"text-label-strong text-text-primary\">{price}</span>\n {channel && (\n <span\n className={cn(\n \"text-body-small text-text-secondary\",\n layout === \"grid\" && \"hidden @min-[240px]:inline\",\n )}\n >\n · {channel}\n </span>\n )}\n </p>\n );\n\n if (layout === \"featured\") {\n return (\n <article\n data-slot=\"card-program\"\n data-layout=\"featured\"\n className={cn(\n \"@container relative min-w-0 rounded-xl bg-background shadow-s interactive hover-lift hover-zoom-media\",\n className,\n )}\n {...props}\n >\n <div className=\"flex flex-col @min-[640px]:flex-row\">\n <ImageSlot\n image={image}\n className=\"h-40 rounded-t-xl @min-[640px]:h-auto @min-[640px]:min-h-60 @min-[640px]:w-1/2 @min-[640px]:rounded-tr-none @min-[640px]:rounded-l-xl\"\n />\n <div className=\"flex min-w-0 flex-1 flex-col justify-center gap-4 p-6 @min-[640px]:p-8\">\n {(badge || metric) && (\n <div className=\"flex flex-wrap gap-2\">\n {badge && <Badge variant=\"brand\">{badge}</Badge>}\n {metric && <Badge>{metric}</Badge>}\n </div>\n )}\n <Heading className=\"text-heading-h2 text-text-primary\">\n {heading}\n </Heading>\n {description && (\n <p className=\"text-body-small text-text-secondary\">\n {description}\n </p>\n )}\n <div className=\"flex flex-wrap items-center justify-between gap-4\">\n {priceRow}\n {action && <div className=\"relative z-10 flex\">{action}</div>}\n </div>\n </div>\n </div>\n </article>\n );\n }\n\n return (\n <article\n data-slot=\"card-program\"\n data-layout=\"grid\"\n className={cn(\n \"@container relative flex min-w-0 flex-col rounded-xl bg-background shadow-s interactive hover-lift hover-zoom-media\",\n className,\n )}\n {...props}\n >\n <ImageSlot\n image={image}\n className=\"h-27.5 rounded-t-xl @min-[240px]:h-40\"\n >\n {badge && (\n <Badge\n variant=\"brand\"\n className=\"absolute top-3 left-3 hidden @min-[240px]:inline-flex\"\n >\n {badge}\n </Badge>\n )}\n </ImageSlot>\n <div className=\"flex flex-1 flex-col gap-2 p-3 @min-[240px]:p-4\">\n <Heading className=\"text-label-strong text-text-primary @min-[240px]:text-heading-h4\">\n {heading}\n </Heading>\n {description && (\n <p className=\"line-clamp-3 hidden text-body-small text-text-secondary @min-[240px]:block\">\n {description}\n </p>\n )}\n <div className=\"mt-auto flex items-center gap-2\">\n <div className=\"min-w-0 flex-1\">{priceRow}</div>\n <span\n aria-hidden\n className=\"hidden size-7 shrink-0 items-center justify-center rounded-full bg-brand text-text-inverse @min-[240px]:flex\"\n >\n <ArrowNarrowRightIcon\n size=\"sm\"\n className=\"interactive motion-safe:hover-triggered:translate-x-0.5\"\n />\n </span>\n </div>\n </div>\n </article>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -87,7 +87,7 @@ function ChatComposer({
|
|
|
87
87
|
className: cn(
|
|
88
88
|
"block max-h-36 min-h-control-md min-w-0 flex-1 resize-none overflow-y-auto field-sizing-content",
|
|
89
89
|
"rounded-3xl border border-border-input bg-surface px-4 py-2.5 text-body-default text-text-primary",
|
|
90
|
-
"ring-1 ring-transparent ring-inset
|
|
90
|
+
"ring-1 ring-transparent ring-inset interactive hover:border-border-strong",
|
|
91
91
|
controlPlaceholder,
|
|
92
92
|
selfControlStates
|
|
93
93
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-composer.js","sources":["../../../src/components/chat/chat-composer.tsx"],"sourcesContent":["import {\n type ComponentProps,\n type KeyboardEvent,\n type ReactNode,\n useState,\n} from \"react\";\n\nimport { PaperclipIcon, Send01Icon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { IconButton } from \"../icon-button\";\nimport { controlPlaceholder, selfControlStates } from \"../input/control-styles\";\n\nexport interface ChatComposerProps\n extends Omit<ComponentProps<\"div\">, \"children\" | \"defaultValue\"> {\n /** Controlled text. */\n value?: string;\n /** Initial text when uncontrolled. */\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n /**\n * Called with the trimmed text on the send button or Enter (Shift+Enter adds a\n * line). The field then empties (`onValueChange(\"\")` when controlled).\n */\n onSend: (message: string) => void;\n /**\n * Shows the paperclip (\"Adjuntar archivo\"): open your file picker here — a photo of\n * the lesion, a report, a previous prescription.\n */\n onAttach?: () => void;\n /** Defaults to \"Escribe un mensaje\". It is also the field's accessible name. */\n placeholder?: string;\n /**\n * The consultation has ended and nobody would read the message: the field goes\n * away and the composer says why, with a way out.\n */\n closed?: boolean;\n /** With `closed`. Defaults to \"Esta consulta ha finalizado.\". */\n closedMessage?: ReactNode;\n /** With `closed`: a `Link` (\"Pedir otra consulta\"). */\n closedAction?: ReactNode;\n}\n\n/**\n * Where the patient writes: attach · field · send.\n *\n * - Send is disabled while the field is empty and turns on as soon as there is\n * text: an always-on send button that does nothing is a button that lies.\n * - Attach is a paperclip, not a \"+\": it says exactly what it does.\n * - The field grows with the text up to 5 lines and then scrolls; the buttons stay\n * at the bottom, not centred.\n * - Text at 16px: below 16 iOS zooms in when the field is focused.\n */\nexport function ChatComposer({\n value,\n defaultValue = \"\",\n onValueChange,\n onSend,\n onAttach,\n placeholder,\n closed = false,\n closedMessage,\n closedAction,\n className,\n ...props\n}: ChatComposerProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const text = value ?? uncontrolledValue;\n const canSend = text.trim().length > 0;\n\n const setText = (next: string): void => {\n if (value === undefined) {\n setUncontrolledValue(next);\n }\n onValueChange?.(next);\n };\n\n const send = (): void => {\n if (!canSend) {\n return;\n }\n onSend(text.trim());\n setText(\"\");\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {\n if (\n event.key === \"Enter\" &&\n !event.shiftKey &&\n !event.nativeEvent.isComposing\n ) {\n event.preventDefault();\n send();\n }\n };\n\n const fieldLabel = placeholder ?? labels.chatComposerPlaceholder;\n\n return (\n <div\n data-slot=\"chat-composer\"\n data-closed={closed || undefined}\n className={cn(\n \"border-border-subtle border-t bg-background p-3\",\n className,\n )}\n {...props}\n >\n {closed ? (\n <div className=\"flex flex-col items-center gap-1 text-center\">\n <p className=\"text-body-small text-text-secondary\">\n {closedMessage ?? labels.chatClosed}\n </p>\n {closedAction}\n </div>\n ) : (\n <form\n className=\"flex items-end gap-2\"\n onSubmit={(event) => {\n event.preventDefault();\n send();\n }}\n >\n {onAttach && (\n <IconButton\n variant=\"ghost\"\n icon={<PaperclipIcon />}\n aria-label={labels.chatAttach}\n onClick={onAttach}\n />\n )}\n <textarea\n data-slot=\"chat-composer-field\"\n rows={1}\n value={text}\n placeholder={fieldLabel}\n aria-label={fieldLabel}\n onChange={(event) => setText(event.currentTarget.value)}\n onKeyDown={handleKeyDown}\n className={cn(\n \"block max-h-36 min-h-control-md min-w-0 flex-1 resize-none overflow-y-auto field-sizing-content\",\n \"rounded-3xl border border-border-input bg-surface px-4 py-2.5 text-body-default text-text-primary\",\n \"ring-1 ring-transparent ring-inset
|
|
1
|
+
{"version":3,"file":"chat-composer.js","sources":["../../../src/components/chat/chat-composer.tsx"],"sourcesContent":["import {\n type ComponentProps,\n type KeyboardEvent,\n type ReactNode,\n useState,\n} from \"react\";\n\nimport { PaperclipIcon, Send01Icon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { IconButton } from \"../icon-button\";\nimport { controlPlaceholder, selfControlStates } from \"../input/control-styles\";\n\nexport interface ChatComposerProps\n extends Omit<ComponentProps<\"div\">, \"children\" | \"defaultValue\"> {\n /** Controlled text. */\n value?: string;\n /** Initial text when uncontrolled. */\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n /**\n * Called with the trimmed text on the send button or Enter (Shift+Enter adds a\n * line). The field then empties (`onValueChange(\"\")` when controlled).\n */\n onSend: (message: string) => void;\n /**\n * Shows the paperclip (\"Adjuntar archivo\"): open your file picker here — a photo of\n * the lesion, a report, a previous prescription.\n */\n onAttach?: () => void;\n /** Defaults to \"Escribe un mensaje\". It is also the field's accessible name. */\n placeholder?: string;\n /**\n * The consultation has ended and nobody would read the message: the field goes\n * away and the composer says why, with a way out.\n */\n closed?: boolean;\n /** With `closed`. Defaults to \"Esta consulta ha finalizado.\". */\n closedMessage?: ReactNode;\n /** With `closed`: a `Link` (\"Pedir otra consulta\"). */\n closedAction?: ReactNode;\n}\n\n/**\n * Where the patient writes: attach · field · send.\n *\n * - Send is disabled while the field is empty and turns on as soon as there is\n * text: an always-on send button that does nothing is a button that lies.\n * - Attach is a paperclip, not a \"+\": it says exactly what it does.\n * - The field grows with the text up to 5 lines and then scrolls; the buttons stay\n * at the bottom, not centred.\n * - Text at 16px: below 16 iOS zooms in when the field is focused.\n */\nexport function ChatComposer({\n value,\n defaultValue = \"\",\n onValueChange,\n onSend,\n onAttach,\n placeholder,\n closed = false,\n closedMessage,\n closedAction,\n className,\n ...props\n}: ChatComposerProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const text = value ?? uncontrolledValue;\n const canSend = text.trim().length > 0;\n\n const setText = (next: string): void => {\n if (value === undefined) {\n setUncontrolledValue(next);\n }\n onValueChange?.(next);\n };\n\n const send = (): void => {\n if (!canSend) {\n return;\n }\n onSend(text.trim());\n setText(\"\");\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {\n if (\n event.key === \"Enter\" &&\n !event.shiftKey &&\n !event.nativeEvent.isComposing\n ) {\n event.preventDefault();\n send();\n }\n };\n\n const fieldLabel = placeholder ?? labels.chatComposerPlaceholder;\n\n return (\n <div\n data-slot=\"chat-composer\"\n data-closed={closed || undefined}\n className={cn(\n \"border-border-subtle border-t bg-background p-3\",\n className,\n )}\n {...props}\n >\n {closed ? (\n <div className=\"flex flex-col items-center gap-1 text-center\">\n <p className=\"text-body-small text-text-secondary\">\n {closedMessage ?? labels.chatClosed}\n </p>\n {closedAction}\n </div>\n ) : (\n <form\n className=\"flex items-end gap-2\"\n onSubmit={(event) => {\n event.preventDefault();\n send();\n }}\n >\n {onAttach && (\n <IconButton\n variant=\"ghost\"\n icon={<PaperclipIcon />}\n aria-label={labels.chatAttach}\n onClick={onAttach}\n />\n )}\n <textarea\n data-slot=\"chat-composer-field\"\n rows={1}\n value={text}\n placeholder={fieldLabel}\n aria-label={fieldLabel}\n onChange={(event) => setText(event.currentTarget.value)}\n onKeyDown={handleKeyDown}\n className={cn(\n \"block max-h-36 min-h-control-md min-w-0 flex-1 resize-none overflow-y-auto field-sizing-content\",\n \"rounded-3xl border border-border-input bg-surface px-4 py-2.5 text-body-default text-text-primary\",\n \"ring-1 ring-transparent ring-inset interactive hover:border-border-strong\",\n controlPlaceholder,\n selfControlStates,\n )}\n />\n <IconButton\n type=\"submit\"\n icon={<Send01Icon />}\n aria-label={labels.chatSend}\n disabled={!canSend}\n />\n </form>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAqDO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAyC;AACvC,QAAM,SAAS,kBAAA;AACf,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,YAAY;AACvE,QAAM,OAAO,SAAS;AACtB,QAAM,UAAU,KAAK,KAAA,EAAO,SAAS;AAErC,QAAM,UAAU,CAAC,SAAuB;AACtC,QAAI,UAAU,QAAW;AACvB,2BAAqB,IAAI;AAAA,IAC3B;AACA,oBAAgB,IAAI;AAAA,EACtB;AAEA,QAAM,OAAO,MAAY;AACvB,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AACA,WAAO,KAAK,MAAM;AAClB,YAAQ,EAAE;AAAA,EACZ;AAEA,QAAM,gBAAgB,CAAC,UAAoD;AACzE,QACE,MAAM,QAAQ,WACd,CAAC,MAAM,YACP,CAAC,MAAM,YAAY,aACnB;AACA,YAAM,eAAA;AACN,WAAA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,eAAe,OAAO;AAEzC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAa,UAAU;AAAA,MACvB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEH,UAAA,SACC,qBAAC,OAAA,EAAI,WAAU,gDACb,UAAA;AAAA,QAAA,oBAAC,KAAA,EAAE,WAAU,uCACV,UAAA,iBAAiB,OAAO,YAC3B;AAAA,QACC;AAAA,MAAA,EAAA,CACH,IAEA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,UAAU,CAAC,UAAU;AACnB,kBAAM,eAAA;AACN,iBAAA;AAAA,UACF;AAAA,UAEC,UAAA;AAAA,YAAA,YACC;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,0BAAO,eAAA,EAAc;AAAA,gBACrB,cAAY,OAAO;AAAA,gBACnB,SAAS;AAAA,cAAA;AAAA,YAAA;AAAA,YAGb;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,aAAU;AAAA,gBACV,MAAM;AAAA,gBACN,OAAO;AAAA,gBACP,aAAa;AAAA,gBACb,cAAY;AAAA,gBACZ,UAAU,CAAC,UAAU,QAAQ,MAAM,cAAc,KAAK;AAAA,gBACtD,WAAW;AAAA,gBACX,WAAW;AAAA,kBACT;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,gBAAA;AAAA,cACF;AAAA,YAAA;AAAA,YAEF;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,0BAAO,YAAA,EAAW;AAAA,gBAClB,cAAY,OAAO;AAAA,gBACnB,UAAU,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,UACb;AAAA,QAAA;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AAIR;"}
|
|
@@ -50,7 +50,7 @@ function ChatConversationRow({
|
|
|
50
50
|
"data-slot": "chat-conversation-row",
|
|
51
51
|
"data-state": state,
|
|
52
52
|
className: cn(
|
|
53
|
-
"relative flex min-h-18 items-center gap-3 bg-background px-4 py-3
|
|
53
|
+
"relative flex min-h-18 items-center gap-3 bg-background px-4 py-3 interactive",
|
|
54
54
|
selected ? "bg-brand-subtle" : "hover:bg-surface",
|
|
55
55
|
className
|
|
56
56
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-conversation-row.js","sources":["../../../src/components/chat/chat-conversation-row.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, MouseEventHandler, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Badge } from \"../badge\";\n\nexport type ChatConversationState = \"read\" | \"unread\" | \"selected\";\n\nexport interface ChatConversationRowProps\n extends Omit<ComponentProps<\"li\">, \"onClick\"> {\n /** The other person: \"Dra. Laura Martín\". It names the link. */\n name: ReactNode;\n /**\n * The last message, always: without it people have to open every chat to know if\n * something is pending. One line.\n */\n preview: ReactNode;\n /** Time of the last message, \"10:04\" (or \"Ayer\", \"lunes\"). */\n time: string;\n /** Machine-readable moment for `<time>` (ISO 8601). */\n dateTime?: string;\n /** `<Avatar size={40} src alt=\"\" />`: the name is already next to it. */\n avatar?: ReactNode;\n /**\n * Unread messages. Above 0 the row is unread: the preview goes Medium in\n * `text-primary`, the counter appears and the link's name says it (\"Dra. Laura\n * Martín, 2 mensajes sin leer\").\n */\n unreadCount?: number;\n /**\n * Desktop, with the conversation open on the right: brand fill plus the 3px rail\n * (`brand-subtle` on white alone is 1.14:1) and `aria-current=\"page\"`.\n */\n selected?: boolean;\n /** The conversation's URL: the whole row is the link. */\n href?: string;\n /** Router links: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n onClick?: MouseEventHandler<HTMLElement>;\n}\n\n/**\n * A conversation in the chat list: avatar · name + time · last message + unread\n * counter. It is an `<li>`: put the rows in a `<ul>`.\n *\n * Unread shows without colour (weight and the counter); the purple time only\n * reinforces it. The time is `text-secondary`, never the brand's decorative hint.\n */\nexport function ChatConversationRow({\n name,\n preview,\n time,\n dateTime,\n avatar,\n unreadCount = 0,\n selected = false,\n href,\n render,\n onClick,\n className,\n ...props\n}: ChatConversationRowProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const unread = unreadCount > 0;\n const state: ChatConversationState = selected\n ? \"selected\"\n : unread\n ? \"unread\"\n : \"read\";\n const isLink = Boolean(href || render);\n\n const target = useRender({\n render,\n defaultTagName: isLink ? \"a\" : \"button\",\n props: {\n \"data-slot\": \"chat-conversation-row-target\",\n href,\n onClick,\n type: isLink ? undefined : \"button\",\n \"aria-current\": selected ? \"page\" : undefined,\n className: cn(\n \"block w-full cursor-pointer truncate text-left outline-none\",\n \"after:absolute after:inset-0\",\n \"focus-visible:after:outline-2 focus-visible:after:outline-border-focus focus-visible:after:-outline-offset-2\",\n ),\n children: (\n <>\n {name}\n {unread && (\n <span className=\"sr-only\">\n , {labels.chatUnreadMessages(unreadCount)}\n </span>\n )}\n </>\n ),\n },\n });\n\n return (\n <li\n data-slot=\"chat-conversation-row\"\n data-state={state}\n className={cn(\n \"relative flex min-h-18 items-center gap-3 bg-background px-4 py-3
|
|
1
|
+
{"version":3,"file":"chat-conversation-row.js","sources":["../../../src/components/chat/chat-conversation-row.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, MouseEventHandler, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Badge } from \"../badge\";\n\nexport type ChatConversationState = \"read\" | \"unread\" | \"selected\";\n\nexport interface ChatConversationRowProps\n extends Omit<ComponentProps<\"li\">, \"onClick\"> {\n /** The other person: \"Dra. Laura Martín\". It names the link. */\n name: ReactNode;\n /**\n * The last message, always: without it people have to open every chat to know if\n * something is pending. One line.\n */\n preview: ReactNode;\n /** Time of the last message, \"10:04\" (or \"Ayer\", \"lunes\"). */\n time: string;\n /** Machine-readable moment for `<time>` (ISO 8601). */\n dateTime?: string;\n /** `<Avatar size={40} src alt=\"\" />`: the name is already next to it. */\n avatar?: ReactNode;\n /**\n * Unread messages. Above 0 the row is unread: the preview goes Medium in\n * `text-primary`, the counter appears and the link's name says it (\"Dra. Laura\n * Martín, 2 mensajes sin leer\").\n */\n unreadCount?: number;\n /**\n * Desktop, with the conversation open on the right: brand fill plus the 3px rail\n * (`brand-subtle` on white alone is 1.14:1) and `aria-current=\"page\"`.\n */\n selected?: boolean;\n /** The conversation's URL: the whole row is the link. */\n href?: string;\n /** Router links: `render={<Link to=\"…\" />}`. */\n render?: useRender.RenderProp;\n onClick?: MouseEventHandler<HTMLElement>;\n}\n\n/**\n * A conversation in the chat list: avatar · name + time · last message + unread\n * counter. It is an `<li>`: put the rows in a `<ul>`.\n *\n * Unread shows without colour (weight and the counter); the purple time only\n * reinforces it. The time is `text-secondary`, never the brand's decorative hint.\n */\nexport function ChatConversationRow({\n name,\n preview,\n time,\n dateTime,\n avatar,\n unreadCount = 0,\n selected = false,\n href,\n render,\n onClick,\n className,\n ...props\n}: ChatConversationRowProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const unread = unreadCount > 0;\n const state: ChatConversationState = selected\n ? \"selected\"\n : unread\n ? \"unread\"\n : \"read\";\n const isLink = Boolean(href || render);\n\n const target = useRender({\n render,\n defaultTagName: isLink ? \"a\" : \"button\",\n props: {\n \"data-slot\": \"chat-conversation-row-target\",\n href,\n onClick,\n type: isLink ? undefined : \"button\",\n \"aria-current\": selected ? \"page\" : undefined,\n className: cn(\n \"block w-full cursor-pointer truncate text-left outline-none\",\n \"after:absolute after:inset-0\",\n \"focus-visible:after:outline-2 focus-visible:after:outline-border-focus focus-visible:after:-outline-offset-2\",\n ),\n children: (\n <>\n {name}\n {unread && (\n <span className=\"sr-only\">\n , {labels.chatUnreadMessages(unreadCount)}\n </span>\n )}\n </>\n ),\n },\n });\n\n return (\n <li\n data-slot=\"chat-conversation-row\"\n data-state={state}\n className={cn(\n \"relative flex min-h-18 items-center gap-3 bg-background px-4 py-3 interactive\",\n selected ? \"bg-brand-subtle\" : \"hover:bg-surface\",\n className,\n )}\n {...props}\n >\n {selected && (\n <span\n aria-hidden\n className=\"absolute inset-y-0 left-0 w-0.75 bg-text-brand\"\n />\n )}\n {avatar}\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <div className=\"flex items-center gap-2\">\n <div className=\"min-w-0 flex-1 text-label-strong text-text-primary\">\n {target}\n </div>\n <time\n dateTime={dateTime}\n className={cn(\n \"shrink-0 text-caption-default\",\n selected\n ? \"text-text-primary\"\n : unread\n ? \"text-text-brand\"\n : \"text-text-secondary\",\n )}\n >\n {time}\n </time>\n </div>\n <div className=\"flex items-center gap-2\">\n <p\n className={cn(\n \"min-w-0 flex-1 truncate\",\n unread ? \"text-label-strong\" : \"text-body-small\",\n unread || selected ? \"text-text-primary\" : \"text-text-secondary\",\n )}\n >\n {preview}\n </p>\n {unread && (\n <Badge variant=\"brand\" aria-hidden>\n {unreadCount}\n </Badge>\n )}\n </div>\n </div>\n </li>\n );\n}\n"],"names":[],"mappings":";;;;;AAiDO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgD;AAC9C,QAAM,SAAS,kBAAA;AACf,QAAM,SAAS,cAAc;AAC7B,QAAM,QAA+B,WACjC,aACA,SACE,WACA;AACN,QAAM,SAAS,QAAQ,QAAQ,MAAM;AAErC,QAAM,SAAS,UAAU;AAAA,IACvB;AAAA,IACA,gBAAgB,SAAS,MAAM;AAAA,IAC/B,OAAO;AAAA,MACL,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,MAAM,SAAS,SAAY;AAAA,MAC3B,gBAAgB,WAAW,SAAS;AAAA,MACpC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,UACE,qBAAA,UAAA,EACG,UAAA;AAAA,QAAA;AAAA,QACA,UACC,qBAAC,QAAA,EAAK,WAAU,WAAU,UAAA;AAAA,UAAA;AAAA,UACrB,OAAO,mBAAmB,WAAW;AAAA,QAAA,EAAA,CAC1C;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA;AAAA,EAEJ,CACD;AAED,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA,WAAW,oBAAoB;AAAA,QAC/B;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,YACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,eAAW;AAAA,YACX,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,QAGb;AAAA,QACD,qBAAC,OAAA,EAAI,WAAU,wCACb,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,YAAA,oBAAC,OAAA,EAAI,WAAU,sDACZ,UAAA,QACH;AAAA,YACA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC;AAAA,gBACA,WAAW;AAAA,kBACT;AAAA,kBACA,WACI,sBACA,SACE,oBACA;AAAA,gBAAA;AAAA,gBAGP,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,UACH,GACF;AAAA,UACA,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,YAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA,kBACA,SAAS,sBAAsB;AAAA,kBAC/B,UAAU,WAAW,sBAAsB;AAAA,gBAAA;AAAA,gBAG5C,UAAA;AAAA,cAAA;AAAA,YAAA;AAAA,YAEF,UACC,oBAAC,OAAA,EAAM,SAAQ,SAAQ,eAAW,MAC/B,UAAA,YAAA,CACH;AAAA,UAAA,EAAA,CAEJ;AAAA,QAAA,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|