@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.
- package/dist/components/badge/badge.js +1 -1
- package/dist/components/badge/badge.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.d.ts +12 -5
- package/dist/components/breadcrumb/breadcrumb.js +22 -6
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/detail-header/detail-header.d.ts +49 -0
- package/dist/components/detail-header/detail-header.js +92 -0
- package/dist/components/detail-header/detail-header.js.map +1 -0
- package/dist/components/detail-header/index.d.ts +1 -0
- package/dist/components/fact-card/fact-card.d.ts +34 -0
- package/dist/components/fact-card/fact-card.js +86 -0
- package/dist/components/fact-card/fact-card.js.map +1 -0
- package/dist/components/fact-card/index.d.ts +1 -0
- package/dist/components/stat-card/index.d.ts +1 -0
- package/dist/components/stat-card/stat-card.d.ts +31 -0
- package/dist/components/stat-card/stat-card.js +85 -0
- package/dist/components/stat-card/stat-card.js.map +1 -0
- package/dist/components/table/table.d.ts +1 -1
- package/dist/components/table/table.js +18 -5
- package/dist/components/table/table.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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-
|
|
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-
|
|
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
|
|
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
|
|
30
|
-
* by a chevron. On mobile, the parent level gets a back arrow in front
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
+
link,
|
|
43
59
|
/* @__PURE__ */ jsx(
|
|
44
60
|
ChevronRightIcon,
|
|
45
61
|
{
|
|
46
62
|
size: "sm",
|
|
47
|
-
className: "text-text-
|
|
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-
|
|
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\";\
|
|
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(
|
|
72
|
-
|
|
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-
|
|
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-
|
|
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":"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"}
|
|
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":";;;;;;;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;AAAA,EACvB;AAAA,EACA,GAAG;AACL,GAAqC;AACnC,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;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;"}
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|