@godxjp/ui 23.1.0 → 23.2.1

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.
Files changed (58) hide show
  1. package/dist/components/data-display/card.d.ts +45 -2
  2. package/dist/components/data-display/card.js +4 -2
  3. package/dist/components/data-display/feature-list.d.ts +26 -0
  4. package/dist/components/data-display/feature-list.js +29 -0
  5. package/dist/components/data-display/index.d.ts +6 -0
  6. package/dist/components/data-display/index.js +6 -0
  7. package/dist/components/data-display/swatch.d.ts +24 -0
  8. package/dist/components/data-display/swatch.js +19 -0
  9. package/dist/components/data-display/thumbnail.d.ts +37 -0
  10. package/dist/components/data-display/thumbnail.js +20 -0
  11. package/dist/components/feedback/index.d.ts +2 -2
  12. package/dist/components/feedback/index.js +19 -1
  13. package/dist/components/feedback/skeleton.d.ts +33 -3
  14. package/dist/components/feedback/skeleton.js +154 -3
  15. package/dist/components/layout/flex.d.ts +1 -1
  16. package/dist/components/layout/flex.js +17 -5
  17. package/dist/components/layout/sidebar.js +9 -2
  18. package/dist/components/navigation/tabs-scroll.d.ts +27 -0
  19. package/dist/components/navigation/tabs-scroll.js +36 -1
  20. package/dist/components/navigation/tabs.d.ts +2 -2
  21. package/dist/components/navigation/tabs.js +44 -12
  22. package/dist/i18n/messages/en.json +5 -0
  23. package/dist/i18n/messages/ja.json +5 -0
  24. package/dist/i18n/messages/vi.json +5 -0
  25. package/dist/props/components/data-display.prop.d.ts +93 -0
  26. package/dist/props/components/feedback.prop.d.ts +85 -1
  27. package/dist/props/components/layout.prop.d.ts +72 -1
  28. package/dist/props/components/navigation.prop.d.ts +72 -1
  29. package/dist/props/registry.d.ts +118 -0
  30. package/dist/props/registry.js +134 -0
  31. package/dist/styles/alert-layout.css +139 -0
  32. package/dist/styles/card-layout.css +48 -0
  33. package/dist/styles/data-display-layout.css +88 -0
  34. package/dist/styles/navigation-layout.css +76 -3
  35. package/dist/styles/shell-layout.css +18 -0
  36. package/dist/tokens/components/card.css +2 -0
  37. package/dist/tokens/components/data-display.css +23 -0
  38. package/dist/tokens/components/feedback.css +18 -0
  39. package/dist/tokens/components/navigation.css +2 -0
  40. package/dist/tokens/foundation.css +18 -16
  41. package/docs/CONSUMER-RULES.md +1 -1
  42. package/docs/DESIGN-AUTHORITY.md +2 -1
  43. package/docs/FRAME-COVERAGE-REPORT.md +29 -3
  44. package/docs/assets/shot-landscape.svg +12 -0
  45. package/docs/assets/shot-portrait.svg +10 -0
  46. package/docs/data-display/card/index.tsx +40 -0
  47. package/docs/data-display/feature-list.tsx +159 -0
  48. package/docs/data-display/swatch.tsx +107 -0
  49. package/docs/data-display/thumbnail.tsx +128 -0
  50. package/docs/feedback/skeleton.tsx +57 -0
  51. package/docs/layout/flex.tsx +29 -0
  52. package/docs/layout/sidebar.tsx +66 -10
  53. package/docs/navigation/tabs.tsx +59 -0
  54. package/package.json +4 -7
  55. package/scripts/_agent-setup.mjs +164 -28
  56. package/scripts/consumer-rule.md +16 -0
  57. package/scripts/postinstall.mjs +9 -0
  58. package/scripts/ui-audit.mjs +70 -28
@@ -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
- /** Surface fill — plain card, muted band, borderless outline, or emphasized featured ring. */
17
- type CardVariant = "default" | "muted" | "outline" | "featured";
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 { Skeleton, SkeletonRows, SkeletonTable, SkeletonDetail, SkeletonStat } from "./skeleton.js";
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
- export type SkeletonProps = React.HTMLAttributes<HTMLDivElement>;
3
- export declare function Skeleton({ className, ...props }: SkeletonProps): React.JSX.Element;
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 Skeleton({ className, ...props }) {
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;