@astryxdesign/core 0.4.2 โ 0.4.3
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/CHANGELOG.md +45 -0
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +3 -19
- package/dist/Banner/Banner.d.ts +6 -2
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +71 -30
- package/dist/Banner/index.d.ts +14 -12
- package/dist/Banner/index.d.ts.map +1 -1
- package/dist/Banner/index.js +10 -8
- package/dist/Chat/ChatTokenizedText.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts +38 -4
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +96 -35
- package/dist/ComplexSelector/index.d.ts +2 -2
- package/dist/ComplexSelector/index.d.ts.map +1 -1
- package/dist/ComplexSelector/index.js +1 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +55 -12
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +4 -1
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +2 -4
- package/dist/Table/columnUtils.d.ts.map +1 -1
- package/dist/Table/columnUtils.js +4 -1
- package/dist/TextArea/TextArea.d.ts +6 -3
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +17 -6
- package/dist/TreeList/TreeList.js +2 -1
- package/dist/astryx.css +6 -4
- package/dist/astryx.umd.js +51 -51
- package/dist/astryx.umd.js.map +4 -4
- package/dist/theme/defineTheme.d.ts +8 -3
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +36 -47
- package/dist/theme/mergeComponents.d.ts +20 -0
- package/dist/theme/mergeComponents.d.ts.map +1 -0
- package/dist/theme/mergeComponents.js +56 -0
- package/dist/theme/onMediaTokens.d.ts +6 -1
- package/dist/theme/onMediaTokens.d.ts.map +1 -1
- package/dist/theme/onMediaTokens.js +11 -3
- package/dist/utils/characters.d.ts +27 -0
- package/dist/utils/characters.d.ts.map +1 -0
- package/dist/utils/characters.js +83 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/package.json +2 -2
- package/src/Avatar/Avatar.test.tsx +6 -1
- package/src/Avatar/Avatar.tsx +3 -21
- package/src/Banner/Banner.doc.mjs +9 -7
- package/src/Banner/Banner.test.tsx +68 -0
- package/src/Banner/Banner.tsx +97 -36
- package/src/Banner/index.ts +15 -13
- package/src/Chat/ChatTokenizedText.tsx +1 -1
- package/src/ComplexSelector/ComplexSelector.doc.mjs +55 -6
- package/src/ComplexSelector/ComplexSelector.test.tsx +197 -6
- package/src/ComplexSelector/ComplexSelector.tsx +153 -32
- package/src/ComplexSelector/index.ts +3 -1
- package/src/Markdown/parser.test.ts +53 -0
- package/src/Markdown/parser.ts +53 -12
- package/src/PowerSearch/PowerSearch.test.tsx +48 -3
- package/src/PowerSearch/PowerSearch.tsx +4 -1
- package/src/PowerSearch/formatFilterValue.test.ts +22 -0
- package/src/PowerSearch/formatFilterValue.ts +2 -4
- package/src/Table/Table.test.tsx +6 -0
- package/src/Table/columnUtils.ts +3 -1
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.test.tsx +72 -0
- package/src/TextArea/TextArea.tsx +26 -8
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/TreeList/TreeList.tsx +1 -1
- package/src/theme/defineTheme.test.ts +127 -0
- package/src/theme/defineTheme.ts +48 -51
- package/src/theme/extensibleAxes.test.ts +365 -0
- package/src/theme/mergeComponents.ts +59 -0
- package/src/theme/onMediaTokens.ts +9 -2
- package/src/utils/characters.test.ts +141 -0
- package/src/utils/characters.ts +88 -0
- package/src/utils/index.ts +2 -0
|
@@ -112,9 +112,14 @@ export interface DefineThemeInput {
|
|
|
112
112
|
/** Theme name โ used for data-astryx-theme attribute and identification */
|
|
113
113
|
name: string;
|
|
114
114
|
/**
|
|
115
|
-
* Base theme to extend. When provided, the new theme starts with
|
|
116
|
-
* base
|
|
117
|
-
*
|
|
115
|
+
* Base theme to extend. When provided, the new theme starts with everything
|
|
116
|
+
* the base resolved to โ tokens, component overrides, icons, indicators, and
|
|
117
|
+
* its `onDark`/`onLight` surfaces โ then applies this input on top. The base
|
|
118
|
+
* theme's values have lowest precedence.
|
|
119
|
+
*
|
|
120
|
+
* The result is flat: an extended theme carries its inheritance in its own
|
|
121
|
+
* resolved output, so `astryx theme build` emits one self-contained
|
|
122
|
+
* stylesheet and the base's CSS does not need to be loaded alongside it.
|
|
118
123
|
*
|
|
119
124
|
* Use this to create variant themes that customize only a few aspects
|
|
120
125
|
* (e.g. icons, accent color) without re-specifying the full theme.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defineTheme.d.ts","sourceRoot":"","sources":["../../src/theme/defineTheme.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAC,gBAAgB,EAAa,MAAM,SAAS,CAAC;AAC1D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EAAoB,KAAK,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAoB,KAAK,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAmB,KAAK,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAE3E,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEpD,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"defineTheme.d.ts","sourceRoot":"","sources":["../../src/theme/defineTheme.ts"],"names":[],"mappings":"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"}
|
|
@@ -44,6 +44,7 @@ import { expandRadiusScale } from "./expandRadiusScale.js";
|
|
|
44
44
|
import { expandColorScale } from "./expandColorScale.js";
|
|
45
45
|
import { domainTokenDefaults } from "./domainTokens/index.js";
|
|
46
46
|
import { registerTheme } from "./themeRegistry.js";
|
|
47
|
+
import { deepMergeComponents } from "./mergeComponents.js";
|
|
47
48
|
|
|
48
49
|
// =============================================================================
|
|
49
50
|
// Types
|
|
@@ -155,48 +156,6 @@ function resolveTokenValue(value) {
|
|
|
155
156
|
return value;
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
/**
|
|
159
|
-
* Deep-merge two component style maps.
|
|
160
|
-
* Properties in `overrides` take precedence over `base`.
|
|
161
|
-
* This allows typeScale-generated rules to be overridden by explicit components.
|
|
162
|
-
*/
|
|
163
|
-
function deepMergeComponents(base, overrides) {
|
|
164
|
-
if (!base && !overrides) {
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
if (!base) {
|
|
168
|
-
return overrides;
|
|
169
|
-
}
|
|
170
|
-
if (!overrides) {
|
|
171
|
-
return base;
|
|
172
|
-
}
|
|
173
|
-
const result = {};
|
|
174
|
-
|
|
175
|
-
// Start with all base entries
|
|
176
|
-
for (const [component, rules] of Object.entries(base)) {
|
|
177
|
-
result[component] = {
|
|
178
|
-
...rules
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Merge overrides on top
|
|
183
|
-
for (const [component, rules] of Object.entries(overrides)) {
|
|
184
|
-
if (!result[component]) {
|
|
185
|
-
result[component] = {
|
|
186
|
-
...rules
|
|
187
|
-
};
|
|
188
|
-
} else {
|
|
189
|
-
for (const [key, styles] of Object.entries(rules)) {
|
|
190
|
-
result[component][key] = {
|
|
191
|
-
...result[component][key],
|
|
192
|
-
...styles
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return result;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
159
|
/**
|
|
201
160
|
* Resolve a FontWeight name to a var() reference.
|
|
202
161
|
* Named weights map to var(--font-weight-*); raw values pass through.
|
|
@@ -226,6 +185,24 @@ function buildFontFamily(family, fallbacks) {
|
|
|
226
185
|
return quoted;
|
|
227
186
|
}
|
|
228
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Describe a rejected `extends` value for the error message โ enough to tell a
|
|
190
|
+
* missed import (`undefined`) from a module namespace or a plain object.
|
|
191
|
+
*/
|
|
192
|
+
function describeBadBase(value) {
|
|
193
|
+
if (value === undefined) {
|
|
194
|
+
return 'undefined';
|
|
195
|
+
}
|
|
196
|
+
if (value === null) {
|
|
197
|
+
return 'null';
|
|
198
|
+
}
|
|
199
|
+
if (typeof value !== 'object') {
|
|
200
|
+
return typeof value;
|
|
201
|
+
}
|
|
202
|
+
const keys = Object.keys(value);
|
|
203
|
+
return `an object with keys [${keys.slice(0, 4).join(', ')}${keys.length > 4 ? ', โฆ' : ''}]`;
|
|
204
|
+
}
|
|
205
|
+
|
|
229
206
|
/**
|
|
230
207
|
* Create an Astryx theme.
|
|
231
208
|
*
|
|
@@ -239,7 +216,15 @@ function buildFontFamily(family, fallbacks) {
|
|
|
239
216
|
export function defineTheme(input) {
|
|
240
217
|
const tokens = {};
|
|
241
218
|
|
|
242
|
-
// 0. Pre-seed from base theme when `extends` is provided (lowest precedence)
|
|
219
|
+
// 0. Pre-seed from base theme when `extends` is provided (lowest precedence).
|
|
220
|
+
// A base that is not a theme is refused rather than ignored: `extends` used
|
|
221
|
+
// to inherit nothing when its value was undefined, which is what a named
|
|
222
|
+
// import silently resolving to the wrong module hands over, and the theme
|
|
223
|
+
// then built into a plausible-looking stylesheet missing everything it was
|
|
224
|
+
// supposed to inherit.
|
|
225
|
+
if ('extends' in input && !isDefinedTheme(input.extends)) {
|
|
226
|
+
throw new Error(`defineTheme("${input.name}"): \`extends\` must be a theme from defineTheme(), got ${describeBadBase(input.extends)}. ` + `Check that the import naming your base theme resolves to its source and exports that name โ ` + `a generated \`<theme>.js\` artifact sitting next to the source exports \`<name>Theme\`, not the source's own export.`);
|
|
227
|
+
}
|
|
243
228
|
const base = input.extends;
|
|
244
229
|
if (base) {
|
|
245
230
|
for (const [key, value] of Object.entries(base.tokens)) {
|
|
@@ -370,9 +355,10 @@ export function defineTheme(input) {
|
|
|
370
355
|
components = deepMergeComponents(base.components, components);
|
|
371
356
|
}
|
|
372
357
|
|
|
373
|
-
// 4. Resolve on-media token overrides (
|
|
374
|
-
|
|
375
|
-
const
|
|
358
|
+
// 4. Resolve on-media token overrides (base's resolved surface, then
|
|
359
|
+
// defaults, then this theme's own overrides)
|
|
360
|
+
const __onDark = resolveOnMedia('dark', input.onDark, base?.__onDark);
|
|
361
|
+
const __onLight = resolveOnMedia('light', input.onLight, base?.__onLight);
|
|
376
362
|
|
|
377
363
|
// 5. Merge icons โ input icons override base icons
|
|
378
364
|
const icons = input.icons && base?.icons ? {
|
|
@@ -392,7 +378,10 @@ export function defineTheme(input) {
|
|
|
392
378
|
components,
|
|
393
379
|
icons,
|
|
394
380
|
indicators,
|
|
395
|
-
__inputTokens: input.tokens
|
|
381
|
+
__inputTokens: base?.__inputTokens || input.tokens ? {
|
|
382
|
+
...base?.__inputTokens,
|
|
383
|
+
...input.tokens
|
|
384
|
+
} : undefined,
|
|
396
385
|
__onDark,
|
|
397
386
|
__onLight
|
|
398
387
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Component style-map merging
|
|
3
|
+
*
|
|
4
|
+
* One merge rule for component overrides, shared by every layer that composes
|
|
5
|
+
* them: `extends` inheritance, generated type-scale rules, and the on-media
|
|
6
|
+
* (`onDark`/`onLight`) surfaces. Merging is per style key, so a child that
|
|
7
|
+
* restates one property of `button.base` keeps the rest of the base's.
|
|
8
|
+
*
|
|
9
|
+
* @input two ComponentStyleMaps โ the base and the overrides that win
|
|
10
|
+
* @output a merged ComponentStyleMap
|
|
11
|
+
* @position packages/core/src/theme/mergeComponents.ts
|
|
12
|
+
*/
|
|
13
|
+
import type { ComponentStyleMap } from './defineTheme';
|
|
14
|
+
/**
|
|
15
|
+
* Deep-merge component style maps: `overrides` wins per style key, and every
|
|
16
|
+
* component and key the base declared that the overrides do not mention is
|
|
17
|
+
* carried through untouched.
|
|
18
|
+
*/
|
|
19
|
+
export declare function deepMergeComponents(base?: ComponentStyleMap, overrides?: ComponentStyleMap): ComponentStyleMap | undefined;
|
|
20
|
+
//# sourceMappingURL=mergeComponents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeComponents.d.ts","sourceRoot":"","sources":["../../src/theme/mergeComponents.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,CAAC,EAAE,iBAAiB,EACxB,SAAS,CAAC,EAAE,iBAAiB,GAC5B,iBAAiB,GAAG,SAAS,CAiC/B"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Component style-map merging
|
|
5
|
+
*
|
|
6
|
+
* One merge rule for component overrides, shared by every layer that composes
|
|
7
|
+
* them: `extends` inheritance, generated type-scale rules, and the on-media
|
|
8
|
+
* (`onDark`/`onLight`) surfaces. Merging is per style key, so a child that
|
|
9
|
+
* restates one property of `button.base` keeps the rest of the base's.
|
|
10
|
+
*
|
|
11
|
+
* @input two ComponentStyleMaps โ the base and the overrides that win
|
|
12
|
+
* @output a merged ComponentStyleMap
|
|
13
|
+
* @position packages/core/src/theme/mergeComponents.ts
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Deep-merge component style maps: `overrides` wins per style key, and every
|
|
18
|
+
* component and key the base declared that the overrides do not mention is
|
|
19
|
+
* carried through untouched.
|
|
20
|
+
*/
|
|
21
|
+
export function deepMergeComponents(base, overrides) {
|
|
22
|
+
if (!base && !overrides) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
if (!base) {
|
|
26
|
+
return overrides;
|
|
27
|
+
}
|
|
28
|
+
if (!overrides) {
|
|
29
|
+
return base;
|
|
30
|
+
}
|
|
31
|
+
const result = {};
|
|
32
|
+
|
|
33
|
+
// Start with all base entries
|
|
34
|
+
for (const [component, rules] of Object.entries(base)) {
|
|
35
|
+
result[component] = {
|
|
36
|
+
...rules
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Merge overrides on top
|
|
41
|
+
for (const [component, rules] of Object.entries(overrides)) {
|
|
42
|
+
if (!result[component]) {
|
|
43
|
+
result[component] = {
|
|
44
|
+
...rules
|
|
45
|
+
};
|
|
46
|
+
} else {
|
|
47
|
+
for (const [key, styles] of Object.entries(rules)) {
|
|
48
|
+
result[component][key] = {
|
|
49
|
+
...result[component][key],
|
|
50
|
+
...styles
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
@@ -53,6 +53,11 @@ export declare const defaultOnLightTokens: Record<string, string>;
|
|
|
53
53
|
/**
|
|
54
54
|
* Resolve on-media overrides: merge user tokens with defaults,
|
|
55
55
|
* pass through component overrides.
|
|
56
|
+
*
|
|
57
|
+
* `base` is the already-resolved surface of a theme being extended. It sits
|
|
58
|
+
* between the defaults and this theme's own input, so a child theme inherits
|
|
59
|
+
* the surface customizations of the theme it extends instead of silently
|
|
60
|
+
* reverting them to the defaults.
|
|
56
61
|
*/
|
|
57
|
-
export declare function resolveOnMedia(surface: 'dark' | 'light', input?: OnMediaOverrides): ResolvedOnMedia;
|
|
62
|
+
export declare function resolveOnMedia(surface: 'dark' | 'light', input?: OnMediaOverrides, base?: ResolvedOnMedia): ResolvedOnMedia;
|
|
58
63
|
//# sourceMappingURL=onMediaTokens.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onMediaTokens.d.ts","sourceRoot":"","sources":["../../src/theme/onMediaTokens.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAC,UAAU,EAAE,iBAAiB,EAAC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"onMediaTokens.d.ts","sourceRoot":"","sources":["../../src/theme/onMediaTokens.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAC,UAAU,EAAE,iBAAiB,EAAC,MAAM,eAAe,CAAC;AAGjE;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7C,yDAAyD;IACzD,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,yDAAyD;IACzD,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAStD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CASvD,CAAC;AAYF;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,GAAG,OAAO,EACzB,KAAK,CAAC,EAAE,gBAAgB,EACxB,IAAI,CAAC,EAAE,eAAe,GACrB,eAAe,CAkBjB"}
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
* `onDark`/`onLight` fields in `defineTheme()`.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
import { deepMergeComponents } from "./mergeComponents.js";
|
|
24
|
+
|
|
23
25
|
/**
|
|
24
26
|
* On-media theme overrides โ same shape as the main theme but scoped
|
|
25
27
|
* to a surface luminance context.
|
|
@@ -71,11 +73,17 @@ function resolveValue(value) {
|
|
|
71
73
|
/**
|
|
72
74
|
* Resolve on-media overrides: merge user tokens with defaults,
|
|
73
75
|
* pass through component overrides.
|
|
76
|
+
*
|
|
77
|
+
* `base` is the already-resolved surface of a theme being extended. It sits
|
|
78
|
+
* between the defaults and this theme's own input, so a child theme inherits
|
|
79
|
+
* the surface customizations of the theme it extends instead of silently
|
|
80
|
+
* reverting them to the defaults.
|
|
74
81
|
*/
|
|
75
|
-
export function resolveOnMedia(surface, input) {
|
|
82
|
+
export function resolveOnMedia(surface, input, base) {
|
|
76
83
|
const defaults = surface === 'dark' ? defaultOnDarkTokens : defaultOnLightTokens;
|
|
77
84
|
const tokens = {
|
|
78
|
-
...defaults
|
|
85
|
+
...defaults,
|
|
86
|
+
...base?.tokens
|
|
79
87
|
};
|
|
80
88
|
if (input?.tokens) {
|
|
81
89
|
for (const [key, value] of Object.entries(input.tokens)) {
|
|
@@ -86,6 +94,6 @@ export function resolveOnMedia(surface, input) {
|
|
|
86
94
|
}
|
|
87
95
|
return {
|
|
88
96
|
tokens,
|
|
89
|
-
components: input?.components
|
|
97
|
+
components: deepMergeComponents(base?.components, input?.components)
|
|
90
98
|
};
|
|
91
99
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number of characters in a string โ the count a person would give, where one
|
|
3
|
+
* emoji, flag, or accented letter is one character. Replacement for `.length`
|
|
4
|
+
* on user-visible strings, which counts internal storage units instead.
|
|
5
|
+
*/
|
|
6
|
+
export declare function characterCount(str: string): number;
|
|
7
|
+
/**
|
|
8
|
+
* The first character of a string, or '' when empty. Replacement for
|
|
9
|
+
* `.charAt(0)`, which can return half of an emoji. Reads only the first
|
|
10
|
+
* character, so cost does not scale with the length of the string.
|
|
11
|
+
*/
|
|
12
|
+
export declare function firstCharacter(str: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Truncate to at most `max` characters, ellipsis included: strings within
|
|
15
|
+
* `max` characters pass through unchanged; longer strings are cut so the
|
|
16
|
+
* result โ content plus `ellipsis` โ is exactly `max` characters (or just the
|
|
17
|
+
* ellipsis, when `max` is smaller than the ellipsis itself). Replacement for
|
|
18
|
+
* `str.slice(0, n) + 'โฆ'`, which can cut an emoji in half.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```
|
|
22
|
+
* truncateCharacters('abcdefghij', 5) // 'abcdโฆ'
|
|
23
|
+
* truncateCharacters('abcdefghij', 8, '...') // 'abcde...'
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function truncateCharacters(str: string, max: number, ellipsis?: string): string;
|
|
27
|
+
//# sourceMappingURL=characters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"characters.d.ts","sourceRoot":"","sources":["../../src/utils/characters.ts"],"names":[],"mappings":"AAqCA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,SAAM,GACb,MAAM,CAOR"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file characters.ts
|
|
5
|
+
* @input Strings that may contain characters made of several code units
|
|
6
|
+
* (emoji, flags, joined emoji sequences, accented letters)
|
|
7
|
+
* @output Exports characterCount, firstCharacter, truncateCharacters โ
|
|
8
|
+
* replacements for .length / .charAt(0) / .slice(0, n) that measure
|
|
9
|
+
* and cut user-visible strings the way a person reads them
|
|
10
|
+
* @position Shared utility; consumed by Avatar, TextArea, PowerSearch, Table
|
|
11
|
+
*
|
|
12
|
+
* SYNC: When modified, update:
|
|
13
|
+
* - /packages/core/src/utils/characters.test.ts
|
|
14
|
+
* - /packages/core/src/utils/index.ts
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reuse a single segmenter when the runtime supports Intl.Segmenter.
|
|
19
|
+
* `grapheme` is the Intl granularity name for a user-perceived character.
|
|
20
|
+
*/
|
|
21
|
+
const characterSegmenter = typeof Intl.Segmenter === 'function' ? new Intl.Segmenter(undefined, {
|
|
22
|
+
granularity: 'grapheme'
|
|
23
|
+
}) : null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Split a string into user-perceived characters. The code-point fallback
|
|
27
|
+
* keeps surrogate pairs intact but may split joined emoji sequences and flag
|
|
28
|
+
* pairs on runtimes without Intl.Segmenter.
|
|
29
|
+
*/
|
|
30
|
+
function splitCharacters(str) {
|
|
31
|
+
if (characterSegmenter) {
|
|
32
|
+
return [...characterSegmenter.segment(str)].map(s => s.segment);
|
|
33
|
+
}
|
|
34
|
+
return [...str];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Number of characters in a string โ the count a person would give, where one
|
|
39
|
+
* emoji, flag, or accented letter is one character. Replacement for `.length`
|
|
40
|
+
* on user-visible strings, which counts internal storage units instead.
|
|
41
|
+
*/
|
|
42
|
+
export function characterCount(str) {
|
|
43
|
+
if (str === '') {
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
return splitCharacters(str).length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The first character of a string, or '' when empty. Replacement for
|
|
51
|
+
* `.charAt(0)`, which can return half of an emoji. Reads only the first
|
|
52
|
+
* character, so cost does not scale with the length of the string.
|
|
53
|
+
*/
|
|
54
|
+
export function firstCharacter(str) {
|
|
55
|
+
if (characterSegmenter) {
|
|
56
|
+
const first = characterSegmenter.segment(str)[Symbol.iterator]().next();
|
|
57
|
+
return first.done ? '' : first.value.segment;
|
|
58
|
+
}
|
|
59
|
+
const codePoint = str.codePointAt(0);
|
|
60
|
+
return codePoint == null ? '' : String.fromCodePoint(codePoint);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Truncate to at most `max` characters, ellipsis included: strings within
|
|
65
|
+
* `max` characters pass through unchanged; longer strings are cut so the
|
|
66
|
+
* result โ content plus `ellipsis` โ is exactly `max` characters (or just the
|
|
67
|
+
* ellipsis, when `max` is smaller than the ellipsis itself). Replacement for
|
|
68
|
+
* `str.slice(0, n) + 'โฆ'`, which can cut an emoji in half.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```
|
|
72
|
+
* truncateCharacters('abcdefghij', 5) // 'abcdโฆ'
|
|
73
|
+
* truncateCharacters('abcdefghij', 8, '...') // 'abcde...'
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function truncateCharacters(str, max, ellipsis = 'โฆ') {
|
|
77
|
+
const characters = splitCharacters(str);
|
|
78
|
+
if (characters.length <= max) {
|
|
79
|
+
return str;
|
|
80
|
+
}
|
|
81
|
+
const keep = Math.max(max - characterCount(ellipsis), 0);
|
|
82
|
+
return characters.slice(0, keep).join('') + ellipsis;
|
|
83
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { parseISOTime, formatISOTime, formatDisplayTime12h, formatDisplayTime24h
|
|
|
13
13
|
export type { ISOTimeString, ParsedTime } from './timeParser';
|
|
14
14
|
export { parseStyleKey } from './parseStyleKey';
|
|
15
15
|
export { getKey, type Key, type KeyFallback } from './getKey';
|
|
16
|
+
export { characterCount, firstCharacter, truncateCharacters } from './characters';
|
|
16
17
|
export { mergeProps } from './mergeProps';
|
|
17
18
|
export { mergeRefs } from './mergeRefs';
|
|
18
19
|
export { isFocusDetached } from './focusReturn';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,YAAY,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAEhF,OAAO,EACL,cAAc,EACd,SAAS,EACT,QAAQ,EACR,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,8BAA8B,EAC9B,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,SAAS,EACT,UAAU,EACV,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,MAAM,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAC,UAAU,EAAE,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAC7D,YAAY,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AACtD,YAAY,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,aAAa,EAAE,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AACzC,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAC,MAAM,aAAa,CAAC;AAEhE,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAC,IAAI,EAAC,MAAM,SAAS,CAAC;AAElC,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAItC,OAAO,EAAC,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,YAAY,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAEhF,OAAO,EACL,cAAc,EACd,SAAS,EACT,QAAQ,EACR,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,8BAA8B,EAC9B,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,SAAS,EACT,UAAU,EACV,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,MAAM,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAC,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAEhF,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAC,UAAU,EAAE,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAC7D,YAAY,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AACtD,YAAY,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,aAAa,EAAE,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AACzC,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAC,MAAM,aAAa,CAAC;AAEhE,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAC,IAAI,EAAC,MAAM,SAAS,CAAC;AAElC,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAItC,OAAO,EAAC,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,uBAAuB,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { plainDateCreate, plainDateFromISO, plainDateToISO, plainDateToDate, pla
|
|
|
12
12
|
export { parseISOTime, formatISOTime, formatDisplayTime12h, formatDisplayTime24h, parseTimeInput, compareTime, isTimeInRange, clampTime, adjustTime, createISOTimeString } from "./timeParser.js";
|
|
13
13
|
export { parseStyleKey } from "./parseStyleKey.js";
|
|
14
14
|
export { getKey } from "./getKey.js";
|
|
15
|
+
export { characterCount, firstCharacter, truncateCharacters } from "./characters.js";
|
|
15
16
|
export { mergeProps } from "./mergeProps.js";
|
|
16
17
|
export { mergeRefs } from "./mergeRefs.js";
|
|
17
18
|
export { isFocusDetached } from "./focusReturn.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -663,7 +663,7 @@
|
|
|
663
663
|
"@testing-library/jest-dom": "^6.6.0",
|
|
664
664
|
"@testing-library/react": "^16.3.2",
|
|
665
665
|
"rimraf": "^6.0.1",
|
|
666
|
-
"@astryxdesign/cli": "0.4.
|
|
666
|
+
"@astryxdesign/cli": "0.4.3"
|
|
667
667
|
},
|
|
668
668
|
"dependencies": {
|
|
669
669
|
"intl-messageformat": "^11.2.9"
|
|
@@ -91,11 +91,16 @@ describe('Avatar', () => {
|
|
|
91
91
|
expect(screen.getByTestId('avatar')).toHaveTextContent('๐A');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it('preserves a complete
|
|
94
|
+
it('preserves a complete character when generating initials', () => {
|
|
95
95
|
render(<Avatar name="๐ฌ๐ง Ada" data-testid="avatar" />);
|
|
96
96
|
expect(screen.getByTestId('avatar')).toHaveTextContent('๐ฌ๐งA');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('keeps a ZWJ family emoji intact when generating initials', () => {
|
|
100
|
+
render(<Avatar name="๐จโ๐ฉโ๐งโ๐ฆ Ada" data-testid="avatar" />);
|
|
101
|
+
expect(screen.getByTestId('avatar')).toHaveTextContent('๐จโ๐ฉโ๐งโ๐ฆA');
|
|
102
|
+
});
|
|
103
|
+
|
|
99
104
|
it('retries a new src after a previous src failed to load', () => {
|
|
100
105
|
const {rerender} = render(
|
|
101
106
|
<Avatar name="Ada" src="https://example.com/broken.jpg" />,
|
package/src/Avatar/Avatar.tsx
CHANGED
|
@@ -31,6 +31,7 @@ import {AvatarSizeContext} from './AvatarSizeContext';
|
|
|
31
31
|
import {useAvatarGroup} from '../AvatarGroup/AvatarGroupContext';
|
|
32
32
|
import {mergeProps, mergeRefs} from '../utils';
|
|
33
33
|
import {themeProps} from '../utils/themeProps';
|
|
34
|
+
import {firstCharacter} from '../utils/characters';
|
|
34
35
|
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
35
36
|
import {useTooltip} from '../Tooltip/useTooltip';
|
|
36
37
|
import {useDevWarning} from '../hooks/useDevWarning';
|
|
@@ -317,25 +318,6 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
|
|
|
317
318
|
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
318
319
|
}
|
|
319
320
|
|
|
320
|
-
/**
|
|
321
|
-
* Reuse a single segmenter when the runtime supports Intl.Segmenter.
|
|
322
|
-
*/
|
|
323
|
-
const graphemeSegmenter =
|
|
324
|
-
typeof Intl.Segmenter === 'function'
|
|
325
|
-
? new Intl.Segmenter(undefined, {granularity: 'grapheme'})
|
|
326
|
-
: null;
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Return the first user-perceived character, with a code-point fallback.
|
|
330
|
-
*/
|
|
331
|
-
function firstGrapheme(word: string): string {
|
|
332
|
-
if (graphemeSegmenter) {
|
|
333
|
-
return [...graphemeSegmenter.segment(word)][0]?.segment ?? '';
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return [...word][0] ?? '';
|
|
337
|
-
}
|
|
338
|
-
|
|
339
321
|
/**
|
|
340
322
|
* Generates initials from a name string.
|
|
341
323
|
* Takes the first letter of the first two words.
|
|
@@ -351,10 +333,10 @@ function getInitials(name: string): string {
|
|
|
351
333
|
return '';
|
|
352
334
|
}
|
|
353
335
|
if (words.length === 1) {
|
|
354
|
-
return
|
|
336
|
+
return firstCharacter(words[0]).toUpperCase();
|
|
355
337
|
}
|
|
356
338
|
return (
|
|
357
|
-
|
|
339
|
+
firstCharacter(words[0]) + firstCharacter(words[words.length - 1])
|
|
358
340
|
).toUpperCase();
|
|
359
341
|
}
|
|
360
342
|
|
|
@@ -17,11 +17,13 @@ export const docs = {
|
|
|
17
17
|
{guidance: true, description: 'Keep titles short and scannable: "Payment failed" not "There was a problem processing your most recent payment."'},
|
|
18
18
|
{guidance: false, description: 'Use Banner for short-lived messages that disappear on their own; use Toast instead.'},
|
|
19
19
|
{guidance: false, description: 'Stack multiple banners with the same status; combine related messages into one banner.'},
|
|
20
|
+
{guidance: true, description: 'Error and warning banners render as role="alert"; info and success render as role="status". Mount an alert banner in response to an event rather than on first paint, so assistive tech has a change to report.'},
|
|
21
|
+
{guidance: false, description: 'Rely on the status color or icon alone to carry meaning; say which status it is in the title text, because the icon is decorative to a screen reader.'},
|
|
20
22
|
],
|
|
21
23
|
anatomy: [
|
|
22
24
|
{name: 'Icon', required: true, description: 'Automatically set based on the status (info, warning, error, success).'},
|
|
23
|
-
{name: 'Title', required:
|
|
24
|
-
{name: 'Description', required: false, description: 'Additional detail below the title.
|
|
25
|
+
{name: 'Title', required: true, description: 'The main message. Always required.'},
|
|
26
|
+
{name: 'Description', required: false, description: 'Additional detail below the title.'},
|
|
25
27
|
{name: 'Action button', required: false, description: 'A button for the user to act on the message, like "Review" or "Retry".'},
|
|
26
28
|
{name: 'Dismiss button', required: false, description: 'Lets the user close the banner. Enabled by setting isDismissable.'},
|
|
27
29
|
{name: 'Collapsible content', required: false, description: 'Extra detail that expands below the banner header, like a list of errors.'},
|
|
@@ -123,7 +125,7 @@ export const docs = {
|
|
|
123
125
|
{className: 'astryx-banner-content', visualProps: ['container', 'status']},
|
|
124
126
|
],
|
|
125
127
|
vars: [
|
|
126
|
-
{name: '--_banner-radius', description: 'Border radius
|
|
128
|
+
{name: '--_banner-radius', description: 'Border radius of the card container (header, content area and the elevated root silhouette)', default: 'var(--radius-container)', private: true},
|
|
127
129
|
],
|
|
128
130
|
derived: [
|
|
129
131
|
{property: 'borderRadius', vars: ['--_banner-radius']},
|
|
@@ -148,8 +150,8 @@ export const docsZh = {
|
|
|
148
150
|
],
|
|
149
151
|
anatomy: [
|
|
150
152
|
{name: 'Icon', required: true, description: 'Automatically set based on the status (info, warning, error, success).'},
|
|
151
|
-
{name: 'Title', required:
|
|
152
|
-
{name: 'Description', required: false, description: 'Additional detail below the title.
|
|
153
|
+
{name: 'Title', required: true, description: 'The main message. Always required.'},
|
|
154
|
+
{name: 'Description', required: false, description: 'Additional detail below the title.'},
|
|
153
155
|
{name: 'Action button', required: false, description: 'A button for the user to act on the message, like "Review" or "Retry".'},
|
|
154
156
|
{name: 'Dismiss button', required: false, description: 'Lets the user close the banner. Enabled by setting isDismissable.'},
|
|
155
157
|
{name: 'Collapsible content', required: false, description: 'Extra detail that expands below the banner header, like a list of errors.'},
|
|
@@ -198,7 +200,7 @@ export const docsZh = {
|
|
|
198
200
|
},
|
|
199
201
|
],
|
|
200
202
|
vars: [
|
|
201
|
-
{name: '--_banner-radius', description: 'Border radius
|
|
203
|
+
{name: '--_banner-radius', description: 'Border radius of the card container (header, content area and the elevated root silhouette)', default: 'var(--radius-container)', private: true},
|
|
202
204
|
],
|
|
203
205
|
derived: [
|
|
204
206
|
{property: 'borderRadius', vars: ['--_banner-radius']},
|
|
@@ -222,7 +224,7 @@ export const docsDense = {
|
|
|
222
224
|
],
|
|
223
225
|
anatomy: [
|
|
224
226
|
{name: 'Icon', required: true, description: 'Set automatically from status.'},
|
|
225
|
-
{name: 'Title', required:
|
|
227
|
+
{name: 'Title', required: true, description: 'Main message text.'},
|
|
226
228
|
{name: 'Description', required: false, description: 'Detail below title.'},
|
|
227
229
|
{name: 'Action button', required: false, description: 'CTA like Review or Retry.'},
|
|
228
230
|
{name: 'Dismiss button', required: false, description: 'Close button via isDismissable.'},
|