@nadicodeai/ui 12.1.128 → 12.1.130
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/agent-list-row.d.ts +1 -1
- package/dist/components/agent-list-row.js +1 -1
- package/dist/components/badge.d.ts +7 -6
- package/dist/components/badge.d.ts.map +1 -1
- package/dist/components/badge.js +7 -6
- package/dist/components/data-table.d.ts +5 -3
- package/dist/components/data-table.d.ts.map +1 -1
- package/dist/components/data-table.js +2 -1
- package/dist/components/identity-mark.d.ts +10 -5
- package/dist/components/identity-mark.d.ts.map +1 -1
- package/dist/components/identity-mark.js +9 -5
- package/dist/components/record-identity.d.ts +1 -1
- package/dist/components/record-identity.d.ts.map +1 -1
- package/dist/components/record-identity.js +3 -3
- package/dist/components/record-list.d.ts +1 -1
- package/dist/components/record-list.js +2 -2
- package/dist/components/settings-section.d.ts +1 -1
- package/dist/components/settings-section.d.ts.map +1 -1
- package/dist/components/settings-section.js +7 -3
- package/dist/components/status-badge.d.ts +20 -25
- package/dist/components/status-badge.d.ts.map +1 -1
- package/dist/components/status-badge.js +40 -62
- package/dist/internal/status-mark.d.ts +16 -0
- package/dist/internal/status-mark.d.ts.map +1 -0
- package/dist/internal/status-mark.js +128 -0
- package/dist/internal/table-compact-line.d.ts +4 -0
- package/dist/internal/table-compact-line.d.ts.map +1 -1
- package/dist/internal/table-compact-line.js +15 -2
- package/dist/showcase/usage.generated.js +16 -16
- package/dist/styles/components.css +1 -0
- package/package.json +2 -2
|
@@ -1,56 +1,51 @@
|
|
|
1
1
|
import type { ComponentProps } from "react";
|
|
2
|
-
import { type BadgeSize } from "./badge.js";
|
|
3
2
|
type StatusBadgeTone = "success" | "warning" | "danger" | "info" | "neutral";
|
|
4
|
-
type
|
|
3
|
+
type StatusBadgeSize = "sm" | "md" | "lg";
|
|
5
4
|
type StatusBadgeProps = Omit<ComponentProps<"span">, "children" | "onClick"> & {
|
|
6
5
|
tone: StatusBadgeTone;
|
|
7
6
|
label: string;
|
|
8
|
-
size?:
|
|
9
|
-
/** `subtle` in rows, tables, lists and menus; `solid` where the status stands on its own. */
|
|
10
|
-
emphasis?: StatusBadgeEmphasis;
|
|
7
|
+
size?: StatusBadgeSize;
|
|
11
8
|
/** How long the status has held, set after the word in tabular figures. */
|
|
12
9
|
since?: string;
|
|
13
10
|
};
|
|
14
11
|
/**
|
|
15
|
-
* A record's state: a
|
|
12
|
+
* A record's state: a shape of dots and a word, in one of five tones.
|
|
16
13
|
*
|
|
17
14
|
* @family Status
|
|
18
15
|
* @when "Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge)
|
|
19
16
|
* Ours: a record's state, when it sets the row apart: failed, waiting on a person, a step in
|
|
20
17
|
* motion, paused, connected. Gray is the default and every terminal state that needs no action;
|
|
21
18
|
* green is healthy or complete; red is unusable or failed; amber is waiting on a person's
|
|
22
|
-
* decision; blue is a step in motion.
|
|
19
|
+
* decision; blue is a step in motion. Each tone has its own shape: green a circle, blue a diamond,
|
|
20
|
+
* amber a triangle, red a square, gray an empty ring.
|
|
23
21
|
* "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge)
|
|
24
22
|
* Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state's
|
|
25
23
|
* name, in our colour values. The tone and the word are required, and the type asks for both.
|
|
26
24
|
* @instead "For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot)
|
|
27
25
|
* Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For
|
|
28
26
|
* plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use
|
|
29
|
-
* `ActionStatus`. A state beside its name still uses this
|
|
27
|
+
* `ActionStatus`. A state beside its name still uses this component.
|
|
30
28
|
* @behaviour "Badges are static labels." (https://vercel.com/geist/badge)
|
|
31
29
|
* "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge)
|
|
32
|
-
* Ours: a status never moves and is never a button.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge)
|
|
41
|
-
* Ours: it always draws its tone's mark, even for success and error: the mark repeats the tone as a
|
|
42
|
-
* shape so a status reads without colour. Each app keeps one value-to-tone map in one module;
|
|
43
|
-
* nothing else picks a status colour. A healthy record shows no badge, except the
|
|
44
|
-
* operational-state and run-outcome columns defined by DESIGN.md's Status rule.
|
|
30
|
+
* Ours: a status never moves and is never a button. It is the tone's shape and the word, with no
|
|
31
|
+
* pill, no fill and no border, as Vercel's deployment list sets a state beside its time. The shape
|
|
32
|
+
* is filled with dots on a honeycomb grid, the material of the Agent orbs: an outline of evenly
|
|
33
|
+
* spaced dots and a field of dots inside it; gray's ring stays empty, the quiet tone. The dots
|
|
34
|
+
* take the tone's colour and the word the foreground colour; amber's outline takes its deep step
|
|
35
|
+
* so the triangle holds on white. `size`
|
|
36
|
+
* follows the type beside it: `sm` (a 14 px shape, 12 px word) in a small line such as an Agent's
|
|
37
|
+
* state line, `md` (16 px, 13 px) in a row, `lg` (20 px, 14 px) beside an object's name.
|
|
45
38
|
* @words "Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge)
|
|
46
39
|
* Ours: a state, not a verb (*Failed*, *Waiting for you*), in sentence case, one to three words, no
|
|
47
40
|
* trailing ellipsis. Italian runs a third longer: *In attesa di te*.
|
|
48
41
|
* "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge)
|
|
49
42
|
* "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)
|
|
50
43
|
* @access "Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge)
|
|
51
|
-
* Ours: the word is the name; the
|
|
52
|
-
* Not focusable.
|
|
44
|
+
* Ours: the word is the name; the shape is `aria-hidden`, so colour never carries the state alone.
|
|
45
|
+
* Not focusable. Each app keeps one value-to-tone map in one module; nothing else picks a status
|
|
46
|
+
* colour. A healthy record shows no status, except the operational-state and run-outcome columns
|
|
47
|
+
* defined by DESIGN.md's Status rule.
|
|
53
48
|
*/
|
|
54
|
-
declare function StatusBadge({ tone, label, since, className, size,
|
|
55
|
-
export { StatusBadge, type
|
|
49
|
+
declare function StatusBadge({ tone, label, since, className, size, ...props }: StatusBadgeProps): import("react").JSX.Element;
|
|
50
|
+
export { StatusBadge, type StatusBadgeProps, type StatusBadgeTone };
|
|
56
51
|
//# sourceMappingURL=status-badge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-badge.d.ts","sourceRoot":"","sources":["../../src/components/status-badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"status-badge.d.ts","sourceRoot":"","sources":["../../src/components/status-badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAS5C,KAAK,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7E,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAsD1C,KAAK,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,GAAG;IAC7E,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,iBAAS,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAW,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,+BA4B9F;AAED,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,CAAC"}
|
|
@@ -1,93 +1,71 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { Badge } from "./badge.js";
|
|
2
|
+
import { STATUS_MARK_DOT_RADIUS, STATUS_MARK_PATHS, } from "../internal/status-mark.js";
|
|
4
3
|
import { cn } from "../lib/utils.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
},
|
|
4
|
+
// One shape per tone, always the same one: the tone said again as a shape, so a
|
|
5
|
+
// status reads without colour. Amber's edge takes the deep step, because giallo
|
|
6
|
+
// alone does not hold a shape on white.
|
|
7
|
+
const TONE = {
|
|
8
|
+
success: { name: "green", shape: "circle", ink: "text-success" },
|
|
11
9
|
warning: {
|
|
12
10
|
name: "amber",
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
danger: {
|
|
17
|
-
name: "red",
|
|
18
|
-
subtle: "bg-destructive-soft text-destructive-soft-foreground",
|
|
19
|
-
solid: "bg-destructive text-destructive-foreground",
|
|
20
|
-
},
|
|
21
|
-
info: {
|
|
22
|
-
name: "blue",
|
|
23
|
-
subtle: "bg-info-soft text-info-soft-foreground",
|
|
24
|
-
solid: "bg-info text-info-foreground",
|
|
11
|
+
shape: "triangle",
|
|
12
|
+
ink: "text-warning",
|
|
13
|
+
edge: "text-warning-soft-foreground",
|
|
25
14
|
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
solid: "bg-muted-foreground text-background",
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
// One mark per tone, always the same one: the tone said again as a shape, so a
|
|
33
|
-
// status reads without colour. The info dot is filled and still.
|
|
34
|
-
const MARK_BY_TONE = {
|
|
35
|
-
success: CircleCheck,
|
|
36
|
-
warning: Clock,
|
|
37
|
-
danger: CircleX,
|
|
38
|
-
info: CircleDot,
|
|
39
|
-
neutral: CirclePause,
|
|
15
|
+
danger: { name: "red", shape: "square", ink: "text-destructive" },
|
|
16
|
+
info: { name: "blue", shape: "diamond", ink: "text-info" },
|
|
17
|
+
neutral: { name: "gray", shape: "ring", ink: "text-muted-foreground" },
|
|
40
18
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
md: "has-data-[icon=inline-start]:pl-1.25",
|
|
46
|
-
lg: "has-data-[icon=inline-start]:pl-2",
|
|
19
|
+
const SIZE = {
|
|
20
|
+
sm: { text: "text-caption gap-1", mark: "size-3.5" },
|
|
21
|
+
md: { text: "text-label-sm gap-1.5", mark: "size-4" },
|
|
22
|
+
lg: { text: "text-label gap-2", mark: "size-5" },
|
|
47
23
|
};
|
|
24
|
+
function StatusMark({ shape, className, edge, }) {
|
|
25
|
+
const paths = STATUS_MARK_PATHS[shape];
|
|
26
|
+
return (_jsxs("svg", { "aria-hidden": "true", className: cn("shrink-0", className), "data-slot": "status-badge-mark", "data-mark": shape, fill: "none", stroke: "currentColor", strokeLinecap: "round", viewBox: "-10 -10 20 20", children: [_jsx("path", { d: paths.inner, strokeOpacity: 0.92, strokeWidth: 2 * STATUS_MARK_DOT_RADIUS.inner }), _jsx("path", { className: edge, d: paths.edge, strokeWidth: 2 * STATUS_MARK_DOT_RADIUS.edge })] }));
|
|
27
|
+
}
|
|
48
28
|
/**
|
|
49
|
-
* A record's state: a
|
|
29
|
+
* A record's state: a shape of dots and a word, in one of five tones.
|
|
50
30
|
*
|
|
51
31
|
* @family Status
|
|
52
32
|
* @when "Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge)
|
|
53
33
|
* Ours: a record's state, when it sets the row apart: failed, waiting on a person, a step in
|
|
54
34
|
* motion, paused, connected. Gray is the default and every terminal state that needs no action;
|
|
55
35
|
* green is healthy or complete; red is unusable or failed; amber is waiting on a person's
|
|
56
|
-
* decision; blue is a step in motion.
|
|
36
|
+
* decision; blue is a step in motion. Each tone has its own shape: green a circle, blue a diamond,
|
|
37
|
+
* amber a triangle, red a square, gray an empty ring.
|
|
57
38
|
* "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge)
|
|
58
39
|
* Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state's
|
|
59
40
|
* name, in our colour values. The tone and the word are required, and the type asks for both.
|
|
60
41
|
* @instead "For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot)
|
|
61
42
|
* Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For
|
|
62
43
|
* plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use
|
|
63
|
-
* `ActionStatus`. A state beside its name still uses this
|
|
44
|
+
* `ActionStatus`. A state beside its name still uses this component.
|
|
64
45
|
* @behaviour "Badges are static labels." (https://vercel.com/geist/badge)
|
|
65
46
|
* "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge)
|
|
66
|
-
* Ours: a status never moves and is never a button.
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge)
|
|
75
|
-
* Ours: it always draws its tone's mark, even for success and error: the mark repeats the tone as a
|
|
76
|
-
* shape so a status reads without colour. Each app keeps one value-to-tone map in one module;
|
|
77
|
-
* nothing else picks a status colour. A healthy record shows no badge, except the
|
|
78
|
-
* operational-state and run-outcome columns defined by DESIGN.md's Status rule.
|
|
47
|
+
* Ours: a status never moves and is never a button. It is the tone's shape and the word, with no
|
|
48
|
+
* pill, no fill and no border, as Vercel's deployment list sets a state beside its time. The shape
|
|
49
|
+
* is filled with dots on a honeycomb grid, the material of the Agent orbs: an outline of evenly
|
|
50
|
+
* spaced dots and a field of dots inside it; gray's ring stays empty, the quiet tone. The dots
|
|
51
|
+
* take the tone's colour and the word the foreground colour; amber's outline takes its deep step
|
|
52
|
+
* so the triangle holds on white. `size`
|
|
53
|
+
* follows the type beside it: `sm` (a 14 px shape, 12 px word) in a small line such as an Agent's
|
|
54
|
+
* state line, `md` (16 px, 13 px) in a row, `lg` (20 px, 14 px) beside an object's name.
|
|
79
55
|
* @words "Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge)
|
|
80
56
|
* Ours: a state, not a verb (*Failed*, *Waiting for you*), in sentence case, one to three words, no
|
|
81
57
|
* trailing ellipsis. Italian runs a third longer: *In attesa di te*.
|
|
82
58
|
* "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge)
|
|
83
59
|
* "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)
|
|
84
60
|
* @access "Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge)
|
|
85
|
-
* Ours: the word is the name; the
|
|
86
|
-
* Not focusable.
|
|
61
|
+
* Ours: the word is the name; the shape is `aria-hidden`, so colour never carries the state alone.
|
|
62
|
+
* Not focusable. Each app keeps one value-to-tone map in one module; nothing else picks a status
|
|
63
|
+
* colour. A healthy record shows no status, except the operational-state and run-outcome columns
|
|
64
|
+
* defined by DESIGN.md's Status rule.
|
|
87
65
|
*/
|
|
88
|
-
function StatusBadge({ tone, label, since, className, size = "md",
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
return (_jsxs(
|
|
66
|
+
function StatusBadge({ tone, label, since, className, size = "md", ...props }) {
|
|
67
|
+
const { name, shape, ink, edge } = TONE[tone];
|
|
68
|
+
const { text, mark } = SIZE[size];
|
|
69
|
+
return (_jsxs("span", { className: cn("inline-flex w-fit max-w-full shrink-0 items-center font-medium whitespace-nowrap text-foreground", text, className), "data-size": size, "data-slot": "status-badge", "data-variant": name, ...props, children: [_jsx(StatusMark, { className: cn(mark, ink), edge: edge, shape: shape }), label, since ? (_jsx("span", { className: "font-normal text-muted-foreground tabular-nums", "data-slot": "status-badge-since", children: since })) : null] }));
|
|
92
70
|
}
|
|
93
71
|
export { StatusBadge };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type StatusMarkShape = "circle" | "diamond" | "triangle" | "square" | "ring";
|
|
2
|
+
export type StatusMarkDot = {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
edge: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const STATUS_MARK_DOTS: Record<StatusMarkShape, readonly StatusMarkDot[]>;
|
|
8
|
+
export declare const STATUS_MARK_DOT_RADIUS: {
|
|
9
|
+
readonly inner: 0.7;
|
|
10
|
+
readonly edge: 0.76;
|
|
11
|
+
};
|
|
12
|
+
export declare const STATUS_MARK_PATHS: Record<StatusMarkShape, {
|
|
13
|
+
inner: string;
|
|
14
|
+
edge: string;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=status-mark.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-mark.d.ts","sourceRoot":"","sources":["../../src/internal/status-mark.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpF,MAAM,MAAM,aAAa,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AA+GpE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,eAAe,EAAE,SAAS,aAAa,EAAE,CAM9E,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;CAAsC,CAAC;AAgB1E,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,eAAe,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMtF,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The five status marks: a shape filled with dots on a honeycomb grid, the
|
|
3
|
+
* material of the Agent orbs' dotted forms. Every coordinate is on a 20-unit
|
|
4
|
+
* square centred on 0, the size the marks are tuned at; a row of evenly spaced
|
|
5
|
+
* edge dots draws the outline and the inner dots keep clear of it, so the
|
|
6
|
+
* shape reads before the texture. The dots are computed once, at load.
|
|
7
|
+
*/
|
|
8
|
+
const PITCH = 1.9;
|
|
9
|
+
// Sized by eye so the five carry the same weight beside a word; the triangle
|
|
10
|
+
// sits low because its weight is in its base.
|
|
11
|
+
const MARKS = {
|
|
12
|
+
circle: { outline: { kind: "circle", r: 6.7 } },
|
|
13
|
+
diamond: {
|
|
14
|
+
outline: {
|
|
15
|
+
kind: "polygon",
|
|
16
|
+
corners: [
|
|
17
|
+
[0, -7.8],
|
|
18
|
+
[7.8, 0],
|
|
19
|
+
[0, 7.8],
|
|
20
|
+
[-7.8, 0],
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
triangle: {
|
|
25
|
+
outline: {
|
|
26
|
+
kind: "polygon",
|
|
27
|
+
corners: [
|
|
28
|
+
[0, -6.9],
|
|
29
|
+
[7.9, 6.6],
|
|
30
|
+
[-7.9, 6.6],
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
square: {
|
|
35
|
+
outline: {
|
|
36
|
+
kind: "polygon",
|
|
37
|
+
corners: [
|
|
38
|
+
[-5.8, -5.8],
|
|
39
|
+
[5.8, -5.8],
|
|
40
|
+
[5.8, 5.8],
|
|
41
|
+
[-5.8, 5.8],
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
ring: { outline: { kind: "circle", r: 6.7 }, hollow: true },
|
|
46
|
+
};
|
|
47
|
+
function sides(corners) {
|
|
48
|
+
return corners.map((start, index) => [start, corners[(index + 1) % corners.length]]);
|
|
49
|
+
}
|
|
50
|
+
function contains(outline, [x, y]) {
|
|
51
|
+
if (outline.kind === "circle")
|
|
52
|
+
return Math.hypot(x, y) <= outline.r;
|
|
53
|
+
let inside = false;
|
|
54
|
+
for (const [[ax, ay], [bx, by]] of sides(outline.corners)) {
|
|
55
|
+
if (ay > y !== by > y && x < ((bx - ax) * (y - ay)) / (by - ay) + ax)
|
|
56
|
+
inside = !inside;
|
|
57
|
+
}
|
|
58
|
+
return inside;
|
|
59
|
+
}
|
|
60
|
+
function distanceToEdge(outline, [x, y]) {
|
|
61
|
+
if (outline.kind === "circle")
|
|
62
|
+
return outline.r - Math.hypot(x, y);
|
|
63
|
+
return Math.min(...sides(outline.corners).map(([[ax, ay], [bx, by]]) => {
|
|
64
|
+
const dx = bx - ax;
|
|
65
|
+
const dy = by - ay;
|
|
66
|
+
const t = Math.max(0, Math.min(1, ((x - ax) * dx + (y - ay) * dy) / (dx * dx + dy * dy)));
|
|
67
|
+
return Math.hypot(x - ax - t * dx, y - ay - t * dy);
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
// A dot on every corner, then evenly spaced along each side.
|
|
71
|
+
function edgeDots(outline) {
|
|
72
|
+
if (outline.kind === "circle") {
|
|
73
|
+
const count = Math.round((2 * Math.PI * outline.r) / PITCH);
|
|
74
|
+
return Array.from({ length: count }, (_, index) => {
|
|
75
|
+
const angle = -Math.PI / 2 + (index / count) * 2 * Math.PI;
|
|
76
|
+
return [Math.cos(angle) * outline.r, Math.sin(angle) * outline.r];
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return sides(outline.corners).flatMap(([[ax, ay], [bx, by]]) => {
|
|
80
|
+
const count = Math.max(1, Math.round(Math.hypot(bx - ax, by - ay) / PITCH));
|
|
81
|
+
return Array.from({ length: count }, (_, step) => [ax + ((bx - ax) * step) / count, ay + ((by - ay) * step) / count]);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function innerDots(outline) {
|
|
85
|
+
const rowHeight = (PITCH * Math.sqrt(3)) / 2;
|
|
86
|
+
const dots = [];
|
|
87
|
+
for (let row = -6; row <= 6; row++) {
|
|
88
|
+
for (let column = -6; column <= 6; column++) {
|
|
89
|
+
const dot = [column * PITCH + (row % 2 === 0 ? 0 : PITCH / 2), row * rowHeight];
|
|
90
|
+
if (contains(outline, dot) && distanceToEdge(outline, dot) >= PITCH * 0.85)
|
|
91
|
+
dots.push(dot);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return dots;
|
|
95
|
+
}
|
|
96
|
+
function dotsOf(shape) {
|
|
97
|
+
const { outline, hollow } = MARKS[shape];
|
|
98
|
+
return [
|
|
99
|
+
...(hollow ? [] : innerDots(outline)).map(([x, y]) => ({ x, y, edge: false })),
|
|
100
|
+
...edgeDots(outline).map(([x, y]) => ({ x, y, edge: true })),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
export const STATUS_MARK_DOTS = {
|
|
104
|
+
circle: dotsOf("circle"),
|
|
105
|
+
diamond: dotsOf("diamond"),
|
|
106
|
+
triangle: dotsOf("triangle"),
|
|
107
|
+
square: dotsOf("square"),
|
|
108
|
+
ring: dotsOf("ring"),
|
|
109
|
+
};
|
|
110
|
+
export const STATUS_MARK_DOT_RADIUS = { inner: 0.7, edge: 0.76 };
|
|
111
|
+
// Each layer of dots as one path of zero-length strokes: drawn with round caps
|
|
112
|
+
// at twice the dot's radius, every `M x y h0` is one dot, so a mark is two
|
|
113
|
+
// nodes however many dots it has.
|
|
114
|
+
const pathOf = (dots) => dots.map(({ x, y }) => `M${x.toFixed(2)} ${y.toFixed(2)}h0`).join("");
|
|
115
|
+
function pathsOf(shape) {
|
|
116
|
+
const dots = STATUS_MARK_DOTS[shape];
|
|
117
|
+
return {
|
|
118
|
+
inner: pathOf(dots.filter((dot) => !dot.edge)),
|
|
119
|
+
edge: pathOf(dots.filter((dot) => dot.edge)),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export const STATUS_MARK_PATHS = {
|
|
123
|
+
circle: pathsOf("circle"),
|
|
124
|
+
diamond: pathsOf("diamond"),
|
|
125
|
+
triangle: pathsOf("triangle"),
|
|
126
|
+
square: pathsOf("square"),
|
|
127
|
+
ring: pathsOf("ring"),
|
|
128
|
+
};
|
|
@@ -6,6 +6,8 @@ export type TableDetailFact = {
|
|
|
6
6
|
value: React.ReactNode;
|
|
7
7
|
/** The value is prose a reader needs whole, such as why a check failed: the line wraps instead of ending in an ellipsis. */
|
|
8
8
|
wraps?: boolean;
|
|
9
|
+
/** The value stands beside the record's name instead, which ends in an ellipsis before it: a status. */
|
|
10
|
+
besideName?: boolean;
|
|
9
11
|
};
|
|
10
12
|
export declare function DetailFacts({ facts, leadingSeparator, }: {
|
|
11
13
|
facts: readonly TableDetailFact[];
|
|
@@ -25,6 +27,8 @@ export declare function TableCompactLine({ children, ...value }: TableCompactLin
|
|
|
25
27
|
}): React.JSX.Element;
|
|
26
28
|
/** The compact facts of the row this stands in, after a middle dot; nothing outside such a row or on a card wide enough for the columns. */
|
|
27
29
|
export declare function TableCompactFacts(): React.JSX.Element | null;
|
|
30
|
+
/** The compact facts that stand beside the name of the row this stands in, which no ellipsis cuts; nothing outside such a row or on a card wide enough for the columns. */
|
|
31
|
+
export declare function TableCompactNameFacts(): React.JSX.Element | null;
|
|
28
32
|
/**
|
|
29
33
|
* Whether the entity rows around it are laid out compact now, read from the layout the browser
|
|
30
34
|
* drew: a row's cells move what does not fit beside the name under it instead.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,gBAAwB,GACzB,EAAE;IACD,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,uBAQA;AAED,KAAK,qBAAqB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAC;AAI/F;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAEvD;AAED,4IAA4I;AAC5I,wBAAgB,iBAAiB,6BAiBhC;AAED,2KAA2K;AAC3K,wBAAgB,qBAAqB,6BAkBpC;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAEA;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
|
|
@@ -18,9 +18,22 @@ export function TableCompactLine({ children, ...value }) {
|
|
|
18
18
|
/** The compact facts of the row this stands in, after a middle dot; nothing outside such a row or on a card wide enough for the columns. */
|
|
19
19
|
export function TableCompactFacts() {
|
|
20
20
|
const line = React.useContext(TableCompactLineContext);
|
|
21
|
-
if (!line
|
|
21
|
+
if (!line)
|
|
22
22
|
return null;
|
|
23
|
-
|
|
23
|
+
const facts = line.facts.filter((fact) => !fact.besideName);
|
|
24
|
+
if (facts.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
return (_jsx("span", { className: cn("[&_[data-slot=tag]]:contents [&>span>:is(div,p,.block,.grid,.flex)]:inline", tableCompactLayouts[line.layout].details), "data-slot": "table-detail-line", "data-wraps": facts.some((fact) => fact.wraps) || undefined, children: _jsx(DetailFacts, { facts: facts, leadingSeparator: true }) }));
|
|
27
|
+
}
|
|
28
|
+
/** The compact facts that stand beside the name of the row this stands in, which no ellipsis cuts; nothing outside such a row or on a card wide enough for the columns. */
|
|
29
|
+
export function TableCompactNameFacts() {
|
|
30
|
+
const line = React.useContext(TableCompactLineContext);
|
|
31
|
+
if (!line)
|
|
32
|
+
return null;
|
|
33
|
+
const facts = line.facts.filter((fact) => fact.besideName);
|
|
34
|
+
if (facts.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
return (_jsx("span", { className: cn("flex shrink-0 items-center gap-2", tableCompactLayouts[line.layout].details), "data-slot": "table-name-facts", children: facts.map((fact) => (_jsxs("span", { className: "flex items-center", children: [_jsxs("span", { className: "sr-only", children: [fact.label, ": "] }), fact.value] }, fact.id))) }));
|
|
24
37
|
}
|
|
25
38
|
const TableLaidOutCompactContext = React.createContext(false);
|
|
26
39
|
/**
|