@gtivr4/a1-design-system-react 0.19.0 → 0.20.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/package.json +1 -1
- package/src/components/card/Card.d.ts +9 -0
- package/src/components/card/Card.jsx +22 -0
- package/src/components/card/card.css +21 -2
- package/src/components/code/Code.jsx +33 -24
- package/src/components/code/code.css +8 -1
- package/src/components/top-header/top-header.css +7 -0
package/package.json
CHANGED
|
@@ -25,6 +25,15 @@ export interface CardProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
25
25
|
* Default: "action"
|
|
26
26
|
*/
|
|
27
27
|
heroColor?: "action" | "neutral" | "info" | "success" | "warn" | "error" | (string & {});
|
|
28
|
+
/** Badge label overlaid on the hero (only renders when `iconDisplay="hero"`). */
|
|
29
|
+
heroBadge?: React.ReactNode;
|
|
30
|
+
/** Status colour of the hero badge. Default: "neutral" */
|
|
31
|
+
heroBadgeStatus?: "neutral" | "info" | "success" | "warn" | "error";
|
|
32
|
+
/** Placement of the hero badge on a 3×3 grid ("{top|middle|bottom}-{start|center|end}"). Default: "top-end" */
|
|
33
|
+
heroBadgePosition?:
|
|
34
|
+
| "top-start" | "top-center" | "top-end"
|
|
35
|
+
| "middle-start" | "middle-center" | "middle-end"
|
|
36
|
+
| "bottom-start" | "bottom-center" | "bottom-end";
|
|
28
37
|
children?: React.ReactNode;
|
|
29
38
|
}
|
|
30
39
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./card.css";
|
|
2
2
|
import { Icon } from "../icon/Icon.jsx";
|
|
3
|
+
import { MessageBadge } from "../message/Message.jsx";
|
|
3
4
|
|
|
4
5
|
const HERO_COLORS = {
|
|
5
6
|
action: "var(--semantic-color-action-background)",
|
|
@@ -12,6 +13,14 @@ const HERO_COLORS = {
|
|
|
12
13
|
|
|
13
14
|
const VALID_ICON_DISPLAY = ["none", "default", "hero"];
|
|
14
15
|
|
|
16
|
+
// 3×3 placement of a hero badge: "{block}-{inline}" where block ∈ top|middle|bottom
|
|
17
|
+
// and inline ∈ start|center|end.
|
|
18
|
+
const VALID_HERO_BADGE_POSITIONS = [
|
|
19
|
+
"top-start", "top-center", "top-end",
|
|
20
|
+
"middle-start", "middle-center", "middle-end",
|
|
21
|
+
"bottom-start", "bottom-center", "bottom-end",
|
|
22
|
+
];
|
|
23
|
+
|
|
15
24
|
export function Card({
|
|
16
25
|
as,
|
|
17
26
|
bare = false,
|
|
@@ -20,6 +29,9 @@ export function Card({
|
|
|
20
29
|
icon,
|
|
21
30
|
iconDisplay = "default",
|
|
22
31
|
heroColor = "action",
|
|
32
|
+
heroBadge,
|
|
33
|
+
heroBadgeStatus = "neutral",
|
|
34
|
+
heroBadgePosition = "top-end",
|
|
23
35
|
className = "",
|
|
24
36
|
children,
|
|
25
37
|
...props
|
|
@@ -47,12 +59,22 @@ export function Card({
|
|
|
47
59
|
? { type: "button" }
|
|
48
60
|
: {};
|
|
49
61
|
|
|
62
|
+
const badgePos = VALID_HERO_BADGE_POSITIONS.includes(heroBadgePosition)
|
|
63
|
+
? heroBadgePosition
|
|
64
|
+
: "top-end";
|
|
65
|
+
const [badgeBlock, badgeInline] = badgePos.split("-");
|
|
66
|
+
|
|
50
67
|
return (
|
|
51
68
|
<Component className={classes} href={href} {...interactiveProps} {...props}>
|
|
52
69
|
<div className="a1-card__layout">
|
|
53
70
|
{resolvedDisplay === "hero" && (
|
|
54
71
|
<div className="a1-card__hero" style={{ "--a1-card-hero-bg": heroBg }}>
|
|
55
72
|
<Icon name={icon} aria-hidden="true" />
|
|
73
|
+
{heroBadge && (
|
|
74
|
+
<span className={`a1-card__hero-badge a1-card__hero-badge--${badgeBlock} a1-card__hero-badge--${badgeInline}`}>
|
|
75
|
+
<MessageBadge status={heroBadgeStatus} size="sm">{heroBadge}</MessageBadge>
|
|
76
|
+
</span>
|
|
77
|
+
)}
|
|
56
78
|
</div>
|
|
57
79
|
)}
|
|
58
80
|
{resolvedDisplay === "default" && (
|
|
@@ -79,6 +79,7 @@ button.a1-card--navigation {
|
|
|
79
79
|
|
|
80
80
|
.a1-card__hero {
|
|
81
81
|
/* Bleed out to the card edges on all four sides then add inner padding */
|
|
82
|
+
position: relative;
|
|
82
83
|
margin-top: calc(-1 * var(--component-card-padding));
|
|
83
84
|
margin-inline: calc(-1 * var(--component-card-padding));
|
|
84
85
|
margin-bottom: var(--component-card-padding);
|
|
@@ -91,8 +92,26 @@ button.a1-card--navigation {
|
|
|
91
92
|
--a1-icon-opsz: 48;
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
/* Hero badge — overlaid on the hero, placed via a 3×3 grid. */
|
|
96
|
+
.a1-card__hero-badge {
|
|
97
|
+
position: absolute;
|
|
98
|
+
z-index: 1;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.a1-card__hero-badge--top { inset-block-start: var(--base-spacing-8); }
|
|
102
|
+
.a1-card__hero-badge--bottom { inset-block-end: var(--base-spacing-8); }
|
|
103
|
+
.a1-card__hero-badge--middle { inset-block-start: 50%; }
|
|
104
|
+
.a1-card__hero-badge--start { inset-inline-start: var(--base-spacing-8); }
|
|
105
|
+
.a1-card__hero-badge--end { inset-inline-end: var(--base-spacing-8); }
|
|
106
|
+
.a1-card__hero-badge--center { inset-inline-start: 50%; }
|
|
107
|
+
|
|
108
|
+
/* Centre transforms for the middle/centre axes (combine when both). */
|
|
109
|
+
.a1-card__hero-badge--middle:not(.a1-card__hero-badge--center) { transform: translateY(-50%); }
|
|
110
|
+
.a1-card__hero-badge--center:not(.a1-card__hero-badge--middle) { transform: translateX(-50%); }
|
|
111
|
+
.a1-card__hero-badge--middle.a1-card__hero-badge--center { transform: translate(-50%, -50%); }
|
|
112
|
+
|
|
94
113
|
/* Higher specificity (0,2,0) beats .a1-icon (0,1,0) so font-size is not overridden by inherit */
|
|
95
|
-
.a1-card__hero .a1-icon {
|
|
114
|
+
.a1-card__hero > .a1-icon {
|
|
96
115
|
font-size: var(--base-spacing-64);
|
|
97
116
|
color: var(--semantic-color-text-inverse);
|
|
98
117
|
}
|
|
@@ -120,7 +139,7 @@ button.a1-card--navigation {
|
|
|
120
139
|
border-end-end-radius: 0;
|
|
121
140
|
}
|
|
122
141
|
|
|
123
|
-
.a1-card__hero .a1-icon {
|
|
142
|
+
.a1-card__hero > .a1-icon {
|
|
124
143
|
font-size: var(--base-spacing-128);
|
|
125
144
|
}
|
|
126
145
|
|
|
@@ -146,7 +146,11 @@ export function Code({
|
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
// Cap the height whenever collapsible + not expanded (so the overflow check has
|
|
150
|
+
// a clamped height to measure against); `clipped` adds the fade only when the
|
|
151
|
+
// content actually overflows the cap.
|
|
152
|
+
const collapsed = collapses && !expanded;
|
|
153
|
+
const clipped = collapsed && overflows;
|
|
150
154
|
|
|
151
155
|
return (
|
|
152
156
|
<div
|
|
@@ -155,6 +159,7 @@ export function Code({
|
|
|
155
159
|
copyCode && "a1-code-block--copyable",
|
|
156
160
|
editable && "a1-code-block--editable",
|
|
157
161
|
collapsed && "a1-code-block--collapsed",
|
|
162
|
+
clipped && "a1-code-block--clipped",
|
|
158
163
|
className,
|
|
159
164
|
]
|
|
160
165
|
.filter(Boolean)
|
|
@@ -185,29 +190,33 @@ export function Code({
|
|
|
185
190
|
</code>
|
|
186
191
|
</pre>
|
|
187
192
|
)}
|
|
188
|
-
{copyCode && (
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
193
|
+
{(copyCode || (collapses && overflows)) && (
|
|
194
|
+
<div className="a1-code-block__actions">
|
|
195
|
+
{copyCode && (
|
|
196
|
+
<Button
|
|
197
|
+
className="a1-code-block__copy"
|
|
198
|
+
icon="content_copy"
|
|
199
|
+
size="sm"
|
|
200
|
+
variant="tertiary"
|
|
201
|
+
onClick={handleCopy}
|
|
202
|
+
type="button"
|
|
203
|
+
>
|
|
204
|
+
{copied ? copiedLabel : copyLabel}
|
|
205
|
+
</Button>
|
|
206
|
+
)}
|
|
207
|
+
{collapses && overflows && (
|
|
208
|
+
<Button
|
|
209
|
+
className="a1-code-block__toggle"
|
|
210
|
+
icon={expanded ? "expand_less" : "expand_more"}
|
|
211
|
+
size="sm"
|
|
212
|
+
variant="tertiary"
|
|
213
|
+
onClick={() => setExpanded((v) => !v)}
|
|
214
|
+
type="button"
|
|
215
|
+
>
|
|
216
|
+
{expanded ? showLessLabel : showMoreLabel}
|
|
217
|
+
</Button>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
211
220
|
)}
|
|
212
221
|
</div>
|
|
213
222
|
);
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
overflow-y: hidden;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
.a1-code-block--
|
|
98
|
+
.a1-code-block--clipped .a1-code-block__pre::after {
|
|
99
99
|
content: "";
|
|
100
100
|
position: absolute;
|
|
101
101
|
inset-inline: 0;
|
|
@@ -110,3 +110,10 @@
|
|
|
110
110
|
.a1-code-block__toggle {
|
|
111
111
|
margin: 0;
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
/* Copy + Show more/less sit inline on one row. */
|
|
115
|
+
.a1-code-block__actions {
|
|
116
|
+
display: flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
gap: var(--base-spacing-8);
|
|
119
|
+
}
|
|
@@ -213,6 +213,13 @@
|
|
|
213
213
|
color: var(--semantic-color-text-muted);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
/* When the parent is the current/selected item (a child page is active), its
|
|
217
|
+
leading icon matches the selected text colour instead of staying muted. */
|
|
218
|
+
.a1-top-header__flyout-trigger.a1-menu-item--active .a1-top-header__flyout-icon,
|
|
219
|
+
.a1-top-header__flyout-trigger[aria-current="page"] .a1-top-header__flyout-icon {
|
|
220
|
+
color: currentColor;
|
|
221
|
+
}
|
|
222
|
+
|
|
216
223
|
.a1-top-header__flyout-chevron {
|
|
217
224
|
margin-inline-start: auto;
|
|
218
225
|
flex-shrink: 0;
|