@cloudgrid-io/ui 0.40.0 → 0.41.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/blocks/action-link.d.ts +31 -0
- package/dist/blocks/action-link.d.ts.map +1 -0
- package/dist/blocks/action-link.js +40 -0
- package/dist/blocks/action-link.js.map +1 -0
- package/dist/blocks/activity-row.d.ts +42 -0
- package/dist/blocks/activity-row.d.ts.map +1 -0
- package/dist/blocks/activity-row.js +37 -0
- package/dist/blocks/activity-row.js.map +1 -0
- package/dist/blocks/add-tile.d.ts +31 -0
- package/dist/blocks/add-tile.d.ts.map +1 -0
- package/dist/blocks/add-tile.js +29 -0
- package/dist/blocks/add-tile.js.map +1 -0
- package/dist/blocks/avatar-stack.d.ts +40 -0
- package/dist/blocks/avatar-stack.d.ts.map +1 -0
- package/dist/blocks/avatar-stack.js +41 -0
- package/dist/blocks/avatar-stack.js.map +1 -0
- package/dist/blocks/avatar.d.ts +31 -5
- package/dist/blocks/avatar.d.ts.map +1 -1
- package/dist/blocks/avatar.js +57 -9
- package/dist/blocks/avatar.js.map +1 -1
- package/dist/blocks/copy-button.d.ts +59 -0
- package/dist/blocks/copy-button.d.ts.map +1 -0
- package/dist/blocks/copy-button.js +76 -0
- package/dist/blocks/copy-button.js.map +1 -0
- package/dist/blocks/dashed-banner.d.ts +36 -0
- package/dist/blocks/dashed-banner.d.ts.map +1 -0
- package/dist/blocks/dashed-banner.js +31 -0
- package/dist/blocks/dashed-banner.js.map +1 -0
- package/dist/blocks/dashed-line.d.ts +44 -0
- package/dist/blocks/dashed-line.d.ts.map +1 -0
- package/dist/blocks/dashed-line.js +46 -0
- package/dist/blocks/dashed-line.js.map +1 -0
- package/dist/blocks/empty-creations.d.ts +18 -1
- package/dist/blocks/empty-creations.d.ts.map +1 -1
- package/dist/blocks/empty-creations.js +25 -9
- package/dist/blocks/empty-creations.js.map +1 -1
- package/dist/blocks/entity-card.d.ts +37 -2
- package/dist/blocks/entity-card.d.ts.map +1 -1
- package/dist/blocks/entity-card.js +70 -7
- package/dist/blocks/entity-card.js.map +1 -1
- package/dist/blocks/filter-tabs.d.ts +45 -0
- package/dist/blocks/filter-tabs.d.ts.map +1 -0
- package/dist/blocks/filter-tabs.js +48 -0
- package/dist/blocks/filter-tabs.js.map +1 -0
- package/dist/blocks/header-menu.d.ts +54 -0
- package/dist/blocks/header-menu.d.ts.map +1 -0
- package/dist/blocks/header-menu.js +63 -0
- package/dist/blocks/header-menu.js.map +1 -0
- package/dist/blocks/header-pill.d.ts +41 -0
- package/dist/blocks/header-pill.d.ts.map +1 -0
- package/dist/blocks/header-pill.js +67 -0
- package/dist/blocks/header-pill.js.map +1 -0
- package/dist/blocks/icons.d.ts +19 -2
- package/dist/blocks/icons.d.ts.map +1 -1
- package/dist/blocks/icons.js +22 -2
- package/dist/blocks/icons.js.map +1 -1
- package/dist/blocks/menu-trigger.d.ts +35 -0
- package/dist/blocks/menu-trigger.d.ts.map +1 -0
- package/dist/blocks/menu-trigger.js +24 -0
- package/dist/blocks/menu-trigger.js.map +1 -0
- package/dist/blocks/meter.d.ts +9 -1
- package/dist/blocks/meter.d.ts.map +1 -1
- package/dist/blocks/meter.js +6 -2
- package/dist/blocks/meter.js.map +1 -1
- package/dist/blocks/phosphor-marks.d.ts +8 -2
- package/dist/blocks/phosphor-marks.d.ts.map +1 -1
- package/dist/blocks/phosphor-marks.js +12 -3
- package/dist/blocks/phosphor-marks.js.map +1 -1
- package/dist/blocks/ranked-list.d.ts +15 -1
- package/dist/blocks/ranked-list.d.ts.map +1 -1
- package/dist/blocks/ranked-list.js +18 -5
- package/dist/blocks/ranked-list.js.map +1 -1
- package/dist/blocks/scroll-rail.d.ts +15 -0
- package/dist/blocks/scroll-rail.d.ts.map +1 -0
- package/dist/blocks/scroll-rail.js +134 -0
- package/dist/blocks/scroll-rail.js.map +1 -0
- package/dist/blocks/section-heading.d.ts +21 -0
- package/dist/blocks/section-heading.d.ts.map +1 -0
- package/dist/blocks/section-heading.js +18 -0
- package/dist/blocks/section-heading.js.map +1 -0
- package/dist/blocks/stat-tile.d.ts +21 -1
- package/dist/blocks/stat-tile.d.ts.map +1 -1
- package/dist/blocks/stat-tile.js +11 -6
- package/dist/blocks/stat-tile.js.map +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/theme.css +2 -0
- package/dist/tokens/base.json +42 -2
- package/dist/tokens.css +18 -2
- package/dist/tokens.json +6 -2
- package/dist/ui/button.d.ts +1 -1
- package/dist/ui/button.d.ts.map +1 -1
- package/dist/ui/button.js +10 -0
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/drawer.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
2
|
+
/**
|
|
3
|
+
* The teal text link of the 2026 Home (#382): "Explore All", "Open",
|
|
4
|
+
* "Dismiss", "onboard an agent" (spec §4.3 to §4.8, Figma Tertiary Dark).
|
|
5
|
+
*
|
|
6
|
+
* Body (15) in `--cg-tertiary-dark` (#45CDD4), no underline at rest, an
|
|
7
|
+
* underline on hover (the frames draw no hover state; this is the library's
|
|
8
|
+
* feedback for a text link), the focus ring on keyboard focus.
|
|
9
|
+
*
|
|
10
|
+
* ## The colour is under AA, on purpose
|
|
11
|
+
*
|
|
12
|
+
* #45CDD4 measures 1.92:1 on white. The design uses it knowingly (Gilad,
|
|
13
|
+
* 2026-11-01, spec §6.1), and every link drawn in it also carries an
|
|
14
|
+
* accessible name, so a reader who cannot see the colour still gets the words.
|
|
15
|
+
*
|
|
16
|
+
* ## The token comes from ui#381
|
|
17
|
+
*
|
|
18
|
+
* `--cg-tertiary-dark` is the header set's token (ui#381); this block reads it
|
|
19
|
+
* and does not declare it.
|
|
20
|
+
*
|
|
21
|
+
* `ACTION_LINK_CLASS` is exported for a control that is not an anchor but
|
|
22
|
+
* should look like one: Dismiss (a button) and the empty state's "onboard an
|
|
23
|
+
* agent" (a `CopyButton`).
|
|
24
|
+
*/
|
|
25
|
+
declare const ACTION_LINK_CLASS = "inline-flex items-center rounded-2xs text-body-sm text-(--cg-tertiary-dark) no-underline underline-offset-4 hover:underline outline-none focus-visible:ring-3 focus-visible:ring-ring";
|
|
26
|
+
declare function ActionLink({ render, weight, className, ...props }: useRender.ComponentProps<"a"> & {
|
|
27
|
+
/** `regular` (Body: Open, "onboard an agent") or `bold` (Body Bold: Explore All, Dismiss). */
|
|
28
|
+
weight?: "regular" | "bold";
|
|
29
|
+
}): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
30
|
+
export { ACTION_LINK_CLASS, ActionLink };
|
|
31
|
+
//# sourceMappingURL=action-link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-link.d.ts","sourceRoot":"","sources":["../../src/blocks/action-link.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAIrD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,iBAAiB,0LACkK,CAAA;AAEzL,iBAAS,UAAU,CAAC,EAClB,MAAM,EACN,MAAkB,EAClB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG;IACjC,8FAA8F;IAC9F,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;CAC5B,8FAUA;AAED,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { cn } from "../lib/cloudgrid-cn.js";
|
|
4
|
+
/**
|
|
5
|
+
* The teal text link of the 2026 Home (#382): "Explore All", "Open",
|
|
6
|
+
* "Dismiss", "onboard an agent" (spec §4.3 to §4.8, Figma Tertiary Dark).
|
|
7
|
+
*
|
|
8
|
+
* Body (15) in `--cg-tertiary-dark` (#45CDD4), no underline at rest, an
|
|
9
|
+
* underline on hover (the frames draw no hover state; this is the library's
|
|
10
|
+
* feedback for a text link), the focus ring on keyboard focus.
|
|
11
|
+
*
|
|
12
|
+
* ## The colour is under AA, on purpose
|
|
13
|
+
*
|
|
14
|
+
* #45CDD4 measures 1.92:1 on white. The design uses it knowingly (Gilad,
|
|
15
|
+
* 2026-11-01, spec §6.1), and every link drawn in it also carries an
|
|
16
|
+
* accessible name, so a reader who cannot see the colour still gets the words.
|
|
17
|
+
*
|
|
18
|
+
* ## The token comes from ui#381
|
|
19
|
+
*
|
|
20
|
+
* `--cg-tertiary-dark` is the header set's token (ui#381); this block reads it
|
|
21
|
+
* and does not declare it.
|
|
22
|
+
*
|
|
23
|
+
* `ACTION_LINK_CLASS` is exported for a control that is not an anchor but
|
|
24
|
+
* should look like one: Dismiss (a button) and the empty state's "onboard an
|
|
25
|
+
* agent" (a `CopyButton`).
|
|
26
|
+
*/
|
|
27
|
+
const ACTION_LINK_CLASS = "inline-flex items-center rounded-2xs text-body-sm text-(--cg-tertiary-dark) no-underline underline-offset-4 hover:underline outline-none focus-visible:ring-3 focus-visible:ring-ring";
|
|
28
|
+
function ActionLink({ render, weight = "regular", className, ...props }) {
|
|
29
|
+
return useRender({
|
|
30
|
+
render,
|
|
31
|
+
defaultTagName: "a",
|
|
32
|
+
props: {
|
|
33
|
+
"data-slot": "action-link",
|
|
34
|
+
className: cn(ACTION_LINK_CLASS, weight === "bold" && "font-bold", className),
|
|
35
|
+
...props,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export { ACTION_LINK_CLASS, ActionLink };
|
|
40
|
+
//# sourceMappingURL=action-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-link.js","sourceRoot":"","sources":["../../src/blocks/action-link.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAErD,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AAEvC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,iBAAiB,GACrB,uLAAuL,CAAA;AAEzL,SAAS,UAAU,CAAC,EAClB,MAAM,EACN,MAAM,GAAG,SAAS,EAClB,SAAS,EACT,GAAG,KAAK,EAIT;IACC,OAAO,SAAS,CAAC;QACf,MAAM;QACN,cAAc,EAAE,GAAG;QACnB,KAAK,EAAE;YACL,WAAW,EAAE,aAAa;YAC1B,SAAS,EAAE,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,MAAM,IAAI,WAAW,EAAE,SAAS,CAAC;YAC7E,GAAG,KAAK;SACT;KACF,CAAC,CAAA;AACJ,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
/**
|
|
4
|
+
* One line of activity: Home's "My Focus" row (#382, spec §4.7, Figma
|
|
5
|
+
* `11323:8714`, drawn there as "Grid Pulse").
|
|
6
|
+
*
|
|
7
|
+
* White, a 1px hairline, radius 12, 16 inside (the hairline drawn inside it, so
|
|
8
|
+
* the row is the frame's 54 tall), the content centred 20 apart: the time
|
|
9
|
+
* (Small, muted), a picture, one sentence (Body) and a teal "Open".
|
|
10
|
+
*
|
|
11
|
+
* ## The picture overhangs
|
|
12
|
+
*
|
|
13
|
+
* The frame lays the picture out in a 96 x 22 box (a Body line) and paints the
|
|
14
|
+
* 96 x 64 image centred on it, so it pokes 5px past the row's top and bottom
|
|
15
|
+
* and the row stays one line tall. Drawn the same way: a 22px box and an
|
|
16
|
+
* absolutely placed image. Decorative (`alt=""`); the sentence names the thing.
|
|
17
|
+
*
|
|
18
|
+
* ## Narrow widths
|
|
19
|
+
*
|
|
20
|
+
* The row lays out by its own width (a container query). Under 36rem the
|
|
21
|
+
* sentence wraps onto its own line under the picture (spec §4.12).
|
|
22
|
+
*
|
|
23
|
+
* The time is muted-strong rather than the frame's slate-500 (3.41:1): it is
|
|
24
|
+
* content (#232). The action is described by the row's sentence
|
|
25
|
+
* (`aria-describedby`), so a list of rows' "Open" links reads apart.
|
|
26
|
+
*/
|
|
27
|
+
declare function ActivityRow({ time, thumbnailSrc, actionLabel, href, render, className, children, ...props }: Omit<React.ComponentProps<"div">, "children"> & {
|
|
28
|
+
/** "Just now". Pass a `<time>` for a machine-readable date. */
|
|
29
|
+
time: React.ReactNode;
|
|
30
|
+
/** The creation's picture. */
|
|
31
|
+
thumbnailSrc?: string;
|
|
32
|
+
/** The sentence. */
|
|
33
|
+
children: React.ReactNode;
|
|
34
|
+
/** The action's words. Default "Open". */
|
|
35
|
+
actionLabel?: string;
|
|
36
|
+
/** Where the action goes. */
|
|
37
|
+
href?: string;
|
|
38
|
+
/** The action's element, for a router's link: `render={<Link href="…" />}`. */
|
|
39
|
+
render?: useRender.ComponentProps<"a">["render"];
|
|
40
|
+
}): React.JSX.Element;
|
|
41
|
+
export { ActivityRow };
|
|
42
|
+
//# sourceMappingURL=activity-row.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-row.d.ts","sourceRoot":"","sources":["../../src/blocks/activity-row.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAK1D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iBAAS,WAAW,CAAC,EACnB,IAAI,EACJ,YAAY,EACZ,WAAoB,EACpB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACjD,+DAA+D;IAC/D,IAAI,EAAE,KAAK,CAAC,SAAS,CAAA;IACrB,8BAA8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,oBAAoB;IACpB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,MAAM,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;CACjD,qBA2CA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/cloudgrid-cn.js";
|
|
4
|
+
import { ActionLink } from "./action-link.js";
|
|
5
|
+
/**
|
|
6
|
+
* One line of activity: Home's "My Focus" row (#382, spec §4.7, Figma
|
|
7
|
+
* `11323:8714`, drawn there as "Grid Pulse").
|
|
8
|
+
*
|
|
9
|
+
* White, a 1px hairline, radius 12, 16 inside (the hairline drawn inside it, so
|
|
10
|
+
* the row is the frame's 54 tall), the content centred 20 apart: the time
|
|
11
|
+
* (Small, muted), a picture, one sentence (Body) and a teal "Open".
|
|
12
|
+
*
|
|
13
|
+
* ## The picture overhangs
|
|
14
|
+
*
|
|
15
|
+
* The frame lays the picture out in a 96 x 22 box (a Body line) and paints the
|
|
16
|
+
* 96 x 64 image centred on it, so it pokes 5px past the row's top and bottom
|
|
17
|
+
* and the row stays one line tall. Drawn the same way: a 22px box and an
|
|
18
|
+
* absolutely placed image. Decorative (`alt=""`); the sentence names the thing.
|
|
19
|
+
*
|
|
20
|
+
* ## Narrow widths
|
|
21
|
+
*
|
|
22
|
+
* The row lays out by its own width (a container query). Under 36rem the
|
|
23
|
+
* sentence wraps onto its own line under the picture (spec §4.12).
|
|
24
|
+
*
|
|
25
|
+
* The time is muted-strong rather than the frame's slate-500 (3.41:1): it is
|
|
26
|
+
* content (#232). The action is described by the row's sentence
|
|
27
|
+
* (`aria-describedby`), so a list of rows' "Open" links reads apart.
|
|
28
|
+
*/
|
|
29
|
+
function ActivityRow({ time, thumbnailSrc, actionLabel = "Open", href, render, className, children, ...props }) {
|
|
30
|
+
const hasAction = href !== undefined || render !== undefined;
|
|
31
|
+
// Every row's action is "Open", so a screen reader's list of links would show
|
|
32
|
+
// N identical entries. The link is described by its own row's sentence.
|
|
33
|
+
const textId = React.useId();
|
|
34
|
+
return (_jsx("div", { "data-slot": "activity-row", className: cn("@container/activity-row rounded-md border border-hairline bg-card p-[15px] text-ink", className), ...props, children: _jsxs("div", { "data-slot": "activity-row-content", className: "flex items-center justify-center gap-5 @max-[36rem]/activity-row:flex-wrap @max-[36rem]/activity-row:justify-start", children: [_jsx("span", { "data-slot": "activity-row-time", className: "shrink-0 text-caption text-faint-strong", children: time }), thumbnailSrc !== undefined ? (_jsx("span", { "data-slot": "activity-row-thumbnail", className: "relative block h-[22px] w-24 shrink-0", children: _jsx("img", { src: thumbnailSrc, alt: "", decoding: "async", draggable: false, className: "absolute top-1/2 left-0 block h-16 w-24 -translate-y-1/2 rounded-xs object-cover" }) })) : null, _jsx("span", { id: textId, "data-slot": "activity-row-text", className: "min-w-0 text-body-sm @max-[36rem]/activity-row:basis-full", children: children }), hasAction ? (_jsx(ActionLink, { href: href, render: render, "aria-describedby": textId, className: "shrink-0", children: actionLabel })) : null] }) }));
|
|
35
|
+
}
|
|
36
|
+
export { ActivityRow };
|
|
37
|
+
//# sourceMappingURL=activity-row.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-row.js","sourceRoot":"","sources":["../../src/blocks/activity-row.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAS,WAAW,CAAC,EACnB,IAAI,EACJ,YAAY,EACZ,WAAW,GAAG,MAAM,EACpB,IAAI,EACJ,MAAM,EACN,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EAcT;IACC,MAAM,SAAS,GAAG,IAAI,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,CAAA;IAC5D,8EAA8E;IAC9E,wEAAwE;IACxE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAA;IAE5B,OAAO,CACL,2BACY,cAAc,EACxB,SAAS,EAAE,EAAE,CAAC,qFAAqF,EAAE,SAAS,CAAC,KAC3G,KAAK,YAIT,4BACY,sBAAsB,EAChC,SAAS,EAAC,oHAAoH,aAEhI,4BAAgB,mBAAmB,EAAC,SAAS,EAAC,yCAAyC,YACpF,IAAI,GACA,EACN,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,CAC5B,4BAAgB,wBAAwB,EAAC,SAAS,EAAC,uCAAuC,YACxF,cACE,GAAG,EAAE,YAAY,EACjB,GAAG,EAAC,EAAE,EACN,QAAQ,EAAC,OAAO,EAChB,SAAS,EAAE,KAAK,EAChB,SAAS,EAAC,kFAAkF,GAC5F,GACG,CACR,CAAC,CAAC,CAAC,IAAI,EACR,eAAM,EAAE,EAAE,MAAM,eAAY,mBAAmB,EAAC,SAAS,EAAC,2DAA2D,YAClH,QAAQ,GACJ,EACN,SAAS,CAAC,CAAC,CAAC,CACX,KAAC,UAAU,IAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,sBAAoB,MAAM,EAAE,SAAS,EAAC,UAAU,YACnF,WAAW,GACD,CACd,CAAC,CAAC,CAAC,IAAI,IACF,GACF,CACP,CAAA;AACH,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type EntityCardTileSize } from "./entity-card.js";
|
|
3
|
+
/**
|
|
4
|
+
* The dashed Add tile at the end of a row of cards (#382, spec §4.4, Figma
|
|
5
|
+
* `11324:91317`).
|
|
6
|
+
*
|
|
7
|
+
* Home's My Creations ends its Recent row with it: beside a single card in the
|
|
8
|
+
* "one creation" frame, past the scroll in the "several" frame. It is the box
|
|
9
|
+
* of the card it sits beside, read from `ENTITY_CARD_TILE_BOX` so the two cannot
|
|
10
|
+
* differ, at radius 20 with the library's one dashed line, holding the 48px
|
|
11
|
+
* `cta` Add (`Button size="2xl"`) with its plus after the label.
|
|
12
|
+
*
|
|
13
|
+
* The tile is not itself the control: the button is, so the hit area and the
|
|
14
|
+
* focus ring are the button's, as drawn. With `href` it renders as a link
|
|
15
|
+
* through `linkComponent` (Next's `Link`) and keeps the link role, the shape
|
|
16
|
+
* `EmptyCreations` uses; with only `onClick` it is a button.
|
|
17
|
+
*/
|
|
18
|
+
declare function AddTile({ size, label, href, linkComponent, onClick, className, ...props }: Omit<React.ComponentProps<"div">, "children" | "onClick"> & {
|
|
19
|
+
/** The card box it matches: `tile-lg` 329x242 (default) or `tile` 250x184. */
|
|
20
|
+
size?: EntityCardTileSize;
|
|
21
|
+
/** The button's label, and so its accessible name. */
|
|
22
|
+
label?: string;
|
|
23
|
+
/** Where Add goes. Renders the control as a link. */
|
|
24
|
+
href?: string;
|
|
25
|
+
/** The element a link renders through, for a router's own link. */
|
|
26
|
+
linkComponent?: React.ElementType;
|
|
27
|
+
/** Called when Add is pressed. With `href` too, it runs before the navigation. */
|
|
28
|
+
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
29
|
+
}): React.JSX.Element;
|
|
30
|
+
export { AddTile };
|
|
31
|
+
//# sourceMappingURL=add-tile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-tile.d.ts","sourceRoot":"","sources":["../../src/blocks/add-tile.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAGzF;;;;;;;;;;;;;;GAcG;AACH,iBAAS,OAAO,CAAC,EACf,IAAgB,EAChB,KAAa,EACb,IAAI,EACJ,aAAa,EACb,OAAO,EACP,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,GAAG;IAC7D,8EAA8E;IAC9E,IAAI,CAAC,EAAE,kBAAkB,CAAA;IACzB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mEAAmE;IACnE,aAAa,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACjC,kFAAkF;IAClF,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;CAC/C,qBAuCA;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/cloudgrid-cn.js";
|
|
4
|
+
import { Button } from "../ui/button.js";
|
|
5
|
+
import { DASHED_LINE_CLASS } from "./dashed-line.js";
|
|
6
|
+
import { ENTITY_CARD_TILE_BOX } from "./entity-card.js";
|
|
7
|
+
import { PlusIcon } from "./icons.js";
|
|
8
|
+
/**
|
|
9
|
+
* The dashed Add tile at the end of a row of cards (#382, spec §4.4, Figma
|
|
10
|
+
* `11324:91317`).
|
|
11
|
+
*
|
|
12
|
+
* Home's My Creations ends its Recent row with it: beside a single card in the
|
|
13
|
+
* "one creation" frame, past the scroll in the "several" frame. It is the box
|
|
14
|
+
* of the card it sits beside, read from `ENTITY_CARD_TILE_BOX` so the two cannot
|
|
15
|
+
* differ, at radius 20 with the library's one dashed line, holding the 48px
|
|
16
|
+
* `cta` Add (`Button size="2xl"`) with its plus after the label.
|
|
17
|
+
*
|
|
18
|
+
* The tile is not itself the control: the button is, so the hit area and the
|
|
19
|
+
* focus ring are the button's, as drawn. With `href` it renders as a link
|
|
20
|
+
* through `linkComponent` (Next's `Link`) and keeps the link role, the shape
|
|
21
|
+
* `EmptyCreations` uses; with only `onClick` it is a button.
|
|
22
|
+
*/
|
|
23
|
+
function AddTile({ size = "tile-lg", label = "Add", href, linkComponent, onClick, className, ...props }) {
|
|
24
|
+
const Link = linkComponent ?? "a";
|
|
25
|
+
const content = (_jsxs(_Fragment, { children: [label, _jsx(PlusIcon, {})] }));
|
|
26
|
+
return (_jsx("div", { "data-slot": "add-tile", className: cn("flex shrink-0 items-center justify-center rounded-container", ENTITY_CARD_TILE_BOX[size], DASHED_LINE_CLASS, className), ...props, children: href !== undefined ? (_jsx(Button, { "data-slot": "add-tile-action", variant: "cta", size: "2xl", render: _jsx(Link, { href: href }), nativeButton: false, role: "link", onClick: onClick, children: content })) : (_jsx(Button, { "data-slot": "add-tile-action", variant: "cta", size: "2xl", type: "button", onClick: onClick, children: content })) }));
|
|
27
|
+
}
|
|
28
|
+
export { AddTile };
|
|
29
|
+
//# sourceMappingURL=add-tile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-tile.js","sourceRoot":"","sources":["../../src/blocks/add-tile.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAA2B,MAAM,2BAA2B,CAAA;AACzF,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAE9C;;;;;;;;;;;;;;GAcG;AACH,SAAS,OAAO,CAAC,EACf,IAAI,GAAG,SAAS,EAChB,KAAK,GAAG,KAAK,EACb,IAAI,EACJ,aAAa,EACb,OAAO,EACP,SAAS,EACT,GAAG,KAAK,EAYT;IACC,MAAM,IAAI,GAAsB,aAAa,IAAI,GAAG,CAAA;IACpD,MAAM,OAAO,GAAG,CACd,8BACG,KAAK,EACN,KAAC,QAAQ,KAAG,IACX,CACJ,CAAA;IAED,OAAO,CACL,2BACY,UAAU,EACpB,SAAS,EAAE,EAAE,CACX,6DAA6D,EAC7D,oBAAoB,CAAC,IAAI,CAAC,EAC1B,iBAAiB,EACjB,SAAS,CACV,KACG,KAAK,YAER,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CACpB,KAAC,MAAM,iBACK,iBAAiB,EAC3B,OAAO,EAAC,KAAK,EACb,IAAI,EAAC,KAAK,EACV,MAAM,EAAE,KAAC,IAAI,IAAC,IAAI,EAAE,IAAI,GAAI,EAC5B,YAAY,EAAE,KAAK,EACnB,IAAI,EAAC,MAAM,EACX,OAAO,EAAE,OAAO,YAEf,OAAO,GACD,CACV,CAAC,CAAC,CAAC,CACF,KAAC,MAAM,iBAAW,iBAAiB,EAAC,OAAO,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,OAAO,YACxF,OAAO,GACD,CACV,GACG,CACP,CAAA;AACH,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { type AvatarDiameter } from "./avatar.js";
|
|
4
|
+
/**
|
|
5
|
+
* THE AVATAR STACK (#381): overlapping faces with a "+N" chip, as the
|
|
6
|
+
* CloudGrid-2026 header draws the people in a grid (frame 11493:9953).
|
|
7
|
+
*
|
|
8
|
+
* Measured: 32px faces, a 1.5px ring in the page colour inside each face, a
|
|
9
|
+
* 12px overlap (a 20px step), and the chip last and on top: the page colour,
|
|
10
|
+
* a 1px slate-500 line, 13px slate-500 text, as big as a face.
|
|
11
|
+
*
|
|
12
|
+
* The Console carried two local copies of this (`chrome/InviteControl.tsx`,
|
|
13
|
+
* `onboarding/invite/AvatarStack.tsx`); both move onto it.
|
|
14
|
+
*
|
|
15
|
+
* One accessible name for the whole stack, from `label`. The faces and the
|
|
16
|
+
* count are hidden, because the label says what they show ("Members, 6"). With
|
|
17
|
+
* `onClick` it is a button, with `href` an anchor, with neither a named image,
|
|
18
|
+
* and with no label it is decorative.
|
|
19
|
+
*/
|
|
20
|
+
export type AvatarStackPerson = {
|
|
21
|
+
name: string;
|
|
22
|
+
seed?: string;
|
|
23
|
+
src?: string;
|
|
24
|
+
};
|
|
25
|
+
declare function AvatarStack({ people, max, size, label, href, onClick, render, className, ...props }: Omit<useRender.ComponentProps<"span">, "children"> & {
|
|
26
|
+
/** Who is in the stack, in the order they are drawn, first leftmost. */
|
|
27
|
+
people: readonly AvatarStackPerson[];
|
|
28
|
+
/** Faces drawn before the chip. Default 3; anything below 1 draws one. */
|
|
29
|
+
max?: number;
|
|
30
|
+
/** The diameter of every face and of the chip. Default 32, the frame's. */
|
|
31
|
+
size?: AvatarDiameter;
|
|
32
|
+
/** The accessible name of the whole stack, e.g. "Members, 6". */
|
|
33
|
+
label?: string;
|
|
34
|
+
/** Renders an anchor. */
|
|
35
|
+
href?: string;
|
|
36
|
+
/** Renders a `<button type="button">`. */
|
|
37
|
+
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
38
|
+
}): React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | null;
|
|
39
|
+
export { AvatarStack };
|
|
40
|
+
//# sourceMappingURL=avatar-stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar-stack.d.ts","sourceRoot":"","sources":["../../src/blocks/avatar-stack.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAGrD,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAElE;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAI7E,iBAAS,WAAW,CAAC,EACnB,MAAM,EACN,GAAO,EACP,IAAS,EACT,KAAK,EACL,IAAI,EACJ,OAAO,EACP,MAAM,EACN,SAAS,EACT,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IACtD,wEAAwE;IACxE,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAA;IACpC,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2EAA2E;IAC3E,IAAI,CAAC,EAAE,cAAc,CAAA;IACrB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0CAA0C;IAC1C,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;CAC/C,iFAmEA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
5
|
+
import { cn } from "../lib/cloudgrid-cn.js";
|
|
6
|
+
import { Avatar } from "./avatar.js";
|
|
7
|
+
const OVERLAP = "-ml-3";
|
|
8
|
+
function AvatarStack({ people, max = 3, size = 32, label, href, onClick, render, className, ...props }) {
|
|
9
|
+
const shown = Math.min(people.length, Math.max(1, Math.floor(max)));
|
|
10
|
+
const rest = people.length - shown;
|
|
11
|
+
const interactive = Boolean(render || href || onClick);
|
|
12
|
+
const tag = href ? "a" : onClick ? "button" : "span";
|
|
13
|
+
// An interactive stack is never nameless: without a label it says its head count.
|
|
14
|
+
const name = label ?? (interactive ? `${people.length} ${people.length === 1 ? "person" : "people"}` : undefined);
|
|
15
|
+
const element = useRender({
|
|
16
|
+
render,
|
|
17
|
+
defaultTagName: tag,
|
|
18
|
+
props: {
|
|
19
|
+
"data-slot": "avatar-stack",
|
|
20
|
+
...(tag === "button" && !render ? { type: "button" } : {}),
|
|
21
|
+
...(href ? { href } : {}),
|
|
22
|
+
...(onClick ? { onClick } : {}),
|
|
23
|
+
...(interactive
|
|
24
|
+
? { "aria-label": name }
|
|
25
|
+
: label
|
|
26
|
+
? { role: "img", "aria-label": label }
|
|
27
|
+
: { "aria-hidden": true }),
|
|
28
|
+
className: cn("inline-flex items-center text-ink no-underline", interactive &&
|
|
29
|
+
"cursor-pointer rounded-pill focus-visible:ring-3 focus-visible:ring-ring focus-visible:outline-none", className),
|
|
30
|
+
children: (_jsxs(_Fragment, { children: [people.slice(0, shown).map((person, i) => (_jsxs("span", { "data-slot": "avatar-stack-face", "aria-hidden": "true", className: cn("relative inline-flex shrink-0 rounded-pill", i > 0 && OVERLAP), children: [_jsx(Avatar, { name: person.name, seed: person.seed, src: person.src, size: size }), _jsx("span", { "data-slot": "avatar-stack-ring", className: "pointer-events-none absolute inset-0 rounded-pill", style: {
|
|
31
|
+
boxShadow: "inset 0 0 0 var(--cg-border-width-strong) var(--cg-surface-page)",
|
|
32
|
+
} })] }, `${person.seed ?? person.name}-${i}`))), rest > 0 ? (_jsxs("span", { "data-slot": "avatar-stack-more", "aria-hidden": "true", className: cn("relative inline-flex shrink-0 items-center justify-center rounded-pill border border-steel bg-page text-caption text-steel", OVERLAP), style: { width: size, height: size }, children: ["+", rest] })) : null] })),
|
|
33
|
+
...props,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
if (people.length === 0)
|
|
37
|
+
return null;
|
|
38
|
+
return element;
|
|
39
|
+
}
|
|
40
|
+
export { AvatarStack };
|
|
41
|
+
//# sourceMappingURL=avatar-stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"avatar-stack.js","sourceRoot":"","sources":["../../src/blocks/avatar-stack.tsx"],"names":[],"mappings":"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"}
|
package/dist/blocks/avatar.d.ts
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
* alone with no name in the markup.
|
|
60
60
|
*/
|
|
61
61
|
/**
|
|
62
|
-
* The ramp: 18, 24, 32, 38. The rung IS the diameter.
|
|
62
|
+
* The ramp: 18, 24, 32, 38, 56, 64. The rung IS the diameter.
|
|
63
63
|
*
|
|
64
64
|
* ## Why the letters went away (#212)
|
|
65
65
|
*
|
|
@@ -92,8 +92,8 @@
|
|
|
92
92
|
* 56 (cloudgrid-console#944), and `AvatarPicker` renders that picture through
|
|
93
93
|
* this ramp. There is no letter for it, for the reason there is none for 32.
|
|
94
94
|
*/
|
|
95
|
-
export declare const AVATAR_DIAMETERS: readonly [18, 24, 32, 38, 56];
|
|
96
|
-
/** 18, 24, 32, 38 or
|
|
95
|
+
export declare const AVATAR_DIAMETERS: readonly [18, 24, 32, 38, 56, 64];
|
|
96
|
+
/** 18, 24, 32, 38, 56 or 64 across. */
|
|
97
97
|
export type AvatarDiameter = (typeof AVATAR_DIAMETERS)[number];
|
|
98
98
|
/**
|
|
99
99
|
* The 0.26 letter names, and what each one was worth in pixels.
|
|
@@ -117,7 +117,28 @@ export declare const AVATAR_SIZES: {
|
|
|
117
117
|
};
|
|
118
118
|
/** @deprecated Use `AvatarDiameter`. Removed in 0.29. See #216. */
|
|
119
119
|
export type AvatarSize = keyof typeof AVATAR_SIZES;
|
|
120
|
-
|
|
120
|
+
/**
|
|
121
|
+
* #381 — THE GRID AVATAR of the CloudGrid-2026 header: a rounded square on a
|
|
122
|
+
* pastel ground with one Ink letter (frames 11323:8600, 11324:90635, 11324:90641).
|
|
123
|
+
*
|
|
124
|
+
* The pastel is picked by `entityIdentitySeed`, the library's one identity hash,
|
|
125
|
+
* so a grid keeps the same pastel at every size and shape, for every member,
|
|
126
|
+
* on every screen. The letters are Ink and do not flip: the pastels are
|
|
127
|
+
* identity colours declared at `:root` only, so on a purple panel the mark
|
|
128
|
+
* keeps its ground and its letter rather than inverting into a second look.
|
|
129
|
+
* Measured: 12.02:1, 10.74:1 and 11.43:1.
|
|
130
|
+
*
|
|
131
|
+
* The frames draw "A" for Atomic Labs, so the pastel look draws ONE letter, the
|
|
132
|
+
* first of the name; an explicit `monogram` still wins. Letters are Manrope
|
|
133
|
+
* Regular at 15/32 of the box (15px at 32, as the frame).
|
|
134
|
+
*/
|
|
135
|
+
export declare const AVATAR_PASTELS: readonly ["--cg-avatar-pastel-1", "--cg-avatar-pastel-2", "--cg-avatar-pastel-3"];
|
|
136
|
+
export type AvatarPastel = (typeof AVATAR_PASTELS)[number];
|
|
137
|
+
export type AvatarShape = "circle" | "square";
|
|
138
|
+
export type AvatarLook = "identity" | "pastel";
|
|
139
|
+
/** The pastel a seed lands on: the same hash the identity look reads. */
|
|
140
|
+
export declare function avatarPastel(seed: string): AvatarPastel;
|
|
141
|
+
declare function Avatar({ name, seed, src, alt, size, monogram, shape, look: lookName, className, style, ...props }: Omit<React.ComponentProps<"span">, "children"> & {
|
|
121
142
|
/** Whose mark this is. Read for the initials, and for the hue unless `seed` is given. */
|
|
122
143
|
name: string;
|
|
123
144
|
/**
|
|
@@ -136,7 +157,8 @@ declare function Avatar({ name, seed, src, alt, size, monogram, className, style
|
|
|
136
157
|
*/
|
|
137
158
|
alt?: string;
|
|
138
159
|
/**
|
|
139
|
-
* The diameter: `18`, `24`, `32`, `38` or `
|
|
160
|
+
* The diameter: `18`, `24`, `32`, `38`, `56` or `64` (#381, the account
|
|
161
|
+
* menu's header).
|
|
140
162
|
*
|
|
141
163
|
* The 0.26 letters — `'s'`, `'m'`, `'l'` — still resolve to 18, 24 and 38
|
|
142
164
|
* through 0.28. They are deprecated; see `AVATAR_DIAMETERS`.
|
|
@@ -144,6 +166,10 @@ declare function Avatar({ name, seed, src, alt, size, monogram, className, style
|
|
|
144
166
|
size?: AvatarDiameter | AvatarSize;
|
|
145
167
|
/** Overrides the derived initials, for a name they read badly from. */
|
|
146
168
|
monogram?: string;
|
|
169
|
+
/** `circle` (the default) or `square`, the grid avatar's rounded square (#381). */
|
|
170
|
+
shape?: AvatarShape;
|
|
171
|
+
/** `identity` (the default) or `pastel`, the grid avatar's pastel ground and Ink letter (#381). */
|
|
172
|
+
look?: AvatarLook;
|
|
147
173
|
}): import("react").JSX.Element;
|
|
148
174
|
export { Avatar };
|
|
149
175
|
//# sourceMappingURL=avatar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../src/blocks/avatar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../src/blocks/avatar.tsx"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,gBAAgB,mCAAoC,CAAA;AAEjE,uCAAuC;AACvC,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY;;;;CAIf,CAAA;AAEV,mEAAmE;AACnE,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,YAAY,CAAA;AAgBlD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,mFAIjB,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAC1D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC7C,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAA;AAE9C,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAEvD;AAuBD,iBAAS,MAAM,CAAC,EACd,IAAI,EACJ,IAAI,EACJ,GAAG,EACH,GAAG,EACH,IAAS,EACT,QAAQ,EACR,KAAgB,EAChB,IAAI,EAAE,QAAqB,EAC3B,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,GAAG;IAClD,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,cAAc,GAAG,UAAU,CAAA;IAClC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,mGAAmG;IACnG,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB,+BAoEA;AAED,OAAO,EAAE,MAAM,EAAE,CAAA"}
|
package/dist/blocks/avatar.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../lib/cloudgrid-cn.js";
|
|
3
|
-
import { entityIdentityLook, entityMonogram } from "./entity-identity.js";
|
|
3
|
+
import { entityIdentityLook, entityIdentitySeed, entityMonogram, } from "./entity-identity.js";
|
|
4
4
|
/**
|
|
5
5
|
* A person, as a mark: their picture if there is one, their initials on a
|
|
6
6
|
* derived ground if there is not.
|
|
@@ -62,7 +62,7 @@ import { entityIdentityLook, entityMonogram } from "./entity-identity.js";
|
|
|
62
62
|
* alone with no name in the markup.
|
|
63
63
|
*/
|
|
64
64
|
/**
|
|
65
|
-
* The ramp: 18, 24, 32, 38. The rung IS the diameter.
|
|
65
|
+
* The ramp: 18, 24, 32, 38, 56, 64. The rung IS the diameter.
|
|
66
66
|
*
|
|
67
67
|
* ## Why the letters went away (#212)
|
|
68
68
|
*
|
|
@@ -95,7 +95,7 @@ import { entityIdentityLook, entityMonogram } from "./entity-identity.js";
|
|
|
95
95
|
* 56 (cloudgrid-console#944), and `AvatarPicker` renders that picture through
|
|
96
96
|
* this ramp. There is no letter for it, for the reason there is none for 32.
|
|
97
97
|
*/
|
|
98
|
-
export const AVATAR_DIAMETERS = [18, 24, 32, 38, 56];
|
|
98
|
+
export const AVATAR_DIAMETERS = [18, 24, 32, 38, 56, 64];
|
|
99
99
|
/**
|
|
100
100
|
* The 0.26 letter names, and what each one was worth in pixels.
|
|
101
101
|
*
|
|
@@ -129,26 +129,74 @@ export const AVATAR_SIZES = {
|
|
|
129
129
|
* at 10.6px, which is where the console's own 24px avatar sat, and 32 at 14.1px.
|
|
130
130
|
*/
|
|
131
131
|
const MONOGRAM_RATIO = 0.44;
|
|
132
|
-
|
|
132
|
+
/**
|
|
133
|
+
* #381 — THE GRID AVATAR of the CloudGrid-2026 header: a rounded square on a
|
|
134
|
+
* pastel ground with one Ink letter (frames 11323:8600, 11324:90635, 11324:90641).
|
|
135
|
+
*
|
|
136
|
+
* The pastel is picked by `entityIdentitySeed`, the library's one identity hash,
|
|
137
|
+
* so a grid keeps the same pastel at every size and shape, for every member,
|
|
138
|
+
* on every screen. The letters are Ink and do not flip: the pastels are
|
|
139
|
+
* identity colours declared at `:root` only, so on a purple panel the mark
|
|
140
|
+
* keeps its ground and its letter rather than inverting into a second look.
|
|
141
|
+
* Measured: 12.02:1, 10.74:1 and 11.43:1.
|
|
142
|
+
*
|
|
143
|
+
* The frames draw "A" for Atomic Labs, so the pastel look draws ONE letter, the
|
|
144
|
+
* first of the name; an explicit `monogram` still wins. Letters are Manrope
|
|
145
|
+
* Regular at 15/32 of the box (15px at 32, as the frame).
|
|
146
|
+
*/
|
|
147
|
+
export const AVATAR_PASTELS = [
|
|
148
|
+
"--cg-avatar-pastel-1",
|
|
149
|
+
"--cg-avatar-pastel-2",
|
|
150
|
+
"--cg-avatar-pastel-3",
|
|
151
|
+
];
|
|
152
|
+
/** The pastel a seed lands on: the same hash the identity look reads. */
|
|
153
|
+
export function avatarPastel(seed) {
|
|
154
|
+
return AVATAR_PASTELS[entityIdentitySeed(seed) % AVATAR_PASTELS.length];
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The square's corner per diameter. 24 and 32 are the frames' (6 and 8); the
|
|
158
|
+
* rest are the radius token nearest a quarter of the diameter.
|
|
159
|
+
*/
|
|
160
|
+
const SQUARE_RADIUS = {
|
|
161
|
+
18: "--cg-radius-2xs",
|
|
162
|
+
24: "--cg-radius-2xs",
|
|
163
|
+
32: "--cg-radius-xs",
|
|
164
|
+
38: "--cg-radius-sm",
|
|
165
|
+
56: "--cg-radius-lg",
|
|
166
|
+
64: "--cg-radius-xl",
|
|
167
|
+
};
|
|
168
|
+
const PASTEL_RATIO = 15 / 32;
|
|
169
|
+
const firstLetter = (name) => {
|
|
170
|
+
// Array.from walks code points, so an emoji first is drawn whole, never half a pair.
|
|
171
|
+
const ch = Array.from(name.trim())[0] ?? "";
|
|
172
|
+
return ch ? ch.toUpperCase() : "?";
|
|
173
|
+
};
|
|
174
|
+
function Avatar({ name, seed, src, alt, size = 24, monogram, shape = "circle", look: lookName = "identity", className, style, ...props }) {
|
|
133
175
|
// One resolved diameter, whichever spelling arrived. Everything below reads
|
|
134
176
|
// `box` and nothing below knows that letters were ever a thing.
|
|
135
177
|
const box = typeof size === "number" ? size : AVATAR_SIZES[size];
|
|
136
178
|
const look = entityIdentityLook(seed ?? name);
|
|
137
|
-
const
|
|
179
|
+
const pastel = lookName === "pastel";
|
|
180
|
+
const pastelToken = pastel ? avatarPastel(seed ?? name) : null;
|
|
181
|
+
const letters = monogram ?? (pastel ? firstLetter(name) : entityMonogram(name));
|
|
182
|
+
const squareRadius = shape === "square" ? SQUARE_RADIUS[box] ?? "--cg-radius-xs" : null;
|
|
138
183
|
// One rule, resolved once, so the three elements below cannot disagree about
|
|
139
184
|
// whether this mark is announced. See the docblock: silent by default.
|
|
140
185
|
const spoken = alt !== undefined;
|
|
141
|
-
return (_jsxs("span", { "data-slot": "avatar", "data-size": box, "data-look": look.name, ...(spoken ? { role: "img", "aria-label": alt } : { "aria-hidden": true }), className: cn("relative isolate inline-flex shrink-0 select-none items-center justify-center overflow-hidden rounded-pill font-medium", className), style: {
|
|
186
|
+
return (_jsxs("span", { "data-slot": "avatar", "data-size": box, "data-look": pastelToken ? `pastel-${AVATAR_PASTELS.indexOf(pastelToken) + 1}` : look.name, "data-shape": shape, ...(spoken ? { role: "img", "aria-label": alt } : { "aria-hidden": true }), className: cn("relative isolate inline-flex shrink-0 select-none items-center justify-center overflow-hidden", squareRadius ? null : "rounded-pill", pastel ? "font-normal" : "font-medium", className), style: {
|
|
142
187
|
width: box,
|
|
143
188
|
height: box,
|
|
144
|
-
fontSize: Math.round(box * MONOGRAM_RATIO * 10) / 10,
|
|
189
|
+
fontSize: Math.round(box * (pastel ? PASTEL_RATIO : MONOGRAM_RATIO) * 10) / 10,
|
|
190
|
+
...(squareRadius ? { borderRadius: `var(${squareRadius})` } : null),
|
|
145
191
|
lineHeight: 1,
|
|
146
192
|
// The identity tile's measured shade, and mixed toward --cg-ink the same
|
|
147
193
|
// way. Not mixed against --cg-surface-card: that token is `transparent`
|
|
148
194
|
// inside a purple panel, and mixing a hue into transparent gives a
|
|
149
195
|
// half-transparent hue rather than a shade.
|
|
150
|
-
background:
|
|
151
|
-
|
|
196
|
+
background: pastelToken
|
|
197
|
+
? `var(${pastelToken})`
|
|
198
|
+
: `color-mix(in srgb, var(${look.hue}) ${look.tile}, var(--cg-ink))`,
|
|
199
|
+
color: pastel ? "var(--cg-ink)" : "var(--cg-text-on-primary)",
|
|
152
200
|
...style,
|
|
153
201
|
}, ...props, children: [_jsx("span", { "data-slot": "avatar-initials", "aria-hidden": "true", children: letters }), src ? (
|
|
154
202
|
// Over the initials, never instead of them. `alt=""` when the mark is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.js","sourceRoot":"","sources":["../../src/blocks/avatar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAA;AACvC,OAAO,
|
|
1
|
+
{"version":3,"file":"avatar.js","sourceRoot":"","sources":["../../src/blocks/avatar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
/**
|
|
4
|
+
* Copy a value, and say so (#382, spec §4.10).
|
|
5
|
+
*
|
|
6
|
+
* Home copies one command from two places: the onboard bar under the page, and
|
|
7
|
+
* "or onboard an agent" in the empty state. Each says what happened in its own
|
|
8
|
+
* words: "Copied" for two seconds, or "Couldn't copy" when the clipboard
|
|
9
|
+
* refuses (an insecure origin, a denied permission, a browser without the
|
|
10
|
+
* API). The words are also put in a polite live region, because a label that
|
|
11
|
+
* changes under a screen reader that has already read the button is not heard.
|
|
12
|
+
*
|
|
13
|
+
* ## The pieces
|
|
14
|
+
*
|
|
15
|
+
* `copyText(value, clipboard)` is the one clipboard call: it resolves `copied`
|
|
16
|
+
* or `blocked` and never throws. `useCopy()` holds the state and the timer: each
|
|
17
|
+
* copy restarts the two seconds, and the timer is cleared on unmount, so a
|
|
18
|
+
* component that goes away mid-cycle does not set state afterwards.
|
|
19
|
+
* `CopyButton` is a button (or whatever `render` gives it) wired to both.
|
|
20
|
+
*
|
|
21
|
+
* ## No look of its own
|
|
22
|
+
*
|
|
23
|
+
* The block adds no visual class: the caller styles it, or renders it as
|
|
24
|
+
* something styled. "onboard an agent" passes `ACTION_LINK_CLASS`; the onboard
|
|
25
|
+
* bar passes `render` and a function child, so its tool marks stay while its
|
|
26
|
+
* line switches.
|
|
27
|
+
*
|
|
28
|
+
* `CodeBlock`, `CommandField`, `AgentPrompt`, `PromptBlock` and `InvitePopup`
|
|
29
|
+
* each carry their own copy of this cycle; they can move onto `useCopy` later.
|
|
30
|
+
*/
|
|
31
|
+
export type CopyState = "idle" | "copied" | "blocked";
|
|
32
|
+
/** Writes `value` to `clipboard`. Resolves `copied` or `blocked`; never throws. */
|
|
33
|
+
declare function copyText(value: string, clipboard: Pick<Clipboard, "writeText"> | undefined): Promise<"copied" | "blocked">;
|
|
34
|
+
declare function useCopy({ resetAfter }?: {
|
|
35
|
+
resetAfter?: number;
|
|
36
|
+
}): {
|
|
37
|
+
state: CopyState;
|
|
38
|
+
copy: (value: string) => Promise<CopyState>;
|
|
39
|
+
};
|
|
40
|
+
declare function CopyButton({ value, children, copiedLabel, blockedLabel, announceCopied, announceBlocked, resetAfter, onStateChange, render, onClick, ...props }: Omit<useRender.ComponentProps<"button">, "children" | "value"> & {
|
|
41
|
+
/** What is copied, exactly. */
|
|
42
|
+
value: string;
|
|
43
|
+
/** The idle content, or a function of the state for content that keeps parts of itself. */
|
|
44
|
+
children: React.ReactNode | ((state: CopyState) => React.ReactNode);
|
|
45
|
+
/** Replaces node children while copied. */
|
|
46
|
+
copiedLabel?: React.ReactNode;
|
|
47
|
+
/** Replaces node children while blocked. */
|
|
48
|
+
blockedLabel?: React.ReactNode;
|
|
49
|
+
/** What the live region says on a copy. */
|
|
50
|
+
announceCopied?: string;
|
|
51
|
+
/** What the live region says on a refusal. */
|
|
52
|
+
announceBlocked?: string;
|
|
53
|
+
/** How long the result shows, in ms. Default 2000. */
|
|
54
|
+
resetAfter?: number;
|
|
55
|
+
/** Called with each new state, e.g. to show the command in a tip when blocked. */
|
|
56
|
+
onStateChange?: (state: CopyState) => void;
|
|
57
|
+
}): React.JSX.Element;
|
|
58
|
+
export { CopyButton, copyText, useCopy };
|
|
59
|
+
//# sourceMappingURL=copy-button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-button.d.ts","sourceRoot":"","sources":["../../src/blocks/copy-button.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;AAErD,mFAAmF;AACnF,iBAAe,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAQzH;AAED,iBAAS,OAAO,CAAC,EAAE,UAAiB,EAAE,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO;;kBAclD,MAAM,KAAG,OAAO,CAAC,SAAS,CAAC;EAe5C;AAED,iBAAS,UAAU,CAAC,EAClB,KAAK,EACL,QAAQ,EACR,WAAsB,EACtB,YAA8B,EAC9B,cAA0C,EAC1C,eAAqE,EACrE,UAAiB,EACjB,aAAa,EACb,MAAM,EACN,OAAO,EACP,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG;IAClE,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,2FAA2F;IAC3F,QAAQ,EAAE,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,SAAS,KAAK,KAAK,CAAC,SAAS,CAAC,CAAA;IACnE,2CAA2C;IAC3C,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,8CAA8C;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAA;CAC3C,qBA0CA;AAED,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAA"}
|