@mediquo/medicore 0.1.1 → 0.3.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.d.ts +7 -3
- package/dist/components/badge/badge.js +27 -4
- package/dist/components/badge/badge.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.d.ts +12 -5
- package/dist/components/breadcrumb/breadcrumb.js +20 -4
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/button/button.js +9 -9
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/chip/chip.d.ts +3 -0
- package/dist/components/chip/chip.js +41 -10
- package/dist/components/chip/chip.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/input/control-styles.d.ts +1 -1
- package/dist/components/input/control-styles.js +4 -4
- package/dist/components/input/control-styles.js.map +1 -1
- 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
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ComponentProps } from "react";
|
|
2
2
|
export declare const badgeVariants: (props?: ({
|
|
3
|
-
variant?: "error" | "brand" | "neutral" | "success" | "warning" | "info" | null | undefined;
|
|
3
|
+
variant?: "outline" | "error" | "brand" | "neutral" | "success" | "warning" | "info" | null | undefined;
|
|
4
4
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
5
|
-
export type BadgeVariant = "neutral" | "brand" | "success" | "warning" | "error" | "info";
|
|
5
|
+
export type BadgeVariant = "neutral" | "outline" | "brand" | "success" | "warning" | "error" | "info";
|
|
6
6
|
export interface BadgeProps extends ComponentProps<"span"> {
|
|
7
7
|
variant?: BadgeVariant;
|
|
8
|
+
/** A coloured dot in front: for statuses ("Abierta", "Pagada"), not for categories. */
|
|
9
|
+
dot?: boolean;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Category or status tag (documents: Receta, Informe, Orden médica; program cards).
|
|
@@ -12,9 +14,11 @@ export interface BadgeProps extends ComponentProps<"span"> {
|
|
|
12
14
|
* it isn't a badge, it's text.
|
|
13
15
|
*
|
|
14
16
|
* - It is NOT a button: no click, no focus, no close icon. Removable → `Chip`.
|
|
17
|
+
* - `outline` is the quiet one for plain metadata (a clinic's name next to a
|
|
18
|
+
* professional); the tinted variants tell a state.
|
|
15
19
|
* - On a photo use `neutral` (solid). Never a translucent pill: its contrast depends
|
|
16
20
|
* on the photo.
|
|
17
21
|
* - `warning` keeps its text in text-primary: the orange-on-orange pair doesn't reach
|
|
18
22
|
* 4.5:1 with the current tokens.
|
|
19
23
|
*/
|
|
20
|
-
export declare function Badge({ variant, className, ...props }: BadgeProps): React.JSX.Element;
|
|
24
|
+
export declare function Badge({ variant, dot, className, children, ...props }: BadgeProps): React.JSX.Element;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs, 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.5 whitespace-nowrap rounded-full px-2.5 text-label-small font-medium [&_svg]:size-icon-xs",
|
|
6
6
|
{
|
|
7
7
|
variants: {
|
|
8
8
|
variant: {
|
|
9
9
|
neutral: "bg-surface-raised text-text-primary",
|
|
10
|
+
outline: "bg-background text-text-secondary ring-1 ring-border ring-inset",
|
|
10
11
|
brand: "bg-brand-subtle text-text-brand",
|
|
11
12
|
success: "bg-success-subtle text-text-success",
|
|
12
13
|
// Token gap, not an aesthetic choice: warning-strong on warning-subtle is
|
|
@@ -21,17 +22,39 @@ const badgeVariants = cva(
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
);
|
|
25
|
+
const badgeDotVariants = cva("size-1.5 shrink-0 rounded-full", {
|
|
26
|
+
variants: {
|
|
27
|
+
variant: {
|
|
28
|
+
neutral: "bg-text-secondary",
|
|
29
|
+
outline: "bg-text-secondary",
|
|
30
|
+
brand: "bg-brand",
|
|
31
|
+
success: "bg-success",
|
|
32
|
+
warning: "bg-warning",
|
|
33
|
+
error: "bg-error",
|
|
34
|
+
info: "bg-info"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: "neutral"
|
|
39
|
+
}
|
|
40
|
+
});
|
|
24
41
|
function Badge({
|
|
25
42
|
variant,
|
|
43
|
+
dot = false,
|
|
26
44
|
className,
|
|
45
|
+
children,
|
|
27
46
|
...props
|
|
28
47
|
}) {
|
|
29
|
-
return /* @__PURE__ */
|
|
48
|
+
return /* @__PURE__ */ jsxs(
|
|
30
49
|
"span",
|
|
31
50
|
{
|
|
32
51
|
"data-slot": "badge",
|
|
33
52
|
className: cn(badgeVariants({ variant }), className),
|
|
34
|
-
...props
|
|
53
|
+
...props,
|
|
54
|
+
children: [
|
|
55
|
+
dot && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: badgeDotVariants({ variant }) }),
|
|
56
|
+
children
|
|
57
|
+
]
|
|
35
58
|
}
|
|
36
59
|
);
|
|
37
60
|
}
|
|
@@ -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.5 whitespace-nowrap rounded-full px-2.5 text-label-small font-medium [&_svg]:size-icon-xs\",\n {\n variants: {\n variant: {\n neutral: \"bg-surface-raised text-text-primary\",\n outline:\n \"bg-background text-text-secondary ring-1 ring-border ring-inset\",\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\nconst badgeDotVariants = cva(\"size-1.5 shrink-0 rounded-full\", {\n variants: {\n variant: {\n neutral: \"bg-text-secondary\",\n outline: \"bg-text-secondary\",\n brand: \"bg-brand\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n error: \"bg-error\",\n info: \"bg-info\",\n },\n },\n defaultVariants: {\n variant: \"neutral\",\n },\n});\n\nexport type BadgeVariant =\n | \"neutral\"\n | \"outline\"\n | \"brand\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"info\";\n\nexport interface BadgeProps extends ComponentProps<\"span\"> {\n variant?: BadgeVariant;\n /** A coloured dot in front: for statuses (\"Abierta\", \"Pagada\"), not for categories. */\n dot?: boolean;\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 * - `outline` is the quiet one for plain metadata (a clinic's name next to a\n * professional); the tinted variants tell a state.\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 dot = false,\n className,\n children,\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 {dot && (\n <span aria-hidden=\"true\" className={badgeDotVariants({ variant })} />\n )}\n {children}\n </span>\n );\n}\n"],"names":[],"mappings":";;;AAKO,MAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SACE;AAAA,QACF,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;AAEA,MAAM,mBAAmB,IAAI,kCAAkC;AAAA,EAC7D,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,IAAA;AAAA,EACR;AAAA,EAEF,iBAAiB;AAAA,IACf,SAAS;AAAA,EAAA;AAEb,CAAC;AA8BM,SAAS,MAAM;AAAA,EACpB;AAAA,EACA,MAAM;AAAA,EACN;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,MAEH,UAAA;AAAA,QAAA,OACC,oBAAC,UAAK,eAAY,QAAO,WAAW,iBAAiB,EAAE,QAAA,CAAS,GAAG;AAAA,QAEpE;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;"}
|
|
@@ -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/Default in text-secondary, brand on hover), with
|
|
21
|
+
* a chevron in border-strong 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,
|
|
@@ -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
|
}) {
|
|
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-default text-text-secondary transition-colors hover:text-text-brand",
|
|
50
|
+
className
|
|
51
|
+
),
|
|
52
|
+
children,
|
|
53
|
+
...props
|
|
54
|
+
}
|
|
55
|
+
});
|
|
40
56
|
return /* @__PURE__ */ jsxs("li", { "data-slot": "breadcrumb-item", className: "flex items-center gap-2", 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-
|
|
63
|
+
className: "text-border-strong 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/Default in text-secondary, brand on hover), with\n * a chevron in border-strong between them, so the trail never competes with the\n * page title under it. The links are not underlined: inside the breadcrumb landmark\n * and its list, their position already tells they are links (WCAG 1.4.1 applies to\n * links in running text).\n *\n * Below the `lg` breakpoint only the level right above shows, as a way back with an\n * arrow (\"‹ Programas especializados\").\n */\nexport function Breadcrumb({\n \"aria-label\": ariaLabel,\n className,\n children,\n ...props\n}: BreadcrumbProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n\n return (\n <nav\n data-slot=\"breadcrumb\"\n aria-label={ariaLabel ?? labels.breadcrumb}\n className={className}\n {...props}\n >\n <ol\n className={cn(\n \"flex flex-wrap items-center gap-2\",\n // Mobile: only the parent level (the second-to-last item) stays.\n \"max-lg:[&>li:not(:nth-last-child(2))]:hidden\",\n )}\n >\n {children}\n </ol>\n </nav>\n );\n}\n\nexport interface BreadcrumbItemProps extends useRender.ComponentProps<\"a\"> {\n /** The level's name, the same as the section or page it links to. */\n children: ReactNode;\n}\n\n/**\n * A level above the current page: a link (`href`, or the router's with `render`)\n * followed by a chevron. On mobile, the parent level gets a back arrow in front\n * instead.\n */\nexport function BreadcrumbItem({\n render,\n ref,\n className,\n children,\n ...props\n}: BreadcrumbItemProps): React.JSX.Element {\n const link = useRender({\n render,\n ref,\n defaultTagName: \"a\",\n props: {\n \"data-slot\": \"breadcrumb-link\",\n className: cn(\n \"focus-ring touch-target inline-flex items-center gap-1 rounded-xs text-label-default text-text-secondary transition-colors hover:text-text-brand\",\n className,\n ),\n children,\n ...props,\n },\n });\n\n return (\n <li data-slot=\"breadcrumb-item\" className=\"flex items-center gap-2\">\n <ChevronLeftIcon size=\"sm\" className=\"text-text-secondary lg:hidden\" />\n {link}\n <ChevronRightIcon\n size=\"sm\"\n className=\"text-border-strong max-lg:hidden\"\n />\n </li>\n );\n}\n\nexport interface BreadcrumbPageProps extends ComponentProps<\"span\"> {}\n\n/** The current level: text, not a link, with `aria-current=\"page\"`. Hidden on mobile. */\nexport function BreadcrumbPage({\n className,\n ...props\n}: BreadcrumbPageProps): React.JSX.Element {\n return (\n <li data-slot=\"breadcrumb-page\" className=\"flex items-center\">\n <span\n aria-current=\"page\"\n className={cn(\"text-label-strong text-text-primary\", className)}\n {...props}\n />\n </li>\n );\n}\n"],"names":[],"mappings":";;;;;;AAkCO,SAAS,WAAW;AAAA,EACzB,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAuC;AACrC,QAAM,SAAS,kBAAA;AAEf,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,aAAa,OAAO;AAAA,MAChC;AAAA,MACC,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA;AAAA,YAEA;AAAA,UAAA;AAAA,UAGD;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN;AAYO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,QAAM,OAAO,UAAU;AAAA,IACrB;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,OAAO;AAAA,MACL,aAAa;AAAA,MACb,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MAAA;AAAA,MAEF;AAAA,MACA,GAAG;AAAA,IAAA;AAAA,EACL,CACD;AAED,SACE,qBAAC,MAAA,EAAG,aAAU,mBAAkB,WAAU,2BACxC,UAAA;AAAA,IAAA,oBAAC,iBAAA,EAAgB,MAAK,MAAK,WAAU,iCAAgC;AAAA,IACpE;AAAA,IACD;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EACZ,GACF;AAEJ;AAKO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA,GAAG;AACL,GAA2C;AACzC,SACE,oBAAC,MAAA,EAAG,aAAU,mBAAkB,WAAU,qBACxC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,gBAAa;AAAA,MACb,WAAW,GAAG,uCAAuC,SAAS;AAAA,MAC7D,GAAG;AAAA,IAAA;AAAA,EAAA,GAER;AAEJ;"}
|
|
@@ -6,20 +6,20 @@ import { Spinner } from "../spinner/spinner.js";
|
|
|
6
6
|
const buttonVariants = cva(
|
|
7
7
|
[
|
|
8
8
|
"inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap",
|
|
9
|
-
"rounded-md border border-transparent text-label-strong transition-
|
|
9
|
+
"rounded-md border border-transparent text-label-strong transition-[color,background-color,border-color,box-shadow]",
|
|
10
10
|
"focus-ring touch-target",
|
|
11
11
|
"[&_svg]:shrink-0 [&_svg]:size-icon-sm",
|
|
12
|
-
"data-disabled:cursor-not-allowed aria-busy:cursor-progress"
|
|
12
|
+
"data-disabled:cursor-not-allowed data-disabled:shadow-none aria-busy:cursor-progress"
|
|
13
13
|
],
|
|
14
14
|
{
|
|
15
15
|
variants: {
|
|
16
16
|
variant: {
|
|
17
|
-
primary: "bg-brand text-text-inverse not-data-disabled:hover:bg-brand-strong",
|
|
18
|
-
secondary: "border-text-brand bg-brand-subtle text-text-brand not-data-disabled:hover:bg-
|
|
19
|
-
outline: "border-border
|
|
17
|
+
primary: "bg-brand text-text-inverse shadow-xs not-data-disabled:hover:bg-brand-strong",
|
|
18
|
+
secondary: "border-text-brand bg-brand-subtle text-text-brand not-data-disabled:hover:bg-background",
|
|
19
|
+
outline: "border-border bg-background text-text-primary shadow-xs not-data-disabled:hover:border-border-strong not-data-disabled:hover:bg-surface",
|
|
20
20
|
ghost: "bg-transparent text-text-primary not-data-disabled:hover:bg-surface",
|
|
21
|
-
destructive: "bg-error-strong text-text-inverse",
|
|
22
|
-
inverse: "bg-background text-text-brand not-data-disabled:hover:bg-surface",
|
|
21
|
+
destructive: "bg-error-strong text-text-inverse shadow-xs",
|
|
22
|
+
inverse: "bg-background text-text-brand shadow-xs not-data-disabled:hover:bg-surface",
|
|
23
23
|
"inverse-ghost": "bg-transparent text-text-inverse not-data-disabled:hover:bg-overlay-light"
|
|
24
24
|
},
|
|
25
25
|
size: {
|
|
@@ -37,12 +37,12 @@ const buttonVariants = cva(
|
|
|
37
37
|
{
|
|
38
38
|
variant: ["primary", "destructive"],
|
|
39
39
|
disabled: true,
|
|
40
|
-
className: "bg-surface text-text-disabled"
|
|
40
|
+
className: "bg-surface-raised text-text-disabled"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
variant: ["secondary", "outline"],
|
|
44
44
|
disabled: true,
|
|
45
|
-
className: "border-border bg-surface text-text-disabled"
|
|
45
|
+
className: "border-border-subtle bg-surface text-text-disabled"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
variant: "ghost",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Spinner } from \"../spinner\";\n\nexport const buttonVariants = cva(\n [\n \"inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap\",\n \"rounded-md border border-transparent text-label-strong transition-
|
|
1
|
+
{"version":3,"file":"button.js","sources":["../../../src/components/button/button.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/cn\";\nimport { Spinner } from \"../spinner\";\n\nexport const buttonVariants = cva(\n [\n \"inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap\",\n \"rounded-md border border-transparent text-label-strong transition-[color,background-color,border-color,box-shadow]\",\n \"focus-ring touch-target\",\n \"[&_svg]:shrink-0 [&_svg]:size-icon-sm\",\n \"data-disabled:cursor-not-allowed data-disabled:shadow-none aria-busy:cursor-progress\",\n ],\n {\n variants: {\n variant: {\n primary:\n \"bg-brand text-text-inverse shadow-xs not-data-disabled:hover:bg-brand-strong\",\n secondary:\n \"border-text-brand bg-brand-subtle text-text-brand not-data-disabled:hover:bg-background\",\n outline:\n \"border-border bg-background text-text-primary shadow-xs not-data-disabled:hover:border-border-strong not-data-disabled:hover:bg-surface\",\n ghost:\n \"bg-transparent text-text-primary not-data-disabled:hover:bg-surface\",\n destructive: \"bg-error-strong text-text-inverse shadow-xs\",\n inverse:\n \"bg-background text-text-brand shadow-xs not-data-disabled:hover:bg-surface\",\n \"inverse-ghost\":\n \"bg-transparent text-text-inverse not-data-disabled:hover:bg-overlay-light\",\n },\n size: {\n default: \"h-control-md px-4\",\n small: \"h-control-sm px-3\",\n },\n disabled: {\n true: \"\",\n false: \"\",\n },\n },\n // Disabled uses explicit colours, not opacity: an opacity can't be a token and it\n // also dims the border.\n compoundVariants: [\n {\n variant: [\"primary\", \"destructive\"],\n disabled: true,\n className: \"bg-surface-raised text-text-disabled\",\n },\n {\n variant: [\"secondary\", \"outline\"],\n disabled: true,\n className: \"border-border-subtle bg-surface text-text-disabled\",\n },\n {\n variant: \"ghost\",\n disabled: true,\n className: \"text-text-disabled\",\n },\n {\n variant: \"inverse\",\n disabled: true,\n className: \"bg-border-inverse text-text-inverse-secondary\",\n },\n {\n variant: \"inverse-ghost\",\n disabled: true,\n className: \"text-text-inverse-secondary\",\n },\n ],\n defaultVariants: {\n variant: \"primary\",\n size: \"default\",\n disabled: false,\n },\n },\n);\n\ntype ButtonVariantProps = Omit<VariantProps<typeof buttonVariants>, \"disabled\">;\n\nexport interface ButtonProps\n extends Omit<BaseButton.Props, \"className\">,\n ButtonVariantProps {\n /** Keeps the variant colour, shows a spinner and ignores clicks without losing focus. */\n loading?: boolean;\n leadingIcon?: ReactNode;\n trailingIcon?: ReactNode;\n className?: string;\n}\n\n/**\n * Action button. A button ACTS; to navigate use `Link` (or `render={<a href />}` when\n * it has to look like a button).\n *\n * Hierarchy, from most to least weight: primary → secondary → outline → ghost.\n * `destructive` is a warning, not a level. `inverse` and `inverse-ghost` are only\n * for dark surfaces (`bg-brand-dark`). One primary action per block.\n *\n * The drawing is 40px (36 small) but the hit area is always 44px. Full width: pass\n * `className=\"w-full\"`.\n */\nexport function Button({\n variant,\n size,\n disabled = false,\n loading = false,\n leadingIcon,\n trailingIcon,\n className,\n children,\n type = \"button\",\n ...props\n}: ButtonProps): React.JSX.Element {\n return (\n <BaseButton\n data-slot=\"button\"\n type={type}\n disabled={disabled || loading}\n focusableWhenDisabled={loading}\n aria-busy={loading || undefined}\n className={cn(buttonVariants({ variant, size, disabled }), className)}\n {...props}\n >\n {loading ? (\n <Spinner size={16} label={null} className=\"text-current\" />\n ) : (\n leadingIcon\n )}\n {children}\n {!loading && trailingIcon}\n </BaseButton>\n );\n}\n"],"names":["BaseButton"],"mappings":";;;;;AAOO,MAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SACE;AAAA,QACF,WACE;AAAA,QACF,SACE;AAAA,QACF,OACE;AAAA,QACF,aAAa;AAAA,QACb,SACE;AAAA,QACF,iBACE;AAAA,MAAA;AAAA,MAEJ,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAAA,MAET,UAAU;AAAA,QACR,MAAM;AAAA,QACN,OAAO;AAAA,MAAA;AAAA,IACT;AAAA;AAAA;AAAA,IAIF,kBAAkB;AAAA,MAChB;AAAA,QACE,SAAS,CAAC,WAAW,aAAa;AAAA,QAClC,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS,CAAC,aAAa,SAAS;AAAA,QAChC,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,MAAA;AAAA,IACb;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,MACN,UAAU;AAAA,IAAA;AAAA,EACZ;AAEJ;AAyBO,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,GAAG;AACL,GAAmC;AACjC,SACE;AAAA,IAACA;AAAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,UAAU,YAAY;AAAA,MACtB,uBAAuB;AAAA,MACvB,aAAW,WAAW;AAAA,MACtB,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,SAAA,CAAU,GAAG,SAAS;AAAA,MACnE,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,UACC,oBAAC,WAAQ,MAAM,IAAI,OAAO,MAAM,WAAU,gBAAe,IAEzD;AAAA,QAED;AAAA,QACA,CAAC,WAAW;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGnB;"}
|
|
@@ -14,6 +14,8 @@ interface ChipOwnProps {
|
|
|
14
14
|
size?: ChipSize;
|
|
15
15
|
/** A MediCore icon before the label. While selected the check takes its place. */
|
|
16
16
|
leadingIcon?: ReactNode;
|
|
17
|
+
/** Counter after the label ("Abiertas 5"): how many items the filter would show. */
|
|
18
|
+
count?: ReactNode;
|
|
17
19
|
className?: string;
|
|
18
20
|
}
|
|
19
21
|
export interface ChipToggleProps extends Omit<Toggle.Props<string>, "className">, ChipOwnProps {
|
|
@@ -40,6 +42,7 @@ export type ChipProps = ChipToggleProps | ChipRemovableProps;
|
|
|
40
42
|
* - It is a `<button aria-pressed>` (Base UI Toggle): `pressed` / `defaultPressed` /
|
|
41
43
|
* `onPressedChange`, or `value` inside a `ChipGroup`.
|
|
42
44
|
* - With `onRemove` it is a remove button for an applied filter instead.
|
|
45
|
+
* - `count` adds a counter pill at the end, brand-coloured while selected.
|
|
43
46
|
*/
|
|
44
47
|
export declare function Chip(props: ChipProps): React.JSX.Element;
|
|
45
48
|
export interface ChipGroupProps<Value extends string = string> extends Omit<ToggleGroup.Props<Value>, "className"> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Button } from "@base-ui/react/button";
|
|
3
3
|
import { Toggle } from "@base-ui/react/toggle";
|
|
4
4
|
import { ToggleGroup } from "@base-ui/react/toggle-group";
|
|
@@ -10,15 +10,14 @@ import { useMediCoreLabels } from "../../provider/medicore-provider.js";
|
|
|
10
10
|
const chipVariants = cva(
|
|
11
11
|
[
|
|
12
12
|
"group/chip inline-flex shrink-0 cursor-pointer select-none items-center justify-center whitespace-nowrap",
|
|
13
|
-
|
|
14
|
-
"rounded-full border border-border-input bg-surface text-text-primary transition-colors",
|
|
13
|
+
"rounded-full border border-border bg-background text-text-primary shadow-xs transition-[color,background-color,border-color,box-shadow]",
|
|
15
14
|
// 32 (24 small) drawn, 44 hit area.
|
|
16
15
|
"focus-ring touch-target",
|
|
17
16
|
"[&_svg]:shrink-0",
|
|
18
|
-
"not-data-disabled:not-data-pressed:hover:bg-surface
|
|
19
|
-
// Selected is not just a colour: check in front,
|
|
20
|
-
"not-data-disabled:data-pressed:border-
|
|
21
|
-
"data-disabled:cursor-not-allowed data-disabled:border-border-subtle data-disabled:text-text-disabled"
|
|
17
|
+
"not-data-disabled:not-data-pressed:hover:border-border-strong not-data-disabled:not-data-pressed:hover:bg-surface",
|
|
18
|
+
// Selected is not just a colour: check in front, brand border, Medium label.
|
|
19
|
+
"not-data-disabled:data-pressed:border-brand not-data-disabled:data-pressed:bg-brand-subtle not-data-disabled:data-pressed:text-text-brand not-data-disabled:data-pressed:shadow-none",
|
|
20
|
+
"data-disabled:cursor-not-allowed data-disabled:border-border-subtle data-disabled:bg-surface data-disabled:text-text-disabled data-disabled:shadow-none"
|
|
22
21
|
],
|
|
23
22
|
{
|
|
24
23
|
variants: {
|
|
@@ -32,10 +31,40 @@ const chipVariants = cva(
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
);
|
|
34
|
+
const chipCountVariants = cva(
|
|
35
|
+
[
|
|
36
|
+
"inline-flex shrink-0 items-center justify-center rounded-full bg-surface-raised text-caption-default text-text-secondary tabular-nums transition-colors",
|
|
37
|
+
"group-data-pressed/chip:bg-brand group-data-pressed/chip:text-text-inverse",
|
|
38
|
+
"group-data-disabled/chip:bg-transparent group-data-disabled/chip:text-text-disabled"
|
|
39
|
+
],
|
|
40
|
+
{
|
|
41
|
+
variants: {
|
|
42
|
+
size: {
|
|
43
|
+
default: "-me-1 h-5 min-w-5 px-1.5",
|
|
44
|
+
small: "-me-0.5 h-4 min-w-4 px-1"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
defaultVariants: {
|
|
48
|
+
size: "default"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
function ChipCount({
|
|
53
|
+
size,
|
|
54
|
+
count
|
|
55
|
+
}) {
|
|
56
|
+
if (count === void 0 || count === null) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
|
+
" ",
|
|
61
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "chip-count", className: chipCountVariants({ size }), children: count })
|
|
62
|
+
] });
|
|
63
|
+
}
|
|
35
64
|
function Chip(props) {
|
|
36
65
|
const labels = useMediCoreLabels();
|
|
37
66
|
if (props.onRemove) {
|
|
38
|
-
const { size: size2, leadingIcon: leadingIcon2, className: className2, children: children2, onRemove: onRemove2, ...rest2 } = props;
|
|
67
|
+
const { size: size2, leadingIcon: leadingIcon2, count: count2, className: className2, children: children2, onRemove: onRemove2, ...rest2 } = props;
|
|
39
68
|
return /* @__PURE__ */ jsxs(
|
|
40
69
|
Button,
|
|
41
70
|
{
|
|
@@ -50,12 +79,13 @@ function Chip(props) {
|
|
|
50
79
|
" "
|
|
51
80
|
] }),
|
|
52
81
|
children2,
|
|
82
|
+
/* @__PURE__ */ jsx(ChipCount, { size: size2, count: count2 }),
|
|
53
83
|
/* @__PURE__ */ jsx(XCloseIcon, {})
|
|
54
84
|
]
|
|
55
85
|
}
|
|
56
86
|
);
|
|
57
87
|
}
|
|
58
|
-
const { size, leadingIcon, className, children, onRemove, ...rest } = props;
|
|
88
|
+
const { size, leadingIcon, count, className, children, onRemove, ...rest } = props;
|
|
59
89
|
return /* @__PURE__ */ jsxs(
|
|
60
90
|
Toggle,
|
|
61
91
|
{
|
|
@@ -65,7 +95,8 @@ function Chip(props) {
|
|
|
65
95
|
children: [
|
|
66
96
|
/* @__PURE__ */ jsx(CheckIcon, { className: "hidden group-data-pressed/chip:block" }),
|
|
67
97
|
leadingIcon && /* @__PURE__ */ jsx("span", { className: "contents group-data-pressed/chip:hidden", children: leadingIcon }),
|
|
68
|
-
children
|
|
98
|
+
children,
|
|
99
|
+
/* @__PURE__ */ jsx(ChipCount, { size, count })
|
|
69
100
|
]
|
|
70
101
|
}
|
|
71
102
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chip.js","sources":["../../../src/components/chip/chip.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { Toggle } from \"@base-ui/react/toggle\";\nimport { ToggleGroup } from \"@base-ui/react/toggle-group\";\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\n\nimport { CheckIcon, XCloseIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport const chipVariants = cva(\n [\n \"group/chip inline-flex shrink-0 cursor-pointer select-none items-center justify-center whitespace-nowrap\",\n
|
|
1
|
+
{"version":3,"file":"chip.js","sources":["../../../src/components/chip/chip.tsx"],"sourcesContent":["import { Button as BaseButton } from \"@base-ui/react/button\";\nimport { Toggle } from \"@base-ui/react/toggle\";\nimport { ToggleGroup } from \"@base-ui/react/toggle-group\";\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\n\nimport { CheckIcon, XCloseIcon } from \"../../icons\";\nimport { cn } from \"../../lib/cn\";\nimport { useMediCoreLabels } from \"../../provider\";\n\nexport const chipVariants = cva(\n [\n \"group/chip inline-flex shrink-0 cursor-pointer select-none items-center justify-center whitespace-nowrap\",\n \"rounded-full border border-border bg-background text-text-primary shadow-xs transition-[color,background-color,border-color,box-shadow]\",\n // 32 (24 small) drawn, 44 hit area.\n \"focus-ring touch-target\",\n \"[&_svg]:shrink-0\",\n \"not-data-disabled:not-data-pressed:hover:border-border-strong not-data-disabled:not-data-pressed:hover:bg-surface\",\n // Selected is not just a colour: check in front, brand border, Medium label.\n \"not-data-disabled:data-pressed:border-brand not-data-disabled:data-pressed:bg-brand-subtle not-data-disabled:data-pressed:text-text-brand not-data-disabled:data-pressed:shadow-none\",\n \"data-disabled:cursor-not-allowed data-disabled:border-border-subtle data-disabled:bg-surface data-disabled:text-text-disabled data-disabled:shadow-none\",\n ],\n {\n variants: {\n size: {\n default:\n \"h-8 gap-1.5 px-3 text-label-default data-pressed:text-label-strong [&_svg]:size-icon-sm\",\n small: \"h-6 gap-1 px-2 text-label-small [&_svg]:size-icon-xs\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n },\n);\n\nconst chipCountVariants = cva(\n [\n \"inline-flex shrink-0 items-center justify-center rounded-full bg-surface-raised text-caption-default text-text-secondary tabular-nums transition-colors\",\n \"group-data-pressed/chip:bg-brand group-data-pressed/chip:text-text-inverse\",\n \"group-data-disabled/chip:bg-transparent group-data-disabled/chip:text-text-disabled\",\n ],\n {\n variants: {\n size: {\n default: \"-me-1 h-5 min-w-5 px-1.5\",\n small: \"-me-0.5 h-4 min-w-4 px-1\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n },\n);\n\nexport type ChipSize = \"default\" | \"small\";\n\ninterface ChipOwnProps {\n /**\n * `small` only for dense metadata, never as the main filter: at 24px it relies on\n * the invisible hit area to reach 44.\n */\n size?: ChipSize;\n /** A MediCore icon before the label. While selected the check takes its place. */\n leadingIcon?: ReactNode;\n /** Counter after the label (\"Abiertas 5\"): how many items the filter would show. */\n count?: ReactNode;\n className?: string;\n}\n\nexport interface ChipToggleProps\n extends Omit<Toggle.Props<string>, \"className\">,\n ChipOwnProps {\n onRemove?: undefined;\n}\n\nexport interface ChipRemovableProps\n extends Omit<BaseButton.Props, \"className\" | \"onClick\">,\n ChipOwnProps {\n /**\n * Turns the chip into a remove button for an applied filter: pressing anywhere on\n * it calls this. Its accessible name becomes \"Quitar <label>\" (`remove` label).\n * A removable chip is not a toggle.\n */\n onRemove: () => void;\n pressed?: undefined;\n defaultPressed?: undefined;\n onPressedChange?: undefined;\n}\n\nexport type ChipProps = ChipToggleProps | ChipRemovableProps;\n\nfunction ChipCount({\n size,\n count,\n}: {\n size: ChipSize | undefined;\n count: ReactNode;\n}): React.JSX.Element | null {\n if (count === undefined || count === null) {\n return null;\n }\n return (\n <>\n {\" \"}\n <span data-slot=\"chip-count\" className={chipCountVariants({ size })}>\n {count}\n </span>\n </>\n );\n}\n\n/**\n * Pressable filter (\"Dermatología\"). The filter row of lists and the admin.\n *\n * - **A Chip is not a Badge.** A Badge tells a state and can't be pressed\n * (\"Receta\", \"Cancelada\"); a Chip filters and is pressed. If it doesn't respond to\n * a tap, it's a Badge.\n * - It is a `<button aria-pressed>` (Base UI Toggle): `pressed` / `defaultPressed` /\n * `onPressedChange`, or `value` inside a `ChipGroup`.\n * - With `onRemove` it is a remove button for an applied filter instead.\n * - `count` adds a counter pill at the end, brand-coloured while selected.\n */\nexport function Chip(props: ChipProps): React.JSX.Element {\n const labels = useMediCoreLabels();\n\n if (props.onRemove) {\n const { size, leadingIcon, count, className, children, onRemove, ...rest } =\n props;\n\n return (\n <BaseButton\n data-slot=\"chip\"\n className={cn(chipVariants({ size }), className)}\n onClick={onRemove}\n {...rest}\n >\n {leadingIcon}\n <span className=\"sr-only\">{labels.remove} </span>\n {children}\n <ChipCount size={size} count={count} />\n <XCloseIcon />\n </BaseButton>\n );\n }\n\n const { size, leadingIcon, count, className, children, onRemove, ...rest } =\n props;\n\n return (\n <Toggle\n data-slot=\"chip\"\n className={cn(chipVariants({ size }), className)}\n {...rest}\n >\n <CheckIcon className=\"hidden group-data-pressed/chip:block\" />\n {leadingIcon && (\n <span className=\"contents group-data-pressed/chip:hidden\">\n {leadingIcon}\n </span>\n )}\n {children}\n <ChipCount size={size} count={count} />\n </Toggle>\n );\n}\n\nexport interface ChipGroupProps<Value extends string = string>\n extends Omit<ToggleGroup.Props<Value>, \"className\"> {\n /**\n * `wrap` (default) breaks into lines; `scroll` keeps one line that scrolls\n * sideways — the usual filter row on mobile.\n */\n overflow?: \"wrap\" | \"scroll\";\n className?: string;\n}\n\n/**\n * A row of filter chips with shared state: `value` is the array of pressed chips.\n * Multi-select by default (`multiple={false}` for one at a time — but an exclusive\n * choice between 2–3 options is a `SegmentedControl`). One tab stop; ←/→ move\n * between chips. Name it with `aria-label` (\"Filtrar por especialidad\").\n */\nexport function ChipGroup<Value extends string>({\n overflow = \"wrap\",\n multiple = true,\n className,\n ...props\n}: ChipGroupProps<Value>): React.JSX.Element {\n return (\n <ToggleGroup\n data-slot=\"chip-group\"\n multiple={multiple}\n className={cn(\n \"flex gap-2\",\n overflow === \"wrap\"\n ? \"flex-wrap\"\n : // Room for the hit area and the focus ring inside the scroll box.\n \"-m-1.5 overflow-x-auto p-1.5 [scrollbar-width:none]\",\n className,\n )}\n {...props}\n />\n );\n}\n"],"names":["size","leadingIcon","count","className","children","onRemove","rest","BaseButton"],"mappings":";;;;;;;;;AAUO,MAAM,eAAe;AAAA,EAC1B;AAAA,IACE;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,SACE;AAAA,QACF,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,IAEF,iBAAiB;AAAA,MACf,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ;AAEA,MAAM,oBAAoB;AAAA,EACxB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,IAEF,iBAAiB;AAAA,MACf,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ;AAuCA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AACF,GAG6B;AAC3B,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AACA,SACE,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA;AAAA,IACD,oBAAC,QAAA,EAAK,aAAU,cAAa,WAAW,kBAAkB,EAAE,KAAA,CAAM,GAC/D,UAAA,MAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAaO,SAAS,KAAK,OAAqC;AACxD,QAAM,SAAS,kBAAA;AAEf,MAAI,MAAM,UAAU;AAClB,UAAM,EAAE,MAAAA,OAAM,aAAAC,cAAa,OAAAC,QAAO,WAAAC,YAAW,UAAAC,WAAU,UAAAC,WAAU,GAAGC,UAClE;AAEF,WACE;AAAA,MAACC;AAAAA,MAAA;AAAA,QACC,aAAU;AAAA,QACV,WAAW,GAAG,aAAa,EAAE,MAAAP,MAAAA,CAAM,GAAGG,UAAS;AAAA,QAC/C,SAASE;AAAAA,QACR,GAAGC;AAAAA,QAEH,UAAA;AAAA,UAAAL;AAAAA,UACD,qBAAC,QAAA,EAAK,WAAU,WAAW,UAAA;AAAA,YAAA,OAAO;AAAA,YAAO;AAAA,UAAA,GAAC;AAAA,UACzCG;AAAAA,UACD,oBAAC,WAAA,EAAU,MAAMJ,OAAM,OAAOE,QAAO;AAAA,8BACpC,YAAA,CAAA,CAAW;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGlB;AAEA,QAAM,EAAE,MAAM,aAAa,OAAO,WAAW,UAAU,UAAU,GAAG,KAAA,IAClE;AAEF,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,aAAa,EAAE,KAAA,CAAM,GAAG,SAAS;AAAA,MAC9C,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA,oBAAC,WAAA,EAAU,WAAU,uCAAA,CAAuC;AAAA,QAC3D,eACC,oBAAC,QAAA,EAAK,WAAU,2CACb,UAAA,aACH;AAAA,QAED;AAAA,QACD,oBAAC,WAAA,EAAU,MAAY,MAAA,CAAc;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG3C;AAkBO,SAAS,UAAgC;AAAA,EAC9C,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AAAA,EACA,GAAG;AACL,GAA6C;AAC3C,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,aAAa,SACT;AAAA;AAAA,UAEA;AAAA;AAAA,QACJ;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;"}
|
|
@@ -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";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Border, radius, fill and text colour of the box. */
|
|
2
|
-
export declare const controlBox = "w-full min-w-0 rounded-md border border-border
|
|
2
|
+
export declare const controlBox = "w-full min-w-0 rounded-md border border-border bg-background text-text-primary shadow-xs ring-1 ring-transparent ring-inset transition-[border-color,box-shadow]";
|
|
3
3
|
/** One-line content: 12px side padding, 8px between icon and text, Label/Default (14). */
|
|
4
4
|
export declare const controlRow = "flex items-center gap-2 px-3 text-label-default";
|
|
5
5
|
/** control-md (40) and control-sm (36): the same tokens as Button, so they line up. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const controlBox = "w-full min-w-0 rounded-md border border-border
|
|
1
|
+
const controlBox = "w-full min-w-0 rounded-md border border-border bg-background text-text-primary shadow-xs ring-1 ring-transparent ring-inset transition-[border-color,box-shadow]";
|
|
2
2
|
const controlRow = "flex items-center gap-2 px-3 text-label-default";
|
|
3
3
|
const controlHeight = {
|
|
4
4
|
default: "h-control-md",
|
|
@@ -7,17 +7,17 @@ const controlHeight = {
|
|
|
7
7
|
const wrappedControlStates = [
|
|
8
8
|
"has-[[aria-invalid=true]]:border-error has-[[aria-invalid=true]]:ring-error",
|
|
9
9
|
"has-[:focus:not(button)]:border-border-focus has-[:focus:not(button)]:ring-border-focus",
|
|
10
|
-
"has-[:is(input,select):disabled]:cursor-not-allowed has-[:is(input,select):disabled]:border-border has-[:is(input,select):disabled]:bg-surface has-[:is(input,select):disabled]:text-text-disabled"
|
|
10
|
+
"has-[:is(input,select):disabled]:cursor-not-allowed has-[:is(input,select):disabled]:border-border-subtle has-[:is(input,select):disabled]:bg-surface has-[:is(input,select):disabled]:text-text-disabled has-[:is(input,select):disabled]:shadow-none"
|
|
11
11
|
].join(" ");
|
|
12
12
|
const selfControlStates = [
|
|
13
13
|
"outline-none focus:border-border-focus focus:ring-border-focus",
|
|
14
14
|
"aria-invalid:not-focus:border-error aria-invalid:not-focus:ring-error",
|
|
15
|
-
"disabled:cursor-not-allowed disabled:border-border disabled:bg-surface disabled:text-text-disabled"
|
|
15
|
+
"disabled:cursor-not-allowed disabled:border-border-subtle disabled:bg-surface disabled:text-text-disabled disabled:shadow-none"
|
|
16
16
|
].join(" ");
|
|
17
17
|
const triggerControlStates = [
|
|
18
18
|
"outline-none focus:border-border-focus focus:ring-border-focus data-popup-open:border-border-focus data-popup-open:ring-border-focus",
|
|
19
19
|
"aria-invalid:not-focus:not-data-popup-open:border-error aria-invalid:not-focus:not-data-popup-open:ring-error",
|
|
20
|
-
"data-disabled:cursor-not-allowed data-disabled:border-border data-disabled:bg-surface data-disabled:text-text-disabled"
|
|
20
|
+
"data-disabled:cursor-not-allowed data-disabled:border-border-subtle data-disabled:bg-surface data-disabled:text-text-disabled data-disabled:shadow-none"
|
|
21
21
|
].join(" ");
|
|
22
22
|
const controlPlaceholder = "placeholder:text-text-secondary disabled:placeholder:text-text-disabled";
|
|
23
23
|
const controlIcon = "flex shrink-0 text-text-secondary [&_svg]:size-icon-sm group-has-[:is(input,select):disabled]/control:text-text-disabled group-data-disabled/control:text-text-disabled";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-styles.js","sources":["../../../src/components/input/control-styles.ts"],"sourcesContent":["/*\n * The box shared by Input, Textarea, Select, NativeSelect and PhoneInput. Figma draws\n * them as the same object on purpose: same radius, padding, border and states, so a\n * field and a dropdown in the same row only differ by the chevron.\n *\n * Focus and Error are a 2px border (border-thick), not just a colour change. The\n * second pixel is an inset ring, so the content never moves when the state changes.\n * Focus wins over Error: the message under the field keeps saying what is wrong.\n */\n\n/** Border, radius, fill and text colour of the box. */\nexport const controlBox =\n \"w-full min-w-0 rounded-md border border-border
|
|
1
|
+
{"version":3,"file":"control-styles.js","sources":["../../../src/components/input/control-styles.ts"],"sourcesContent":["/*\n * The box shared by Input, Textarea, Select, NativeSelect and PhoneInput. Figma draws\n * them as the same object on purpose: same radius, padding, border and states, so a\n * field and a dropdown in the same row only differ by the chevron.\n *\n * Focus and Error are a 2px border (border-thick), not just a colour change. The\n * second pixel is an inset ring, so the content never moves when the state changes.\n * Focus wins over Error: the message under the field keeps saying what is wrong.\n */\n\n/** Border, radius, fill and text colour of the box. */\nexport const controlBox =\n \"w-full min-w-0 rounded-md border border-border bg-background text-text-primary shadow-xs ring-1 ring-transparent ring-inset transition-[border-color,box-shadow]\";\n\n/** One-line content: 12px side padding, 8px between icon and text, Label/Default (14). */\nexport const controlRow = \"flex items-center gap-2 px-3 text-label-default\";\n\n/** control-md (40) and control-sm (36): the same tokens as Button, so they line up. */\nexport const controlHeight = {\n default: \"h-control-md\",\n small: \"h-control-sm\",\n} as const;\n\nexport type ControlSize = keyof typeof controlHeight;\n\n/**\n * States for a box that WRAPS the real control (Input, NativeSelect, the phone\n * prefix). Read from the control inside: `aria-invalid` (set by `invalid` or by\n * Field), `:focus` and `:disabled` (of the input/select only: a native select's\n * placeholder `<option disabled>` must not grey the box out). Buttons inside the box\n * (reveal password, clear) keep their own focus ring and don't light the box up.\n */\nexport const wrappedControlStates = [\n \"has-[[aria-invalid=true]]:border-error has-[[aria-invalid=true]]:ring-error\",\n \"has-[:focus:not(button)]:border-border-focus has-[:focus:not(button)]:ring-border-focus\",\n \"has-[:is(input,select):disabled]:cursor-not-allowed has-[:is(input,select):disabled]:border-border-subtle has-[:is(input,select):disabled]:bg-surface has-[:is(input,select):disabled]:text-text-disabled has-[:is(input,select):disabled]:shadow-none\",\n].join(\" \");\n\n/** States for a box that IS the control (Textarea). */\nexport const selfControlStates = [\n \"outline-none focus:border-border-focus focus:ring-border-focus\",\n \"aria-invalid:not-focus:border-error aria-invalid:not-focus:ring-error\",\n \"disabled:cursor-not-allowed disabled:border-border-subtle disabled:bg-surface disabled:text-text-disabled disabled:shadow-none\",\n].join(\" \");\n\n/**\n * States for a button box (Select trigger). It keeps the focus border while its popup\n * is open, because focus is inside the list then.\n */\nexport const triggerControlStates = [\n \"outline-none focus:border-border-focus focus:ring-border-focus data-popup-open:border-border-focus data-popup-open:ring-border-focus\",\n \"aria-invalid:not-focus:not-data-popup-open:border-error aria-invalid:not-focus:not-data-popup-open:ring-error\",\n \"data-disabled:cursor-not-allowed data-disabled:border-border-subtle data-disabled:bg-surface data-disabled:text-text-disabled data-disabled:shadow-none\",\n].join(\" \");\n\n/** Placeholder: the same Value node in text-secondary, never a variant. */\nexport const controlPlaceholder =\n \"placeholder:text-text-secondary disabled:placeholder:text-text-disabled\";\n\n/**\n * Leading / trailing icon slot: 16px in text-secondary (5.07:1), disabled with the\n * box. The box carries `group/control`.\n */\nexport const controlIcon =\n \"flex shrink-0 text-text-secondary [&_svg]:size-icon-sm group-has-[:is(input,select):disabled]/control:text-text-disabled group-data-disabled/control:text-text-disabled\";\n\n/** Small icon button inside the box (reveal password, clear). 44px hit area. */\nexport const controlAction =\n \"focus-ring touch-target -me-1 inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-sm text-text-secondary transition-colors hover:text-text-primary disabled:cursor-not-allowed disabled:text-text-disabled [&_svg]:size-icon-sm\";\n"],"names":[],"mappings":"AAWO,MAAM,aACX;AAGK,MAAM,aAAa;AAGnB,MAAM,gBAAgB;AAAA,EAC3B,SAAS;AAAA,EACT,OAAO;AACT;AAWO,MAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAGH,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAMH,MAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAGH,MAAM,qBACX;AAMK,MAAM,cACX;AAGK,MAAM,gBACX;"}
|
|
@@ -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":"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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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|