@godxjp/ui 23.1.0 → 23.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-display/card.d.ts +45 -2
- package/dist/components/data-display/card.js +4 -2
- package/dist/components/data-display/feature-list.d.ts +26 -0
- package/dist/components/data-display/feature-list.js +29 -0
- package/dist/components/data-display/index.d.ts +6 -0
- package/dist/components/data-display/index.js +6 -0
- package/dist/components/data-display/swatch.d.ts +24 -0
- package/dist/components/data-display/swatch.js +19 -0
- package/dist/components/data-display/thumbnail.d.ts +37 -0
- package/dist/components/data-display/thumbnail.js +20 -0
- package/dist/components/feedback/index.d.ts +2 -2
- package/dist/components/feedback/index.js +19 -1
- package/dist/components/feedback/skeleton.d.ts +33 -3
- package/dist/components/feedback/skeleton.js +154 -3
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +17 -5
- package/dist/components/layout/sidebar.js +9 -2
- package/dist/components/navigation/tabs-scroll.d.ts +27 -0
- package/dist/components/navigation/tabs-scroll.js +36 -1
- package/dist/components/navigation/tabs.d.ts +2 -2
- package/dist/components/navigation/tabs.js +44 -12
- package/dist/i18n/messages/en.json +5 -0
- package/dist/i18n/messages/ja.json +5 -0
- package/dist/i18n/messages/vi.json +5 -0
- package/dist/props/components/data-display.prop.d.ts +93 -0
- package/dist/props/components/feedback.prop.d.ts +85 -1
- package/dist/props/components/layout.prop.d.ts +72 -1
- package/dist/props/components/navigation.prop.d.ts +72 -1
- package/dist/props/registry.d.ts +118 -0
- package/dist/props/registry.js +134 -0
- package/dist/styles/alert-layout.css +139 -0
- package/dist/styles/card-layout.css +48 -0
- package/dist/styles/data-display-layout.css +88 -0
- package/dist/styles/navigation-layout.css +76 -3
- package/dist/styles/shell-layout.css +18 -0
- package/dist/tokens/components/card.css +2 -0
- package/dist/tokens/components/data-display.css +23 -0
- package/dist/tokens/components/feedback.css +18 -0
- package/dist/tokens/components/navigation.css +2 -0
- package/dist/tokens/foundation.css +18 -16
- package/docs/CONSUMER-RULES.md +1 -1
- package/docs/DESIGN-AUTHORITY.md +2 -1
- package/docs/FRAME-COVERAGE-REPORT.md +29 -3
- package/docs/assets/shot-landscape.svg +12 -0
- package/docs/assets/shot-portrait.svg +10 -0
- package/docs/data-display/card/index.tsx +40 -0
- package/docs/data-display/feature-list.tsx +159 -0
- package/docs/data-display/swatch.tsx +107 -0
- package/docs/data-display/thumbnail.tsx +128 -0
- package/docs/feedback/skeleton.tsx +57 -0
- package/docs/layout/flex.tsx +29 -0
- package/docs/layout/sidebar.tsx +66 -10
- package/docs/navigation/tabs.tsx +59 -0
- package/package.json +4 -7
- package/scripts/consumer-rule.md +16 -0
- package/scripts/ui-audit.mjs +3 -4
|
@@ -13,8 +13,13 @@ type CardAccent = "primary" | "success" | "warning" | "info" | "attention" | "de
|
|
|
13
13
|
* borrowing the brand colour.
|
|
14
14
|
*/
|
|
15
15
|
type CardAccentPlacement = "edge" | "perimeter";
|
|
16
|
-
/**
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Surface fill and edge. `outline` is antd's `outlined` — it drops the FILL and keeps the
|
|
18
|
+
* hairline; `borderless` is antd's `variant="borderless"` (its deprecated `bordered={false}`) —
|
|
19
|
+
* it drops the HAIRLINE and keeps the fill. They are two different cards, which is why both
|
|
20
|
+
* values exist: neither one substitutes for the other.
|
|
21
|
+
*/
|
|
22
|
+
type CardVariant = "default" | "muted" | "outline" | "borderless" | "featured";
|
|
18
23
|
/** Padding density — base 16px · tight 12px · cozy 20px. */
|
|
19
24
|
type CardDensity = "tight" | "cozy";
|
|
20
25
|
export type CardProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
@@ -26,6 +31,16 @@ export type CardProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
26
31
|
accentPlacement?: CardAccentPlacement;
|
|
27
32
|
variant?: CardVariant;
|
|
28
33
|
density?: CardDensity;
|
|
34
|
+
/**
|
|
35
|
+
* Lift the card on hover (antd `hoverable`) — the resting elevation steps up to
|
|
36
|
+
* `--card-hover-shadow` and the pointer becomes a `pointer`.
|
|
37
|
+
*
|
|
38
|
+
* This is a PRESENTATION flag, not an interaction: it announces nothing and binds no handler.
|
|
39
|
+
* A card that looks clickable has to BE clickable for everyone, so pair it with a real control
|
|
40
|
+
* — a `Link`/`Button` in the header or footer, or the whole card rendered as one — never with a
|
|
41
|
+
* bare `onClick` on this div, which a keyboard or screen-reader user cannot reach.
|
|
42
|
+
*/
|
|
43
|
+
hoverable?: boolean;
|
|
29
44
|
};
|
|
30
45
|
export declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|
|
31
46
|
accent?: CardAccent;
|
|
@@ -36,6 +51,16 @@ export declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<
|
|
|
36
51
|
accentPlacement?: CardAccentPlacement;
|
|
37
52
|
variant?: CardVariant;
|
|
38
53
|
density?: CardDensity;
|
|
54
|
+
/**
|
|
55
|
+
* Lift the card on hover (antd `hoverable`) — the resting elevation steps up to
|
|
56
|
+
* `--card-hover-shadow` and the pointer becomes a `pointer`.
|
|
57
|
+
*
|
|
58
|
+
* This is a PRESENTATION flag, not an interaction: it announces nothing and binds no handler.
|
|
59
|
+
* A card that looks clickable has to BE clickable for everyone, so pair it with a real control
|
|
60
|
+
* — a `Link`/`Button` in the header or footer, or the whole card rendered as one — never with a
|
|
61
|
+
* bare `onClick` on this div, which a keyboard or screen-reader user cannot reach.
|
|
62
|
+
*/
|
|
63
|
+
hoverable?: boolean;
|
|
39
64
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
40
65
|
/** Full-bleed cover media — first child; header below uses section top (φ⁰), not shell. */
|
|
41
66
|
export type CardCoverProps = React.HTMLAttributes<HTMLDivElement>;
|
|
@@ -90,12 +115,30 @@ export type CardFooterProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
90
115
|
separated?: boolean;
|
|
91
116
|
/** Full-bleed footer (the conventional `actions` bar). */
|
|
92
117
|
flush?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Split the footer into EQUAL-WIDTH cells divided by vertical rules — antd's `actions` row.
|
|
120
|
+
*
|
|
121
|
+
* A different band from `separated`, which packs its children at the inline end at their
|
|
122
|
+
* natural widths: that is the right shape for a Save/Cancel pair and the wrong one for antd's
|
|
123
|
+
* divided strip. Self-sufficient — it draws its own top rule and full-bleed edges, because
|
|
124
|
+
* upstream's actions row is never anything else.
|
|
125
|
+
*/
|
|
126
|
+
actions?: boolean;
|
|
93
127
|
};
|
|
94
128
|
export declare const CardFooter: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|
|
95
129
|
/** Top border + symmetric action band — form Save/Cancel, table summary. */
|
|
96
130
|
separated?: boolean;
|
|
97
131
|
/** Full-bleed footer (the conventional `actions` bar). */
|
|
98
132
|
flush?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Split the footer into EQUAL-WIDTH cells divided by vertical rules — antd's `actions` row.
|
|
135
|
+
*
|
|
136
|
+
* A different band from `separated`, which packs its children at the inline end at their
|
|
137
|
+
* natural widths: that is the right shape for a Save/Cancel pair and the wrong one for antd's
|
|
138
|
+
* divided strip. Self-sufficient — it draws its own top rule and full-bleed edges, because
|
|
139
|
+
* upstream's actions row is never anything else.
|
|
140
|
+
*/
|
|
141
|
+
actions?: boolean;
|
|
99
142
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
100
143
|
export type CardBarProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
101
144
|
pad?: PadProp;
|
|
@@ -3,13 +3,14 @@ import { padStyle, padStepToken } from "../../lib/variants.js";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../../lib/utils.js";
|
|
5
5
|
const Card = React.forwardRef(
|
|
6
|
-
({ className, accent, accentPlacement, variant, density, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
6
|
+
({ className, accent, accentPlacement, variant, density, hoverable, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
7
7
|
"div",
|
|
8
8
|
{
|
|
9
9
|
ref,
|
|
10
10
|
className: cn("group/card", className),
|
|
11
11
|
"data-slot": "card",
|
|
12
12
|
"data-accent": accent,
|
|
13
|
+
"data-hoverable": hoverable ? "" : void 0,
|
|
13
14
|
"data-accent-placement": accentPlacement === "perimeter" ? "perimeter" : void 0,
|
|
14
15
|
"data-variant": variant && variant !== "default" ? variant : void 0,
|
|
15
16
|
"data-density": density,
|
|
@@ -61,13 +62,14 @@ const CardContent = React.forwardRef(
|
|
|
61
62
|
);
|
|
62
63
|
CardContent.displayName = "CardContent";
|
|
63
64
|
const CardFooter = React.forwardRef(
|
|
64
|
-
({ className, separated, flush, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
65
|
+
({ className, separated, flush, actions, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
65
66
|
"div",
|
|
66
67
|
{
|
|
67
68
|
ref,
|
|
68
69
|
"data-slot": "card-footer",
|
|
69
70
|
"data-separated": separated ? "" : void 0,
|
|
70
71
|
"data-flush": flush ? "" : void 0,
|
|
72
|
+
"data-actions": actions ? "" : void 0,
|
|
71
73
|
className,
|
|
72
74
|
...props
|
|
73
75
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type { FeatureItemProp, FeatureListProp, FeatureListProp as FeatureListProps, FeatureStateProp, } from "../../props/components/data-display.prop.js";
|
|
2
|
+
import type { FeatureListProp } from "../../props/components/data-display.prop.js";
|
|
3
|
+
/**
|
|
4
|
+
* FeatureList — a list of STATEMENTS, each carrying a leading state glyph: what a plan includes,
|
|
5
|
+
* what a tier supports, which requirements a submission met.
|
|
6
|
+
*
|
|
7
|
+
* ## Why it is not one of the three components it looks like
|
|
8
|
+
*
|
|
9
|
+
* - **`ListRow`** is a single-line ENTITY row — a session, a token, a passkey — with entity
|
|
10
|
+
* padding, a divider between every row and a trailing action slot. A feature line is none of
|
|
11
|
+
* those: it has no action, it is not a thing you can open, and reading a plan's contents
|
|
12
|
+
* through a stack of ruled rows makes six facts look like six records.
|
|
13
|
+
* - **`Timeline`** has the glyph rail, but it is an ORDERED event list: a connector line runs
|
|
14
|
+
* dot-to-dot and the statuses are `done`/`current`/`pending`, which is time, not inclusion.
|
|
15
|
+
* - **`Descriptions`** is a term/value grid. A feature line has no value column — the state IS
|
|
16
|
+
* the value, and it is drawn, not written.
|
|
17
|
+
*
|
|
18
|
+
* ## The alignment, which is the actual reason this exists
|
|
19
|
+
*
|
|
20
|
+
* The glyph column is one line box tall (`1lh`) and the glyph is centred in it, so it lands on
|
|
21
|
+
* the FIRST line of a label that wraps to three. That is why the component can be handed a
|
|
22
|
+
* one-word label and a paragraph and both come out aligned, at any font size and at any density,
|
|
23
|
+
* with no number in the call site. The shape it replaces reached for `mt-0.5` — 2px, below
|
|
24
|
+
* `--space-1`, off every scale, and wrong the moment the text beside it changes size.
|
|
25
|
+
*/
|
|
26
|
+
export declare function FeatureList({ items, className, ...props }: FeatureListProp): import("react").JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Check, Minus, X } from "lucide-react";
|
|
4
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
5
|
+
import { cn } from "../../lib/utils.js";
|
|
6
|
+
const STATE = {
|
|
7
|
+
included: { Icon: Check, key: "dataDisplay.featureList.included" },
|
|
8
|
+
excluded: { Icon: X, key: "dataDisplay.featureList.excluded" },
|
|
9
|
+
limited: { Icon: Minus, key: "dataDisplay.featureList.limited" }
|
|
10
|
+
};
|
|
11
|
+
function FeatureList({ items, className, ...props }) {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
return /* @__PURE__ */ jsx("ul", { className: cn("ui-feature-list", className), ...props, children: items.map((item, index) => {
|
|
14
|
+
const { Icon, key } = STATE[item.state];
|
|
15
|
+
return /* @__PURE__ */ jsxs("li", { className: "ui-feature-list-item", "data-state": item.state, children: [
|
|
16
|
+
/* @__PURE__ */ jsx("span", { className: "ui-feature-list-mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Icon, {}) }),
|
|
17
|
+
/* @__PURE__ */ jsxs("span", { className: "ui-feature-list-body", children: [
|
|
18
|
+
/* @__PURE__ */ jsxs("span", { className: "ui-feature-list-label", children: [
|
|
19
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: t(key) }),
|
|
20
|
+
item.label
|
|
21
|
+
] }),
|
|
22
|
+
item.description != null ? /* @__PURE__ */ jsx("span", { className: "ui-feature-list-description", children: item.description }) : null
|
|
23
|
+
] })
|
|
24
|
+
] }, index);
|
|
25
|
+
}) });
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
FeatureList
|
|
29
|
+
};
|
|
@@ -2,6 +2,12 @@ export { Badge, StatusBadge } from "./badge.js";
|
|
|
2
2
|
export type { BadgeProps, BadgeTone } from "./badge.js";
|
|
3
3
|
export { Legend } from "./legend.js";
|
|
4
4
|
export type { LegendItemProp, LegendProp, LegendProps } from "./legend.js";
|
|
5
|
+
export { Swatch } from "./swatch.js";
|
|
6
|
+
export type { SwatchProp, SwatchProps } from "./swatch.js";
|
|
7
|
+
export { FeatureList } from "./feature-list.js";
|
|
8
|
+
export type { FeatureItemProp, FeatureListProp, FeatureListProps, FeatureStateProp, } from "./feature-list.js";
|
|
9
|
+
export { Thumbnail } from "./thumbnail.js";
|
|
10
|
+
export type { ThumbnailProp, ThumbnailProps, ThumbnailSizeProp } from "./thumbnail.js";
|
|
5
11
|
export { ListRow } from "./list-row.js";
|
|
6
12
|
export type { ListRowProps, ListRowDensity } from "./list-row.js";
|
|
7
13
|
export { CredentialReveal } from "./credential-reveal.js";
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Badge, StatusBadge } from "./badge.js";
|
|
2
2
|
import { Legend } from "./legend.js";
|
|
3
|
+
import { Swatch } from "./swatch.js";
|
|
4
|
+
import { FeatureList } from "./feature-list.js";
|
|
5
|
+
import { Thumbnail } from "./thumbnail.js";
|
|
3
6
|
import { ListRow } from "./list-row.js";
|
|
4
7
|
import { CredentialReveal } from "./credential-reveal.js";
|
|
5
8
|
import { QrCode } from "./qr-code.js";
|
|
@@ -90,6 +93,7 @@ export {
|
|
|
90
93
|
DataTable,
|
|
91
94
|
Descriptions,
|
|
92
95
|
EmptyState,
|
|
96
|
+
FeatureList,
|
|
93
97
|
HoverCard,
|
|
94
98
|
HoverCardContent,
|
|
95
99
|
HoverCardTrigger,
|
|
@@ -114,12 +118,14 @@ export {
|
|
|
114
118
|
ServiceLauncherCardSkeleton,
|
|
115
119
|
StatCard,
|
|
116
120
|
StatusBadge,
|
|
121
|
+
Swatch,
|
|
117
122
|
Table,
|
|
118
123
|
TableBody,
|
|
119
124
|
TableCell,
|
|
120
125
|
TableHead,
|
|
121
126
|
TableHeader,
|
|
122
127
|
TableRow,
|
|
128
|
+
Thumbnail,
|
|
123
129
|
Timeline,
|
|
124
130
|
TimelineGrid,
|
|
125
131
|
Tree,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SwatchProp } from "../../props/components/data-display.prop.js";
|
|
2
|
+
export type { SwatchProp, SwatchProp as SwatchProps, } from "../../props/components/data-display.prop.js";
|
|
3
|
+
/**
|
|
4
|
+
* Swatch — a READ-ONLY sample of one colour a person chose (gh#527).
|
|
5
|
+
*
|
|
6
|
+
* The colour arrives as a VALUE, never as a token: it is a brand's `primary_color`, a calendar
|
|
7
|
+
* category, a tag a user tinted. That is the axis `Legend` cannot express — its `tone` is a closed
|
|
8
|
+
* set of semantic roles — and the reason there was no legal way to draw one: a hand-rolled
|
|
9
|
+
* `<span className="w-[10px] h-[10px] rounded-[2px] bg-[#c0392f]" />` is blocked by ui-audit three
|
|
10
|
+
* times over, so the only remaining move was a suppression comment.
|
|
11
|
+
*
|
|
12
|
+
* It is the same square mark `Legend` draws (a sample reads as a SAMPLE; a pill reads as a chip
|
|
13
|
+
* you could click), one step larger, because it stands beside a name rather than inside an 11px
|
|
14
|
+
* key. The hairline is not decoration: without it a white or near-white value is invisible on a
|
|
15
|
+
* white card, and the sample stops showing anything at all.
|
|
16
|
+
*
|
|
17
|
+
* ## The accessible name
|
|
18
|
+
*
|
|
19
|
+
* Given an `aria-label` the mark is a `role="img"` that announces it, which is what lets the
|
|
20
|
+
* colour be shown with no visible label beside it. Given none it is `aria-hidden` — the Legend
|
|
21
|
+
* rule, for the same reason: a swatch that repeats a label already on screen would announce the
|
|
22
|
+
* same thing twice. Colour is never the sole carrier either way.
|
|
23
|
+
*/
|
|
24
|
+
export declare function Swatch({ color, className, style, ...props }: SwatchProp): import("react").JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/utils.js";
|
|
3
|
+
function Swatch({ color, className, style, ...props }) {
|
|
4
|
+
const named = props["aria-label"] !== void 0 || props["aria-labelledby"] !== void 0;
|
|
5
|
+
return /* @__PURE__ */ jsx(
|
|
6
|
+
"span",
|
|
7
|
+
{
|
|
8
|
+
"data-slot": "swatch",
|
|
9
|
+
className: cn("ui-swatch", className),
|
|
10
|
+
style: { ...style, ["--swatch-color"]: color },
|
|
11
|
+
role: named ? "img" : void 0,
|
|
12
|
+
"aria-hidden": named ? void 0 : true,
|
|
13
|
+
...props
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
Swatch
|
|
19
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type { ThumbnailProp, ThumbnailProp as ThumbnailProps, ThumbnailSizeProp, } from "../../props/components/data-display.prop.js";
|
|
3
|
+
/**
|
|
4
|
+
* Thumbnail — a framed picture at a FIXED HEIGHT and its OWN intrinsic width.
|
|
5
|
+
*
|
|
6
|
+
* That combination is the whole component. A wrapping row of screenshots holds portrait and
|
|
7
|
+
* landscape shots together; equal heights make the row read as one strip, and letting each
|
|
8
|
+
* width follow its own ratio is what keeps every shot uncropped.
|
|
9
|
+
*
|
|
10
|
+
* ## Why not the four components it is mistaken for
|
|
11
|
+
*
|
|
12
|
+
* - **`AspectRatio`** constrains a RATIO and spans `width: 100%`. Two different intrinsic ratios
|
|
13
|
+
* in one row can only be forced into one number by letterboxing or cropping them, which is the
|
|
14
|
+
* opposite of the requirement. It also draws no frame.
|
|
15
|
+
* - **`Avatar`** is an identity mark (with initials fallback), not a picture.
|
|
16
|
+
* - **`Card`** puts padding between its edge and the picture. The frame here is ON the image.
|
|
17
|
+
* - **`CardCover`** is the full-bleed media slot INSIDE a Card, not a standalone thumbnail.
|
|
18
|
+
*
|
|
19
|
+
* ## The frame is on the `<img>` itself
|
|
20
|
+
*
|
|
21
|
+
* No wrapper element. A wrapper would have to be told the image's width to hug it, which is
|
|
22
|
+
* exactly the number nobody has until the picture loads; a border on the replaced element is
|
|
23
|
+
* flush by construction at every ratio.
|
|
24
|
+
*
|
|
25
|
+
* ## Reserve the width before the picture arrives
|
|
26
|
+
*
|
|
27
|
+
* With an intrinsic width there is nothing to lay out until the bytes land, so a row of these
|
|
28
|
+
* reflows as they load. Pass the file's real pixel `width` and `height` attributes — the browser
|
|
29
|
+
* derives the ratio from them, and with the block size already fixed the frame takes its final
|
|
30
|
+
* width on the first paint (`<Thumbnail width={1280} height={800} … />`).
|
|
31
|
+
*/
|
|
32
|
+
export declare const Thumbnail: React.ForwardRefExoticComponent<Omit<React.ImgHTMLAttributes<HTMLImageElement>, "alt"> & {
|
|
33
|
+
src: string;
|
|
34
|
+
alt: string;
|
|
35
|
+
size?: import("./thumbnail.js").ThumbnailSizeProp;
|
|
36
|
+
className?: import("../../props/index.js").ClassNameProp;
|
|
37
|
+
} & React.RefAttributes<HTMLImageElement>>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
const Thumbnail = React.forwardRef(
|
|
5
|
+
({ size = "md", className, alt, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
6
|
+
"img",
|
|
7
|
+
{
|
|
8
|
+
ref,
|
|
9
|
+
"data-slot": "thumbnail",
|
|
10
|
+
"data-size": size,
|
|
11
|
+
alt,
|
|
12
|
+
className: cn("ui-thumbnail", className),
|
|
13
|
+
...props
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
);
|
|
17
|
+
Thumbnail.displayName = "Thumbnail";
|
|
18
|
+
export {
|
|
19
|
+
Thumbnail
|
|
20
|
+
};
|
|
@@ -6,8 +6,8 @@ export { TwoFactorSetup } from "./two-factor-setup.js";
|
|
|
6
6
|
export type { TwoFactorSetupLabels, TwoFactorSetupProps } from "./two-factor-setup.js";
|
|
7
7
|
export { Toaster } from "./sonner.js";
|
|
8
8
|
export { toast } from "./use-toast.js";
|
|
9
|
-
export { Skeleton, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat } from "./skeleton.js";
|
|
10
|
-
export type { SkeletonProps } from "./skeleton.js";
|
|
9
|
+
export { Skeleton, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat, SkeletonArticle, SkeletonAvatar, SkeletonButton, SkeletonInput, SkeletonNode, SkeletonImage, } from "./skeleton.js";
|
|
10
|
+
export type { SkeletonProp, SkeletonProps, SkeletonWidth, SkeletonArticleProp, SkeletonArticleProps, SkeletonAvatarProp, SkeletonAvatarProps, SkeletonButtonProp, SkeletonButtonProps, SkeletonInputProp, SkeletonInputProps, SkeletonNodeProp, SkeletonNodeProps, SkeletonImageProp, SkeletonImageProps, } from "./skeleton.js";
|
|
11
11
|
export { Banner } from "./banner.js";
|
|
12
12
|
export type { BannerProp, BannerProps } from "./banner.js";
|
|
13
13
|
export { Alert, AlertTitle, AlertContent, AlertDescription, AlertActions, AlertQueryError, } from "./alert.js";
|
|
@@ -42,7 +42,19 @@ import {
|
|
|
42
42
|
import { TwoFactorSetup } from "./two-factor-setup.js";
|
|
43
43
|
import { Toaster } from "./sonner.js";
|
|
44
44
|
import { toast } from "./use-toast.js";
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
Skeleton,
|
|
47
|
+
SkeletonRows,
|
|
48
|
+
SkeletonTable,
|
|
49
|
+
SkeletonDetail,
|
|
50
|
+
SkeletonStat,
|
|
51
|
+
SkeletonArticle,
|
|
52
|
+
SkeletonAvatar,
|
|
53
|
+
SkeletonButton,
|
|
54
|
+
SkeletonInput,
|
|
55
|
+
SkeletonNode,
|
|
56
|
+
SkeletonImage
|
|
57
|
+
} from "./skeleton.js";
|
|
46
58
|
import { Banner } from "./banner.js";
|
|
47
59
|
import {
|
|
48
60
|
Alert,
|
|
@@ -98,7 +110,13 @@ export {
|
|
|
98
110
|
SheetTitle,
|
|
99
111
|
SheetTrigger,
|
|
100
112
|
Skeleton,
|
|
113
|
+
SkeletonArticle,
|
|
114
|
+
SkeletonAvatar,
|
|
115
|
+
SkeletonButton,
|
|
101
116
|
SkeletonDetail,
|
|
117
|
+
SkeletonImage,
|
|
118
|
+
SkeletonInput,
|
|
119
|
+
SkeletonNode,
|
|
102
120
|
SkeletonRows,
|
|
103
121
|
SkeletonStat,
|
|
104
122
|
SkeletonTable,
|
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
export
|
|
2
|
+
import type { SkeletonArticleProp, SkeletonAvatarProp, SkeletonButtonProp, SkeletonImageProp, SkeletonInputProp, SkeletonNodeProp, SkeletonProp } from "../../props/components/feedback.prop.js";
|
|
3
|
+
export type { SkeletonProp, SkeletonProp as SkeletonProps, SkeletonWidth, SkeletonArticleProp, SkeletonArticleProp as SkeletonArticleProps, SkeletonAvatarProp, SkeletonAvatarProp as SkeletonAvatarProps, SkeletonButtonProp, SkeletonButtonProp as SkeletonButtonProps, SkeletonInputProp, SkeletonInputProp as SkeletonInputProps, SkeletonNodeProp, SkeletonNodeProp as SkeletonNodeProps, SkeletonImageProp, SkeletonImageProp as SkeletonImageProps, } from "../../props/components/feedback.prop.js";
|
|
4
|
+
declare function SkeletonBlock({ active, loading, className, children, ...props }: SkeletonProp): React.JSX.Element;
|
|
5
|
+
/** Stands in for an `Avatar` — circle for a person, square for an entity mark. */
|
|
6
|
+
export declare function SkeletonAvatar({ size, shape, active, className }: SkeletonAvatarProp): React.JSX.Element;
|
|
7
|
+
/** Stands in for a `Button` — two control-heights wide, like antd's `controlHeight * 2`. */
|
|
8
|
+
export declare function SkeletonButton({ size, shape, block, active, className }: SkeletonButtonProp): React.JSX.Element;
|
|
9
|
+
/** Stands in for an `Input` — five control-heights wide, like antd's `controlHeight * 5`. */
|
|
10
|
+
export declare function SkeletonInput({ size, block, active, className }: SkeletonInputProp): React.JSX.Element;
|
|
11
|
+
/** A square placeholder for a media or custom slot; `children` centre inside it. */
|
|
12
|
+
export declare function SkeletonNode({ active, children, className }: SkeletonNodeProp): React.JSX.Element;
|
|
13
|
+
/** `SkeletonNode` carrying the image glyph — antd ships its own path; here it is the DS icon. */
|
|
14
|
+
export declare function SkeletonImage({ active, className }: SkeletonImageProp): React.JSX.Element;
|
|
15
|
+
/**
|
|
16
|
+
* SkeletonArticle — antd's own `<Skeleton>` shape: an optional avatar beside a heading line and a
|
|
17
|
+
* paragraph. Reach for it for a comment, a profile card or a feed item; `Skeleton` stays the bare
|
|
18
|
+
* block, and `SkeletonRows` / `SkeletonTable` / `SkeletonDetail` / `SkeletonStat` stay the house
|
|
19
|
+
* shapes for a list, a table, a record and a KPI tile.
|
|
20
|
+
*/
|
|
21
|
+
export declare function SkeletonArticle({ avatar, title, paragraph, round, active, loading, children, className, }: SkeletonArticleProp): React.JSX.Element;
|
|
22
|
+
/**
|
|
23
|
+
* Skeleton — the placeholder BLOCK, and the namespace antd hangs its element presets on. The
|
|
24
|
+
* statics are the antd spelling (`Skeleton.Button`); the same components are also named exports, so
|
|
25
|
+
* a consumer importing `SkeletonButton` finds it in the catalog.
|
|
26
|
+
*/
|
|
27
|
+
export declare const Skeleton: typeof SkeletonBlock & {
|
|
28
|
+
Avatar: typeof SkeletonAvatar;
|
|
29
|
+
Button: typeof SkeletonButton;
|
|
30
|
+
Input: typeof SkeletonInput;
|
|
31
|
+
Node: typeof SkeletonNode;
|
|
32
|
+
Image: typeof SkeletonImage;
|
|
33
|
+
Article: typeof SkeletonArticle;
|
|
34
|
+
};
|
|
4
35
|
interface SkeletonRowsProps {
|
|
5
36
|
rows?: number;
|
|
6
37
|
columns?: number;
|
|
@@ -14,4 +45,3 @@ export declare function SkeletonTable({ rows, columns }: SkeletonRowsProps): Rea
|
|
|
14
45
|
export declare function SkeletonDetail(): React.JSX.Element;
|
|
15
46
|
/** Skeleton matching a stat card / dashboard tile. */
|
|
16
47
|
export declare function SkeletonStat(): React.JSX.Element;
|
|
17
|
-
export {};
|
|
@@ -1,17 +1,162 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ImageIcon } from "lucide-react";
|
|
2
3
|
import { cn } from "../../lib/utils.js";
|
|
3
4
|
import { tableCellPaddingClass, tableRowHeightClass } from "../../lib/control-styles.js";
|
|
4
|
-
function
|
|
5
|
+
function measureStyle(width) {
|
|
6
|
+
if (width === void 0) return void 0;
|
|
7
|
+
const value = typeof width === "number" ? `${width}px` : width;
|
|
8
|
+
return { "--skeleton-line-width": value };
|
|
9
|
+
}
|
|
10
|
+
function SkeletonBlock({ active, loading, className, children, ...props }) {
|
|
11
|
+
if (loading === false) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
5
12
|
return /* @__PURE__ */ jsx(
|
|
6
13
|
"div",
|
|
7
14
|
{
|
|
8
15
|
"aria-busy": "true",
|
|
9
16
|
"aria-live": "polite",
|
|
17
|
+
"data-active": active ? "" : void 0,
|
|
10
18
|
className: cn("ui-skeleton-block", className),
|
|
11
|
-
...props
|
|
19
|
+
...props,
|
|
20
|
+
children
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
function SkeletonElement({
|
|
25
|
+
kind,
|
|
26
|
+
size,
|
|
27
|
+
shape,
|
|
28
|
+
block,
|
|
29
|
+
active,
|
|
30
|
+
className,
|
|
31
|
+
children
|
|
32
|
+
}) {
|
|
33
|
+
return /* @__PURE__ */ jsx(
|
|
34
|
+
SkeletonBlock,
|
|
35
|
+
{
|
|
36
|
+
active,
|
|
37
|
+
"data-skeleton-element": kind,
|
|
38
|
+
"data-size": size,
|
|
39
|
+
"data-shape": shape,
|
|
40
|
+
"data-block": block ? "" : void 0,
|
|
41
|
+
className,
|
|
42
|
+
children
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
function SkeletonAvatar({ size, shape = "circle", active, className }) {
|
|
47
|
+
return /* @__PURE__ */ jsx(
|
|
48
|
+
SkeletonElement,
|
|
49
|
+
{
|
|
50
|
+
kind: "avatar",
|
|
51
|
+
size,
|
|
52
|
+
shape,
|
|
53
|
+
active,
|
|
54
|
+
className
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function SkeletonButton({ size, shape, block, active, className }) {
|
|
59
|
+
return /* @__PURE__ */ jsx(
|
|
60
|
+
SkeletonElement,
|
|
61
|
+
{
|
|
62
|
+
kind: "button",
|
|
63
|
+
size,
|
|
64
|
+
shape,
|
|
65
|
+
block,
|
|
66
|
+
active,
|
|
67
|
+
className
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function SkeletonInput({ size, block, active, className }) {
|
|
72
|
+
return /* @__PURE__ */ jsx(SkeletonElement, { kind: "input", size, block, active, className });
|
|
73
|
+
}
|
|
74
|
+
function SkeletonNode({ active, children, className }) {
|
|
75
|
+
return /* @__PURE__ */ jsx(SkeletonElement, { kind: "node", active, className, children });
|
|
76
|
+
}
|
|
77
|
+
function SkeletonImage({ active, className }) {
|
|
78
|
+
return /* @__PURE__ */ jsx(SkeletonElement, { kind: "image", active, className, children: /* @__PURE__ */ jsx(ImageIcon, { "aria-hidden": "true", focusable: "false", className: "ui-skeleton-image-glyph" }) });
|
|
79
|
+
}
|
|
80
|
+
function avatarDefaults(hasTitle, hasParagraph) {
|
|
81
|
+
if (hasTitle && !hasParagraph) return { size: "lg", shape: "square" };
|
|
82
|
+
return { size: "lg", shape: "circle" };
|
|
83
|
+
}
|
|
84
|
+
function titleDefaultWidth(hasAvatar, hasParagraph) {
|
|
85
|
+
if (!hasAvatar && hasParagraph) return "38%";
|
|
86
|
+
if (hasAvatar && hasParagraph) return "50%";
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
function paragraphDefaults(hasAvatar, hasTitle) {
|
|
90
|
+
return {
|
|
91
|
+
rows: !hasAvatar && hasTitle ? 3 : 2,
|
|
92
|
+
width: !hasAvatar || !hasTitle ? "61%" : void 0
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function rowWidth(index, rows, width) {
|
|
96
|
+
if (Array.isArray(width)) return width[index];
|
|
97
|
+
return rows - 1 === index ? width : void 0;
|
|
98
|
+
}
|
|
99
|
+
function SkeletonArticle({
|
|
100
|
+
avatar = false,
|
|
101
|
+
title = true,
|
|
102
|
+
paragraph = true,
|
|
103
|
+
round,
|
|
104
|
+
active,
|
|
105
|
+
loading,
|
|
106
|
+
children,
|
|
107
|
+
className
|
|
108
|
+
}) {
|
|
109
|
+
if (loading === false) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
110
|
+
const hasAvatar = Boolean(avatar);
|
|
111
|
+
const hasTitle = Boolean(title);
|
|
112
|
+
const hasParagraph = Boolean(paragraph);
|
|
113
|
+
const avatarProps = {
|
|
114
|
+
...avatarDefaults(hasTitle, hasParagraph),
|
|
115
|
+
...typeof avatar === "object" ? avatar : {}
|
|
116
|
+
};
|
|
117
|
+
const titleWidth = (typeof title === "object" ? title.width : void 0) ?? titleDefaultWidth(hasAvatar, hasParagraph);
|
|
118
|
+
const paragraphBase = paragraphDefaults(hasAvatar, hasTitle);
|
|
119
|
+
const paragraphProps = typeof paragraph === "object" ? paragraph : {};
|
|
120
|
+
const rows = paragraphProps.rows ?? paragraphBase.rows;
|
|
121
|
+
const paragraphWidth = paragraphProps.width ?? paragraphBase.width;
|
|
122
|
+
return /* @__PURE__ */ jsxs(
|
|
123
|
+
"div",
|
|
124
|
+
{
|
|
125
|
+
"aria-busy": "true",
|
|
126
|
+
"data-slot": "skeleton-article",
|
|
127
|
+
"data-avatar": hasAvatar ? "" : void 0,
|
|
128
|
+
"data-active": active ? "" : void 0,
|
|
129
|
+
"data-round": round ? "" : void 0,
|
|
130
|
+
className: cn("ui-skeleton-article", className),
|
|
131
|
+
children: [
|
|
132
|
+
hasAvatar ? /* @__PURE__ */ jsx("div", { className: "ui-skeleton-article-header", children: /* @__PURE__ */ jsx(SkeletonAvatar, { ...avatarProps }) }) : null,
|
|
133
|
+
hasTitle || hasParagraph ? /* @__PURE__ */ jsxs("div", { className: "ui-skeleton-article-section", children: [
|
|
134
|
+
hasTitle ? /* @__PURE__ */ jsx(SkeletonBlock, { className: "ui-skeleton-article-title", style: measureStyle(titleWidth) }) : null,
|
|
135
|
+
hasParagraph ? (
|
|
136
|
+
// Deliberately divs, not antd's <ul><li>: its rows carry no text, so a real list would
|
|
137
|
+
// announce "list, 3 items" out of an already `aria-busy` region.
|
|
138
|
+
/* @__PURE__ */ jsx("div", { className: "ui-skeleton-article-paragraph", children: Array.from({ length: rows }).map((_, index) => /* @__PURE__ */ jsx(
|
|
139
|
+
SkeletonBlock,
|
|
140
|
+
{
|
|
141
|
+
className: "ui-skeleton-article-line",
|
|
142
|
+
style: measureStyle(rowWidth(index, rows, paragraphWidth))
|
|
143
|
+
},
|
|
144
|
+
index
|
|
145
|
+
)) })
|
|
146
|
+
) : null
|
|
147
|
+
] }) : null
|
|
148
|
+
]
|
|
12
149
|
}
|
|
13
150
|
);
|
|
14
151
|
}
|
|
152
|
+
const Skeleton = Object.assign(SkeletonBlock, {
|
|
153
|
+
Avatar: SkeletonAvatar,
|
|
154
|
+
Button: SkeletonButton,
|
|
155
|
+
Input: SkeletonInput,
|
|
156
|
+
Node: SkeletonNode,
|
|
157
|
+
Image: SkeletonImage,
|
|
158
|
+
Article: SkeletonArticle
|
|
159
|
+
});
|
|
15
160
|
function SkeletonRows({ rows = 6, columns = 4, className }) {
|
|
16
161
|
return /* @__PURE__ */ jsx("div", { className: cn("ui-skeleton-rows", className), "aria-busy": "true", children: Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "ui-skeleton-row", children: Array.from({ length: columns }).map((_2, j) => /* @__PURE__ */ jsx(
|
|
17
162
|
Skeleton,
|
|
@@ -56,7 +201,13 @@ function SkeletonStat() {
|
|
|
56
201
|
}
|
|
57
202
|
export {
|
|
58
203
|
Skeleton,
|
|
204
|
+
SkeletonArticle,
|
|
205
|
+
SkeletonAvatar,
|
|
206
|
+
SkeletonButton,
|
|
59
207
|
SkeletonDetail,
|
|
208
|
+
SkeletonImage,
|
|
209
|
+
SkeletonInput,
|
|
210
|
+
SkeletonNode,
|
|
60
211
|
SkeletonRows,
|
|
61
212
|
SkeletonStat,
|
|
62
213
|
SkeletonTable
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FlexProp } from "../../props/components/layout.prop.js";
|
|
2
2
|
export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexProp, FlexProp as FlexProps, } from "../../props/components/layout.prop.js";
|
|
3
|
-
export declare function Flex({ as: Element, direction, grow, shrink, surface, bleed, reveal, gap, gapRaw, pad, padRaw, align, justify, wrap, hideBelow, hideFrom, fill, width, className, style, children, ...props }: FlexProp): import("react").JSX.Element;
|
|
3
|
+
export declare function Flex({ as: Element, direction, grow, shrink, surface, bleed, reveal, gap, gapRaw, pad, padRaw, align, justify, wrap, hideBelow, hideFrom, hideBelowRaw, hideFromRaw, fill, width, className, style, children, ...props }: FlexProp): import("react").JSX.Element;
|