@mediquo/medicore 0.1.1 → 0.2.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.
@@ -2,7 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { cva } from "class-variance-authority";
3
3
  import { cn } from "../../lib/cn.js";
4
4
  const badgeVariants = cva(
5
- "inline-flex w-fit shrink-0 items-center whitespace-nowrap rounded-sm px-2 py-1 text-label-small",
5
+ "inline-flex h-6 w-fit shrink-0 items-center gap-1 whitespace-nowrap rounded-full px-2.5 text-label-small [&_svg]:size-icon-xs",
6
6
  {
7
7
  variants: {
8
8
  variant: {
@@ -1 +1 @@
1
- {"version":3,"file":"badge.js","sources":["../../../src/components/badge/badge.tsx"],"sourcesContent":["import { cva } from \"class-variance-authority\";\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\n\nexport const badgeVariants = cva(\n \"inline-flex w-fit shrink-0 items-center whitespace-nowrap rounded-sm px-2 py-1 text-label-small\",\n {\n variants: {\n variant: {\n neutral: \"bg-surface-raised text-text-primary\",\n brand: \"bg-brand-subtle text-text-brand\",\n success: \"bg-success-subtle text-text-success\",\n // Token gap, not an aesthetic choice: warning-strong on warning-subtle is\n // 3.93:1, so the text stays in text-primary and the orange lives in the fill.\n warning: \"bg-warning-subtle text-text-primary\",\n error: \"bg-error-subtle text-text-error\",\n info: \"bg-info-subtle text-text-info\",\n },\n },\n defaultVariants: {\n variant: \"neutral\",\n },\n },\n);\n\nexport type BadgeVariant =\n | \"neutral\"\n | \"brand\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\";\n\nexport interface BadgeProps extends ComponentProps<\"span\"> {\n variant?: BadgeVariant;\n}\n\n/**\n * Category or status tag (documents: Receta, Informe, Orden médica; program cards).\n * 12px (Label/Small) because it is metadata: if people need to read it to decide,\n * it isn't a badge, it's text.\n *\n * - It is NOT a button: no click, no focus, no close icon. Removable → `Chip`.\n * - On a photo use `neutral` (solid). Never a translucent pill: its contrast depends\n * on the photo.\n * - `warning` keeps its text in text-primary: the orange-on-orange pair doesn't reach\n * 4.5:1 with the current tokens.\n */\nexport function Badge({\n variant,\n className,\n ...props\n}: BadgeProps): React.JSX.Element {\n return (\n <span\n data-slot=\"badge\"\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n );\n}\n"],"names":[],"mappings":";;;AAKO,MAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA;AAAA;AAAA,QAGT,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,MAAA;AAAA,IACR;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,IAAA;AAAA,EACX;AAEJ;AAyBO,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkC;AAChC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,cAAc,EAAE,QAAA,CAAS,GAAG,SAAS;AAAA,MAClD,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;"}
1
+ {"version":3,"file":"badge.js","sources":["../../../src/components/badge/badge.tsx"],"sourcesContent":["import { cva } from \"class-variance-authority\";\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\n\nexport const badgeVariants = cva(\n \"inline-flex h-6 w-fit shrink-0 items-center gap-1 whitespace-nowrap rounded-full px-2.5 text-label-small [&_svg]:size-icon-xs\",\n {\n variants: {\n variant: {\n neutral: \"bg-surface-raised text-text-primary\",\n brand: \"bg-brand-subtle text-text-brand\",\n success: \"bg-success-subtle text-text-success\",\n // Token gap, not an aesthetic choice: warning-strong on warning-subtle is\n // 3.93:1, so the text stays in text-primary and the orange lives in the fill.\n warning: \"bg-warning-subtle text-text-primary\",\n error: \"bg-error-subtle text-text-error\",\n info: \"bg-info-subtle text-text-info\",\n },\n },\n defaultVariants: {\n variant: \"neutral\",\n },\n },\n);\n\nexport type BadgeVariant =\n | \"neutral\"\n | \"brand\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\";\n\nexport interface BadgeProps extends ComponentProps<\"span\"> {\n variant?: BadgeVariant;\n}\n\n/**\n * Category or status tag (documents: Receta, Informe, Orden médica; program cards).\n * 12px (Label/Small) because it is metadata: if people need to read it to decide,\n * it isn't a badge, it's text.\n *\n * - It is NOT a button: no click, no focus, no close icon. Removable → `Chip`.\n * - On a photo use `neutral` (solid). Never a translucent pill: its contrast depends\n * on the photo.\n * - `warning` keeps its text in text-primary: the orange-on-orange pair doesn't reach\n * 4.5:1 with the current tokens.\n */\nexport function Badge({\n variant,\n className,\n ...props\n}: BadgeProps): React.JSX.Element {\n return (\n <span\n data-slot=\"badge\"\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n );\n}\n"],"names":[],"mappings":";;;AAKO,MAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA;AAAA;AAAA,QAGT,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,MAAA;AAAA,IACR;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,IAAA;AAAA,EACX;AAEJ;AAyBO,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkC;AAChC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,cAAc,EAAE,QAAA,CAAS,GAAG,SAAS;AAAA,MAClD,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;"}
@@ -1,5 +1,5 @@
1
+ import { useRender } from "@base-ui/react/use-render";
1
2
  import type { ComponentProps, ReactNode } from "react";
2
- import { type LinkProps } from "../link";
3
3
  export interface BreadcrumbProps extends ComponentProps<"nav"> {
4
4
  /** `BreadcrumbItem`s for the levels above, then one `BreadcrumbPage` for the current one. */
5
5
  children: ReactNode;
@@ -17,19 +17,26 @@ export interface BreadcrumbProps extends ComponentProps<"nav"> {
17
17
  * </Breadcrumb>
18
18
  * ```
19
19
  *
20
+ * The levels above are quiet (Label/Small in text-secondary, darker on hover), with
21
+ * a chevron in text-disabled between them, so the trail never competes with the
22
+ * page title under it. The links are not underlined: inside the breadcrumb landmark
23
+ * and its list, their position already tells they are links (WCAG 1.4.1 applies to
24
+ * links in running text).
25
+ *
20
26
  * Below the `lg` breakpoint only the level right above shows, as a way back with an
21
27
  * arrow ("‹ Programas especializados").
22
28
  */
23
29
  export declare function Breadcrumb({ "aria-label": ariaLabel, className, children, ...props }: BreadcrumbProps): React.JSX.Element;
24
- export interface BreadcrumbItemProps extends Omit<LinkProps, "size"> {
30
+ export interface BreadcrumbItemProps extends useRender.ComponentProps<"a"> {
25
31
  /** The level's name, the same as the section or page it links to. */
26
32
  children: ReactNode;
27
33
  }
28
34
  /**
29
- * A level above the current page: a `Link` (pass the router's with `render`) followed
30
- * by a chevron. On mobile, the parent level gets a back arrow in front instead.
35
+ * A level above the current page: a link (`href`, or the router's with `render`)
36
+ * followed by a chevron. On mobile, the parent level gets a back arrow in front
37
+ * instead.
31
38
  */
32
- export declare function BreadcrumbItem({ className, children, ...props }: BreadcrumbItemProps): React.JSX.Element;
39
+ export declare function BreadcrumbItem({ render, ref, className, children, ...props }: BreadcrumbItemProps): React.JSX.Element;
33
40
  export interface BreadcrumbPageProps extends ComponentProps<"span"> {
34
41
  }
35
42
  /** The current level: text, not a link, with `aria-current="page"`. Hidden on mobile. */
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useRender } from "@base-ui/react/use-render";
2
3
  import { ChevronLeftIcon } from "../../icons/generated/chevron-left.js";
3
4
  import { ChevronRightIcon } from "../../icons/generated/chevron-right.js";
4
5
  import { cn } from "../../lib/cn.js";
5
6
  import { useMediCoreLabels } from "../../provider/medicore-provider.js";
6
- import { Link } from "../link/link.js";
7
7
  function Breadcrumb({
8
8
  "aria-label": ariaLabel,
9
9
  className,
@@ -22,7 +22,7 @@ function Breadcrumb({
22
22
  "ol",
23
23
  {
24
24
  className: cn(
25
- "flex flex-wrap items-center gap-2",
25
+ "flex flex-wrap items-center gap-1.5",
26
26
  // Mobile: only the parent level (the second-to-last item) stays.
27
27
  "max-lg:[&>li:not(:nth-last-child(2))]:hidden"
28
28
  ),
@@ -33,18 +33,34 @@ function Breadcrumb({
33
33
  );
34
34
  }
35
35
  function BreadcrumbItem({
36
+ render,
37
+ ref,
36
38
  className,
37
39
  children,
38
40
  ...props
39
41
  }) {
40
- return /* @__PURE__ */ jsxs("li", { "data-slot": "breadcrumb-item", className: "flex items-center gap-2", children: [
42
+ const link = useRender({
43
+ render,
44
+ ref,
45
+ defaultTagName: "a",
46
+ props: {
47
+ "data-slot": "breadcrumb-link",
48
+ className: cn(
49
+ "focus-ring touch-target inline-flex items-center gap-1 rounded-xs text-label-small text-text-secondary transition-colors hover:text-text-primary",
50
+ className
51
+ ),
52
+ children,
53
+ ...props
54
+ }
55
+ });
56
+ return /* @__PURE__ */ jsxs("li", { "data-slot": "breadcrumb-item", className: "flex items-center gap-1.5", children: [
41
57
  /* @__PURE__ */ jsx(ChevronLeftIcon, { size: "sm", className: "text-text-secondary lg:hidden" }),
42
- /* @__PURE__ */ jsx(Link, { standalone: true, className, ...props, children }),
58
+ link,
43
59
  /* @__PURE__ */ jsx(
44
60
  ChevronRightIcon,
45
61
  {
46
62
  size: "sm",
47
- className: "text-text-secondary max-lg:hidden"
63
+ className: "text-text-disabled max-lg:hidden"
48
64
  }
49
65
  )
50
66
  ] });
@@ -57,7 +73,7 @@ function BreadcrumbPage({
57
73
  "span",
58
74
  {
59
75
  "aria-current": "page",
60
- className: cn("text-label-small text-text-primary", className),
76
+ className: cn("text-label-strong text-text-primary", className),
61
77
  ...props
62
78
  }
63
79
  ) });
@@ -1 +1 @@
1
- {"version":3,"file":"breadcrumb.js","sources":["../../../src/components/breadcrumb/breadcrumb.tsx"],"sourcesContent":["import type { ComponentProps, ReactNode } from \"react\";\n\nimport { ChevronLeftIcon, ChevronRightIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\nimport { Link, type LinkProps } from \"../link\";\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 * 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 Omit<LinkProps, \"size\"> {\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` (pass the router's with `render`) followed\n * by a chevron. On mobile, the parent level gets a back arrow in front instead.\n */\nexport function BreadcrumbItem({\n className,\n children,\n ...props\n}: BreadcrumbItemProps): React.JSX.Element {\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 standalone className={className} {...props}>\n {children}\n </Link>\n <ChevronRightIcon\n size=\"sm\"\n className=\"text-text-secondary 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-small text-text-primary\", className)}\n {...props}\n />\n </li>\n );\n}\n"],"names":[],"mappings":";;;;;;AA4BO,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;AAWO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,SACE,qBAAC,MAAA,EAAG,aAAU,mBAAkB,WAAU,2BACxC,UAAA;AAAA,IAAA,oBAAC,iBAAA,EAAgB,MAAK,MAAK,WAAU,iCAAgC;AAAA,wBACpE,MAAA,EAAK,YAAU,MAAC,WAAuB,GAAG,OACxC,UACH;AAAA,IACA;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,sCAAsC,SAAS;AAAA,MAC5D,GAAG;AAAA,IAAA;AAAA,EAAA,GAER;AAEJ;"}
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/Small in text-secondary, darker on hover), with\n * a chevron in text-disabled 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-1.5\",\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-small text-text-secondary transition-colors hover:text-text-primary\",\n className,\n ),\n children,\n ...props,\n },\n });\n\n return (\n <li data-slot=\"breadcrumb-item\" className=\"flex items-center gap-1.5\">\n <ChevronLeftIcon size=\"sm\" className=\"text-text-secondary lg:hidden\" />\n {link}\n <ChevronRightIcon\n size=\"sm\"\n className=\"text-text-disabled 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,6BACxC,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;"}
@@ -0,0 +1,49 @@
1
+ import { useRender } from "@base-ui/react/use-render";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ export type DetailHeaderHeadingLevel = 1 | 2;
4
+ export interface DetailHeaderProps extends Omit<ComponentProps<"header">, "title"> {
5
+ /** The record's name: "Laura Gómez", "Clínica Dental Sonrisas". */
6
+ title: ReactNode;
7
+ /** The scope of what is shown, next to the title in text-secondary: "Septiembre 2026". */
8
+ subtitle?: ReactNode;
9
+ /** Its state, a `Badge` right after the title. */
10
+ badge?: ReactNode;
11
+ /** The way back to the list: a `DetailHeaderBack`. */
12
+ back?: ReactNode;
13
+ /** An `Avatar` or `IconContainer` before the title. */
14
+ leading?: ReactNode;
15
+ /** One line of small print under the title: "Cerrada el 2 de octubre por Víctor". */
16
+ meta?: ReactNode;
17
+ /** The record's `Button`s, primary last. They wrap under the title on narrow screens. */
18
+ actions?: ReactNode;
19
+ /**
20
+ * Level of the title. Default 1: the detail page's `<h1>`. Use 2 when the page
21
+ * already has its `<h1>` (a `MobileHeader` with the section's name, for example).
22
+ */
23
+ headingLevel?: DetailHeaderHeadingLevel;
24
+ /** The figures row: `StatCard`s and `FactCard`s, laid out by the screen. */
25
+ children?: ReactNode;
26
+ }
27
+ /**
28
+ * The header of a record's page (a professional's settlement, a patient, a clinic):
29
+ * the way back, who or what it is, its state, its actions and its key figures, in one
30
+ * raised card so the rest of the page reads as the record's content.
31
+ *
32
+ * - The title looks like Heading/h2 in text-brand, like `PageHeader`: it is the
33
+ * identity of the screen. It IS the page heading (`headingLevel` 1) unless the
34
+ * page already has one.
35
+ * - `actions` sit to the right of the title from `lg` up and wrap under it below.
36
+ * Primary last, `outline` for the rest.
37
+ * - The figures go in `children`: `StatCard`s for amounts and counts, a `FactCard`
38
+ * for the facts that decide what can be done (invoicing data, missing consent).
39
+ * The screen owns their grid.
40
+ */
41
+ export declare function DetailHeader({ title, subtitle, badge, back, leading, meta, actions, headingLevel, className, children, ...props }: DetailHeaderProps): React.JSX.Element;
42
+ export interface DetailHeaderBackProps extends useRender.ComponentProps<"a"> {
43
+ }
44
+ /**
45
+ * The link back to the list: "Liquidaciones". A real link (`href`, or
46
+ * `render={<Link to="…" />}` for the router) drawn as a ghost small `Button` with
47
+ * an arrow, aligned with the title's left edge.
48
+ */
49
+ export declare function DetailHeaderBack({ render, ref, className, children, ...props }: DetailHeaderBackProps): React.JSX.Element;
@@ -0,0 +1,92 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { useRender } from "@base-ui/react/use-render";
3
+ import { ArrowNarrowLeftIcon } from "../../icons/generated/arrow-narrow-left.js";
4
+ import { cn } from "../../lib/cn.js";
5
+ import { buttonVariants } from "../button/button.js";
6
+ function DetailHeader({
7
+ title,
8
+ subtitle,
9
+ badge,
10
+ back,
11
+ leading,
12
+ meta,
13
+ actions,
14
+ headingLevel = 1,
15
+ className,
16
+ children,
17
+ ...props
18
+ }) {
19
+ const Heading = `h${headingLevel}`;
20
+ return /* @__PURE__ */ jsxs(
21
+ "header",
22
+ {
23
+ "data-slot": "detail-header",
24
+ className: cn(
25
+ "flex flex-col gap-5 rounded-2xl bg-background p-5 shadow-s lg:p-6",
26
+ className
27
+ ),
28
+ ...props,
29
+ children: [
30
+ back,
31
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between lg:gap-6", children: [
32
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-4", children: [
33
+ leading,
34
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
35
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", children: [
36
+ /* @__PURE__ */ jsxs(Heading, { className: "min-w-0 text-heading-h2 text-text-brand", children: [
37
+ title,
38
+ subtitle && /* @__PURE__ */ jsxs("span", { className: "text-text-secondary", children: [
39
+ " · ",
40
+ subtitle
41
+ ] })
42
+ ] }),
43
+ badge
44
+ ] }),
45
+ meta && /* @__PURE__ */ jsx("p", { className: "text-body-small text-text-secondary", children: meta })
46
+ ] })
47
+ ] }),
48
+ actions && /* @__PURE__ */ jsx(
49
+ "div",
50
+ {
51
+ "data-slot": "detail-header-actions",
52
+ className: "flex flex-wrap items-center gap-3 lg:shrink-0 lg:justify-end",
53
+ children: actions
54
+ }
55
+ )
56
+ ] }),
57
+ children && /* @__PURE__ */ jsx("div", { "data-slot": "detail-header-figures", className: "flex flex-col gap-3", children })
58
+ ]
59
+ }
60
+ );
61
+ }
62
+ function DetailHeaderBack({
63
+ render,
64
+ ref,
65
+ className,
66
+ children,
67
+ ...props
68
+ }) {
69
+ return useRender({
70
+ render,
71
+ ref,
72
+ defaultTagName: "a",
73
+ props: {
74
+ "data-slot": "detail-header-back",
75
+ className: cn(
76
+ buttonVariants({ variant: "ghost", size: "small" }),
77
+ "-ml-3 self-start",
78
+ className
79
+ ),
80
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
81
+ /* @__PURE__ */ jsx(ArrowNarrowLeftIcon, {}),
82
+ children
83
+ ] }),
84
+ ...props
85
+ }
86
+ });
87
+ }
88
+ export {
89
+ DetailHeader,
90
+ DetailHeaderBack
91
+ };
92
+ //# sourceMappingURL=detail-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail-header.js","sources":["../../../src/components/detail-header/detail-header.tsx"],"sourcesContent":["import { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { ArrowNarrowLeftIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { buttonVariants } from \"../button\";\n\nexport type DetailHeaderHeadingLevel = 1 | 2;\n\nexport interface DetailHeaderProps\n extends Omit<ComponentProps<\"header\">, \"title\"> {\n /** The record's name: \"Laura Gómez\", \"Clínica Dental Sonrisas\". */\n title: ReactNode;\n /** The scope of what is shown, next to the title in text-secondary: \"Septiembre 2026\". */\n subtitle?: ReactNode;\n /** Its state, a `Badge` right after the title. */\n badge?: ReactNode;\n /** The way back to the list: a `DetailHeaderBack`. */\n back?: ReactNode;\n /** An `Avatar` or `IconContainer` before the title. */\n leading?: ReactNode;\n /** One line of small print under the title: \"Cerrada el 2 de octubre por Víctor\". */\n meta?: ReactNode;\n /** The record's `Button`s, primary last. They wrap under the title on narrow screens. */\n actions?: ReactNode;\n /**\n * Level of the title. Default 1: the detail page's `<h1>`. Use 2 when the page\n * already has its `<h1>` (a `MobileHeader` with the section's name, for example).\n */\n headingLevel?: DetailHeaderHeadingLevel;\n /** The figures row: `StatCard`s and `FactCard`s, laid out by the screen. */\n children?: ReactNode;\n}\n\n/**\n * The header of a record's page (a professional's settlement, a patient, a clinic):\n * the way back, who or what it is, its state, its actions and its key figures, in one\n * raised card so the rest of the page reads as the record's content.\n *\n * - The title looks like Heading/h2 in text-brand, like `PageHeader`: it is the\n * identity of the screen. It IS the page heading (`headingLevel` 1) unless the\n * page already has one.\n * - `actions` sit to the right of the title from `lg` up and wrap under it below.\n * Primary last, `outline` for the rest.\n * - The figures go in `children`: `StatCard`s for amounts and counts, a `FactCard`\n * for the facts that decide what can be done (invoicing data, missing consent).\n * The screen owns their grid.\n */\nexport function DetailHeader({\n title,\n subtitle,\n badge,\n back,\n leading,\n meta,\n actions,\n headingLevel = 1,\n className,\n children,\n ...props\n}: DetailHeaderProps): React.JSX.Element {\n const Heading = `h${headingLevel}` as const;\n\n return (\n <header\n data-slot=\"detail-header\"\n className={cn(\n \"flex flex-col gap-5 rounded-2xl bg-background p-5 shadow-s lg:p-6\",\n className,\n )}\n {...props}\n >\n {back}\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between lg:gap-6\">\n <div className=\"flex min-w-0 items-center gap-4\">\n {leading}\n <div className=\"flex min-w-0 flex-col gap-1\">\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1\">\n <Heading className=\"min-w-0 text-heading-h2 text-text-brand\">\n {title}\n {subtitle && (\n <span className=\"text-text-secondary\"> · {subtitle}</span>\n )}\n </Heading>\n {badge}\n </div>\n {meta && (\n <p className=\"text-body-small text-text-secondary\">{meta}</p>\n )}\n </div>\n </div>\n {actions && (\n <div\n data-slot=\"detail-header-actions\"\n className=\"flex flex-wrap items-center gap-3 lg:shrink-0 lg:justify-end\"\n >\n {actions}\n </div>\n )}\n </div>\n {children && (\n <div data-slot=\"detail-header-figures\" className=\"flex flex-col gap-3\">\n {children}\n </div>\n )}\n </header>\n );\n}\n\nexport interface DetailHeaderBackProps extends useRender.ComponentProps<\"a\"> {}\n\n/**\n * The link back to the list: \"Liquidaciones\". A real link (`href`, or\n * `render={<Link to=\"…\" />}` for the router) drawn as a ghost small `Button` with\n * an arrow, aligned with the title's left edge.\n */\nexport function DetailHeaderBack({\n render,\n ref,\n className,\n children,\n ...props\n}: DetailHeaderBackProps): React.JSX.Element {\n return useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"data-slot\": \"detail-header-back\",\n className: cn(\n buttonVariants({ variant: \"ghost\", size: \"small\" }),\n \"-ml-3 self-start\",\n className,\n ),\n children: (\n <>\n <ArrowNarrowLeftIcon />\n {children}\n </>\n ),\n ...props,\n },\n });\n}\n"],"names":[],"mappings":";;;;;AAgDO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAyC;AACvC,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,MAEH,UAAA;AAAA,QAAA;AAAA,QACD,qBAAC,OAAA,EAAI,WAAU,8EACb,UAAA;AAAA,UAAA,qBAAC,OAAA,EAAI,WAAU,mCACZ,UAAA;AAAA,YAAA;AAAA,YACD,qBAAC,OAAA,EAAI,WAAU,+BACb,UAAA;AAAA,cAAA,qBAAC,OAAA,EAAI,WAAU,+CACb,UAAA;AAAA,gBAAA,qBAAC,SAAA,EAAQ,WAAU,2CAChB,UAAA;AAAA,kBAAA;AAAA,kBACA,YACC,qBAAC,QAAA,EAAK,WAAU,uBAAsB,UAAA;AAAA,oBAAA;AAAA,oBAAI;AAAA,kBAAA,EAAA,CAAS;AAAA,gBAAA,GAEvD;AAAA,gBACC;AAAA,cAAA,GACH;AAAA,cACC,QACC,oBAAC,KAAA,EAAE,WAAU,uCAAuC,UAAA,KAAA,CAAK;AAAA,YAAA,EAAA,CAE7D;AAAA,UAAA,GACF;AAAA,UACC,WACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,aAAU;AAAA,cACV,WAAU;AAAA,cAET,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH,GAEJ;AAAA,QACC,YACC,oBAAC,OAAA,EAAI,aAAU,yBAAwB,WAAU,uBAC9C,SAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;AASO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA6C;AAC3C,SAAO,UAAU;AAAA,IACf;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,OAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW;AAAA,QACT,eAAe,EAAE,SAAS,SAAS,MAAM,SAAS;AAAA,QAClD;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,UACE,qBAAA,UAAA,EACE,UAAA;AAAA,QAAA,oBAAC,qBAAA,EAAoB;AAAA,QACpB;AAAA,MAAA,GACH;AAAA,MAEF,GAAG;AAAA,IAAA;AAAA,EACL,CACD;AACH;"}
@@ -0,0 +1 @@
1
+ export { DetailHeader, DetailHeaderBack, type DetailHeaderBackProps, type DetailHeaderHeadingLevel, type DetailHeaderProps, } from "./detail-header";
@@ -0,0 +1,34 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ export declare const factCardVariants: (props?: ({
4
+ tone?: "error" | "neutral" | "success" | "warning" | null | undefined;
5
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
+ export type FactCardTone = "neutral" | "success" | "warning" | "error";
7
+ export interface FactCardProps extends Omit<ComponentProps<"div">, "title">, VariantProps<typeof factCardVariants> {
8
+ /**
9
+ * The fact, in a few words: "Datos de facturación completos", "Falta el NIF".
10
+ * On `error`, say what it blocks: "Falta el NIF: no se puede cerrar el mes".
11
+ */
12
+ title: ReactNode;
13
+ /** The detail: "Laura Gómez Ruiz · NIF 12345678Z · IBAN ···· 1332". */
14
+ description?: ReactNode;
15
+ /** One way to act on it, a `Link` or a small `Button`. */
16
+ action?: ReactNode;
17
+ /** Replaces the tone's icon. */
18
+ icon?: ReactNode;
19
+ /**
20
+ * `success` when the fact is settled, `warning` when it limits something,
21
+ * `error` when it blocks the main action, `neutral` for plain information.
22
+ */
23
+ tone?: FactCardTone;
24
+ }
25
+ /**
26
+ * A fact about the record that decides what can be done with it, next to its figures
27
+ * in a `DetailHeader`: the invoicing data before a close, a missing consent, a pending
28
+ * document. It stays as long as the fact does.
29
+ *
30
+ * Unlike `Banner`, it is a card among cards, not a notice across the page, and it is
31
+ * never dismissible. Text in text-primary on every tone; the icon carries the tone,
32
+ * so the meaning is in the words. `error` is announced with `role="status"`.
33
+ */
34
+ export declare function FactCard({ title, description, action, icon, tone, className, ...props }: FactCardProps): React.JSX.Element;
@@ -0,0 +1,86 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { cva } from "class-variance-authority";
3
+ import { AlertCircleIcon } from "../../icons/generated/alert-circle.js";
4
+ import { AlertTriangleIcon } from "../../icons/generated/alert-triangle.js";
5
+ import { CheckCircleIcon } from "../../icons/generated/check-circle.js";
6
+ import { InfoCircleIcon } from "../../icons/generated/info-circle.js";
7
+ import { cn } from "../../lib/cn.js";
8
+ const factCardVariants = cva(
9
+ "flex min-w-0 items-start gap-3 rounded-xl border p-4",
10
+ {
11
+ variants: {
12
+ tone: {
13
+ neutral: "border-border-subtle bg-background",
14
+ success: "border-success bg-success-subtle",
15
+ warning: "border-warning bg-warning-subtle",
16
+ error: "border-error bg-error-subtle"
17
+ }
18
+ },
19
+ defaultVariants: {
20
+ tone: "neutral"
21
+ }
22
+ }
23
+ );
24
+ const iconClassName = {
25
+ neutral: "text-text-secondary",
26
+ success: "text-text-success",
27
+ warning: "text-text-warning",
28
+ error: "text-text-error"
29
+ };
30
+ const defaultIcon = {
31
+ neutral: /* @__PURE__ */ jsx(InfoCircleIcon, {}),
32
+ success: /* @__PURE__ */ jsx(CheckCircleIcon, {}),
33
+ warning: /* @__PURE__ */ jsx(AlertTriangleIcon, {}),
34
+ error: /* @__PURE__ */ jsx(AlertCircleIcon, {})
35
+ };
36
+ function FactCard({
37
+ title,
38
+ description,
39
+ action,
40
+ icon,
41
+ tone = "neutral",
42
+ className,
43
+ ...props
44
+ }) {
45
+ return /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ "data-slot": "fact-card",
49
+ role: tone === "error" ? "status" : void 0,
50
+ className: cn(factCardVariants({ tone }), className),
51
+ ...props,
52
+ children: [
53
+ /* @__PURE__ */ jsx(
54
+ "span",
55
+ {
56
+ "aria-hidden": "true",
57
+ className: cn(
58
+ "mt-0.5 flex shrink-0 [&_svg]:size-icon-md",
59
+ iconClassName[tone]
60
+ ),
61
+ children: icon ?? defaultIcon[tone]
62
+ }
63
+ ),
64
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
65
+ /* @__PURE__ */ jsx("p", { className: "text-label-strong text-text-primary", children: title }),
66
+ description && /* @__PURE__ */ jsx(
67
+ "p",
68
+ {
69
+ className: cn(
70
+ "text-body-small",
71
+ tone === "neutral" ? "text-text-secondary" : "text-text-primary"
72
+ ),
73
+ children: description
74
+ }
75
+ ),
76
+ action && /* @__PURE__ */ jsx("div", { className: "pt-1", children: action })
77
+ ] })
78
+ ]
79
+ }
80
+ );
81
+ }
82
+ export {
83
+ FactCard,
84
+ factCardVariants
85
+ };
86
+ //# sourceMappingURL=fact-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-card.js","sources":["../../../src/components/fact-card/fact-card.tsx"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport {\n AlertCircleIcon,\n AlertTriangleIcon,\n CheckCircleIcon,\n InfoCircleIcon,\n} from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\n\nexport const factCardVariants = cva(\n \"flex min-w-0 items-start gap-3 rounded-xl border p-4\",\n {\n variants: {\n tone: {\n neutral: \"border-border-subtle bg-background\",\n success: \"border-success bg-success-subtle\",\n warning: \"border-warning bg-warning-subtle\",\n error: \"border-error bg-error-subtle\",\n },\n },\n defaultVariants: {\n tone: \"neutral\",\n },\n },\n);\n\nconst iconClassName: Record<FactCardTone, string> = {\n neutral: \"text-text-secondary\",\n success: \"text-text-success\",\n warning: \"text-text-warning\",\n error: \"text-text-error\",\n};\n\nconst defaultIcon: Record<FactCardTone, ReactNode> = {\n neutral: <InfoCircleIcon />,\n success: <CheckCircleIcon />,\n warning: <AlertTriangleIcon />,\n error: <AlertCircleIcon />,\n};\n\nexport type FactCardTone = \"neutral\" | \"success\" | \"warning\" | \"error\";\n\nexport interface FactCardProps\n extends Omit<ComponentProps<\"div\">, \"title\">,\n VariantProps<typeof factCardVariants> {\n /**\n * The fact, in a few words: \"Datos de facturación completos\", \"Falta el NIF\".\n * On `error`, say what it blocks: \"Falta el NIF: no se puede cerrar el mes\".\n */\n title: ReactNode;\n /** The detail: \"Laura Gómez Ruiz · NIF 12345678Z · IBAN ···· 1332\". */\n description?: ReactNode;\n /** One way to act on it, a `Link` or a small `Button`. */\n action?: ReactNode;\n /** Replaces the tone's icon. */\n icon?: ReactNode;\n /**\n * `success` when the fact is settled, `warning` when it limits something,\n * `error` when it blocks the main action, `neutral` for plain information.\n */\n tone?: FactCardTone;\n}\n\n/**\n * A fact about the record that decides what can be done with it, next to its figures\n * in a `DetailHeader`: the invoicing data before a close, a missing consent, a pending\n * document. It stays as long as the fact does.\n *\n * Unlike `Banner`, it is a card among cards, not a notice across the page, and it is\n * never dismissible. Text in text-primary on every tone; the icon carries the tone,\n * so the meaning is in the words. `error` is announced with `role=\"status\"`.\n */\nexport function FactCard({\n title,\n description,\n action,\n icon,\n tone = \"neutral\",\n className,\n ...props\n}: FactCardProps): React.JSX.Element {\n return (\n <div\n data-slot=\"fact-card\"\n role={tone === \"error\" ? \"status\" : undefined}\n className={cn(factCardVariants({ tone }), className)}\n {...props}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"mt-0.5 flex shrink-0 [&_svg]:size-icon-md\",\n iconClassName[tone],\n )}\n >\n {icon ?? defaultIcon[tone]}\n </span>\n <div className=\"flex min-w-0 flex-col gap-1\">\n <p className=\"text-label-strong text-text-primary\">{title}</p>\n {description && (\n <p\n className={cn(\n \"text-body-small\",\n tone === \"neutral\" ? \"text-text-secondary\" : \"text-text-primary\",\n )}\n >\n {description}\n </p>\n )}\n {action && <div className=\"pt-1\">{action}</div>}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAWO,MAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,IAEF,iBAAiB;AAAA,MACf,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ;AAEA,MAAM,gBAA8C;AAAA,EAClD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AACT;AAEA,MAAM,cAA+C;AAAA,EACnD,6BAAU,gBAAA,EAAe;AAAA,EACzB,6BAAU,iBAAA,EAAgB;AAAA,EAC1B,6BAAU,mBAAA,EAAkB;AAAA,EAC5B,2BAAQ,iBAAA,CAAA,CAAgB;AAC1B;AAkCO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,GAAG;AACL,GAAqC;AACnC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAM,SAAS,UAAU,WAAW;AAAA,MACpC,WAAW,GAAG,iBAAiB,EAAE,KAAA,CAAM,GAAG,SAAS;AAAA,MAClD,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAW;AAAA,cACT;AAAA,cACA,cAAc,IAAI;AAAA,YAAA;AAAA,YAGnB,UAAA,QAAQ,YAAY,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,QAE3B,qBAAC,OAAA,EAAI,WAAU,+BACb,UAAA;AAAA,UAAA,oBAAC,KAAA,EAAE,WAAU,uCAAuC,UAAA,OAAM;AAAA,UACzD,eACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,SAAS,YAAY,wBAAwB;AAAA,cAAA;AAAA,cAG9C,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ,UAAU,oBAAC,OAAA,EAAI,WAAU,QAAQ,UAAA,OAAA,CAAO;AAAA,QAAA,EAAA,CAC3C;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -0,0 +1 @@
1
+ export { FactCard, type FactCardProps, type FactCardTone, factCardVariants, } from "./fact-card";
@@ -0,0 +1 @@
1
+ export { StatCard, type StatCardProps, statCardVariants } from "./stat-card";
@@ -0,0 +1,31 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ import { type IconContainerTone } from "../icon-container";
4
+ export declare const statCardVariants: (props?: ({
5
+ highlighted?: boolean | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export interface StatCardProps extends Omit<ComponentProps<"div">, "children">, VariantProps<typeof statCardVariants> {
8
+ /** What the figure is: "A pagar este mes", "Operaciones". */
9
+ label: ReactNode;
10
+ /** The figure, already formatted by the app: "4.215,00 €", "58". */
11
+ value: ReactNode;
12
+ /** Small print under the figure: "58 operaciones", "Tienen operaciones sin tarifa". */
13
+ detail?: ReactNode;
14
+ /** A MediCore icon, drawn in an `IconContainer`. */
15
+ icon: ReactNode;
16
+ /** Tone of the icon container. Semantic: `error` only when the figure is a problem. */
17
+ tone?: IconContainerTone;
18
+ /** The one figure the screen is about ("Total a pagar"): brand fill and brand border. */
19
+ highlighted?: boolean;
20
+ }
21
+ /**
22
+ * A key figure of an admin screen: icon, label and value, with optional small print.
23
+ * Four in a row sum up a list; three in a `DetailHeader` sum up a record.
24
+ *
25
+ * - The text is a `<dl>`: the label is the term and the figure its value.
26
+ * - The app formats the value (es-ES, symbol after: "4.215,00 €"); the card doesn't.
27
+ * - Text stays in text-primary on the highlighted fill (text-secondary is 4.44:1 there).
28
+ * - Only one card per row is `highlighted`, the one that answers the screen's question.
29
+ * - Not for marketing figures: those are `StatTile`.
30
+ */
31
+ export declare function StatCard({ label, value, detail, icon, tone, highlighted, className, ...props }: StatCardProps): React.JSX.Element;
@@ -0,0 +1,85 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { cva } from "class-variance-authority";
3
+ import { cn } from "../../lib/cn.js";
4
+ import { IconContainer } from "../icon-container/icon-container.js";
5
+ const statCardVariants = cva(
6
+ "flex min-w-0 items-start gap-3 rounded-xl p-4",
7
+ {
8
+ variants: {
9
+ highlighted: {
10
+ true: "border border-brand bg-brand-subtle",
11
+ false: "bg-background shadow-s"
12
+ }
13
+ },
14
+ defaultVariants: {
15
+ highlighted: false
16
+ }
17
+ }
18
+ );
19
+ function StatCard({
20
+ label,
21
+ value,
22
+ detail,
23
+ icon,
24
+ tone = "neutral",
25
+ highlighted = false,
26
+ className,
27
+ ...props
28
+ }) {
29
+ return /* @__PURE__ */ jsxs(
30
+ "div",
31
+ {
32
+ "data-slot": "stat-card",
33
+ className: cn(statCardVariants({ highlighted }), className),
34
+ ...props,
35
+ children: [
36
+ /* @__PURE__ */ jsx(
37
+ IconContainer,
38
+ {
39
+ icon,
40
+ tone: highlighted ? "brand" : tone,
41
+ size: 40,
42
+ className: "shrink-0"
43
+ }
44
+ ),
45
+ /* @__PURE__ */ jsxs("dl", { className: "flex min-w-0 flex-col gap-0.5", children: [
46
+ /* @__PURE__ */ jsx(
47
+ "dt",
48
+ {
49
+ className: cn(
50
+ "text-body-small",
51
+ highlighted ? "text-text-primary" : "text-text-secondary"
52
+ ),
53
+ children: label
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx(
57
+ "dd",
58
+ {
59
+ className: cn(
60
+ "text-heading-h3 tabular-nums",
61
+ highlighted ? "text-text-brand" : "text-text-primary"
62
+ ),
63
+ children: value
64
+ }
65
+ ),
66
+ detail && /* @__PURE__ */ jsx(
67
+ "dd",
68
+ {
69
+ className: cn(
70
+ "text-body-small",
71
+ highlighted ? "text-text-primary" : "text-text-secondary"
72
+ ),
73
+ children: detail
74
+ }
75
+ )
76
+ ] })
77
+ ]
78
+ }
79
+ );
80
+ }
81
+ export {
82
+ StatCard,
83
+ statCardVariants
84
+ };
85
+ //# sourceMappingURL=stat-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stat-card.js","sources":["../../../src/components/stat-card/stat-card.tsx"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { IconContainer, type IconContainerTone } from \"../icon-container\";\n\nexport const statCardVariants = cva(\n \"flex min-w-0 items-start gap-3 rounded-xl p-4\",\n {\n variants: {\n highlighted: {\n true: \"border border-brand bg-brand-subtle\",\n false: \"bg-background shadow-s\",\n },\n },\n defaultVariants: {\n highlighted: false,\n },\n },\n);\n\nexport interface StatCardProps\n extends Omit<ComponentProps<\"div\">, \"children\">,\n VariantProps<typeof statCardVariants> {\n /** What the figure is: \"A pagar este mes\", \"Operaciones\". */\n label: ReactNode;\n /** The figure, already formatted by the app: \"4.215,00 €\", \"58\". */\n value: ReactNode;\n /** Small print under the figure: \"58 operaciones\", \"Tienen operaciones sin tarifa\". */\n detail?: ReactNode;\n /** A MediCore icon, drawn in an `IconContainer`. */\n icon: ReactNode;\n /** Tone of the icon container. Semantic: `error` only when the figure is a problem. */\n tone?: IconContainerTone;\n /** The one figure the screen is about (\"Total a pagar\"): brand fill and brand border. */\n highlighted?: boolean;\n}\n\n/**\n * A key figure of an admin screen: icon, label and value, with optional small print.\n * Four in a row sum up a list; three in a `DetailHeader` sum up a record.\n *\n * - The text is a `<dl>`: the label is the term and the figure its value.\n * - The app formats the value (es-ES, symbol after: \"4.215,00 €\"); the card doesn't.\n * - Text stays in text-primary on the highlighted fill (text-secondary is 4.44:1 there).\n * - Only one card per row is `highlighted`, the one that answers the screen's question.\n * - Not for marketing figures: those are `StatTile`.\n */\nexport function StatCard({\n label,\n value,\n detail,\n icon,\n tone = \"neutral\",\n highlighted = false,\n className,\n ...props\n}: StatCardProps): React.JSX.Element {\n return (\n <div\n data-slot=\"stat-card\"\n className={cn(statCardVariants({ highlighted }), className)}\n {...props}\n >\n <IconContainer\n icon={icon}\n tone={highlighted ? \"brand\" : tone}\n size={40}\n className=\"shrink-0\"\n />\n <dl className=\"flex min-w-0 flex-col gap-0.5\">\n <dt\n className={cn(\n \"text-body-small\",\n highlighted ? \"text-text-primary\" : \"text-text-secondary\",\n )}\n >\n {label}\n </dt>\n <dd\n className={cn(\n \"text-heading-h3 tabular-nums\",\n highlighted ? \"text-text-brand\" : \"text-text-primary\",\n )}\n >\n {value}\n </dd>\n {detail && (\n <dd\n className={cn(\n \"text-body-small\",\n highlighted ? \"text-text-primary\" : \"text-text-secondary\",\n )}\n >\n {detail}\n </dd>\n )}\n </dl>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;AAMO,MAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,aAAa;AAAA,QACX,MAAM;AAAA,QACN,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,IAEF,iBAAiB;AAAA,MACf,aAAa;AAAA,IAAA;AAAA,EACf;AAEJ;AA6BO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAqC;AACnC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iBAAiB,EAAE,YAAA,CAAa,GAAG,SAAS;AAAA,MACzD,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA,MAAM,cAAc,UAAU;AAAA,YAC9B,MAAM;AAAA,YACN,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,QAEZ,qBAAC,MAAA,EAAG,WAAU,iCACZ,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,cAAc,sBAAsB;AAAA,cAAA;AAAA,cAGrC,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAEH;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,cAAc,oBAAoB;AAAA,cAAA;AAAA,cAGnC,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAEF,UACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,cAAc,sBAAsB;AAAA,cAAA;AAAA,cAGrC,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH,EAAA,CAEJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -47,7 +47,7 @@ export interface TableRowProps extends ComponentProps<"tr"> {
47
47
  * A row. Figma has no Table/Row: a row is just cells as wide as their headers. If a
48
48
  * row opens a detail, put a link in its first cell — don't make the `<tr>` clickable.
49
49
  */
50
- export declare function TableRow(props: TableRowProps): React.JSX.Element;
50
+ export declare function TableRow({ className, ...props }: TableRowProps): React.JSX.Element;
51
51
  export declare const tableHeadVariants: (props?: ({
52
52
  type?: "text" | "numeric" | "action" | "badge" | null | undefined;
53
53
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
@@ -43,7 +43,7 @@ function Table({
43
43
  "data-slot": "table-container",
44
44
  ...scrollRegion,
45
45
  className: cn(
46
- "relative w-full overflow-x-auto focus-ring",
46
+ "relative w-full overflow-x-auto rounded-xl border border-border-subtle bg-background focus-ring",
47
47
  containerClassName
48
48
  ),
49
49
  children: /* @__PURE__ */ jsx(
@@ -68,8 +68,21 @@ function TableHeader(props) {
68
68
  function TableBody(props) {
69
69
  return /* @__PURE__ */ jsx("tbody", { "data-slot": "table-body", ...props });
70
70
  }
71
- function TableRow(props) {
72
- return /* @__PURE__ */ jsx("tr", { "data-slot": "table-row", ...props });
71
+ function TableRow({
72
+ className,
73
+ ...props
74
+ }) {
75
+ return /* @__PURE__ */ jsx(
76
+ "tr",
77
+ {
78
+ "data-slot": "table-row",
79
+ className: cn(
80
+ "transition-colors [tbody_&:last-child>td]:border-b-0 [tbody_&:hover>td]:bg-surface",
81
+ className
82
+ ),
83
+ ...props
84
+ }
85
+ );
73
86
  }
74
87
  const alignment = {
75
88
  text: "text-left",
@@ -79,7 +92,7 @@ const alignment = {
79
92
  action: "w-0 whitespace-nowrap text-center"
80
93
  };
81
94
  const tableHeadVariants = cva(
82
- "h-12 border-b border-border-subtle bg-surface px-4 align-middle text-label-strong text-text-secondary data-sorted:text-text-primary",
95
+ "h-11 border-b border-border-subtle bg-surface px-4 align-middle text-label-small text-text-secondary first:rounded-tl-xl last:rounded-tr-xl data-sorted:text-label-strong data-sorted:text-text-primary",
83
96
  { variants: { type: alignment }, defaultVariants: { type: "text" } }
84
97
  );
85
98
  function TableHead({
@@ -135,7 +148,7 @@ function TableHead({
135
148
  );
136
149
  }
137
150
  const tableCellVariants = cva(
138
- "h-16 border-b border-border-subtle bg-background px-4 align-middle text-body-small text-text-primary",
151
+ "h-14 border-b border-border-subtle bg-background px-4 align-middle text-body-small text-text-primary transition-colors",
139
152
  {
140
153
  variants: {
141
154
  type: {
@@ -1 +1 @@
1
- {"version":3,"file":"table.js","sources":["../../../src/components/table/table.tsx"],"sourcesContent":["import { cva } from \"class-variance-authority\";\nimport {\n type ComponentProps,\n type MouseEventHandler,\n useEffect,\n useId,\n useRef,\n useState,\n} from \"react\";\n\nimport { ChevronDownIcon, ChevronUpIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\n/** The type decides the alignment: text and badge left, numbers right, action centred. */\nexport type TableCellType = \"text\" | \"numeric\" | \"badge\" | \"action\";\n\nexport type TableSort = \"none\" | \"ascending\" | \"descending\";\n\n/**\n * Maps a headless library's sort state to `TableSort`, e.g. TanStack's\n * `column.getIsSorted()` (`false | \"asc\" | \"desc\"`).\n */\nexport function tableSortFrom(value: false | \"asc\" | \"desc\"): TableSort {\n if (value === \"asc\") return \"ascending\";\n if (value === \"desc\") return \"descending\";\n return \"none\";\n}\n\nexport interface TableProps extends ComponentProps<\"table\"> {\n /**\n * Keeps the first column (usually the name or the date) in place while the rest\n * scrolls sideways. For dense admin tables that still have to work on a tablet.\n */\n stickyFirstColumn?: boolean;\n /** Classes for the scroll container around the `<table>` (border, radius, max height…). */\n containerClassName?: string;\n}\n\n/**\n * Semantic, composable table: `Table` › `TableHeader` / `TableBody` › `TableRow` ›\n * `TableHead` / `TableCell`. Plain elements with styles, so a headless library\n * (`@tanstack/react-table`) can drive it: map `headerGroups` to `TableRow` +\n * `TableHead` (`sort={tableSortFrom(column.getIsSorted())}`,\n * `onSort={column.getToggleSortingHandler()}`)\n * and `rows` to `TableRow` + `TableCell`.\n *\n * - Name it: `aria-label` (\"Facturas\") or `aria-labelledby` the section title.\n * - Desktop pattern (1024+). On mobile a six-column table doesn't fit: render each\n * row as a `ListRow` (title = concept, subtitle = date · specialty, trailing =\n * amount and action) or a stacked row with the actions in a menu, and switch\n * with `hidden lg:block` / `lg:hidden`. Horizontal scroll (automatic, with\n * `stickyFirstColumn` if needed) is the fallback for in-between widths, not the\n * mobile design.\n * - When it overflows, the scroll container becomes focusable so it can be scrolled\n * with the keyboard.\n */\nexport function Table({\n stickyFirstColumn = false,\n containerClassName,\n className,\n ...props\n}: TableProps): React.JSX.Element {\n const containerRef = useRef<HTMLDivElement>(null);\n const [scrollable, setScrollable] = useState(false);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const update = (): void =>\n setScrollable(container.scrollWidth > container.clientWidth);\n update();\n\n const observer = new ResizeObserver(update);\n observer.observe(container);\n if (container.firstElementChild) {\n observer.observe(container.firstElementChild);\n }\n return () => observer.disconnect();\n }, []);\n\n // Only while it overflows: then it's a named region reachable with Tab, so it\n // can be scrolled with the arrow keys (WCAG 2.1.1). Otherwise it's a plain box.\n const scrollRegion = scrollable\n ? {\n role: \"region\",\n tabIndex: 0,\n \"aria-label\": props[\"aria-label\"],\n \"aria-labelledby\": props[\"aria-labelledby\"],\n }\n : {};\n\n return (\n <div\n ref={containerRef}\n data-slot=\"table-container\"\n {...scrollRegion}\n className={cn(\n \"relative w-full overflow-x-auto focus-ring\",\n containerClassName,\n )}\n >\n <table\n data-slot=\"table\"\n className={cn(\n // Separate borders so they travel with sticky cells.\n \"w-full border-separate border-spacing-0 text-left\",\n stickyFirstColumn &&\n \"[&_tr>:first-child]:sticky [&_tr>:first-child]:left-0 [&_tr>:first-child]:z-10 [&_tr>:first-child]:shadow-[inset_-1px_0_0_0_var(--color-border-subtle)]\",\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nexport interface TableHeaderProps extends ComponentProps<\"thead\"> {}\n\nexport function TableHeader(props: TableHeaderProps): React.JSX.Element {\n return <thead data-slot=\"table-header\" {...props} />;\n}\n\nexport interface TableBodyProps extends ComponentProps<\"tbody\"> {}\n\nexport function TableBody(props: TableBodyProps): React.JSX.Element {\n return <tbody data-slot=\"table-body\" {...props} />;\n}\n\nexport interface TableRowProps extends ComponentProps<\"tr\"> {}\n\n/**\n * A row. Figma has no Table/Row: a row is just cells as wide as their headers. If a\n * row opens a detail, put a link in its first cell — don't make the `<tr>` clickable.\n */\nexport function TableRow(props: TableRowProps): React.JSX.Element {\n return <tr data-slot=\"table-row\" {...props} />;\n}\n\nconst alignment = {\n text: \"text-left\",\n numeric: \"text-right\",\n badge: \"text-left\",\n // Shrinks to its buttons.\n action: \"w-0 whitespace-nowrap text-center\",\n} satisfies Record<TableCellType, string>;\n\nexport const tableHeadVariants = cva(\n \"h-12 border-b border-border-subtle bg-surface px-4 align-middle text-label-strong text-text-secondary data-sorted:text-text-primary\",\n { variants: { type: alignment }, defaultVariants: { type: \"text\" } },\n);\n\nexport interface TableHeadProps extends ComponentProps<\"th\"> {\n /** Same as the column's cells, so the header lines up with them. */\n type?: TableCellType;\n /**\n * Current order of the column. The sorted column is recognised by its chevron,\n * not only by the darker label.\n */\n sort?: TableSort;\n /**\n * Makes the column sortable: the label becomes a `<button>` inside the `<th>`,\n * which carries `aria-sort`. Cycling the order is up to the app.\n */\n onSort?: MouseEventHandler<HTMLButtonElement>;\n}\n\n/** Column header (`<th scope=\"col\">`). Sortable when it gets `onSort`. */\nexport function TableHead({\n type = \"text\",\n sort = \"none\",\n onSort,\n className,\n children,\n ...props\n}: TableHeadProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const descriptionId = useId();\n const sorted = sort !== \"none\";\n const sortLabel = {\n none: labels.notSorted,\n ascending: labels.sortAscending,\n descending: labels.sortDescending,\n }[sort];\n\n return (\n <th\n data-slot=\"table-head\"\n scope=\"col\"\n aria-sort={sorted ? sort : undefined}\n data-sorted={sorted || undefined}\n className={cn(tableHeadVariants({ type }), onSort && \"p-0\", className)}\n {...props}\n >\n {onSort ? (\n <button\n type=\"button\"\n onClick={onSort}\n // The state is announced when the button gets focus, where aria-sort on\n // the <th> isn't always read.\n aria-describedby={descriptionId}\n className={cn(\n \"flex h-12 w-full cursor-pointer items-center gap-1 whitespace-nowrap px-4\",\n \"transition-colors hover:text-text-primary\",\n \"focus-ring focus-visible:-outline-offset-2\",\n // Numbers: the chevron goes on the inside so the label stays aligned\n // with the column.\n type === \"numeric\"\n ? \"flex-row-reverse\"\n : type === \"action\"\n ? \"justify-center\"\n : \"justify-start\",\n )}\n >\n <span>{children}</span>\n {sort === \"ascending\" && <ChevronUpIcon size=\"sm\" />}\n {sort === \"descending\" && <ChevronDownIcon size=\"sm\" />}\n </button>\n ) : (\n children\n )}\n {onSort && (\n <span id={descriptionId} hidden>\n {sortLabel}\n </span>\n )}\n </th>\n );\n}\n\nexport const tableCellVariants = cva(\n \"h-16 border-b border-border-subtle bg-background px-4 align-middle text-body-small text-text-primary\",\n {\n variants: {\n type: {\n ...alignment,\n // Tabular figures so decimals line up and amounts can be compared.\n numeric: \"whitespace-nowrap text-right text-label-strong tabular-nums\",\n },\n },\n defaultVariants: { type: \"text\" },\n },\n);\n\nexport interface TableCellProps extends ComponentProps<\"td\"> {\n /**\n * - `text`: left.\n * - `numeric`: right, tabular figures. The value comes formatted by the app in\n * es-ES (\"29,00 €\", symbol after), the cell doesn't format.\n * - `badge`: left, a `Badge`.\n * - `action`: centred, a ghost small icon button whose `aria-label` carries the\n * row's context (\"Descargar factura del 5 de enero\") — twenty \"Descargar\"\n * buttons are indistinguishable for a screen reader.\n */\n type?: TableCellType;\n}\n\nexport function TableCell({\n type,\n className,\n ...props\n}: TableCellProps): React.JSX.Element {\n return (\n <td\n data-slot=\"table-cell\"\n className={cn(tableCellVariants({ type }), className)}\n {...props}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;AAuBO,SAAS,cAAc,OAA0C;AACtE,MAAI,UAAU,MAAO,QAAO;AAC5B,MAAI,UAAU,OAAQ,QAAO;AAC7B,SAAO;AACT;AA8BO,SAAS,MAAM;AAAA,EACpB,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkC;AAChC,QAAM,eAAe,OAAuB,IAAI;AAChD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAElD,YAAU,MAAM;AACd,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,UAAW;AAEhB,UAAM,SAAS,MACb,cAAc,UAAU,cAAc,UAAU,WAAW;AAC7D,WAAA;AAEA,UAAM,WAAW,IAAI,eAAe,MAAM;AAC1C,aAAS,QAAQ,SAAS;AAC1B,QAAI,UAAU,mBAAmB;AAC/B,eAAS,QAAQ,UAAU,iBAAiB;AAAA,IAC9C;AACA,WAAO,MAAM,SAAS,WAAA;AAAA,EACxB,GAAG,CAAA,CAAE;AAIL,QAAM,eAAe,aACjB;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,cAAc,MAAM,YAAY;AAAA,IAChC,mBAAmB,MAAM,iBAAiB;AAAA,EAAA,IAE5C,CAAA;AAEJ,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK;AAAA,MACL,aAAU;AAAA,MACT,GAAG;AAAA,MACJ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,aAAU;AAAA,UACV,WAAW;AAAA;AAAA,YAET;AAAA,YACA,qBACE;AAAA,YACF;AAAA,UAAA;AAAA,UAED,GAAG;AAAA,QAAA;AAAA,MAAA;AAAA,IACN;AAAA,EAAA;AAGN;AAIO,SAAS,YAAY,OAA4C;AACtE,SAAO,oBAAC,SAAA,EAAM,aAAU,gBAAgB,GAAG,OAAO;AACpD;AAIO,SAAS,UAAU,OAA0C;AAClE,SAAO,oBAAC,SAAA,EAAM,aAAU,cAAc,GAAG,OAAO;AAClD;AAQO,SAAS,SAAS,OAAyC;AAChE,SAAO,oBAAC,MAAA,EAAG,aAAU,aAAa,GAAG,OAAO;AAC9C;AAEA,MAAM,YAAY;AAAA,EAChB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA;AAAA,EAEP,QAAQ;AACV;AAEO,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA,EAAE,UAAU,EAAE,MAAM,UAAA,GAAa,iBAAiB,EAAE,MAAM,OAAA,EAAO;AACnE;AAkBO,SAAS,UAAU;AAAA,EACxB,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsC;AACpC,QAAM,SAAS,kBAAA;AACf,QAAM,gBAAgB,MAAA;AACtB,QAAM,SAAS,SAAS;AACxB,QAAM,YAAY;AAAA,IAChB,MAAM,OAAO;AAAA,IACb,WAAW,OAAO;AAAA,IAClB,YAAY,OAAO;AAAA,EAAA,EACnB,IAAI;AAEN,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,OAAM;AAAA,MACN,aAAW,SAAS,OAAO;AAAA,MAC3B,eAAa,UAAU;AAAA,MACvB,WAAW,GAAG,kBAAkB,EAAE,MAAM,GAAG,UAAU,OAAO,SAAS;AAAA,MACpE,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,SACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YAGT,oBAAkB;AAAA,YAClB,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA;AAAA;AAAA,cAGA,SAAS,YACL,qBACA,SAAS,WACP,mBACA;AAAA,YAAA;AAAA,YAGR,UAAA;AAAA,cAAA,oBAAC,UAAM,UAAS;AAAA,cACf,SAAS,eAAe,oBAAC,eAAA,EAAc,MAAK,MAAK;AAAA,cACjD,SAAS,gBAAgB,oBAAC,iBAAA,EAAgB,MAAK,KAAA,CAAK;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,IAGvD;AAAA,QAED,UACC,oBAAC,QAAA,EAAK,IAAI,eAAe,QAAM,MAC5B,UAAA,UAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;AAEO,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,GAAG;AAAA;AAAA,QAEH,SAAS;AAAA,MAAA;AAAA,IACX;AAAA,IAEF,iBAAiB,EAAE,MAAM,OAAA;AAAA,EAAO;AAEpC;AAeO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsC;AACpC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,kBAAkB,EAAE,KAAA,CAAM,GAAG,SAAS;AAAA,MACnD,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;"}
1
+ {"version":3,"file":"table.js","sources":["../../../src/components/table/table.tsx"],"sourcesContent":["import { cva } from \"class-variance-authority\";\nimport {\n type ComponentProps,\n type MouseEventHandler,\n useEffect,\n useId,\n useRef,\n useState,\n} from \"react\";\n\nimport { ChevronDownIcon, ChevronUpIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\n/** The type decides the alignment: text and badge left, numbers right, action centred. */\nexport type TableCellType = \"text\" | \"numeric\" | \"badge\" | \"action\";\n\nexport type TableSort = \"none\" | \"ascending\" | \"descending\";\n\n/**\n * Maps a headless library's sort state to `TableSort`, e.g. TanStack's\n * `column.getIsSorted()` (`false | \"asc\" | \"desc\"`).\n */\nexport function tableSortFrom(value: false | \"asc\" | \"desc\"): TableSort {\n if (value === \"asc\") return \"ascending\";\n if (value === \"desc\") return \"descending\";\n return \"none\";\n}\n\nexport interface TableProps extends ComponentProps<\"table\"> {\n /**\n * Keeps the first column (usually the name or the date) in place while the rest\n * scrolls sideways. For dense admin tables that still have to work on a tablet.\n */\n stickyFirstColumn?: boolean;\n /** Classes for the scroll container around the `<table>` (border, radius, max height…). */\n containerClassName?: string;\n}\n\n/**\n * Semantic, composable table: `Table` › `TableHeader` / `TableBody` › `TableRow` ›\n * `TableHead` / `TableCell`. Plain elements with styles, so a headless library\n * (`@tanstack/react-table`) can drive it: map `headerGroups` to `TableRow` +\n * `TableHead` (`sort={tableSortFrom(column.getIsSorted())}`,\n * `onSort={column.getToggleSortingHandler()}`)\n * and `rows` to `TableRow` + `TableCell`.\n *\n * - Name it: `aria-label` (\"Facturas\") or `aria-labelledby` the section title.\n * - Desktop pattern (1024+). On mobile a six-column table doesn't fit: render each\n * row as a `ListRow` (title = concept, subtitle = date · specialty, trailing =\n * amount and action) or a stacked row with the actions in a menu, and switch\n * with `hidden lg:block` / `lg:hidden`. Horizontal scroll (automatic, with\n * `stickyFirstColumn` if needed) is the fallback for in-between widths, not the\n * mobile design.\n * - When it overflows, the scroll container becomes focusable so it can be scrolled\n * with the keyboard.\n */\nexport function Table({\n stickyFirstColumn = false,\n containerClassName,\n className,\n ...props\n}: TableProps): React.JSX.Element {\n const containerRef = useRef<HTMLDivElement>(null);\n const [scrollable, setScrollable] = useState(false);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const update = (): void =>\n setScrollable(container.scrollWidth > container.clientWidth);\n update();\n\n const observer = new ResizeObserver(update);\n observer.observe(container);\n if (container.firstElementChild) {\n observer.observe(container.firstElementChild);\n }\n return () => observer.disconnect();\n }, []);\n\n // Only while it overflows: then it's a named region reachable with Tab, so it\n // can be scrolled with the arrow keys (WCAG 2.1.1). Otherwise it's a plain box.\n const scrollRegion = scrollable\n ? {\n role: \"region\",\n tabIndex: 0,\n \"aria-label\": props[\"aria-label\"],\n \"aria-labelledby\": props[\"aria-labelledby\"],\n }\n : {};\n\n return (\n <div\n ref={containerRef}\n data-slot=\"table-container\"\n {...scrollRegion}\n className={cn(\n \"relative w-full overflow-x-auto rounded-xl border border-border-subtle bg-background focus-ring\",\n containerClassName,\n )}\n >\n <table\n data-slot=\"table\"\n className={cn(\n // Separate borders so they travel with sticky cells.\n \"w-full border-separate border-spacing-0 text-left\",\n stickyFirstColumn &&\n \"[&_tr>:first-child]:sticky [&_tr>:first-child]:left-0 [&_tr>:first-child]:z-10 [&_tr>:first-child]:shadow-[inset_-1px_0_0_0_var(--color-border-subtle)]\",\n className,\n )}\n {...props}\n />\n </div>\n );\n}\n\nexport interface TableHeaderProps extends ComponentProps<\"thead\"> {}\n\nexport function TableHeader(props: TableHeaderProps): React.JSX.Element {\n return <thead data-slot=\"table-header\" {...props} />;\n}\n\nexport interface TableBodyProps extends ComponentProps<\"tbody\"> {}\n\nexport function TableBody(props: TableBodyProps): React.JSX.Element {\n return <tbody data-slot=\"table-body\" {...props} />;\n}\n\nexport interface TableRowProps extends ComponentProps<\"tr\"> {}\n\n/**\n * A row. Figma has no Table/Row: a row is just cells as wide as their headers. If a\n * row opens a detail, put a link in its first cell — don't make the `<tr>` clickable.\n */\nexport function TableRow({\n className,\n ...props\n}: TableRowProps): React.JSX.Element {\n return (\n <tr\n data-slot=\"table-row\"\n className={cn(\n \"transition-colors [tbody_&:last-child>td]:border-b-0 [tbody_&:hover>td]:bg-surface\",\n className,\n )}\n {...props}\n />\n );\n}\n\nconst alignment = {\n text: \"text-left\",\n numeric: \"text-right\",\n badge: \"text-left\",\n // Shrinks to its buttons.\n action: \"w-0 whitespace-nowrap text-center\",\n} satisfies Record<TableCellType, string>;\n\nexport const tableHeadVariants = cva(\n \"h-11 border-b border-border-subtle bg-surface px-4 align-middle text-label-small text-text-secondary first:rounded-tl-xl last:rounded-tr-xl data-sorted:text-label-strong data-sorted:text-text-primary\",\n { variants: { type: alignment }, defaultVariants: { type: \"text\" } },\n);\n\nexport interface TableHeadProps extends ComponentProps<\"th\"> {\n /** Same as the column's cells, so the header lines up with them. */\n type?: TableCellType;\n /**\n * Current order of the column. The sorted column is recognised by its chevron,\n * not only by the darker label.\n */\n sort?: TableSort;\n /**\n * Makes the column sortable: the label becomes a `<button>` inside the `<th>`,\n * which carries `aria-sort`. Cycling the order is up to the app.\n */\n onSort?: MouseEventHandler<HTMLButtonElement>;\n}\n\n/** Column header (`<th scope=\"col\">`). Sortable when it gets `onSort`. */\nexport function TableHead({\n type = \"text\",\n sort = \"none\",\n onSort,\n className,\n children,\n ...props\n}: TableHeadProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n const descriptionId = useId();\n const sorted = sort !== \"none\";\n const sortLabel = {\n none: labels.notSorted,\n ascending: labels.sortAscending,\n descending: labels.sortDescending,\n }[sort];\n\n return (\n <th\n data-slot=\"table-head\"\n scope=\"col\"\n aria-sort={sorted ? sort : undefined}\n data-sorted={sorted || undefined}\n className={cn(tableHeadVariants({ type }), onSort && \"p-0\", className)}\n {...props}\n >\n {onSort ? (\n <button\n type=\"button\"\n onClick={onSort}\n // The state is announced when the button gets focus, where aria-sort on\n // the <th> isn't always read.\n aria-describedby={descriptionId}\n className={cn(\n \"flex h-12 w-full cursor-pointer items-center gap-1 whitespace-nowrap px-4\",\n \"transition-colors hover:text-text-primary\",\n \"focus-ring focus-visible:-outline-offset-2\",\n // Numbers: the chevron goes on the inside so the label stays aligned\n // with the column.\n type === \"numeric\"\n ? \"flex-row-reverse\"\n : type === \"action\"\n ? \"justify-center\"\n : \"justify-start\",\n )}\n >\n <span>{children}</span>\n {sort === \"ascending\" && <ChevronUpIcon size=\"sm\" />}\n {sort === \"descending\" && <ChevronDownIcon size=\"sm\" />}\n </button>\n ) : (\n children\n )}\n {onSort && (\n <span id={descriptionId} hidden>\n {sortLabel}\n </span>\n )}\n </th>\n );\n}\n\nexport const tableCellVariants = cva(\n \"h-14 border-b border-border-subtle bg-background px-4 align-middle text-body-small text-text-primary transition-colors\",\n {\n variants: {\n type: {\n ...alignment,\n // Tabular figures so decimals line up and amounts can be compared.\n numeric: \"whitespace-nowrap text-right text-label-strong tabular-nums\",\n },\n },\n defaultVariants: { type: \"text\" },\n },\n);\n\nexport interface TableCellProps extends ComponentProps<\"td\"> {\n /**\n * - `text`: left.\n * - `numeric`: right, tabular figures. The value comes formatted by the app in\n * es-ES (\"29,00 €\", symbol after), the cell doesn't format.\n * - `badge`: left, a `Badge`.\n * - `action`: centred, a ghost small icon button whose `aria-label` carries the\n * row's context (\"Descargar factura del 5 de enero\") — twenty \"Descargar\"\n * buttons are indistinguishable for a screen reader.\n */\n type?: TableCellType;\n}\n\nexport function TableCell({\n type,\n className,\n ...props\n}: TableCellProps): React.JSX.Element {\n return (\n <td\n data-slot=\"table-cell\"\n className={cn(tableCellVariants({ type }), className)}\n {...props}\n />\n );\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -25,8 +25,10 @@ export * from "./components/checkbox";
25
25
  export * from "./components/chip";
26
26
  export * from "./components/date-badge";
27
27
  export * from "./components/day-cell";
28
+ export * from "./components/detail-header";
28
29
  export * from "./components/divider";
29
30
  export * from "./components/empty-state";
31
+ export * from "./components/fact-card";
30
32
  export * from "./components/field";
31
33
  export * from "./components/footer";
32
34
  export * from "./components/health-record";
@@ -58,6 +60,7 @@ export * from "./components/sidebar";
58
60
  export * from "./components/skeleton";
59
61
  export * from "./components/social-button";
60
62
  export * from "./components/spinner";
63
+ export * from "./components/stat-card";
61
64
  export * from "./components/stat-tile";
62
65
  export * from "./components/step-indicator";
63
66
  export * from "./components/store-badge";
package/dist/index.js CHANGED
@@ -38,8 +38,10 @@ import { Chip, ChipGroup, chipVariants } from "./components/chip/chip.js";
38
38
  import { ConfirmModal } from "./components/modal/confirm-modal.js";
39
39
  import { DateBadge } from "./components/date-badge/date-badge.js";
40
40
  import { DayCell, DayCellGroup } from "./components/day-cell/day-cell.js";
41
+ import { DetailHeader, DetailHeaderBack } from "./components/detail-header/detail-header.js";
41
42
  import { Divider, dividerVariants } from "./components/divider/divider.js";
42
43
  import { EmptyState, emptyStateVariants } from "./components/empty-state/empty-state.js";
44
+ import { FactCard, factCardVariants } from "./components/fact-card/fact-card.js";
43
45
  import { Field } from "./components/field/field.js";
44
46
  import { Footer, FooterLink } from "./components/footer/footer.js";
45
47
  import { HealthRecordHeader, HealthRecordRow, HealthRecordSection } from "./components/health-record/health-record.js";
@@ -73,6 +75,7 @@ import { Sidebar, SidebarFooter, SidebarHeader, SidebarItem, SidebarNav, Sidebar
73
75
  import { Skeleton, skeletonVariants } from "./components/skeleton/skeleton.js";
74
76
  import { SocialButton } from "./components/social-button/social-button.js";
75
77
  import { Spinner } from "./components/spinner/spinner.js";
78
+ import { StatCard, statCardVariants } from "./components/stat-card/stat-card.js";
76
79
  import { StatTile } from "./components/stat-tile/stat-tile.js";
77
80
  import { StepIndicator } from "./components/step-indicator/step-indicator.js";
78
81
  import { StoreBadge } from "./components/store-badge/store-badge.js";
@@ -141,8 +144,11 @@ export {
141
144
  DateBadge,
142
145
  DayCell,
143
146
  DayCellGroup,
147
+ DetailHeader,
148
+ DetailHeaderBack,
144
149
  Divider,
145
150
  EmptyState,
151
+ FactCard,
146
152
  Field,
147
153
  Footer,
148
154
  FooterLink,
@@ -203,6 +209,7 @@ export {
203
209
  Skeleton,
204
210
  SocialButton,
205
211
  Spinner,
212
+ StatCard,
206
213
  StatTile,
207
214
  StepIndicator,
208
215
  StoreBadge,
@@ -243,6 +250,7 @@ export {
243
250
  defaultLabels,
244
251
  dividerVariants,
245
252
  emptyStateVariants,
253
+ factCardVariants,
246
254
  getDefaultPhonePrefixes,
247
255
  iconButtonVariants,
248
256
  iconContainerVariants,
@@ -251,6 +259,7 @@ export {
251
259
  radioVariants,
252
260
  severityIndicatorVariants,
253
261
  skeletonVariants,
262
+ statCardVariants,
254
263
  tableCellVariants,
255
264
  tableHeadVariants,
256
265
  tableSortFrom,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mediquo/medicore",
3
3
  "description": "MediCore — design system de las plataformas web de mediQuo",
4
- "version": "0.1.1",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
7
7
  "sideEffects": [