@mitcsutt/kiln-ui 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/actions/Button/Button.module.js.map +1 -1
- package/dist/components/actions/ToggleChip/ToggleChip.d.ts +5 -0
- package/dist/components/actions/ToggleChip/ToggleChip.js +10 -6
- package/dist/components/actions/ToggleChip/ToggleChip.js.map +1 -1
- package/dist/components/actions/ToggleChip/chip.module.js +4 -3
- package/dist/components/actions/ToggleChip/chip.module.js.map +1 -1
- package/dist/components/display/AvatarGroup/AvatarGroup.d.ts +2 -2
- package/dist/components/display/AvatarGroup/AvatarGroup.js +1 -1
- package/dist/components/display/AvatarGroup/AvatarGroup.js.map +1 -1
- package/dist/components/display/Card/Card.d.ts +7 -4
- package/dist/components/display/Card/Card.js +8 -7
- package/dist/components/display/Card/Card.js.map +1 -1
- package/dist/components/display/Card/Card.module.js.map +1 -1
- package/dist/components/display/List/List.d.ts +18 -5
- package/dist/components/display/List/List.js +53 -43
- package/dist/components/display/List/List.js.map +1 -1
- package/dist/components/display/List/List.module.js.map +1 -1
- package/dist/components/display/List/index.d.ts +1 -1
- package/dist/components/display/Marquee/Marquee.js +1 -1
- package/dist/components/display/Marquee/Marquee.js.map +1 -1
- package/dist/components/display/Media/Media.d.ts +15 -0
- package/dist/components/display/Media/Media.js +43 -23
- package/dist/components/display/Media/Media.js.map +1 -1
- package/dist/components/display/Media/Media.module.js.map +1 -1
- package/dist/components/display/Media/index.d.ts +1 -1
- package/dist/components/display/Stat/Stat.module.js.map +1 -1
- package/dist/components/display/Table/Table.d.ts +23 -1
- package/dist/components/display/Table/Table.js +19 -14
- package/dist/components/display/Table/Table.js.map +1 -1
- package/dist/components/display/Table/Table.module.js.map +1 -1
- package/dist/components/display/Table/index.d.ts +1 -1
- package/dist/components/display/Tag/Tag.d.ts +3 -0
- package/dist/components/display/Tag/Tag.js +11 -10
- package/dist/components/display/Tag/Tag.js.map +1 -1
- package/dist/components/display/Tag/Tag.module.js.map +1 -1
- package/dist/components/display/Tag/index.d.ts +1 -1
- package/dist/components/feedback/Flash/Flash.d.ts +47 -0
- package/dist/components/feedback/Flash/Flash.js +36 -0
- package/dist/components/feedback/Flash/Flash.js.map +1 -0
- package/dist/components/feedback/Flash/Flash.module.js +10 -0
- package/dist/components/feedback/Flash/Flash.module.js.map +1 -0
- package/dist/components/feedback/Flash/index.d.ts +2 -0
- package/dist/components/feedback/RelativeTime/RelativeTime.d.ts +7 -0
- package/dist/components/feedback/RelativeTime/RelativeTime.js +11 -11
- package/dist/components/feedback/RelativeTime/RelativeTime.js.map +1 -1
- package/dist/components/feedback/StatusDot/StatusDot.d.ts +1 -1
- package/dist/components/feedback/StatusDot/StatusDot.js.map +1 -1
- package/dist/components/inputs/ChoiceCards/ChoiceCards.js +9 -9
- package/dist/components/inputs/Combobox/Combobox.js +5 -5
- package/dist/components/inputs/PasswordInput/PasswordInput.js +4 -4
- package/dist/components/layout/AppShell/AppShell.d.ts +4 -1
- package/dist/components/layout/AppShell/AppShell.js +58 -44
- package/dist/components/layout/AppShell/AppShell.js.map +1 -1
- package/dist/components/layout/AppShell/AppShell.module.js.map +1 -1
- package/dist/components/layout/Box/Box.d.ts +15 -3
- package/dist/components/layout/Box/Box.js +11 -10
- package/dist/components/layout/Box/Box.js.map +1 -1
- package/dist/components/layout/Box/Box.module.js.map +1 -1
- package/dist/components/layout/Divider/Divider.d.ts +7 -1
- package/dist/components/layout/Divider/Divider.js +18 -20
- package/dist/components/layout/Divider/Divider.js.map +1 -1
- package/dist/components/layout/ScrollArea/ScrollArea.d.ts +36 -0
- package/dist/components/layout/ScrollArea/ScrollArea.js +25 -0
- package/dist/components/layout/ScrollArea/ScrollArea.js.map +1 -0
- package/dist/components/layout/ScrollArea/ScrollArea.module.js +6 -0
- package/dist/components/layout/ScrollArea/ScrollArea.module.js.map +1 -0
- package/dist/components/layout/ScrollArea/index.d.ts +2 -0
- package/dist/components/layout/Section/Section.d.ts +18 -7
- package/dist/components/layout/Section/Section.js +10 -9
- package/dist/components/layout/Section/Section.js.map +1 -1
- package/dist/components/layout/Section/Section.module.js.map +1 -1
- package/dist/components/navigation/Accordion/Accordion.d.ts +6 -1
- package/dist/components/navigation/Accordion/Accordion.js +19 -11
- package/dist/components/navigation/Accordion/Accordion.js.map +1 -1
- package/dist/components/navigation/Accordion/Accordion.module.js +7 -6
- package/dist/components/navigation/Accordion/Accordion.module.js.map +1 -1
- package/dist/components/navigation/NavLinks/NavLinks.d.ts +4 -1
- package/dist/components/navigation/NavLinks/NavLinks.js.map +1 -1
- package/dist/components/navigation/NavLinks/NavLinks.module.js.map +1 -1
- package/dist/components/overlays/Tooltip/Tooltip.d.ts +23 -0
- package/dist/components/overlays/Tooltip/Tooltip.js +77 -31
- package/dist/components/overlays/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/typography/Numeral/Numeral.d.ts +26 -2
- package/dist/components/typography/Numeral/Numeral.js +25 -21
- package/dist/components/typography/Numeral/Numeral.js.map +1 -1
- package/dist/components/typography/Numeral/Numeral.module.js +4 -3
- package/dist/components/typography/Numeral/Numeral.module.js.map +1 -1
- package/dist/components/typography/Text/Text.d.ts +5 -2
- package/dist/components/typography/Text/Text.js.map +1 -1
- package/dist/components/typography/Text/Text.module.js.map +1 -1
- package/dist/icons/Icon.js +1 -0
- package/dist/icons/Icon.js.map +1 -1
- package/dist/icons/icons.d.ts +12 -0
- package/dist/icons/icons.js +63 -13
- package/dist/icons/icons.js.map +1 -1
- package/dist/index.d.ts +19 -24
- package/dist/index.js +114 -112
- package/dist/styles.css +1 -1
- package/dist/themes/fiesta.css +1 -1
- package/dist/utils/tokens.d.ts +2 -0
- package/dist/utils/tokens.js.map +1 -1
- package/package.json +2 -2
- package/skills/custom-theme/SKILL.md +1 -1
- package/skills/design-rules/SKILL.md +8 -4
- package/skills/layout-composition/SKILL.md +2 -0
- package/skills/layout-composition/references/app-shell.md +2 -2
- package/skills/layout-composition/references/box.md +21 -1
- package/skills/layout-composition/references/divider.md +30 -1
- package/skills/layout-composition/references/scroll-area.md +68 -0
- package/skills/layout-composition/references/section.md +24 -2
- package/skills/setup-and-theming/SKILL.md +3 -3
- package/skills/setup-and-theming/references/fiesta.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Marquee.js","names":["styles","Marquee_module_default","SPEED","FALLBACK_PER_ITEM","PauseIcon","
|
|
1
|
+
{"version":3,"file":"Marquee.js","names":["styles","Marquee_module_default","SPEED","FALLBACK_PER_ITEM","PauseIcon","PlayIcon","Marquee","forwardRef","rest","list","items","Children","children","item","paused","setPaused","useState","duration","setDuration","groupRef","useRef","group","measure","width","speed","observer","inertRef","useCallback","seconds","renderSeparator","separator","renderGroup","isValidElement","i","label","cx","className","surface","direction","pauseOnHover","mergeStyles","space","gap","style","pauseControl","p","jsx"],"sources":["../../../../src/components/display/Marquee/Marquee.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { createIcon } from '#icons'\nimport { cx } from '#utils/cx'\nimport { mergeStyles } from '#utils/responsive'\nimport { space, type Space } from '#utils/tokens'\nimport styles from './Marquee.module.css'\n\nexport type MarqueeSpeed = 'slow' | 'normal' | 'fast'\nexport type MarqueeDirection = 'left' | 'right'\nexport type MarqueeSurface = 'plain' | 'inverse' | 'accent'\n\nexport interface MarqueeProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n /**\n * The things that scroll. Pass an array (or several children) — each entry becomes\n * one item with a separator after it, so the loop is seamless.\n */\n items?: ReactNode[]\n /** Alternative to `items`: each direct child is one item. */\n children?: ReactNode\n /** Accessible name for the region, e.g. \"Deploy status\". Required: a ticker needs a name. */\n label: string\n /** Travel speed. Measured in px/s once mounted, so long and short tickers feel the same. */\n speed?: MarqueeSpeed\n /** Which way the content travels. Default `left`. */\n direction?: MarqueeDirection\n /** Pause while hovered or while anything inside has focus. Default `true`. */\n pauseOnHover?: boolean\n /**\n * Show a pause/play button at the end of the band (WCAG 2.2.2: moving content needs a\n * way to stop it that works for keyboard and touch). Default `true`.\n */\n pauseControl?: boolean\n /** Space between an item and its separator, as a step on the space scale. Default 5. */\n gap?: Space\n /**\n * Replaces the separator drawn between items. The default is a slanted hairline\n * themed through `--marquee-separator-*`. Pass `null` for none.\n */\n separator?: ReactNode\n /** Band colour. `plain` sits on the page between hairlines; `inverse` and `accent` fill. */\n surface?: MarqueeSurface\n}\n\n/* Pixels per second. Tuned so text stays readable at a glance. */\nconst SPEED: Record<MarqueeSpeed, number> = { slow: 28, normal: 48, fast: 84 }\n/* Seconds per item, used before the track has been measured (and on the server). */\nconst FALLBACK_PER_ITEM: Record<MarqueeSpeed, number> = { slow: 7, normal: 4.5, fast: 2.5 }\n\nconst PauseIcon = createIcon('PauseIcon', <path d=\"M7.5 5.5v9M12.5 5.5v9\" />)\nconst PlayIcon = createIcon('PlayIcon', <path d=\"M7 5.25v9.5L14.75 10z\" />)\n\n/**\n * A horizontal ticker for live updates, with a pause control, that stops for reduced motion.\n *\n * @remarks\n * `Marquee` scrolls a row of items in a loop, like live departures along a station board. The\n * content is rendered twice for a seamless loop, but the copy is `aria-hidden` and inert, so\n * screen readers and the tab order meet it once.\n *\n * @privateRemarks\n * A horizontal ticker: live status, a features strip, a top-accounts summary.\n *\n * The content is rendered twice for a seamless loop; the copy is `aria-hidden` and\n * `inert`, so screen readers and the tab order see it once. Under\n * `prefers-reduced-motion` the band stops and becomes a scrollable row.\n *\n * <Marquee label=\"Deploy status\" items={[<>Atlas 2.4 deploying</>, …]} surface=\"inverse\" />\n */\nexport const Marquee = forwardRef<HTMLDivElement, MarqueeProps>(function Marquee(\n {\n items,\n children,\n label,\n speed = 'normal',\n direction = 'left',\n pauseOnHover = true,\n pauseControl = true,\n gap = 5,\n separator,\n surface = 'plain',\n className,\n style,\n ...rest\n },\n ref,\n) {\n const list = (items ?? Children.toArray(children)).filter(\n (item) => item !== null && item !== undefined && item !== false,\n )\n const [paused, setPaused] = useState(false)\n const [duration, setDuration] = useState<number | null>(null)\n const groupRef = useRef<HTMLUListElement>(null)\n\n // Measure one copy of the content and derive the loop duration from a constant speed.\n useEffect(() => {\n const group = groupRef.current\n if (!group || typeof ResizeObserver === 'undefined') return\n const measure = () => {\n const width = group.getBoundingClientRect().width\n if (width > 0) setDuration(width / SPEED[speed])\n }\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(group)\n return () => {\n observer.disconnect()\n }\n }, [speed])\n\n // `inert` is only a known prop in React 19, so set the attribute directly (works in 18 too).\n const inertRef = useCallback((node: HTMLUListElement | null) => {\n node?.setAttribute('inert', '')\n }, [])\n\n const seconds = duration ?? Math.max(list.length, 1) * FALLBACK_PER_ITEM[speed]\n\n const renderSeparator = () =>\n separator === undefined ? (\n <span className={styles.rule} />\n ) : separator === null ? null : (\n <span className={styles.custom}>{separator}</span>\n )\n\n const renderGroup = () =>\n list.map((item, i) => (\n <li key={isValidElement(item) && item.key !== null ? item.key : i} className={styles.item}>\n <span className={styles.content}>{item}</span>\n <span className={styles.separator} aria-hidden=\"true\">\n {renderSeparator()}\n </span>\n </li>\n ))\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={label}\n className={cx(styles.marquee, className)}\n data-surface={surface}\n data-direction={direction}\n data-speed={speed}\n data-pause-on-hover={pauseOnHover || undefined}\n data-paused={paused || undefined}\n style={mergeStyles(\n {\n '--marquee-gap': space(gap),\n '--marquee-duration': `${seconds.toFixed(2)}s`,\n } as CSSProperties,\n style,\n )}\n {...rest}\n >\n <div className={styles.viewport}>\n <div className={styles.track}>\n <ul ref={groupRef} role=\"list\" className={styles.group}>\n {renderGroup()}\n </ul>\n <ul\n ref={inertRef}\n role=\"list\"\n className={cx(styles.group, styles.copy)}\n aria-hidden=\"true\"\n >\n {renderGroup()}\n </ul>\n </div>\n </div>\n {pauseControl ? (\n <button\n type=\"button\"\n className={styles.control}\n aria-pressed={paused}\n aria-label={`Pause ${label}`}\n onClick={() => {\n setPaused((p) => !p)\n }}\n >\n {paused ? <PlayIcon /> : <PauseIcon />}\n </button>\n ) : null}\n </div>\n )\n})\n"],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import { type HTMLAttributes, type ImgHTMLAttributes, type ReactNode } from 'rea
|
|
|
2
2
|
export type MediaRatio = '1/1' | '4/3' | '3/2' | '16/9' | '21/9' | '3/4' | '2/3' | 'auto';
|
|
3
3
|
export type MediaFit = 'cover' | 'contain';
|
|
4
4
|
export type MediaRadius = 'none' | 'media' | 'surface';
|
|
5
|
+
/** Fixed heights for inline thumbnails: about 16, 20, 24, 32 and 96px. */
|
|
6
|
+
export type MediaSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
5
7
|
export interface MediaProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
|
|
6
8
|
src: string;
|
|
7
9
|
/** Required. Describe the image; pass `""` only when it's purely decorative. */
|
|
@@ -12,6 +14,19 @@ export interface MediaProps extends Omit<HTMLAttributes<HTMLElement>, 'children'
|
|
|
12
14
|
fit?: MediaFit;
|
|
13
15
|
/** Corner role. Default `media`. */
|
|
14
16
|
radius?: MediaRadius;
|
|
17
|
+
/**
|
|
18
|
+
* A fixed height, for a small image that sits inline beside text (a flag by a name, a logo
|
|
19
|
+
* in a row). The width follows `ratio`, or the image's own ratio when `ratio` is `auto`.
|
|
20
|
+
* With a fixed `ratio`, the image also gets `width` and `height` attributes, so its box is
|
|
21
|
+
* known before it loads; with `auto`, pass them in `imgProps`.
|
|
22
|
+
* Default: none, so the frame fills its container.
|
|
23
|
+
*/
|
|
24
|
+
size?: MediaSize;
|
|
25
|
+
/**
|
|
26
|
+
* Out of play: the image fades toward grey but stays recognisable, like a knocked-out
|
|
27
|
+
* team's flag or a past event's photo. Pair it with a muted `List.Item` or `Table.Row`.
|
|
28
|
+
*/
|
|
29
|
+
dimmed?: boolean;
|
|
15
30
|
/** Renders a `<figure>` with this as its `<figcaption>`. */
|
|
16
31
|
caption?: ReactNode;
|
|
17
32
|
/** Shown inside the frame if the image fails to load. Default: an empty sunken frame. */
|
|
@@ -3,45 +3,65 @@ import t from "./Media.module.js";
|
|
|
3
3
|
import { forwardRef as n, useEffect as r, useRef as i, useState as a } from "react";
|
|
4
4
|
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/display/Media/Media.tsx
|
|
6
|
-
var c =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
var c = {
|
|
7
|
+
xs: 16,
|
|
8
|
+
sm: 20,
|
|
9
|
+
md: 24,
|
|
10
|
+
lg: 32,
|
|
11
|
+
xl: 96
|
|
12
|
+
};
|
|
13
|
+
function l(e, t) {
|
|
14
|
+
if (e === void 0 || t === "auto") return {};
|
|
15
|
+
let [n, r] = t.split("/").map(Number), i = c[e];
|
|
16
|
+
return {
|
|
17
|
+
width: Math.round(i * n / r),
|
|
18
|
+
height: i
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
var u = n(function({ src: n, alt: c, ratio: u = "auto", fit: d = "cover", radius: f = "media", size: p, dimmed: m = !1, caption: h, fallback: g, loading: _ = "lazy", imgProps: v, className: y, ...b }, x) {
|
|
22
|
+
let [S, C] = a(!1), w = i(null);
|
|
23
|
+
r(() => {
|
|
24
|
+
let e = w.current;
|
|
25
|
+
C(!!(e?.complete && e.naturalWidth === 0 && typeof e.currentSrc == "string" && e.currentSrc !== ""));
|
|
26
|
+
}, [n]);
|
|
27
|
+
let T = h === void 0 ? p ? "span" : "div" : "figure", E = p ? "span" : "div";
|
|
28
|
+
return /* @__PURE__ */ s(T, {
|
|
29
|
+
ref: x,
|
|
30
|
+
className: e(t.media, y),
|
|
31
|
+
"data-ratio": u,
|
|
32
|
+
"data-fit": d,
|
|
33
|
+
"data-radius": f,
|
|
34
|
+
"data-size": p,
|
|
35
|
+
"data-dimmed": m || void 0,
|
|
36
|
+
"data-failed": S || void 0,
|
|
37
|
+
...b,
|
|
38
|
+
children: [/* @__PURE__ */ o(E, {
|
|
20
39
|
className: t.frame,
|
|
21
|
-
children:
|
|
40
|
+
children: S ? /* @__PURE__ */ o(E, {
|
|
22
41
|
className: t.fallback,
|
|
23
42
|
role: c ? "img" : void 0,
|
|
24
43
|
"aria-label": c || void 0,
|
|
25
|
-
children:
|
|
44
|
+
children: g
|
|
26
45
|
}) : /* @__PURE__ */ o("img", {
|
|
27
|
-
ref:
|
|
46
|
+
ref: w,
|
|
28
47
|
className: t.image,
|
|
29
48
|
src: n,
|
|
30
49
|
alt: c,
|
|
31
|
-
loading:
|
|
50
|
+
loading: _,
|
|
32
51
|
decoding: "async",
|
|
33
52
|
onError: () => {
|
|
34
|
-
|
|
53
|
+
C(!0);
|
|
35
54
|
},
|
|
36
|
-
...
|
|
55
|
+
...l(p, u),
|
|
56
|
+
...v
|
|
37
57
|
})
|
|
38
|
-
}),
|
|
58
|
+
}), h === void 0 ? null : /* @__PURE__ */ o("figcaption", {
|
|
39
59
|
className: t.caption,
|
|
40
|
-
children:
|
|
60
|
+
children: h
|
|
41
61
|
})]
|
|
42
62
|
});
|
|
43
63
|
});
|
|
44
64
|
//#endregion
|
|
45
|
-
export {
|
|
65
|
+
export { u as Media };
|
|
46
66
|
|
|
47
67
|
//# sourceMappingURL=Media.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Media.js","names":["styles","Media_module_default","Media","forwardRef","rest","failed","setFailed","useState","imgRef","useRef","
|
|
1
|
+
{"version":3,"file":"Media.js","names":["styles","Media_module_default","SIZE_PX","sizeHints","size","ratio","w","h","height","Media","forwardRef","rest","failed","setFailed","useState","imgRef","useRef","img","src","Root","caption","Box","cx","className","fit","radius","dimmed","alt","fallback","imgProps"],"sources":["../../../../src/components/display/Media/Media.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type ImgHTMLAttributes,\n type ReactNode,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport styles from './Media.module.css'\n\nexport type MediaRatio = '1/1' | '4/3' | '3/2' | '16/9' | '21/9' | '3/4' | '2/3' | 'auto'\nexport type MediaFit = 'cover' | 'contain'\nexport type MediaRadius = 'none' | 'media' | 'surface'\n/** Fixed heights for inline thumbnails: about 16, 20, 24, 32 and 96px. */\nexport type MediaSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\n/** Nominal heights in px, for the image's width/height hints; the CSS sets the real size. */\nconst SIZE_PX: Record<MediaSize, number> = { xs: 16, sm: 20, md: 24, lg: 32, xl: 96 }\n\n/**\n * The width and height attributes a sized image carries, so the browser knows its box before it\n * loads (and audits don't flag it). Only known with a fixed ratio: an `auto` frame takes the\n * image's own ratio, so pass `imgProps.width`/`height` there.\n */\nfunction sizeHints(size: MediaSize | undefined, ratio: MediaRatio) {\n if (size === undefined || ratio === 'auto') return {}\n const [w, h] = ratio.split('/').map(Number) as [number, number]\n const height = SIZE_PX[size]\n return { width: Math.round((height * w) / h), height }\n}\n\nexport interface MediaProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {\n src: string\n /** Required. Describe the image; pass `\"\"` only when it's purely decorative. */\n alt: string\n /** Frame ratio. The image is fitted inside. Default `auto` (the image's own ratio). */\n ratio?: MediaRatio\n /** Default `cover`. Use `contain` for logos, diagrams and screenshots that must not crop. */\n fit?: MediaFit\n /** Corner role. Default `media`. */\n radius?: MediaRadius\n /**\n * A fixed height, for a small image that sits inline beside text (a flag by a name, a logo\n * in a row). The width follows `ratio`, or the image's own ratio when `ratio` is `auto`.\n * With a fixed `ratio`, the image also gets `width` and `height` attributes, so its box is\n * known before it loads; with `auto`, pass them in `imgProps`.\n * Default: none, so the frame fills its container.\n */\n size?: MediaSize\n /**\n * Out of play: the image fades toward grey but stays recognisable, like a knocked-out\n * team's flag or a past event's photo. Pair it with a muted `List.Item` or `Table.Row`.\n */\n dimmed?: boolean\n /** Renders a `<figure>` with this as its `<figcaption>`. */\n caption?: ReactNode\n /** Shown inside the frame if the image fails to load. Default: an empty sunken frame. */\n fallback?: ReactNode\n /** Default `lazy`. Use `eager` for the image above the fold. */\n loading?: 'lazy' | 'eager'\n /** Extra attributes for the `<img>` (srcSet, sizes, width/height, fetchPriority). */\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt' | 'loading'>\n}\n\n/**\n * A framed image with a fixed ratio, lazy loading, a caption, and a calm failure state.\n *\n * @remarks\n * `Media` frames an image: it holds a ratio while loading, fits the image, loads lazily, and when\n * the image fails it keeps its shape and shows a quiet fallback instead of a broken-image icon.\n *\n * @privateRemarks\n * A framed image: fixed ratio, fitted, lazy by default, with a caption and a graceful\n * failure state. Screenshots, company logos, scanned documents.\n *\n * <Media src=\"/images/dashboard.png\" alt=\"Revenue dashboard, September\" ratio=\"16/9\" caption=\"Dashboard, 2026\" />\n */\nexport const Media = forwardRef<HTMLElement, MediaProps>(function Media(\n {\n src,\n alt,\n ratio = 'auto',\n fit = 'cover',\n radius = 'media',\n size,\n dimmed = false,\n caption,\n fallback,\n loading = 'lazy',\n imgProps,\n className,\n ...rest\n },\n ref,\n) {\n const [failed, setFailed] = useState(false)\n const imgRef = useRef<HTMLImageElement>(null)\n\n // Reset when the source changes; also catch an error that fired before hydration.\n useEffect(() => {\n const img = imgRef.current\n setFailed(\n Boolean(\n img?.complete &&\n img.naturalWidth === 0 &&\n typeof img.currentSrc === 'string' &&\n img.currentSrc !== '',\n ),\n )\n }, [src])\n\n // A sized thumbnail is phrasing content (spans), so it can sit in a button or a line of text.\n const Root = caption !== undefined ? 'figure' : size ? 'span' : 'div'\n const Box = size ? 'span' : 'div'\n return (\n <Root\n // @ts-expect-error — polymorphic ref across figure/div is safe here\n ref={ref}\n className={cx(styles.media, className)}\n data-ratio={ratio}\n data-fit={fit}\n data-radius={radius}\n data-size={size}\n data-dimmed={dimmed || undefined}\n data-failed={failed || undefined}\n {...rest}\n >\n <Box className={styles.frame}>\n {failed ? (\n <Box\n className={styles.fallback}\n role={alt ? 'img' : undefined}\n aria-label={alt || undefined}\n >\n {fallback}\n </Box>\n ) : (\n <img\n ref={imgRef}\n className={styles.image}\n src={src}\n alt={alt}\n loading={loading}\n decoding=\"async\"\n onError={() => {\n setFailed(true)\n }}\n {...sizeHints(size, ratio)}\n {...imgProps}\n />\n )}\n </Box>\n {caption !== undefined ? <figcaption className={styles.caption}>{caption}</figcaption> : null}\n </Root>\n )\n})\n"],"mappings":";;;;;AAmBA,IAAME,IAAqC;CAAE,IAAI;CAAI,IAAI;CAAI,IAAI;CAAI,IAAI;CAAI,IAAI;AAAG;AAOpF,SAASC,EAAU,GAA6B,GAAmB;CACjE,IAAIC,MAAS,KAAA,KAAaC,MAAU,QAAQ,OAAO,CAAC;CACpD,IAAM,CAACC,GAAGC,KAAKF,EAAM,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM,GACpCG,IAASN,EAAQE;CACvB,OAAO;EAAE,OAAO,KAAK,MAAOI,IAASF,IAAKC,CAAC;EAAG;CAAO;AACvD;AAgDA,IAAaE,IAAQC,EAAoC,SACvD,EACE,QACA,QACA,WAAQ,QACR,SAAM,SACN,YAAS,SACT,SACA,YAAS,IACT,YACA,aACA,aAAU,QACV,aACA,cACA,GAAGC,KAEL,GACA;CACA,IAAM,CAACC,GAAQC,KAAaC,EAAS,EAAK,GACpCC,IAASC,EAAyB,IAAI;CAG5C,QAAgB;EACd,IAAMC,IAAMF,EAAO;EACnB,EACE,GACEE,GAAK,YACLA,EAAI,iBAAiB,KACrB,OAAOA,EAAI,cAAe,YAC1BA,EAAI,eAAe,GAEvB;CACF,GAAG,CAACC,CAAG,CAAC;CAGR,IAAMC,IAAOC,MAAY,KAAA,IAAuBhB,IAAO,SAAS,QAA3B,UAC/BiB,IAAMjB,IAAO,SAAS;CAC5B,OACE,kBAACe,GAAD;EAEO;EACL,WAAWG,EAAGtB,EAAO,OAAOuB,CAAS;EACrC,cAAYlB;EACZ,YAAUmB;EACV,eAAaC;EACb,aAAWrB;EACX,eAAasB,KAAU,KAAA;EACvB,eAAad,KAAU,KAAA;EACvB,GAAID;EAVN,UAAA,CAYE,kBAACU,GAAD;GAAK,WAAWrB,EAAO;GACpB,UAAAY,IACC,kBAACS,GAAD;IACE,WAAWrB,EAAO;IAClB,MAAM2B,IAAM,QAAQ,KAAA;IACpB,cAAYA,KAAO,KAAA;IAElB,UAAAC;GACE,CAAA,IAEL,kBAAC,OAAD;IACE,KAAKb;IACL,WAAWf,EAAO;IACb;IACA;IACI;IACT,UAAS;IACT,eAAe;KACb,EAAU,EAAI;IAChB;IACA,GAAIG,EAAUC,GAAMC,CAAK;IACzB,GAAIwB;GACL,CAAA;EAEA,CAAA,GACJT,MAAY,KAAA,IAA4E,OAAhE,kBAAC,cAAD;GAAY,WAAWpB,EAAO;GAAU,UAAAoB;EAAoB,CAAA,CACjF;;AAEV,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Media.module.js","names":["media","frame","image","fallback","caption","Media_module_default"],"sources":["../../../../src/components/display/Media/Media.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --media-bg default var(--color-surface-sunken) — the frame while loading / on failure\n */\n.media {\n --_radius: var(--radius-media);\n\n display: flex;\n flex-direction: column;\n gap: var(--space-2);\n min-inline-size: 0;\n margin: 0;\n}\n.media[data-radius='none'] {\n --_radius: 0;\n}\n.media[data-radius='surface'] {\n --_radius: var(--radius-surface);\n}\n\n.frame {\n position: relative;\n overflow: hidden;\n border-radius: var(--_radius);\n background: var(--media-bg, var(--color-surface-sunken));\n}\n/* A hairline inside the frame so light images don't dissolve into light canvases. */\n.frame::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-ink) 8%, transparent);\n pointer-events: none;\n}\n\n.media[data-ratio='1/1'] .frame {\n aspect-ratio: 1;\n}\n.media[data-ratio='4/3'] .frame {\n aspect-ratio: 4 / 3;\n}\n.media[data-ratio='3/2'] .frame {\n aspect-ratio: 3 / 2;\n}\n.media[data-ratio='16/9'] .frame {\n aspect-ratio: 16 / 9;\n}\n.media[data-ratio='21/9'] .frame {\n aspect-ratio: 21 / 9;\n}\n.media[data-ratio='3/4'] .frame {\n aspect-ratio: 3 / 4;\n}\n.media[data-ratio='2/3'] .frame {\n aspect-ratio: 2 / 3;\n}\n\n.image {\n display: block;\n inline-size: 100%;\n block-size: auto;\n}\n.media:not([data-ratio='auto']) .image {\n position: absolute;\n inset: 0;\n block-size: 100%;\n object-fit: cover;\n}\n.media[data-fit='contain'] .image {\n object-fit: contain;\n}\n\n.fallback {\n display: grid;\n place-items: center;\n inline-size: 100%;\n block-size: 100%;\n min-block-size: var(--space-9);\n padding: var(--space-4);\n color: var(--color-ink-subtle);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n text-align: center;\n}\n.media:not([data-ratio='auto']) .fallback {\n position: absolute;\n inset: 0;\n min-block-size: 0;\n}\n\n.caption {\n color: var(--color-ink-muted);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n line-height: var(--leading-snug);\n text-wrap: pretty;\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Media.module.js","names":["media","frame","image","fallback","caption","Media_module_default"],"sources":["../../../../src/components/display/Media/Media.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --media-bg default var(--color-surface-sunken) — the frame while loading / on failure\n * --media-dim-filter default saturate(0.3) contrast(0.7) — how a `dimmed` image recedes\n */\n.media {\n --_radius: var(--radius-media);\n\n display: flex;\n flex-direction: column;\n gap: var(--space-2);\n min-inline-size: 0;\n margin: 0;\n}\n.media[data-radius='none'] {\n --_radius: 0;\n}\n.media[data-radius='surface'] {\n --_radius: var(--radius-surface);\n}\n\n/* Sized: an inline thumbnail of fixed height, as wide as its ratio makes it. */\n.media[data-size] {\n --_size: var(--space-4);\n\n display: inline-flex;\n flex: none;\n align-items: flex-start;\n vertical-align: middle;\n}\n.media[data-size='sm'] {\n --_size: calc(var(--space-4) + var(--space-1));\n}\n.media[data-size='md'] {\n --_size: var(--space-5);\n}\n.media[data-size='lg'] {\n --_size: var(--space-6);\n}\n.media[data-size='xl'] {\n --_size: var(--space-9);\n}\n\n.frame {\n position: relative;\n overflow: hidden;\n border-radius: var(--_radius);\n background: var(--media-bg, var(--color-surface-sunken));\n}\n/* A hairline inside the frame so light images don't dissolve into light canvases. */\n.frame::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-ink) 8%, transparent);\n pointer-events: none;\n}\n\n.media[data-ratio='1/1'] .frame {\n aspect-ratio: 1;\n}\n.media[data-ratio='4/3'] .frame {\n aspect-ratio: 4 / 3;\n}\n.media[data-ratio='3/2'] .frame {\n aspect-ratio: 3 / 2;\n}\n.media[data-ratio='16/9'] .frame {\n aspect-ratio: 16 / 9;\n}\n.media[data-ratio='21/9'] .frame {\n aspect-ratio: 21 / 9;\n}\n.media[data-ratio='3/4'] .frame {\n aspect-ratio: 3 / 4;\n}\n.media[data-ratio='2/3'] .frame {\n aspect-ratio: 2 / 3;\n}\n\n.image {\n display: block;\n inline-size: 100%;\n block-size: auto;\n}\n.media[data-size] .frame {\n block-size: var(--_size);\n /* A theme's media radius would round a small thumbnail into a pill: cap it to its height. */\n border-radius: min(var(--_radius), calc(var(--_size) / 5));\n}\n.media[data-size] .image {\n inline-size: auto;\n block-size: 100%;\n}\n.media:not([data-ratio='auto']) .image {\n position: absolute;\n inset: 0;\n block-size: 100%;\n object-fit: cover;\n}\n.media[data-fit='contain'] .image {\n object-fit: contain;\n}\n\n.fallback {\n display: grid;\n place-items: center;\n inline-size: 100%;\n block-size: 100%;\n min-block-size: var(--space-9);\n padding: var(--space-4);\n color: var(--color-ink-subtle);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n text-align: center;\n}\n.media:not([data-ratio='auto']) .fallback {\n position: absolute;\n inset: 0;\n min-block-size: 0;\n}\n\n.media[data-size][data-ratio='auto'][data-failed] .frame {\n aspect-ratio: 1;\n}\n/* No room for words in a thumbnail: the fallback is an empty frame, or a glyph if one is given. */\n.media[data-size] .fallback {\n position: absolute;\n inset: 0;\n min-block-size: 0;\n padding: 0;\n font-size: calc(var(--_size) * 0.6);\n}\n\n/* Dimmed: faded toward a neutral grey, never the canvas's hue, so a flag stays recognisable\n on Fiesta's apricot or Riso's pink and still reads as out of play. */\n.media[data-dimmed] .image {\n filter: var(--media-dim-filter, saturate(0.3) contrast(0.7));\n}\n\n.caption {\n color: var(--color-ink-muted);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n line-height: var(--leading-snug);\n text-wrap: pretty;\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Media } from './Media.js';
|
|
2
|
-
export type { MediaProps, MediaRatio, MediaFit, MediaRadius } from './Media.js';
|
|
2
|
+
export type { MediaProps, MediaRatio, MediaFit, MediaRadius, MediaSize } from './Media.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Stat.module.js","names":["stat","label","figure","value","delta","hint","Stat_module_default"],"sources":["../../../../src/components/display/Stat/Stat.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --stat-value-font default var(--font-numeric)\n * --stat-value-weight default var(--heading-weight)\n * --stat-fit how many value-ems of width a figure is assumed to need, default 6.5\n * (Ledger 7.5: its receipt mono is wide).\n * The value never grows past its size step, and shrinks below it\n * when the Stat is narrower than --stat-fit × that size.\n *\n * Fitting: the Stat is an inline-size container, so a figure in a narrow grid cell scales\n * down (cqi) instead of running into its neighbour. Containment would make a Stat inside a\n * flex row (Inline) zero-wide, so its intrinsic width is stated instead: --stat-fit value-ems.\n * A figure longer than that may still wrap as a last resort, never overlap.\n *\n * The change is a <Delta> set at --text-sm on the value's baseline.\n *\n * The value is set in the theme's numeric face, tabular and lining, so a row of stats\n * (or the same stat updating) never jitters. Label above, change on the value's baseline.\n */\n.stat {\n --_value-size: var(--text-2xl);\n --_value-leading: 1;\n --_fit: var(--stat-fit, 6.5);\n\n container-type: inline-size;\n contain-intrinsic-inline-size: auto calc(var(--_value-size) * var(--_fit));\n\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n min-inline-size: 0;\n font-family: var(--font-text);\n color: var(--color-ink);\n}\n\n.stat[data-size='sm'] {\n --_value-size: var(--text-lg);\n}\n.stat[data-size='md'] {\n --_value-size: var(--text-2xl);\n}\n.stat[data-size='lg'] {\n --_value-size: var(--text-3xl);\n}\n.stat[data-size='hero'] {\n --_value-size: var(--text-display-md);\n --_value-leading: 1;\n\n gap: var(--space-2);\n}\n\n.stat[data-rule] {\n padding-block-start: var(--space-3);\n border-block-start: var(--border-width) solid var(--color-line-strong);\n}\n\n.label {\n color: var(--color-ink-muted);\n font-size: var(--text-sm);\n font-weight: var(--label-weight);\n letter-spacing: var(--label-tracking);\n text-transform: var(--label-transform);\n line-height: var(--leading-snug);\n}\n\n.figure {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--space-1) var(--space-3);\n min-inline-size: 0;\n}\n\n.value {\n font-family: var(--stat-value-font, var(--font-numeric));\n font-weight: var(--stat-value-weight, var(--heading-weight));\n font-stretch: var(--numeric-stretch);\n font-variant-numeric: tabular-nums lining-nums;\n font-size: min(var(--_value-size), calc(100cqi / var(--_fit)));\n line-height: var(--_value-leading);\n letter-spacing: -0.01em;\n max-inline-size: 100%;\n overflow-wrap: anywhere;\n}\n.stat .value > * {\n white-space: normal; /* last resort (see top): a Numeral wraps rather than overlaps */\n}\n.stat[data-tone='positive'] .value {\n color: var(--tone-positive-text);\n}\n.stat[data-tone='critical'] .value {\n color: var(--tone-critical-text);\n}\n.stat[data-tone='accent'] .value {\n color: var(--color-accent-text);\n}\n\n.stat[data-size='hero'] .value {\n letter-spacing: var(--display-tracking);\n}\n\n.delta {\n font-size: var(--text-sm);\n}\n\n.hint {\n color: var(--color-ink-subtle);\n font-size: var(--text-xs);\n line-height: var(--leading-snug);\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Stat.module.js","names":["stat","label","figure","value","delta","hint","Stat_module_default"],"sources":["../../../../src/components/display/Stat/Stat.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --stat-align `start` (default) or `end`: the label, value and hint line up on\n * that edge (List.Trailing sets `end`)\n * --stat-container-type default inline-size, which fits the figure to a narrow cell (see\n * below). `normal` makes the Stat only as wide as its content\n * (List.Trailing sets it: a row's figure shouldn't claim the title's room)\n * --stat-value-font default var(--font-numeric)\n * --stat-value-weight default var(--heading-weight)\n * --stat-fit how many value-ems of width a figure is assumed to need, default 6.5\n * (Ledger 7.5: its receipt mono is wide).\n * The value never grows past its size step, and shrinks below it\n * when the Stat is narrower than --stat-fit × that size.\n *\n * Fitting: the Stat is an inline-size container, so a figure in a narrow grid cell scales\n * down (cqi) instead of running into its neighbour. Containment would make a Stat inside a\n * flex row (Inline) zero-wide, so its intrinsic width is stated instead: --stat-fit value-ems.\n * A figure longer than that may still wrap as a last resort, never overlap.\n *\n * The change is a <Delta> set at --text-sm on the value's baseline.\n *\n * The value is set in the theme's numeric face, tabular and lining, so a row of stats\n * (or the same stat updating) never jitters. Label above, change on the value's baseline.\n */\n.stat {\n --_align: var(--stat-align, start);\n --_value-size: var(--text-2xl);\n --_value-leading: 1;\n --_fit: var(--stat-fit, 6.5);\n\n container-type: var(--stat-container-type, inline-size);\n contain-intrinsic-inline-size: auto calc(var(--_value-size) * var(--_fit));\n\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n min-inline-size: 0;\n font-family: var(--font-text);\n color: var(--color-ink);\n text-align: var(--_align);\n}\n\n.stat[data-size='sm'] {\n --_value-size: var(--text-lg);\n}\n.stat[data-size='md'] {\n --_value-size: var(--text-2xl);\n}\n.stat[data-size='lg'] {\n --_value-size: var(--text-3xl);\n}\n.stat[data-size='hero'] {\n --_value-size: var(--text-display-md);\n --_value-leading: 1;\n\n gap: var(--space-2);\n}\n\n.stat[data-rule] {\n padding-block-start: var(--space-3);\n border-block-start: var(--border-width) solid var(--color-line-strong);\n}\n\n.label {\n color: var(--color-ink-muted);\n font-size: var(--text-sm);\n font-weight: var(--label-weight);\n letter-spacing: var(--label-tracking);\n text-transform: var(--label-transform);\n line-height: var(--leading-snug);\n}\n\n.figure {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: var(--_align);\n gap: var(--space-1) var(--space-3);\n min-inline-size: 0;\n}\n\n.value {\n font-family: var(--stat-value-font, var(--font-numeric));\n font-weight: var(--stat-value-weight, var(--heading-weight));\n font-stretch: var(--numeric-stretch);\n font-variant-numeric: tabular-nums lining-nums;\n font-size: min(var(--_value-size), calc(100cqi / var(--_fit)));\n line-height: var(--_value-leading);\n letter-spacing: -0.01em;\n max-inline-size: 100%;\n overflow-wrap: anywhere;\n}\n.stat .value > * {\n white-space: normal; /* last resort (see top): a Numeral wraps rather than overlaps */\n}\n.stat[data-tone='positive'] .value {\n color: var(--tone-positive-text);\n}\n.stat[data-tone='critical'] .value {\n color: var(--tone-critical-text);\n}\n.stat[data-tone='accent'] .value {\n color: var(--color-accent-text);\n}\n\n.stat[data-size='hero'] .value {\n letter-spacing: var(--display-tracking);\n}\n\n.delta {\n font-size: var(--text-sm);\n}\n\n.hint {\n color: var(--color-ink-subtle);\n font-size: var(--text-xs);\n line-height: var(--leading-snug);\n}\n"],"mappings":""}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { type HTMLAttributes, type TableHTMLAttributes, type TdHTMLAttributes, type ThHTMLAttributes } from 'react';
|
|
2
|
+
import type { CategoryColor } from '../../../utils/tokens.js';
|
|
2
3
|
import { type VisibilityProps } from '../../../utils/visibility.js';
|
|
3
4
|
export type TableDensity = 'compact' | 'regular';
|
|
4
5
|
export type TableVariant = 'rules' | 'plain';
|
|
6
|
+
/** `surface` and `raised` set the table on one sheet, like `Card`'s `outline` and `raised`. */
|
|
7
|
+
export type TableSurface = 'none' | 'surface' | 'raised';
|
|
5
8
|
export type TableAlign = 'start' | 'center' | 'end';
|
|
9
|
+
/** Where body and footer cells sit in a row taller than their content. */
|
|
10
|
+
export type TableVerticalAlign = 'middle' | 'baseline' | 'top';
|
|
6
11
|
export type TableSort = 'asc' | 'desc' | 'none';
|
|
7
12
|
/** `fill` takes the spare width (the name column); `min` shrinks to its content. */
|
|
8
13
|
export type TableColumnWidth = 'fill' | 'min';
|
|
@@ -11,8 +16,21 @@ export interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
|
|
11
16
|
density?: TableDensity;
|
|
12
17
|
/** `rules` (default): a hairline under every row. `plain`: only the header rule. */
|
|
13
18
|
variant?: TableVariant;
|
|
19
|
+
/**
|
|
20
|
+
* Where cell content sits when a row's cells differ in height (a flag and a tag beside plain
|
|
21
|
+
* figures). `baseline` (the default) lines up the first line of text across the row;
|
|
22
|
+
* `middle` centres every cell, so figures sit level with a name that has a flag or a tag beside
|
|
23
|
+
* or under it; `top` pins each cell to the row's top. Header cells always sit on the header rule.
|
|
24
|
+
*/
|
|
25
|
+
valign?: TableVerticalAlign;
|
|
14
26
|
/** Alternate row fills. Off by default — rules usually read better. */
|
|
15
27
|
striped?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Set the table on a sheet so its rows read as rows, not text on the page: `surface` is the
|
|
30
|
+
* surface fill inside a hairline edge, `raised` stands on the theme's surface shadow. Rows run
|
|
31
|
+
* edge to edge, clipped to the sheet's corners, with a little more air. Default `none`.
|
|
32
|
+
*/
|
|
33
|
+
surface?: TableSurface;
|
|
16
34
|
/**
|
|
17
35
|
* Keep the header visible while rows scroll. The wrapper becomes the scroll container
|
|
18
36
|
* (capped by `--table-max-height`, default 70vh), since a horizontally scrolling wrapper
|
|
@@ -25,11 +43,15 @@ export interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
|
|
25
43
|
*/
|
|
26
44
|
label?: string;
|
|
27
45
|
}
|
|
28
|
-
export interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {
|
|
46
|
+
export interface TableRowProps extends Omit<HTMLAttributes<HTMLTableRowElement>, 'color'> {
|
|
29
47
|
/** `--color-highlight` fill — "you", your project, the circled line. */
|
|
30
48
|
highlighted?: boolean;
|
|
31
49
|
/** Hover fill for rows that respond to clicks (put the real link in a cell). */
|
|
32
50
|
interactive?: boolean;
|
|
51
|
+
/** Out of play (eliminated, archived, past): every ink drops to the muted step, which still reads. */
|
|
52
|
+
muted?: boolean;
|
|
53
|
+
/** A categorical colour drawn as a rail at the row's start: whose row it is, matching their `Tag`. */
|
|
54
|
+
color?: CategoryColor;
|
|
33
55
|
}
|
|
34
56
|
export interface TableHeaderCellProps extends Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align' | 'width'>, VisibilityProps {
|
|
35
57
|
align?: TableAlign;
|
|
@@ -5,22 +5,25 @@ import i from "./Table.module.js";
|
|
|
5
5
|
import { forwardRef as a } from "react";
|
|
6
6
|
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/display/Table/Table.tsx
|
|
8
|
-
var c = a(function({ density: t = "regular", variant: n = "rules",
|
|
8
|
+
var c = a(function({ density: t = "regular", variant: n = "rules", valign: r = "baseline", striped: a = !1, surface: s = "none", stickyHeader: c = !1, label: l, className: u, ...d }, f) {
|
|
9
9
|
return /* @__PURE__ */ o("div", {
|
|
10
10
|
className: i.scroll,
|
|
11
11
|
"data-kiln-component": "",
|
|
12
|
-
"data-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
12
|
+
"data-surface": s === "none" ? void 0 : s,
|
|
13
|
+
"data-sticky-header": c || void 0,
|
|
14
|
+
role: l ? "region" : void 0,
|
|
15
|
+
"aria-label": l,
|
|
16
|
+
tabIndex: l ? 0 : void 0,
|
|
16
17
|
children: /* @__PURE__ */ o("table", {
|
|
17
|
-
ref:
|
|
18
|
-
className: e(i.table,
|
|
18
|
+
ref: f,
|
|
19
|
+
className: e(i.table, u),
|
|
19
20
|
"data-density": t,
|
|
20
21
|
"data-variant": n,
|
|
21
|
-
"data-
|
|
22
|
-
"data-
|
|
23
|
-
|
|
22
|
+
"data-valign": r,
|
|
23
|
+
"data-striped": a || void 0,
|
|
24
|
+
"data-surface": s === "none" ? void 0 : s,
|
|
25
|
+
"data-sticky-header": c || void 0,
|
|
26
|
+
...d
|
|
24
27
|
})
|
|
25
28
|
});
|
|
26
29
|
}), l = a(function({ className: t, ...n }, r) {
|
|
@@ -41,13 +44,15 @@ var c = a(function({ density: t = "regular", variant: n = "rules", striped: r =
|
|
|
41
44
|
className: e(i.foot, t),
|
|
42
45
|
...n
|
|
43
46
|
});
|
|
44
|
-
}), f = a(function({ highlighted: t = !1, interactive: n = !1,
|
|
47
|
+
}), f = a(function({ highlighted: t = !1, interactive: n = !1, muted: r = !1, color: a, className: s, ...c }, l) {
|
|
45
48
|
return /* @__PURE__ */ o("tr", {
|
|
46
|
-
ref:
|
|
47
|
-
className: e(i.row,
|
|
49
|
+
ref: l,
|
|
50
|
+
className: e(i.row, s),
|
|
48
51
|
"data-highlighted": t || void 0,
|
|
49
52
|
"data-interactive": n || void 0,
|
|
50
|
-
|
|
53
|
+
"data-muted": r || void 0,
|
|
54
|
+
"data-color": a,
|
|
55
|
+
...c
|
|
51
56
|
});
|
|
52
57
|
}), p = {
|
|
53
58
|
asc: "ascending",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Table.js","names":["styles","Table_module_default","TableRoot","forwardRef","rest","stickyHeader","label","cx","className","density","variant","striped","TableHead","TableBody","TableFoot","TableRow","highlighted","interactive","ARIA_SORT","TableHeaderCell","sortable","sort","onSort","visibilityClass","align","numeric","width","children","jsx","ChevronUpIcon","ChevronDownIcon","TableCell","shared","rowHeader","thRest","TableCaption","Table"],"sources":["../../../../src/components/display/Table/Table.tsx"],"sourcesContent":["import {\n forwardRef,\n type HTMLAttributes,\n type TableHTMLAttributes,\n type TdHTMLAttributes,\n type ThHTMLAttributes,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport { ChevronDownIcon, ChevronUpIcon } from '#icons'\nimport styles from './Table.module.css'\n\nexport type TableDensity = 'compact' | 'regular'\nexport type TableVariant = 'rules' | 'plain'\nexport type TableAlign = 'start' | 'center' | 'end'\nexport type TableSort = 'asc' | 'desc' | 'none'\n/** `fill` takes the spare width (the name column); `min` shrinks to its content. */\nexport type TableColumnWidth = 'fill' | 'min'\n\nexport interface TableProps extends TableHTMLAttributes<HTMLTableElement> {\n /** Row height. Default `regular`. */\n density?: TableDensity\n /** `rules` (default): a hairline under every row. `plain`: only the header rule. */\n variant?: TableVariant\n /** Alternate row fills. Off by default — rules usually read better. */\n striped?: boolean\n /**\n * Keep the header visible while rows scroll. The wrapper becomes the scroll container\n * (capped by `--table-max-height`, default 70vh), since a horizontally scrolling wrapper\n * would otherwise trap a page-sticky header.\n */\n stickyHeader?: boolean\n /**\n * Name for the scroll container. When set, the wrapper becomes a focusable, labelled\n * region so keyboard users can scroll a wide table.\n */\n label?: string\n}\n\n/**\n * A semantic data table with numeric columns that line up, sortable headers and a footer for\n * totals.\n *\n * @remarks\n * `Table` is a real `<table>` with Kiln's styles: rules between rows, numeric columns in tabular\n * figures, and a wrapper that scrolls sideways on narrow screens instead of breaking the layout.\n *\n * ## Density, stripes and sticky headers\n *\n * `density=\"compact\"` for dense data, `striped` for alternating fills (rules usually read better),\n * and `stickyHeader` to keep the header in view while the rows scroll inside the table.\n *\n * @privateRemarks\n * A semantic, styled data table: project boards, ledgers, audit logs. The wrapper scrolls\n * horizontally on narrow screens; `ref` and native props go to the `<table>`.\n *\n * <Table><Table.Head><Table.Row><Table.HeaderCell>Project</Table.HeaderCell>…</Table.Row></Table.Head>…</Table>\n */\nconst TableRoot = forwardRef<HTMLTableElement, TableProps>(function Table(\n {\n density = 'regular',\n variant = 'rules',\n striped = false,\n stickyHeader = false,\n label,\n className,\n ...rest\n },\n ref,\n) {\n return (\n <div\n className={styles.scroll}\n data-kiln-component=\"\"\n data-sticky-header={stickyHeader || undefined}\n role={label ? 'region' : undefined}\n aria-label={label}\n tabIndex={label ? 0 : undefined}\n >\n <table\n ref={ref}\n className={cx(styles.table, className)}\n data-density={density}\n data-variant={variant}\n data-striped={striped || undefined}\n data-sticky-header={stickyHeader || undefined}\n {...rest}\n />\n </div>\n )\n})\n\nconst TableHead = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableHead({ className, ...rest }, ref) {\n return <thead ref={ref} className={cx(styles.head, className)} {...rest} />\n },\n)\n\nconst TableBody = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableBody({ className, ...rest }, ref) {\n return <tbody ref={ref} className={cx(styles.body, className)} {...rest} />\n },\n)\n\n/** Totals row(s). Set above a strong rule. */\nconst TableFoot = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableFoot({ className, ...rest }, ref) {\n return <tfoot ref={ref} className={cx(styles.foot, className)} {...rest} />\n },\n)\n\nexport interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {\n /** `--color-highlight` fill — \"you\", your project, the circled line. */\n highlighted?: boolean\n /** Hover fill for rows that respond to clicks (put the real link in a cell). */\n interactive?: boolean\n}\n\nconst TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(function TableRow(\n { highlighted = false, interactive = false, className, ...rest },\n ref,\n) {\n return (\n <tr\n ref={ref}\n className={cx(styles.row, className)}\n data-highlighted={highlighted || undefined}\n data-interactive={interactive || undefined}\n {...rest}\n />\n )\n})\n\nexport interface TableHeaderCellProps\n extends Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align' | 'width'>, VisibilityProps {\n align?: TableAlign\n /** Numeric column: right-aligned with tabular figures. */\n numeric?: boolean\n /** Column sizing: `fill` takes the spare width, `min` shrinks to fit. Default: automatic. */\n width?: TableColumnWidth\n /** Current sort of this column. With `onSort`, the label becomes a button and `aria-sort` is set. */\n sort?: TableSort\n /** Called when the sort button is pressed. Decide the next direction yourself. */\n onSort?: () => void\n}\n\nconst ARIA_SORT = { asc: 'ascending', desc: 'descending', none: 'none' } as const\n\nconst TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellProps>(\n function TableHeaderCell(\n {\n align,\n numeric = false,\n width,\n sort,\n onSort,\n hideBelow,\n hideAbove,\n scope = 'col',\n className,\n children,\n ...rest\n },\n ref,\n ) {\n const sortable = sort !== undefined && onSort !== undefined\n return (\n <th\n ref={ref}\n scope={scope}\n className={cx(styles.headerCell, visibilityClass({ hideBelow, hideAbove }), className)}\n data-align={align ?? (numeric ? 'end' : undefined)}\n data-numeric={numeric || undefined}\n data-width={width}\n data-sort={sortable ? sort : undefined}\n aria-sort={sort !== undefined ? ARIA_SORT[sort] : undefined}\n {...rest}\n >\n {sortable ? (\n <button type=\"button\" className={styles.sortButton} onClick={onSort}>\n {children}\n <span className={styles.sortGlyph} aria-hidden=\"true\">\n {sort === 'asc' ? <ChevronUpIcon /> : <ChevronDownIcon />}\n </span>\n </button>\n ) : (\n children\n )}\n </th>\n )\n },\n)\n\n/**\n * Hide a column on small screens: put the same `hideBelow` on its HeaderCell and on\n * every Cell in that column (cells are `display: none`, so the columns stay aligned).\n */\nexport interface TableCellProps\n extends Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'>, VisibilityProps {\n align?: TableAlign\n /** Figures: right-aligned, tabular, in the theme's numeric face (mono in Ledger). */\n numeric?: boolean\n /** Render as `<th scope=\"row\">` — the cell that names the row (project, client). */\n rowHeader?: boolean\n}\n\nconst TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(function TableCell(\n { align, numeric = false, rowHeader = false, hideBelow, hideAbove, className, ...rest },\n ref,\n) {\n const shared = {\n className: cx(styles.cell, visibilityClass({ hideBelow, hideAbove }), className),\n 'data-align': align ?? (numeric ? 'end' : undefined),\n 'data-numeric': numeric || undefined,\n 'data-row-header': rowHeader || undefined,\n }\n if (rowHeader) {\n const { scope = 'row', ...thRest } = rest as ThHTMLAttributes<HTMLTableCellElement>\n return <th ref={ref} scope={scope} {...shared} {...thRest} />\n }\n return <td ref={ref} {...shared} {...rest} />\n})\n\nconst TableCaption = forwardRef<HTMLTableCaptionElement, HTMLAttributes<HTMLTableCaptionElement>>(\n function TableCaption({ className, ...rest }, ref) {\n return <caption ref={ref} className={cx(styles.caption, className)} {...rest} />\n },\n)\n\nexport const Table = Object.assign(TableRoot, {\n Head: TableHead,\n Body: TableBody,\n Foot: TableFoot,\n Row: TableRow,\n HeaderCell: TableHeaderCell,\n Cell: TableCell,\n Caption: TableCaption,\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Table.js","names":["styles","Table_module_default","TableRoot","forwardRef","rest","surface","stickyHeader","label","cx","className","density","variant","valign","striped","TableHead","TableBody","TableFoot","TableRow","highlighted","interactive","muted","color","ARIA_SORT","TableHeaderCell","sortable","sort","onSort","visibilityClass","align","numeric","width","children","jsx","ChevronUpIcon","ChevronDownIcon","TableCell","shared","rowHeader","thRest","TableCaption","Table"],"sources":["../../../../src/components/display/Table/Table.tsx"],"sourcesContent":["import {\n forwardRef,\n type HTMLAttributes,\n type TableHTMLAttributes,\n type TdHTMLAttributes,\n type ThHTMLAttributes,\n} from 'react'\nimport { cx } from '#utils/cx'\nimport type { CategoryColor } from '#utils/tokens'\nimport { visibilityClass, type VisibilityProps } from '#utils/visibility'\nimport { ChevronDownIcon, ChevronUpIcon } from '#icons'\nimport styles from './Table.module.css'\n\nexport type TableDensity = 'compact' | 'regular'\nexport type TableVariant = 'rules' | 'plain'\n/** `surface` and `raised` set the table on one sheet, like `Card`'s `outline` and `raised`. */\nexport type TableSurface = 'none' | 'surface' | 'raised'\nexport type TableAlign = 'start' | 'center' | 'end'\n/** Where body and footer cells sit in a row taller than their content. */\nexport type TableVerticalAlign = 'middle' | 'baseline' | 'top'\nexport type TableSort = 'asc' | 'desc' | 'none'\n/** `fill` takes the spare width (the name column); `min` shrinks to its content. */\nexport type TableColumnWidth = 'fill' | 'min'\n\nexport interface TableProps extends TableHTMLAttributes<HTMLTableElement> {\n /** Row height. Default `regular`. */\n density?: TableDensity\n /** `rules` (default): a hairline under every row. `plain`: only the header rule. */\n variant?: TableVariant\n /**\n * Where cell content sits when a row's cells differ in height (a flag and a tag beside plain\n * figures). `baseline` (the default) lines up the first line of text across the row;\n * `middle` centres every cell, so figures sit level with a name that has a flag or a tag beside\n * or under it; `top` pins each cell to the row's top. Header cells always sit on the header rule.\n */\n valign?: TableVerticalAlign\n /** Alternate row fills. Off by default — rules usually read better. */\n striped?: boolean\n /**\n * Set the table on a sheet so its rows read as rows, not text on the page: `surface` is the\n * surface fill inside a hairline edge, `raised` stands on the theme's surface shadow. Rows run\n * edge to edge, clipped to the sheet's corners, with a little more air. Default `none`.\n */\n surface?: TableSurface\n /**\n * Keep the header visible while rows scroll. The wrapper becomes the scroll container\n * (capped by `--table-max-height`, default 70vh), since a horizontally scrolling wrapper\n * would otherwise trap a page-sticky header.\n */\n stickyHeader?: boolean\n /**\n * Name for the scroll container. When set, the wrapper becomes a focusable, labelled\n * region so keyboard users can scroll a wide table.\n */\n label?: string\n}\n\n/**\n * A semantic data table with numeric columns that line up, sortable headers and a footer for\n * totals.\n *\n * @remarks\n * `Table` is a real `<table>` with Kiln's styles: rules between rows, numeric columns in tabular\n * figures, and a wrapper that scrolls sideways on narrow screens instead of breaking the layout.\n *\n * ## Density, stripes and sticky headers\n *\n * `density=\"compact\"` for dense data, `striped` for alternating fills (rules usually read better),\n * and `stickyHeader` to keep the header in view while the rows scroll inside the table.\n *\n * ## On a surface\n *\n * On a busy page, or a theme whose canvas has a texture, `surface=\"surface\"` sets the table on one\n * sheet, and `surface=\"raised\"` stands it on the theme's surface shadow. Rows run edge to edge and\n * get a little more air: a highlighted row is a band across the sheet, a categorical rail is the\n * row's edge, and the sheet scrolls sideways with the table.\n *\n * @privateRemarks\n * A semantic, styled data table: project boards, ledgers, audit logs. The wrapper scrolls\n * horizontally on narrow screens; `ref` and native props go to the `<table>`.\n *\n * <Table><Table.Head><Table.Row><Table.HeaderCell>Project</Table.HeaderCell>…</Table.Row></Table.Head>…</Table>\n */\nconst TableRoot = forwardRef<HTMLTableElement, TableProps>(function Table(\n {\n density = 'regular',\n variant = 'rules',\n valign = 'baseline',\n striped = false,\n surface = 'none',\n stickyHeader = false,\n label,\n className,\n ...rest\n },\n ref,\n) {\n return (\n <div\n className={styles.scroll}\n data-kiln-component=\"\"\n data-surface={surface === 'none' ? undefined : surface}\n data-sticky-header={stickyHeader || undefined}\n role={label ? 'region' : undefined}\n aria-label={label}\n tabIndex={label ? 0 : undefined}\n >\n <table\n ref={ref}\n className={cx(styles.table, className)}\n data-density={density}\n data-variant={variant}\n data-valign={valign}\n data-striped={striped || undefined}\n data-surface={surface === 'none' ? undefined : surface}\n data-sticky-header={stickyHeader || undefined}\n {...rest}\n />\n </div>\n )\n})\n\nconst TableHead = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableHead({ className, ...rest }, ref) {\n return <thead ref={ref} className={cx(styles.head, className)} {...rest} />\n },\n)\n\nconst TableBody = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableBody({ className, ...rest }, ref) {\n return <tbody ref={ref} className={cx(styles.body, className)} {...rest} />\n },\n)\n\n/** Totals row(s). Set above a strong rule. */\nconst TableFoot = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(\n function TableFoot({ className, ...rest }, ref) {\n return <tfoot ref={ref} className={cx(styles.foot, className)} {...rest} />\n },\n)\n\nexport interface TableRowProps extends Omit<HTMLAttributes<HTMLTableRowElement>, 'color'> {\n /** `--color-highlight` fill — \"you\", your project, the circled line. */\n highlighted?: boolean\n /** Hover fill for rows that respond to clicks (put the real link in a cell). */\n interactive?: boolean\n /** Out of play (eliminated, archived, past): every ink drops to the muted step, which still reads. */\n muted?: boolean\n /** A categorical colour drawn as a rail at the row's start: whose row it is, matching their `Tag`. */\n color?: CategoryColor\n}\n\nconst TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(function TableRow(\n { highlighted = false, interactive = false, muted = false, color, className, ...rest },\n ref,\n) {\n return (\n <tr\n ref={ref}\n className={cx(styles.row, className)}\n data-highlighted={highlighted || undefined}\n data-interactive={interactive || undefined}\n data-muted={muted || undefined}\n data-color={color}\n {...rest}\n />\n )\n})\n\nexport interface TableHeaderCellProps\n extends Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align' | 'width'>, VisibilityProps {\n align?: TableAlign\n /** Numeric column: right-aligned with tabular figures. */\n numeric?: boolean\n /** Column sizing: `fill` takes the spare width, `min` shrinks to fit. Default: automatic. */\n width?: TableColumnWidth\n /** Current sort of this column. With `onSort`, the label becomes a button and `aria-sort` is set. */\n sort?: TableSort\n /** Called when the sort button is pressed. Decide the next direction yourself. */\n onSort?: () => void\n}\n\nconst ARIA_SORT = { asc: 'ascending', desc: 'descending', none: 'none' } as const\n\nconst TableHeaderCell = forwardRef<HTMLTableCellElement, TableHeaderCellProps>(\n function TableHeaderCell(\n {\n align,\n numeric = false,\n width,\n sort,\n onSort,\n hideBelow,\n hideAbove,\n scope = 'col',\n className,\n children,\n ...rest\n },\n ref,\n ) {\n const sortable = sort !== undefined && onSort !== undefined\n return (\n <th\n ref={ref}\n scope={scope}\n className={cx(styles.headerCell, visibilityClass({ hideBelow, hideAbove }), className)}\n data-align={align ?? (numeric ? 'end' : undefined)}\n data-numeric={numeric || undefined}\n data-width={width}\n data-sort={sortable ? sort : undefined}\n aria-sort={sort !== undefined ? ARIA_SORT[sort] : undefined}\n {...rest}\n >\n {sortable ? (\n <button type=\"button\" className={styles.sortButton} onClick={onSort}>\n {children}\n <span className={styles.sortGlyph} aria-hidden=\"true\">\n {sort === 'asc' ? <ChevronUpIcon /> : <ChevronDownIcon />}\n </span>\n </button>\n ) : (\n children\n )}\n </th>\n )\n },\n)\n\n/**\n * Hide a column on small screens: put the same `hideBelow` on its HeaderCell and on\n * every Cell in that column (cells are `display: none`, so the columns stay aligned).\n */\nexport interface TableCellProps\n extends Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'>, VisibilityProps {\n align?: TableAlign\n /** Figures: right-aligned, tabular, in the theme's numeric face (mono in Ledger). */\n numeric?: boolean\n /** Render as `<th scope=\"row\">` — the cell that names the row (project, client). */\n rowHeader?: boolean\n}\n\nconst TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(function TableCell(\n { align, numeric = false, rowHeader = false, hideBelow, hideAbove, className, ...rest },\n ref,\n) {\n const shared = {\n className: cx(styles.cell, visibilityClass({ hideBelow, hideAbove }), className),\n 'data-align': align ?? (numeric ? 'end' : undefined),\n 'data-numeric': numeric || undefined,\n 'data-row-header': rowHeader || undefined,\n }\n if (rowHeader) {\n const { scope = 'row', ...thRest } = rest as ThHTMLAttributes<HTMLTableCellElement>\n return <th ref={ref} scope={scope} {...shared} {...thRest} />\n }\n return <td ref={ref} {...shared} {...rest} />\n})\n\nconst TableCaption = forwardRef<HTMLTableCaptionElement, HTMLAttributes<HTMLTableCaptionElement>>(\n function TableCaption({ className, ...rest }, ref) {\n return <caption ref={ref} className={cx(styles.caption, className)} {...rest} />\n },\n)\n\nexport const Table = Object.assign(TableRoot, {\n Head: TableHead,\n Body: TableBody,\n Foot: TableFoot,\n Row: TableRow,\n HeaderCell: TableHeaderCell,\n Cell: TableCell,\n Caption: TableCaption,\n})\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Table.module.js","names":["scroll","table","caption","headerCell","cell","body","row","foot","head","sortButton","sortGlyph","Table_module_default"],"sources":["../../../../src/components/display/Table/Table.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --table-head-font default var(--font-text) — Fiesta sets its display caps\n * --table-head-numeric-font numeric header cells, default var(--table-head-font, var(--font-text))\n * --table-head-size default var(--text-sm)\n * --table-head-weight default var(--label-weight)\n * --table-head-transform default var(--heading-transform)\n * --table-head-bg default var(--color-canvas) — only painted when stickyHeader\n * --table-max-height default 70vh — scroll height when stickyHeader\n * --table-foot-rule the rule over totals, default var(--border-width-strong) solid\n * var(--color-line-strong) — Ledger draws the accountant's double rule\n * --highlight-tone-{positive,caution,critical,info}-text, --highlight-accent-text\n * tone text inside a highlighted row (shared with List). Default:\n * the tone text mixed 25% toward --color-highlight-ink.\n *\n * Headers are labels, so they're set in the text face (monospace as decoration for small\n * labels is banned in DESIGN §2); figures below them carry the numeric face.\n */\n.scroll {\n inline-size: 100%;\n min-inline-size: 0;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n}\n.scroll[data-sticky-header] {\n overflow: auto;\n max-block-size: var(--table-max-height, 70vh);\n}\n.scroll:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n}\n\n.table {\n --_py: var(--space-2);\n --_px: var(--space-3);\n\n inline-size: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-family: var(--font-text);\n font-size: var(--text-sm);\n line-height: var(--leading-snug);\n color: var(--color-ink);\n}\n.table[data-density='compact'] {\n --_py: var(--space-1);\n --_px: var(--space-2);\n}\n\n.caption {\n caption-side: top;\n padding-block-end: var(--space-3);\n color: var(--color-ink-muted);\n font-size: var(--text-sm);\n text-align: start;\n}\n\n/* ── Cells ─────────────────────────────────────────────────────────────── */\n\n.headerCell,\n.cell {\n padding: var(--_py) var(--_px);\n text-align: start;\n vertical-align: baseline;\n}\n.headerCell:first-child,\n.cell:first-child {\n padding-inline-start: var(--space-2);\n}\n.headerCell:last-child,\n.cell:last-child {\n padding-inline-end: var(--space-2);\n}\n/* Figure columns are narrow; tighter gutters keep a 10-column figures table on a phone. */\n:is(.headerCell, .cell)[data-numeric] {\n padding-inline: var(--space-2);\n}\n.headerCell abbr {\n text-decoration: none;\n cursor: help;\n}\n:is(.headerCell, .cell)[data-align='center'] {\n text-align: center;\n}\n:is(.headerCell, .cell)[data-align='end'] {\n text-align: end;\n}\n\n.headerCell {\n padding-block: var(--_py);\n color: var(--color-ink-muted);\n font-family: var(--table-head-font, var(--font-text));\n font-size: var(--table-head-size, var(--text-sm));\n font-weight: var(--table-head-weight, var(--label-weight));\n letter-spacing: var(--label-tracking);\n text-transform: var(--table-head-transform, var(--heading-transform));\n white-space: nowrap;\n vertical-align: bottom;\n border-block-end: var(--border-width-strong) solid var(--color-line-strong);\n}\n\n.headerCell[data-numeric] {\n font-family: var(--table-head-numeric-font, var(--table-head-font, var(--font-text)));\n font-variant-numeric: tabular-nums lining-nums;\n}\n.headerCell[data-width='fill'] {\n inline-size: 100%;\n}\n.headerCell[data-width='min'] {\n inline-size: 1%;\n}\n\n.cell[data-numeric] {\n font-family: var(--font-numeric);\n font-stretch: var(--numeric-stretch);\n font-variant-numeric: tabular-nums lining-nums;\n white-space: nowrap;\n}\n.cell[data-row-header] {\n font-weight: var(--label-weight);\n}\n\n/* ── Rows ──────────────────────────────────────────────────────────────── */\n\n.table[data-variant='rules'] .body .cell {\n border-block-end: var(--border-width) solid var(--color-line);\n}\n.table[data-variant='rules'] .body > .row:last-child > .cell {\n border-block-end-color: var(--color-line-strong);\n}\n\n.table[data-striped] .body > .row:nth-child(even) > .cell {\n background: var(--color-surface-sunken);\n}\n\n/*\n * A highlighted row re-points the colour roles for its cells' content, so a muted date,\n * a tone-coloured Numeral or a LiveIndicator stays legible on the highlight fill (gold in\n * Fiesta night, where the ordinary tone texts are pale). Same approach as Section's\n * inverse band: the row publishes its fill/ink and tone texts under private names, and\n * the cells swap the roles — setting e.g. --tone-positive-text from itself on one element\n * would be a var() cycle.\n */\n.row[data-highlighted] {\n --_band-fill: var(--color-highlight);\n --_band-ink: var(--color-highlight-ink);\n --_band-positive: var(\n --highlight-tone-positive-text,\n color-mix(in oklab, var(--tone-positive-text) 75%, var(--color-highlight-ink))\n );\n --_band-caution: var(\n --highlight-tone-caution-text,\n color-mix(in oklab, var(--tone-caution-text) 75%, var(--color-highlight-ink))\n );\n --_band-critical: var(\n --highlight-tone-critical-text,\n color-mix(in oklab, var(--tone-critical-text) 75%, var(--color-highlight-ink))\n );\n --_band-info: var(\n --highlight-tone-info-text,\n color-mix(in oklab, var(--tone-info-text) 75%, var(--color-highlight-ink))\n );\n --_band-accent: var(\n --highlight-accent-text,\n color-mix(in oklab, var(--color-accent-text) 75%, var(--color-highlight-ink))\n );\n}\n.row[data-highlighted] > .cell {\n --color-ink: var(--_band-ink);\n --color-ink-muted: color-mix(in oklab, var(--_band-ink), var(--_band-fill) 25%);\n --color-ink-subtle: color-mix(in oklab, var(--_band-ink), var(--_band-fill) 35%);\n --tone-positive-text: var(--_band-positive);\n --tone-caution-text: var(--_band-caution);\n --tone-critical-text: var(--_band-critical);\n --tone-info-text: var(--_band-info);\n --color-accent-text: var(--_band-accent);\n /* Soft fills (a soft Badge or Tag) tint the band, like Section's accent band, so the\n re-pointed tone texts above stay legible on them in every mode. */\n --tone-positive-soft: color-mix(in oklab, var(--_band-positive) 8%, var(--_band-fill));\n --tone-caution-soft: color-mix(in oklab, var(--_band-caution) 8%, var(--_band-fill));\n --tone-critical-soft: color-mix(in oklab, var(--_band-critical) 8%, var(--_band-fill));\n --tone-info-soft: color-mix(in oklab, var(--_band-info) 8%, var(--_band-fill));\n --color-accent-soft: color-mix(in oklab, var(--_band-accent) 8%, var(--_band-fill));\n\n background: var(--_band-fill);\n color: var(--_band-ink);\n}\n/* Round the ends of a highlighted row so the fill reads as a marker, not a stripe. */\n.row[data-highlighted] > .cell:first-child {\n border-start-start-radius: var(--radius-field);\n border-end-start-radius: var(--radius-field);\n}\n.row[data-highlighted] > .cell:last-child {\n border-start-end-radius: var(--radius-field);\n border-end-end-radius: var(--radius-field);\n}\n\n.row[data-interactive] {\n cursor: pointer;\n}\n.row > .cell {\n transition: background-color var(--dur-1) var(--ease-out);\n}\n@media (hover: hover) {\n .row[data-interactive]:not([data-highlighted]):hover > .cell {\n background: var(--color-surface-sunken);\n }\n}\n\n.foot > :first-child > .cell {\n border-block-start: var(\n --table-foot-rule,\n var(--border-width-strong) solid var(--color-line-strong)\n );\n}\n.foot .cell {\n font-weight: var(--label-weight);\n}\n/* The totals rule replaces the last row's hairline rather than stacking under it. */\n.table[data-variant='rules'] .body:has(+ .foot) > .row:last-child > .cell {\n border-block-end-width: 0;\n}\n\n/* ── Sticky header ─────────────────────────────────────────────────────── */\n\n.table[data-sticky-header] .head .headerCell {\n position: sticky;\n inset-block-start: 0;\n z-index: 1;\n background: var(--table-head-bg, var(--color-canvas));\n}\n\n/* ── Sort button ───────────────────────────────────────────────────────── */\n\n.sortButton {\n display: inline-flex;\n align-items: center;\n gap: 0.25em;\n margin: calc(var(--space-1) * -1);\n padding: var(--space-1);\n border: 0;\n border-radius: var(--radius-field);\n background: transparent;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n text-transform: inherit;\n cursor: pointer;\n transition: color var(--dur-1) var(--ease-out);\n}\n.headerCell[data-align='end'] .sortButton {\n flex-direction: row-reverse;\n}\n@media (hover: hover) {\n .sortButton:hover {\n color: var(--color-ink);\n }\n}\n.sortButton:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 0;\n}\n.sortGlyph {\n display: inline-flex;\n font-size: 1.1em;\n opacity: 0;\n transition: opacity var(--dur-1) var(--ease-out);\n}\n.headerCell:is([data-sort='asc'], [data-sort='desc']) {\n color: var(--color-ink);\n}\n.headerCell:is([data-sort='asc'], [data-sort='desc']) .sortGlyph,\n.sortButton:hover .sortGlyph,\n.sortButton:focus-visible .sortGlyph {\n opacity: 1;\n}\n.headerCell[data-sort='none'] .sortButton:is(:hover, :focus-visible) .sortGlyph {\n opacity: 0.45;\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Table.module.js","names":["scroll","table","caption","headerCell","cell","body","row","foot","head","sortButton","sortGlyph","Table_module_default"],"sources":["../../../../src/components/display/Table/Table.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --table-head-font default var(--font-text) — Fiesta sets its display caps\n * --table-head-numeric-font numeric header cells, default var(--table-head-font, var(--font-text))\n * --table-head-size default var(--text-sm)\n * --table-head-weight default var(--label-weight)\n * --table-head-transform default var(--heading-transform)\n * --table-head-bg default var(--color-canvas), the sheet's fill on a `surface`\n * table — only painted when stickyHeader\n * --table-max-height default 70vh — scroll height when stickyHeader\n * --table-rail-width default var(--space-1) — the categorical rail of a `color` row\n * --table-surface-bg default var(--color-surface), var(--color-surface-raised) when `raised`\n * --table-surface-radius default var(--radius-surface) — the sheet of a `surface` table\n * --table-foot-rule the rule over totals, default var(--border-width-strong) solid\n * var(--color-line-strong) — Ledger draws the accountant's double rule\n * --highlight-tone-{positive,caution,critical,info}-text, --highlight-accent-text\n * tone text inside a highlighted row (shared with List). Default:\n * the tone text mixed 25% toward --color-highlight-ink.\n *\n * Headers are labels, so they're set in the text face (monospace as decoration for small\n * labels is banned in DESIGN §2); figures below them carry the numeric face.\n */\n.scroll {\n inline-size: 100%;\n min-inline-size: 0;\n overflow-x: auto;\n overscroll-behavior-x: contain;\n}\n.scroll[data-sticky-header] {\n overflow: auto;\n max-block-size: var(--table-max-height, 70vh);\n}\n/*\n * On a surface: the wrapper is the sheet, with Card's edges (`outline` for surface, `raised`\n * for raised), so it scrolls sideways with the table and rows read as rows on a textured or\n * busy canvas. Rows run edge to edge and the scroll container clips them to its radius, so a\n * highlighted row is a band across the sheet and a categorical rail sits flush with the\n * sheet's inner edge, following its corners on the first and last rows.\n */\n.scroll[data-surface] {\n --_sheet-bg: var(--table-surface-bg, var(--color-surface));\n\n border: var(--border-width) solid var(--color-line);\n border-radius: var(--table-surface-radius, var(--radius-surface));\n background: var(--_sheet-bg);\n}\n.scroll[data-surface='raised'] {\n --_sheet-bg: var(--table-surface-bg, var(--color-surface-raised));\n\n border-color: var(--color-line-strong);\n box-shadow: var(--shadow-surface);\n}\n.scroll:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n}\n\n.table {\n --_py: var(--space-2);\n --_px: var(--space-3);\n --_edge: var(--space-2);\n --_row-radius: var(--radius-field);\n --_valign: baseline;\n\n inline-size: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-family: var(--font-text);\n font-size: var(--text-sm);\n line-height: var(--leading-snug);\n color: var(--color-ink);\n}\n.table[data-valign='middle'] {\n --_valign: middle;\n}\n.table[data-valign='top'] {\n --_valign: top;\n}\n.table[data-density='compact'] {\n --_py: var(--space-1);\n --_px: var(--space-2);\n}\n/* On a sheet rows get more air, and the end cells a wider inset so text clears the edge. */\n.table[data-surface] {\n --_py: var(--space-3);\n --_edge: var(--space-4);\n --_row-radius: 0px;\n}\n.table[data-surface][data-density='compact'] {\n --_py: var(--space-2);\n}\n\n.caption {\n caption-side: top;\n padding-block-end: var(--space-3);\n color: var(--color-ink-muted);\n font-size: var(--text-sm);\n text-align: start;\n}\n/* On a sheet the caption lines up with the first column instead of the sheet's edge. */\n.table[data-surface] > .caption {\n padding-block-start: var(--space-3);\n padding-inline: var(--_edge);\n}\n\n/* ── Cells ─────────────────────────────────────────────────────────────── */\n\n.headerCell,\n.cell {\n padding: var(--_py) var(--_px);\n text-align: start;\n vertical-align: var(--_valign);\n}\n.headerCell:first-child,\n.cell:first-child {\n padding-inline-start: var(--_edge);\n}\n.headerCell:last-child,\n.cell:last-child {\n padding-inline-end: var(--_edge);\n}\n/* Figure columns are narrow; tighter gutters keep a 10-column figures table on a phone. */\n:is(.headerCell, .cell)[data-numeric] {\n padding-inline: var(--space-2);\n}\n.headerCell abbr {\n text-decoration: none;\n cursor: help;\n}\n:is(.headerCell, .cell)[data-align='center'] {\n text-align: center;\n}\n:is(.headerCell, .cell)[data-align='end'] {\n text-align: end;\n}\n\n.headerCell {\n padding-block: var(--_py);\n color: var(--color-ink-muted);\n font-family: var(--table-head-font, var(--font-text));\n font-size: var(--table-head-size, var(--text-sm));\n font-weight: var(--table-head-weight, var(--label-weight));\n letter-spacing: var(--label-tracking);\n text-transform: var(--table-head-transform, var(--heading-transform));\n white-space: nowrap;\n vertical-align: bottom;\n border-block-end: var(--border-width-strong) solid var(--color-line-strong);\n}\n\n.headerCell[data-numeric] {\n font-family: var(--table-head-numeric-font, var(--table-head-font, var(--font-text)));\n font-variant-numeric: tabular-nums lining-nums;\n}\n.headerCell[data-width='fill'] {\n inline-size: 100%;\n}\n.headerCell[data-width='min'] {\n inline-size: 1%;\n}\n\n.cell[data-numeric] {\n font-family: var(--font-numeric);\n font-stretch: var(--numeric-stretch);\n font-variant-numeric: tabular-nums lining-nums;\n white-space: nowrap;\n}\n.cell[data-row-header] {\n font-weight: var(--label-weight);\n}\n\n/* ── Rows ──────────────────────────────────────────────────────────────── */\n\n.table[data-variant='rules'] .body .cell {\n border-block-end: var(--border-width) solid var(--color-line);\n}\n.table[data-variant='rules'] .body > .row:last-child > .cell {\n border-block-end-color: var(--color-line-strong);\n}\n/* On a sheet, its edge closes the table: no closing rule just inside it. */\n.table[data-surface][data-variant='rules'] .body:last-child > .row:last-child > .cell {\n border-block-end-color: transparent;\n}\n\n.table[data-striped] .body > .row:nth-child(even) > .cell {\n background: var(--color-surface-sunken);\n}\n\n/*\n * A highlighted row re-points the colour roles for its cells' content, so a muted date,\n * a tone-coloured Numeral or a LiveIndicator stays legible on the highlight fill (gold in\n * Fiesta night, where the ordinary tone texts are pale). Same approach as Section's\n * inverse band: the row publishes its fill/ink and tone texts under private names, and\n * the cells swap the roles — setting e.g. --tone-positive-text from itself on one element\n * would be a var() cycle.\n */\n.row[data-highlighted] {\n --_band-fill: var(--color-highlight);\n --_band-ink: var(--color-highlight-ink);\n --_band-positive: var(\n --highlight-tone-positive-text,\n color-mix(in oklab, var(--tone-positive-text) 75%, var(--color-highlight-ink))\n );\n --_band-caution: var(\n --highlight-tone-caution-text,\n color-mix(in oklab, var(--tone-caution-text) 75%, var(--color-highlight-ink))\n );\n --_band-critical: var(\n --highlight-tone-critical-text,\n color-mix(in oklab, var(--tone-critical-text) 75%, var(--color-highlight-ink))\n );\n --_band-info: var(\n --highlight-tone-info-text,\n color-mix(in oklab, var(--tone-info-text) 75%, var(--color-highlight-ink))\n );\n --_band-accent: var(\n --highlight-accent-text,\n color-mix(in oklab, var(--color-accent-text) 75%, var(--color-highlight-ink))\n );\n}\n.row[data-highlighted] > .cell {\n --color-ink: var(--_band-ink);\n --color-ink-muted: color-mix(in oklab, var(--_band-ink), var(--_band-fill) 25%);\n --color-ink-subtle: color-mix(in oklab, var(--_band-ink), var(--_band-fill) 35%);\n --tone-positive-text: var(--_band-positive);\n --tone-caution-text: var(--_band-caution);\n --tone-critical-text: var(--_band-critical);\n --tone-info-text: var(--_band-info);\n --color-accent-text: var(--_band-accent);\n /* Soft fills (a soft Badge or Tag) tint the band, like Section's accent band, so the\n re-pointed tone texts above stay legible on them in every mode. */\n --tone-positive-soft: color-mix(in oklab, var(--_band-positive) 8%, var(--_band-fill));\n --tone-caution-soft: color-mix(in oklab, var(--_band-caution) 8%, var(--_band-fill));\n --tone-critical-soft: color-mix(in oklab, var(--_band-critical) 8%, var(--_band-fill));\n --tone-info-soft: color-mix(in oklab, var(--_band-info) 8%, var(--_band-fill));\n --color-accent-soft: color-mix(in oklab, var(--_band-accent) 8%, var(--_band-fill));\n\n background: var(--_band-fill);\n color: var(--_band-ink);\n}\n/* Round the ends of a highlighted row so the fill reads as a marker, not a stripe. */\n.row[data-highlighted] > .cell:first-child {\n border-start-start-radius: var(--_row-radius);\n border-end-start-radius: var(--_row-radius);\n}\n.row[data-highlighted] > .cell:last-child {\n border-start-end-radius: var(--_row-radius);\n border-end-end-radius: var(--_row-radius);\n}\n\n.row[data-interactive] {\n cursor: pointer;\n}\n\n/* Muted: out of play. Every ink drops to the muted step, which still clears AA. */\n.row[data-muted] > .cell {\n --color-ink: var(--color-ink-muted);\n\n color: var(--color-ink-muted);\n}\n\n/* A categorical rail at the row's start: whose row it is, in their Tag's colour. */\n.row[data-color] > .cell:first-child {\n position: relative;\n padding-inline-start: calc(var(--_edge) + var(--space-2));\n}\n.row[data-color] > .cell:first-child::before {\n content: '';\n position: absolute;\n inset-block: var(--space-1);\n inset-inline-start: 0;\n inline-size: var(--table-rail-width, var(--space-1));\n border-radius: var(--radius-chip);\n background: var(--_cat);\n}\n/* On a sheet the rail is an edge of the row: full height, square, and over the rule below it. */\n.table[data-surface] .row[data-color] > .cell:first-child::before {\n inset-block: 0;\n border-radius: 0;\n}\n.table[data-surface][data-variant='rules'] .body .row[data-color] > .cell:first-child::before {\n inset-block-end: calc(var(--border-width) * -1);\n}\n.row[data-color='1'] {\n --_cat: var(--color-cat-1);\n}\n.row[data-color='2'] {\n --_cat: var(--color-cat-2);\n}\n.row[data-color='3'] {\n --_cat: var(--color-cat-3);\n}\n.row[data-color='4'] {\n --_cat: var(--color-cat-4);\n}\n.row[data-color='5'] {\n --_cat: var(--color-cat-5);\n}\n.row[data-color='6'] {\n --_cat: var(--color-cat-6);\n}\n.row[data-color='7'] {\n --_cat: var(--color-cat-7);\n}\n.row[data-color='8'] {\n --_cat: var(--color-cat-8);\n}\n.row > .cell {\n transition: background-color var(--dur-1) var(--ease-out);\n}\n@media (hover: hover) {\n .row[data-interactive]:not([data-highlighted]):hover > .cell {\n background: var(--color-surface-sunken);\n }\n}\n\n.foot > :first-child > .cell {\n border-block-start: var(\n --table-foot-rule,\n var(--border-width-strong) solid var(--color-line-strong)\n );\n}\n.foot .cell {\n font-weight: var(--label-weight);\n}\n/* The totals rule replaces the last row's hairline rather than stacking under it. */\n.table[data-variant='rules'] .body:has(+ .foot) > .row:last-child > .cell {\n border-block-end-width: 0;\n}\n\n/* ── Sticky header ─────────────────────────────────────────────────────── */\n\n.table[data-sticky-header] .head .headerCell {\n position: sticky;\n inset-block-start: 0;\n z-index: 1;\n background: var(--table-head-bg, var(--color-canvas));\n}\n.table[data-surface][data-sticky-header] .head .headerCell {\n background: var(--table-head-bg, var(--_sheet-bg));\n}\n\n/* ── Sort button ───────────────────────────────────────────────────────── */\n\n.sortButton {\n display: inline-flex;\n align-items: center;\n gap: 0.25em;\n margin: calc(var(--space-1) * -1);\n padding: var(--space-1);\n border: 0;\n border-radius: var(--radius-field);\n background: transparent;\n color: inherit;\n font: inherit;\n letter-spacing: inherit;\n text-transform: inherit;\n cursor: pointer;\n transition: color var(--dur-1) var(--ease-out);\n}\n.headerCell[data-align='end'] .sortButton {\n flex-direction: row-reverse;\n}\n@media (hover: hover) {\n .sortButton:hover {\n color: var(--color-ink);\n }\n}\n.sortButton:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 0;\n}\n.sortGlyph {\n display: inline-flex;\n font-size: 1.1em;\n opacity: 0;\n transition: opacity var(--dur-1) var(--ease-out);\n}\n.headerCell:is([data-sort='asc'], [data-sort='desc']) {\n color: var(--color-ink);\n}\n.headerCell:is([data-sort='asc'], [data-sort='desc']) .sortGlyph,\n.sortButton:hover .sortGlyph,\n.sortButton:focus-visible .sortGlyph {\n opacity: 1;\n}\n.headerCell[data-sort='none'] .sortButton:is(:hover, :focus-visible) .sortGlyph {\n opacity: 0.45;\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Table } from './Table.js';
|
|
2
|
-
export type { TableProps, TableDensity, TableVariant, TableAlign, TableSort, TableColumnWidth, TableRowProps, TableHeaderCellProps, TableCellProps, } from './Table.js';
|
|
2
|
+
export type { TableProps, TableDensity, TableVariant, TableSurface, TableAlign, TableVerticalAlign, TableSort, TableColumnWidth, TableRowProps, TableHeaderCellProps, TableCellProps, } from './Table.js';
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { type HTMLAttributes, type MouseEvent } from 'react';
|
|
2
2
|
/** Categorical colour slot → `--color-cat-N`. */
|
|
3
3
|
export type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
4
|
+
export type TagSize = 'sm' | 'md';
|
|
4
5
|
export interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {
|
|
5
6
|
/** A categorical colour (category, person, topic). Omit for a neutral tag. */
|
|
6
7
|
color?: TagColor;
|
|
8
|
+
/** `md` (default), or `sm` for dense lists of many tags. */
|
|
9
|
+
size?: TagSize;
|
|
7
10
|
/** Renders a remove button after the label. Not rendered when `asChild`. */
|
|
8
11
|
onRemove?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
9
12
|
/** Accessible name for the remove button. Default `Remove <label>` for text labels. */
|
|
@@ -5,19 +5,20 @@ import { Children as r, forwardRef as i, isValidElement as a } from "react";
|
|
|
5
5
|
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
6
6
|
import { Slot as c } from "radix-ui";
|
|
7
7
|
//#region src/components/display/Tag/Tag.tsx
|
|
8
|
-
var l = i(function({ color: r,
|
|
9
|
-
let
|
|
10
|
-
return /* @__PURE__ */ s(
|
|
11
|
-
ref:
|
|
12
|
-
className: e(n.tag,
|
|
8
|
+
var l = i(function({ color: r, size: i = "md", onRemove: a, removeLabel: l, asChild: u = !1, className: d, children: f, ...p }, m) {
|
|
9
|
+
let h = u ? c.Root : "span", g = !!a && !u, _ = l ?? (typeof f == "string" ? `Remove ${f}` : "Remove");
|
|
10
|
+
return /* @__PURE__ */ s(h, {
|
|
11
|
+
ref: m,
|
|
12
|
+
className: e(n.tag, d),
|
|
13
13
|
"data-color": r,
|
|
14
|
-
"data-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
"data-size": i,
|
|
15
|
+
"data-removable": g || void 0,
|
|
16
|
+
...p,
|
|
17
|
+
children: [/* @__PURE__ */ o(c.Slottable, { children: f }), g ? /* @__PURE__ */ o("button", {
|
|
17
18
|
type: "button",
|
|
18
19
|
className: n.remove,
|
|
19
|
-
"aria-label":
|
|
20
|
-
onClick:
|
|
20
|
+
"aria-label": _,
|
|
21
|
+
onClick: a,
|
|
21
22
|
children: /* @__PURE__ */ o(t, {})
|
|
22
23
|
}) : null]
|
|
23
24
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.js","names":["styles","Tag_module_default","Tag","forwardRef","rest","Comp","asChild","Slot","canRemove","onRemove","label","removeLabel","children","cx","className","color","CloseIcon","TagList","Children","isValidElement","child"],"sources":["../../../../src/components/display/Tag/Tag.tsx"],"sourcesContent":["import { Children, forwardRef, isValidElement, type HTMLAttributes, type MouseEvent } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { CloseIcon } from '#icons'\nimport styles from './Tag.module.css'\n\n/** Categorical colour slot → `--color-cat-N`. */\nexport type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\n\nexport interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {\n /** A categorical colour (category, person, topic). Omit for a neutral tag. */\n color?: TagColor\n /** Renders a remove button after the label. Not rendered when `asChild`. */\n onRemove?: (event: MouseEvent<HTMLButtonElement>) => void\n /** Accessible name for the remove button. Default `Remove <label>` for text labels. */\n removeLabel?: string\n /** Render the single child (e.g. a filter link) as the tag. Incompatible with `onRemove`. */\n asChild?: boolean\n}\n\n/**\n * Quiet metadata, like a category, a facility or a group, in a wrapping list that can be edited.\n *\n * @remarks\n * A `Tag` labels a thing with metadata, and a `TagList` lays several out as a real list that\n * wraps. Tags are quieter than {@link Badge | badges}, which are for status.\n *\n * @privateRemarks\n * Quiet metadata: a category, a stack item, a group. Quieter than `Badge`, which is\n * for status. Group several in a `TagList`.\n */\nexport const Tag = forwardRef<HTMLSpanElement, TagProps>(function Tag(\n { color, onRemove, removeLabel, asChild = false, className, children, ...rest },\n ref,\n) {\n const Comp = asChild ? Slot.Root : 'span'\n const canRemove = Boolean(onRemove) && !asChild\n const label = removeLabel ?? (typeof children === 'string' ? `Remove ${children}` : 'Remove')\n return (\n <Comp\n ref={ref}\n className={cx(styles.tag, className)}\n data-color={color}\n data-removable={canRemove || undefined}\n {...rest}\n >\n <Slot.Slottable>{children}</Slot.Slottable>\n {canRemove ? (\n <button type=\"button\" className={styles.remove} aria-label={label} onClick={onRemove}>\n <CloseIcon />\n </button>\n ) : null}\n </Comp>\n )\n})\n\nexport type TagListProps = HTMLAttributes<HTMLUListElement>\n\n/** A wrapping list of tags. Each child is wrapped in an `<li>`. */\nexport const TagList = forwardRef<HTMLUListElement, TagListProps>(function TagList(\n { className, children, ...rest },\n ref,\n) {\n return (\n <ul ref={ref} role=\"list\" className={cx(styles.list, className)} {...rest}>\n {Children.map(children, (child) =>\n isValidElement(child) || typeof child === 'string' || typeof child === 'number' ? (\n <li key={isValidElement(child) ? child.key : undefined}>{child}</li>\n ) : null,\n )}\n </ul>\n )\n})\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"Tag.js","names":["styles","Tag_module_default","Tag","forwardRef","rest","Comp","asChild","Slot","canRemove","onRemove","label","removeLabel","children","cx","className","color","size","CloseIcon","TagList","Children","isValidElement","child"],"sources":["../../../../src/components/display/Tag/Tag.tsx"],"sourcesContent":["import { Children, forwardRef, isValidElement, type HTMLAttributes, type MouseEvent } from 'react'\nimport { Slot } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { CloseIcon } from '#icons'\nimport styles from './Tag.module.css'\n\n/** Categorical colour slot → `--color-cat-N`. */\nexport type TagColor = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8\nexport type TagSize = 'sm' | 'md'\n\nexport interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {\n /** A categorical colour (category, person, topic). Omit for a neutral tag. */\n color?: TagColor\n /** `md` (default), or `sm` for dense lists of many tags. */\n size?: TagSize\n /** Renders a remove button after the label. Not rendered when `asChild`. */\n onRemove?: (event: MouseEvent<HTMLButtonElement>) => void\n /** Accessible name for the remove button. Default `Remove <label>` for text labels. */\n removeLabel?: string\n /** Render the single child (e.g. a filter link) as the tag. Incompatible with `onRemove`. */\n asChild?: boolean\n}\n\n/**\n * Quiet metadata, like a category, a facility or a group, in a wrapping list that can be edited.\n *\n * @remarks\n * A `Tag` labels a thing with metadata, and a `TagList` lays several out as a real list that\n * wraps. Tags are quieter than {@link Badge | badges}, which are for status.\n *\n * @privateRemarks\n * Quiet metadata: a category, a stack item, a group. Quieter than `Badge`, which is\n * for status. Group several in a `TagList`.\n */\nexport const Tag = forwardRef<HTMLSpanElement, TagProps>(function Tag(\n { color, size = 'md', onRemove, removeLabel, asChild = false, className, children, ...rest },\n ref,\n) {\n const Comp = asChild ? Slot.Root : 'span'\n const canRemove = Boolean(onRemove) && !asChild\n const label = removeLabel ?? (typeof children === 'string' ? `Remove ${children}` : 'Remove')\n return (\n <Comp\n ref={ref}\n className={cx(styles.tag, className)}\n data-color={color}\n data-size={size}\n data-removable={canRemove || undefined}\n {...rest}\n >\n <Slot.Slottable>{children}</Slot.Slottable>\n {canRemove ? (\n <button type=\"button\" className={styles.remove} aria-label={label} onClick={onRemove}>\n <CloseIcon />\n </button>\n ) : null}\n </Comp>\n )\n})\n\nexport type TagListProps = HTMLAttributes<HTMLUListElement>\n\n/** A wrapping list of tags. Each child is wrapped in an `<li>`. */\nexport const TagList = forwardRef<HTMLUListElement, TagListProps>(function TagList(\n { className, children, ...rest },\n ref,\n) {\n return (\n <ul ref={ref} role=\"list\" className={cx(styles.list, className)} {...rest}>\n {Children.map(children, (child) =>\n isValidElement(child) || typeof child === 'string' || typeof child === 'number' ? (\n <li key={isValidElement(child) ? child.key : undefined}>{child}</li>\n ) : null,\n )}\n </ul>\n )\n})\n"],"mappings":";;;;;;;AAkCA,IAAaE,IAAMC,EAAsC,SACvD,EAAE,UAAO,UAAO,MAAM,aAAU,gBAAa,aAAU,IAAO,cAAW,aAAU,GAAGC,KACtF,GACA;CACA,IAAMC,IAAOC,IAAUC,EAAK,OAAO,QAC7BC,IAAY,EAAQC,KAAa,CAACH,GAClCI,IAAQC,MAAgB,OAAOC,KAAa,WAAW,UAAUA,MAAa;CACpF,OACE,kBAACP,GAAD;EACO;EACL,WAAWQ,EAAGb,EAAO,KAAKc,CAAS;EACnC,cAAYC;EACZ,aAAWC;EACX,kBAAgBR,KAAa,KAAA;EAC7B,GAAIJ;EANN,UAAA,CAQE,kBAACG,EAAK,WAAN,EAAiB,YAAyB,CAAA,GACzCC,IACC,kBAAC,UAAD;GAAQ,MAAK;GAAS,WAAWR,EAAO;GAAQ,cAAYU;GAAO,SAASD;GAC1E,UAAA,kBAACQ,GAAD,CAAY,CAAA;EACN,CAAA,IACN,IACA;;AAEV,CAAC,GAKYC,IAAUf,EAA2C,SAChE,EAAE,cAAW,aAAU,GAAGC,KAC1B,GACA;CACA,OACE,kBAAC,MAAD;EAAS;EAAK,MAAK;EAAO,WAAWS,EAAGb,EAAO,MAAMc,CAAS;EAAG,GAAIV;EAClE,UAAAe,EAAS,IAAIP,IAAW,MACvBQ,EAAeC,CAAK,KAAK,OAAOA,KAAU,YAAY,OAAOA,KAAU,WACrE,kBAAC,MAAD,EAAA,UAAyDA,EAAU,GAA1DD,EAAeC,CAAK,IAAIA,EAAM,MAAM,KAAA,CAAsB,IACjE,IACN;CACE,CAAA;AAER,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.module.js","names":["tag","remove","list","Tag_module_default"],"sources":["../../../../src/components/display/Tag/Tag.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --tag-radius default var(--radius-chip)\n *\n * Quieter than Badge: text weight, no status colour. Neutral tags are a hairline;\n * categorical tags are a flat fill of --color-cat-N with --color-cat-ink (AA on all 8).\n */\n.tag {\n --_bg: transparent;\n --_ink: var(--color-ink-muted);\n --_border: var(--color-line);\n\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: var(--space-1);\n min-block-size: calc(var(--space-5) * 0.9);\n padding-inline: var(--space-2);\n border: var(--border-width) solid var(--_border);\n border-radius: var(--tag-radius, var(--radius-chip));\n background: var(--_bg);\n color: var(--_ink);\n font-family: var(--font-text);\n font-size: var(--text-xs);\n font-weight: 450;\n line-height: 1;\n white-space: nowrap;\n text-decoration: none;\n vertical-align: middle;\n transition:\n background-color var(--dur-1) var(--ease-out),\n border-color var(--dur-1) var(--ease-out),\n color var(--dur-1) var(--ease-out);\n}\n\n.tag[data-removable] {\n padding-inline-end: calc(var(--space-1) / 2);\n}\n\n.tag[data-color] {\n --_ink: var(--color-cat-ink);\n --_border: var(--_bg);\n}\n.tag[data-color='1'] {\n --_bg: var(--color-cat-1);\n}\n.tag[data-color='2'] {\n --_bg: var(--color-cat-2);\n}\n.tag[data-color='3'] {\n --_bg: var(--color-cat-3);\n}\n.tag[data-color='4'] {\n --_bg: var(--color-cat-4);\n}\n.tag[data-color='5'] {\n --_bg: var(--color-cat-5);\n}\n.tag[data-color='6'] {\n --_bg: var(--color-cat-6);\n}\n.tag[data-color='7'] {\n --_bg: var(--color-cat-7);\n}\n.tag[data-color='8'] {\n --_bg: var(--color-cat-8);\n}\n\n/* As a link (asChild): the edge firms up on hover. */\n@media (hover: hover) {\n a.tag:hover {\n --_border: var(--color-line-strong);\n --_ink: var(--color-ink);\n }\n a.tag[data-color]:hover {\n --_border: var(--color-cat-ink);\n --_ink: var(--color-cat-ink);\n }\n}\n.tag:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n}\n\n.remove {\n position: relative;\n display: inline-grid;\n place-items: center;\n flex: none;\n inline-size: 1.5em;\n block-size: 1.5em;\n padding: 0;\n border: 0;\n border-radius: var(--tag-radius, var(--radius-chip));\n background: transparent;\n color: inherit;\n font-size: inherit;\n opacity: 0.7;\n cursor: pointer;\n transition:\n opacity var(--dur-1) var(--ease-out),\n background-color var(--dur-1) var(--ease-out);\n}\n/* Larger hit target than the glyph, without growing the tag. */\n.remove::after {\n content: '';\n position: absolute;\n inset: -0.35em;\n}\n.remove > svg {\n inline-size: 1.1em;\n block-size: 1.1em;\n}\n@media (hover: hover) {\n .remove:hover {\n opacity: 1;\n background: color-mix(in oklch, currentColor 14%, transparent);\n }\n}\n.remove:focus-visible {\n opacity: 1;\n outline: 2px solid var(--color-focus);\n outline-offset: 1px;\n}\n\n.list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.list > li {\n display: flex;\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Tag.module.js","names":["tag","remove","list","Tag_module_default"],"sources":["../../../../src/components/display/Tag/Tag.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --tag-radius default var(--radius-chip)\n *\n * Quieter than Badge: text weight, no status colour. Neutral tags are a hairline;\n * categorical tags are a flat fill of --color-cat-N with --color-cat-ink (AA on all 8).\n */\n.tag {\n --_bg: transparent;\n --_ink: var(--color-ink-muted);\n --_border: var(--color-line);\n\n display: inline-flex;\n flex: none;\n align-items: center;\n gap: var(--space-1);\n min-block-size: calc(var(--space-5) * 0.9);\n padding-inline: var(--space-2);\n border: var(--border-width) solid var(--_border);\n border-radius: var(--tag-radius, var(--radius-chip));\n background: var(--_bg);\n color: var(--_ink);\n font-family: var(--font-text);\n font-size: var(--text-xs);\n font-weight: 450;\n line-height: 1;\n white-space: nowrap;\n text-decoration: none;\n vertical-align: middle;\n transition:\n background-color var(--dur-1) var(--ease-out),\n border-color var(--dur-1) var(--ease-out),\n color var(--dur-1) var(--ease-out);\n}\n\n.tag[data-size='sm'] {\n gap: calc(var(--space-1) / 2);\n min-block-size: var(--space-4);\n padding-inline: var(--space-1);\n font-size: var(--text-2xs);\n}\n\n.tag[data-removable] {\n padding-inline-end: calc(var(--space-1) / 2);\n}\n\n.tag[data-color] {\n --_ink: var(--color-cat-ink);\n --_border: var(--_bg);\n}\n.tag[data-color='1'] {\n --_bg: var(--color-cat-1);\n}\n.tag[data-color='2'] {\n --_bg: var(--color-cat-2);\n}\n.tag[data-color='3'] {\n --_bg: var(--color-cat-3);\n}\n.tag[data-color='4'] {\n --_bg: var(--color-cat-4);\n}\n.tag[data-color='5'] {\n --_bg: var(--color-cat-5);\n}\n.tag[data-color='6'] {\n --_bg: var(--color-cat-6);\n}\n.tag[data-color='7'] {\n --_bg: var(--color-cat-7);\n}\n.tag[data-color='8'] {\n --_bg: var(--color-cat-8);\n}\n\n/* As a link (asChild): the edge firms up on hover. */\n@media (hover: hover) {\n a.tag:hover {\n --_border: var(--color-line-strong);\n --_ink: var(--color-ink);\n }\n a.tag[data-color]:hover {\n --_border: var(--color-cat-ink);\n --_ink: var(--color-cat-ink);\n }\n}\n.tag:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n}\n\n.remove {\n position: relative;\n display: inline-grid;\n place-items: center;\n flex: none;\n inline-size: 1.5em;\n block-size: 1.5em;\n padding: 0;\n border: 0;\n border-radius: var(--tag-radius, var(--radius-chip));\n background: transparent;\n color: inherit;\n font-size: inherit;\n opacity: 0.7;\n cursor: pointer;\n transition:\n opacity var(--dur-1) var(--ease-out),\n background-color var(--dur-1) var(--ease-out);\n}\n/* Larger hit target than the glyph, without growing the tag. */\n.remove::after {\n content: '';\n position: absolute;\n inset: -0.35em;\n}\n.remove > svg {\n inline-size: 1.1em;\n block-size: 1.1em;\n}\n@media (hover: hover) {\n .remove:hover {\n opacity: 1;\n background: color-mix(in oklch, currentColor 14%, transparent);\n }\n}\n.remove:focus-visible {\n opacity: 1;\n outline: 2px solid var(--color-focus);\n outline-offset: 1px;\n}\n\n.list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-2);\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.list > li {\n display: flex;\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Tag, TagList } from './Tag.js';
|
|
2
|
-
export type { TagProps, TagColor, TagListProps } from './Tag.js';
|
|
2
|
+
export type { TagProps, TagColor, TagSize, TagListProps } from './Tag.js';
|