@astryxdesign/core 0.4.2 → 0.4.3-canary.0a9618e
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/README.md +79 -45
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +3 -19
- package/dist/Banner/Banner.d.ts +9 -2
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +84 -33
- 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/BottomSheet/BottomSheet.d.ts +66 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheet.js +381 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts +57 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetPanel.js +395 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts +61 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetSwitcher.js +410 -0
- package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts +21 -0
- package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetSwitcherContext.js +13 -0
- package/dist/BottomSheet/index.d.ts +11 -0
- package/dist/BottomSheet/index.d.ts.map +1 -0
- package/dist/BottomSheet/index.js +12 -0
- package/dist/BottomSheet/snapOffsets.d.ts +72 -0
- package/dist/BottomSheet/snapOffsets.d.ts.map +1 -0
- package/dist/BottomSheet/snapOffsets.js +130 -0
- package/dist/BottomSheet/useMobileKeyboard.d.ts +25 -0
- package/dist/BottomSheet/useMobileKeyboard.d.ts.map +1 -0
- package/dist/BottomSheet/useMobileKeyboard.js +384 -0
- package/dist/BottomSheet/useSheetGestures.d.ts +170 -0
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -0
- package/dist/BottomSheet/useSheetGestures.js +881 -0
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +3 -4
- 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 +98 -31
- 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/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +9 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +31 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +2 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +55 -12
- package/dist/MobileNav/MobileNav.d.ts +12 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +102 -30
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +10 -1
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +9 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +4 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +2 -4
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +10 -1
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +5 -1
- 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/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +8 -1
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +4 -1
- package/dist/TreeList/TreeList.js +2 -1
- package/dist/Typeahead/BaseTypeahead.js +1 -1
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +10 -1
- package/dist/astryx.css +25 -2
- package/dist/hooks/index.d.ts +8 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +8 -1
- package/dist/hooks/useFocusTrap.d.ts +0 -11
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +1 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/defineTheme.d.ts +30 -7
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +36 -47
- package/dist/theme/expandColorScale.d.ts +17 -2
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +63 -33
- 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/ime.d.ts +5 -0
- package/dist/utils/ime.d.ts.map +1 -0
- package/dist/utils/ime.js +63 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/package.json +9 -8
- package/src/Avatar/Avatar.test.tsx +6 -1
- package/src/Avatar/Avatar.tsx +3 -21
- package/src/Banner/Banner.doc.mjs +12 -10
- package/src/Banner/Banner.test.tsx +108 -0
- package/src/Banner/Banner.tsx +124 -38
- package/src/Banner/index.ts +15 -13
- package/src/BottomSheet/BottomSheet.doc.mjs +266 -0
- package/src/BottomSheet/BottomSheet.test.tsx +1865 -0
- package/src/BottomSheet/BottomSheet.tsx +539 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +240 -0
- package/src/BottomSheet/BottomSheetPanel.tsx +542 -0
- package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +219 -0
- package/src/BottomSheet/BottomSheetSwitcher.test.tsx +850 -0
- package/src/BottomSheet/BottomSheetSwitcher.tsx +628 -0
- package/src/BottomSheet/BottomSheetSwitcherContext.ts +51 -0
- package/src/BottomSheet/index.ts +15 -0
- package/src/BottomSheet/snapOffsets.test.ts +187 -0
- package/src/BottomSheet/snapOffsets.ts +159 -0
- package/src/BottomSheet/useMobileKeyboard.ts +566 -0
- package/src/BottomSheet/useSheetGestures.test.ts +833 -0
- package/src/BottomSheet/useSheetGestures.ts +1333 -0
- package/src/Chat/ChatComposerInput.tsx +3 -4
- 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 +156 -28
- package/src/ComplexSelector/index.ts +3 -1
- package/src/ContextMenu/ContextMenu.tsx +4 -2
- package/src/DateInput/DateInput.test.tsx +21 -0
- package/src/DateInput/DateInput.tsx +9 -1
- package/src/DateTimeInput/DateTimeInput.doc.mjs +4 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +137 -0
- package/src/DateTimeInput/DateTimeInput.tsx +39 -2
- package/src/Dialog/Dialog.tsx +2 -1
- package/src/Markdown/parser.test.ts +53 -0
- package/src/Markdown/parser.ts +53 -12
- package/src/MobileNav/MobileNav.tsx +128 -37
- package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +426 -0
- package/src/MobileNav/MobileNavCloseTiming.test.ts +58 -0
- package/src/MobileNav/MobileNavCloseVisibility.test.tsx +172 -0
- package/src/MultiSelector/MultiSelector.test.tsx +31 -0
- package/src/MultiSelector/MultiSelector.tsx +10 -1
- package/src/NumberInput/NumberInput.test.tsx +18 -0
- package/src/NumberInput/NumberInput.tsx +9 -1
- package/src/PowerSearch/PowerSearch.test.tsx +48 -3
- package/src/PowerSearch/PowerSearch.tsx +4 -1
- package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
- package/src/PowerSearch/formatFilterValue.test.ts +22 -0
- package/src/PowerSearch/formatFilterValue.ts +2 -4
- package/src/Selector/Selector.test.tsx +33 -0
- package/src/Selector/Selector.tsx +10 -1
- package/src/Switch/Switch.test.tsx +16 -0
- package/src/Switch/Switch.tsx +7 -0
- 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/TimeInput/TimeInput.test.tsx +23 -0
- package/src/TimeInput/TimeInput.tsx +8 -0
- package/src/Tooltip/useTooltip.tsx +4 -1
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/TreeList/TreeList.tsx +1 -1
- package/src/Typeahead/BaseTypeahead.tsx +1 -1
- package/src/Typeahead/Typeahead.test.tsx +35 -0
- package/src/Typeahead/Typeahead.tsx +10 -1
- package/src/__tests__/stubMatchMedia.test.ts +79 -0
- package/src/__tests__/stubMatchMedia.ts +95 -0
- package/src/hooks/index.ts +9 -5
- package/src/hooks/useFocusTrap.ts +1 -14
- package/src/index.ts +1 -0
- package/src/theme/defineTheme.test.ts +127 -0
- package/src/theme/defineTheme.ts +70 -55
- package/src/theme/expandColorScale.test.ts +138 -0
- package/src/theme/expandColorScale.ts +89 -44
- 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/ime.test.ts +32 -0
- package/src/utils/ime.ts +69 -0
- package/src/utils/index.ts +4 -0
- package/dist/astryx.umd.js +0 -147
- package/dist/astryx.umd.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @xds/core
|
|
2
2
|
|
|
3
|
+
# 0.4.3
|
|
4
|
+
|
|
5
|
+
#### New Features
|
|
6
|
+
|
|
7
|
+
- New string utilities: `characterCount`, `firstCharacter`, and `truncateCharacters` — replacements for `.length`, `.charAt(0)`, and slice-based truncation that measure and cut user-visible strings by whole characters, so an emoji, flag, or accented letter counts as one and never gets split. Built on `Intl.Segmenter` with a code-point fallback.
|
|
8
|
+
- ComplexSelector: support ghost toolbar triggers, leading icons, popup alignment, and an imperative `handleRef` (open/close/toggle/isOpen) for programmatic control.
|
|
9
|
+
- `useContainerReveal`: two ways to control the reveal without reaching into the hook's private custom properties. `getContainerProps({hoverDelay})` gates the reveal on pointer dwell — the hover-intent idea Tooltip and HoverCard already have as `delay` — so a cursor sweeping down a list no longer lights up every row it grazes, and `getContainerProps({forceState})` pins the container's trigger state when something else owns the interaction (a scroll, a drag, an open row menu). Per element, `getContentRevealProps({forceVisibility})` pins how one child looks regardless of its container. Still CSS-only: no hover state in React, no re-render. Keyboard and touch are untouched — focus always reveals, `forceState: 'inactive'` and `forceVisibility: 'hidden'` both yield to `:focus-within`.
|
|
10
|
+
|
|
11
|
+
#### Fixes
|
|
12
|
+
|
|
13
|
+
- Banner: a dismissed banner no longer drops focus, a custom status no longer loses its ARIA role, and the info banner paints again under the neutral theme.
|
|
14
|
+
Dismissing unmounted the focused dismiss button, so focus landed on `<body>` and a keyboard user lost their place in the page. Banner now records where focus entered from and returns it there, the same handoff `ToastViewport` makes for a dismissed toast. Measured in Chromium: `document.activeElement` was `BODY`, and is now the control the user tabbed in from.
|
|
15
|
+
|
|
16
|
+
`BannerStatusMap` is documented as augmentable, but all four status lookups were closed `Record<BannerStatus, ...>` maps. Adding the augmentation the docs show produced four TypeScript errors inside `Banner.tsx` itself, which a consumer cannot fix, and at runtime an unknown status resolved to `undefined` for its icon, its background and its ARIA role, so the banner stopped being a live region at all. The lookups are partial now: an unrecognized status renders with no status fill, no default glyph and `role="status"`.
|
|
17
|
+
|
|
18
|
+
A theme could not reach the banner's radius. `--_banner-radius` was declared in the doc file and in `derivedVarRegistry.ts`, but no rule read it, so a theme's `borderRadius` on the `banner` target expanded into a variable nothing consumed. The four card-silhouette radii read it now, falling back to `--radius-container`.
|
|
19
|
+
|
|
20
|
+
Under `@astryxdesign/theme-neutral` the info banner had no background at all, light or dark: the override set `background-color` directly and forced `--color-accent-muted` to `transparent`, and a plain CSS property written by a theme lands in `@layer astryx-theme`, which StyleX's `@layer priority4` outranks. Info now goes through `--color-accent-muted` like the other three statuses and like the stone theme already did.
|
|
21
|
+
|
|
22
|
+
Also in this change: `children={false}` (the ordinary `{cond && <ul/>}` idiom) no longer produces an expand toggle that opens an empty box, and `description=""` no longer leaves an empty 20px row, both via `isRenderable`; a long unbroken word in the title or description no longer forces the page into horizontal scrolling at a 320px viewport, measured at `document.scrollWidth` 529px before; and the content area's bottom border uses logical `border-block-end` alongside its inline siblings.
|
|
23
|
+
- Count and cut text the way people read it: the TextArea character counter (and its over-limit state and screen-reader announcements) counts user-perceived characters — an emoji is 1, not 2; PowerSearch token truncation no longer cuts an emoji or accented letter in half; Table's auto-generated headers capitalize astral-plane letters correctly; Avatar's initials now use the shared character utilities.
|
|
24
|
+
- ComplexSelector: honor the `sm`, `md`, and `lg` element-height tokens exactly.
|
|
25
|
+
- TreeList's `variant` axis is themeable, and a new guard keeps every extensible axis honest. `TreeListVariantMap` invites theme packages to add variants — its own JSDoc shows the module augmentation — but `themeProps('tree-list', {density})` never passed `variant`, so a custom variant type-checked, rendered, and produced no selector to style. It is passed now, and documented in the target's `visualProps` so `astryx theme build` stops calling it an unknown prop.
|
|
26
|
+
`packages/core/src/theme/extensibleAxes.test.ts` is the third theming-drift guard, beside the ones covering `targets` and `vars`/`derived`. Those two check what a component renders against what it documents; neither looked at the open prop unions, which is why this went unnoticed. For every `*Map` that types a component prop, it now asserts the three places that have to agree: the interface is declared in the index a consumer augments (a re-export is invisible to both module augmentation and the CLI), the prop is reflected through `themeProps`, and it is documented as a visual prop. It reads the TypeScript AST rather than the type checker, and holds the map's OWNER accountable — a component forwarding `actionVariant` or `statusVariant` to the component that owns the map is not separately responsible for it.
|
|
27
|
+
|
|
28
|
+
Registry maps that widen a set of NAMES rather than a visual prop (`IndicatorMap`, `IndicatorFamilyMap`) are out of scope by construction, not by allowlist: the guard only considers maps whose alias types a prop on a `*Props` interface.
|
|
29
|
+
- Security: reject `javascript:`, `vbscript:` and `data:text/html` URLs in the Markdown parser, so untrusted markdown can no longer produce an executable link href or image src; and fix `escapeRegExp` in `ChatTokenizedText`, whose character class closed early and left `]` and `\` unescaped, so token values containing them were injected raw into a `RegExp`
|
|
30
|
+
- `extends` now reaches the CSS. A theme that extended another built a stylesheet holding only the declarations it stated itself: the base's tokens, component overrides and surface rules were all absent, and because each theme is `@scope`d to its own `data-astryx-theme` value, loading the base's stylesheet alongside could not fill the gap either. Every consumer of an inheritance chain silently got stock geometry, elevation and type with a new palette painted over it (#5067). Nothing warned; the loss only showed up by diffing two generated stylesheets token by token.
|
|
31
|
+
The cause was `theme build` shadowing its own inputs. It writes `<name>.js` next to `<name>.ts`, and the loader resolved a plain `./<name>` specifier to that generated artifact before the source — so the second build of a family read the artifact, which carries no `components` and exports `<name>Theme` rather than whatever the source exports. A named import that missed became `extends: undefined`, and `defineTheme` treated an absent base as no base at all. The loader now resolves source extensions first, which is also the resolution the author's TypeScript sees, so the CSS a build emits matches the theme that type-checked.
|
|
32
|
+
|
|
33
|
+
Three things behind it are fixed too, so the failure cannot come back by another route. `defineTheme` **throws** when `extends` is present but is not a theme, naming the likely cause, instead of inheriting nothing — the one behavior change here, and it turns a silent stylesheet into a build error. A theme's `onDark`/`onLight` surfaces and its `__inputTokens` are now inherited like its tokens and components were, so a child no longer reverts its base's inverted-surface customizations to the defaults or loses its `[light, dark]` tuples. And a built theme module now carries the resolved `components` and surfaces alongside its tokens, so extending one — the `./built` subpath every shipped theme exposes — is no longer lossy. `theme build` also stopped hand-picking fields when it re-resolves a plain object theme file, which dropped `extends`, `color` and `syntax` on the way in.
|
|
34
|
+
|
|
35
|
+
An extended theme is flat: everything it inherits is resolved into its own output, and its stylesheet stands alone. Measured on a 14-theme family (one base, 13 palettes extending it): each palette went from 25 custom properties and no component rules to the base's full 175 and 70, with its own colours still winning.
|
|
36
|
+
|
|
37
|
+
#### Contributors
|
|
38
|
+
|
|
39
|
+
Thanks to everyone who contributed to this release:
|
|
40
|
+
|
|
41
|
+
- @AKnassa
|
|
42
|
+
- @cixzhang
|
|
43
|
+
- @ernestt
|
|
44
|
+
- @Sunil56224972
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
3
48
|
# 0.4.2
|
|
4
49
|
|
|
5
50
|
#### New Features
|
package/README.md
CHANGED
|
@@ -252,67 +252,101 @@ Same CSS imports and providers as above. No build plugins needed; Astryx ships p
|
|
|
252
252
|
### No build step (CDN)
|
|
253
253
|
|
|
254
254
|
For prototypes, embeds, or pages without a bundler, load the components straight
|
|
255
|
-
from a public CDN.
|
|
255
|
+
from a public CDN as ES modules. React 19 removed its UMD builds ("To load React
|
|
256
|
+
19 with a script tag, we recommend using an ESM-based CDN such as esm.sh"), so an
|
|
257
|
+
import map is the way in — there is no `window.React` left for a global bundle to
|
|
258
|
+
bind to.
|
|
256
259
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
The CLI writes this page for you, annotated and pinned to the version you have
|
|
261
|
+
installed:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
npx astryx template --cdn # writes cdn.template.html
|
|
265
|
+
```
|
|
260
266
|
|
|
261
267
|
```html
|
|
262
268
|
<!doctype html>
|
|
263
|
-
<html data-astryx-theme="neutral">
|
|
269
|
+
<html lang="en" data-astryx-theme="neutral">
|
|
264
270
|
<head>
|
|
271
|
+
<meta charset="utf-8" />
|
|
272
|
+
<link
|
|
273
|
+
rel="stylesheet"
|
|
274
|
+
href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap" />
|
|
275
|
+
<link
|
|
276
|
+
rel="stylesheet"
|
|
277
|
+
href="https://cdn.jsdelivr.net/npm/@astryxdesign/core@0.4.1/src/reset.css" />
|
|
265
278
|
<link
|
|
266
279
|
rel="stylesheet"
|
|
267
|
-
href="https://cdn.jsdelivr.net/npm/@astryxdesign/core/
|
|
280
|
+
href="https://cdn.jsdelivr.net/npm/@astryxdesign/core@0.4.1/dist/astryx.css" />
|
|
268
281
|
<link
|
|
269
282
|
rel="stylesheet"
|
|
270
|
-
href="https://cdn.jsdelivr.net/npm/@astryxdesign/
|
|
283
|
+
href="https://cdn.jsdelivr.net/npm/@astryxdesign/theme-neutral@0.4.1/dist/theme.css" />
|
|
284
|
+
<script type="importmap">
|
|
285
|
+
{
|
|
286
|
+
"imports": {
|
|
287
|
+
"react": "https://esm.sh/react@19.2.0",
|
|
288
|
+
"react/jsx-runtime": "https://esm.sh/react@19.2.0/jsx-runtime",
|
|
289
|
+
"react-dom": "https://esm.sh/react-dom@19.2.0",
|
|
290
|
+
"react-dom/client": "https://esm.sh/react-dom@19.2.0/client",
|
|
291
|
+
"@astryxdesign/core": "https://esm.sh/@astryxdesign/core@0.4.1?external=react,react-dom"
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
</script>
|
|
295
|
+
<style>
|
|
296
|
+
body {
|
|
297
|
+
font-family: var(--font-family-body);
|
|
298
|
+
}
|
|
299
|
+
</style>
|
|
271
300
|
</head>
|
|
272
301
|
<body>
|
|
273
302
|
<div id="root"></div>
|
|
274
|
-
<script
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
crossorigin
|
|
279
|
-
src="https://unpkg.com/react-dom@19/umd/react-dom.production.min.js"></script>
|
|
280
|
-
<script src="https://cdn.jsdelivr.net/npm/@astryxdesign/core/dist/astryx.umd.js"></script>
|
|
281
|
-
<script>
|
|
282
|
-
const {Button, Card} = window.Astryx;
|
|
303
|
+
<script type="module">
|
|
304
|
+
import * as React from 'react';
|
|
305
|
+
import {createRoot} from 'react-dom/client';
|
|
306
|
+
import {Card, Stack, Heading, Text, Button} from '@astryxdesign/core';
|
|
283
307
|
const e = React.createElement;
|
|
284
|
-
|
|
285
|
-
e(
|
|
308
|
+
createRoot(document.getElementById('root')).render(
|
|
309
|
+
e(
|
|
310
|
+
Stack,
|
|
311
|
+
{padding: 6, align: 'start'},
|
|
312
|
+
e(
|
|
313
|
+
Card,
|
|
314
|
+
{maxWidth: 480, elevation: 'low'},
|
|
315
|
+
e(
|
|
316
|
+
Stack,
|
|
317
|
+
{gap: 3, align: 'start'},
|
|
318
|
+
e(Heading, {level: 1}, 'Hello from a CDN'),
|
|
319
|
+
e(Text, null, 'No bundler, no install, no build step.'),
|
|
320
|
+
e(Button, {variant: 'primary', label: 'Try me'}),
|
|
321
|
+
),
|
|
322
|
+
),
|
|
323
|
+
),
|
|
286
324
|
);
|
|
287
325
|
</script>
|
|
288
326
|
</body>
|
|
289
327
|
</html>
|
|
290
328
|
```
|
|
291
329
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
> Pin a version in production (e.g. `@astryxdesign/core@0.1.1`) — unversioned CDN URLs
|
|
316
|
-
> resolve to the latest release and are cached aggressively. The raw ESM entry
|
|
317
|
-
> (`dist/index.js`) uses bare `react` imports and will **not** run from a plain
|
|
318
|
-
> `<script src>`; use the UMD global or esm.sh as shown above.
|
|
330
|
+
Six details carry the whole recipe:
|
|
331
|
+
|
|
332
|
+
- **`data-astryx-theme` on `<html>`.** Theme CSS is scoped to that attribute, so
|
|
333
|
+
without it the page renders with the built-in defaults instead of the theme you
|
|
334
|
+
just loaded.
|
|
335
|
+
- **`?external=react,react-dom`.** Without it esm.sh bundles its own React and
|
|
336
|
+
every hook throws `Cannot read properties of null (reading 'useState')`.
|
|
337
|
+
- **`react/jsx-runtime` in the import map.** The published bundle imports it;
|
|
338
|
+
omit the entry and the page dies with `Failed to resolve module specifier`.
|
|
339
|
+
- **A font on `body`.** Nothing in the three stylesheets sets a document font:
|
|
340
|
+
the theme styles prose elements and each component styles itself, and `Button`
|
|
341
|
+
is `font: inherit`. Without that one declaration its label renders in the
|
|
342
|
+
browser's default serif.
|
|
343
|
+
- **The webfont itself.** The theme _names_ Figtree; it never loads it. Without
|
|
344
|
+
the Google Fonts link every viewer silently gets the next family in the stack.
|
|
345
|
+
- **No JSX.** Nothing is compiling this file, so elements are created with
|
|
346
|
+
`React.createElement`.
|
|
347
|
+
|
|
348
|
+
> Pin every version. Unversioned CDN URLs resolve to the latest release and are
|
|
349
|
+
> cached aggressively (0.4.1 above is a real pin; `astryx template --cdn` writes
|
|
350
|
+
> yours). The raw ESM entry (`dist/index.js`) uses bare `react` imports and will
|
|
351
|
+
> not run from a plain `<script src>` — the import map is what makes those
|
|
352
|
+
> specifiers resolvable.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../src/Avatar/Avatar.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAoC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../src/Avatar/Avatar.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAoC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAiB5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAC;AA4BrD;;;;;;GAMG;AACH,KAAK,eAAe,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzD;;GAEG;AACH,KAAK,iBAAiB,GACpB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAE7D;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAgBpD;AA6HD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CAChD;AA0DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,EACrB,GAAG,EACH,aAAa,EAAE,MAAM,EACrB,WAAW,EACX,IAAI,EACJ,IAAW,EACX,GAAG,EACH,MAAM,EACN,OAAc,EACd,IAAI,EACJ,EAAE,EACF,MAAM,EACN,GAAG,EACH,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,+BA+Pb;yBAlRe,MAAM"}
|
package/dist/Avatar/Avatar.js
CHANGED
|
@@ -25,6 +25,7 @@ import { AvatarSizeContext } from "./AvatarSizeContext.js";
|
|
|
25
25
|
import { useAvatarGroup } from "../AvatarGroup/AvatarGroupContext.js";
|
|
26
26
|
import { mergeProps, mergeRefs } from "../utils/index.js";
|
|
27
27
|
import { themeProps } from "../utils/themeProps.js";
|
|
28
|
+
import { firstCharacter } from "../utils/characters.js";
|
|
28
29
|
import { focusOutlineProps } from "../utils/focusOutline.stylex.js";
|
|
29
30
|
import { useTooltip } from "../Tooltip/useTooltip.js";
|
|
30
31
|
import { useDevWarning } from "../hooks/useDevWarning.js";
|
|
@@ -194,23 +195,6 @@ const groupDynamicStyles = {
|
|
|
194
195
|
"--x---_avatar-group-overlap": `${offset}px` != null ? `${offset}px` : undefined
|
|
195
196
|
}]
|
|
196
197
|
};
|
|
197
|
-
/**
|
|
198
|
-
* Reuse a single segmenter when the runtime supports Intl.Segmenter.
|
|
199
|
-
*/
|
|
200
|
-
const graphemeSegmenter = typeof Intl.Segmenter === 'function' ? new Intl.Segmenter(undefined, {
|
|
201
|
-
granularity: 'grapheme'
|
|
202
|
-
}) : null;
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Return the first user-perceived character, with a code-point fallback.
|
|
206
|
-
*/
|
|
207
|
-
function firstGrapheme(word) {
|
|
208
|
-
if (graphemeSegmenter) {
|
|
209
|
-
return [...graphemeSegmenter.segment(word)][0]?.segment ?? '';
|
|
210
|
-
}
|
|
211
|
-
return [...word][0] ?? '';
|
|
212
|
-
}
|
|
213
|
-
|
|
214
198
|
/**
|
|
215
199
|
* Generates initials from a name string.
|
|
216
200
|
* Takes the first letter of the first two words.
|
|
@@ -226,9 +210,9 @@ function getInitials(name) {
|
|
|
226
210
|
return '';
|
|
227
211
|
}
|
|
228
212
|
if (words.length === 1) {
|
|
229
|
-
return
|
|
213
|
+
return firstCharacter(words[0]).toUpperCase();
|
|
230
214
|
}
|
|
231
|
-
return (
|
|
215
|
+
return (firstCharacter(words[0]) + firstCharacter(words[words.length - 1])).toUpperCase();
|
|
232
216
|
}
|
|
233
217
|
|
|
234
218
|
/**
|
package/dist/Banner/Banner.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file Banner.tsx
|
|
3
|
-
* @input Uses React useState, Button, Icon (with registry string names), StyleX
|
|
3
|
+
* @input Uses React useState/useRef/useId, Button, Icon (with registry string names), StyleX
|
|
4
4
|
* @output Exports Banner component, BannerProps, BannerStatus, BannerContainer types
|
|
5
5
|
* @position Core implementation; consumed by index.ts, tested by Banner.test.tsx
|
|
6
6
|
*
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
* - Each visual area owns its own border-radius (no overflow:clip on the container)
|
|
16
16
|
* - When children are provided, a collapse/expand toggle button appears in the end area
|
|
17
17
|
*
|
|
18
|
+
* A status added through `BannerStatusMap` augmentation has no entry in the
|
|
19
|
+
* status lookups, so it renders with no status fill, no default glyph and the
|
|
20
|
+
* polite `role="status"` rather than losing its ARIA role entirely.
|
|
21
|
+
*
|
|
18
22
|
* Title and description render as <div> (not <p>): they accept arbitrary
|
|
19
23
|
* ReactNode content, and <p> cannot legally contain block-level children
|
|
20
24
|
* (the HTML parser reparents them, desyncing SSR markup from the hydrated
|
|
@@ -81,6 +85,9 @@ export interface BannerProps extends BaseProps<HTMLDivElement> {
|
|
|
81
85
|
* Action button rendered in the header area (end-aligned).
|
|
82
86
|
* Typically an Button with a secondary or ghost variant.
|
|
83
87
|
*
|
|
88
|
+
* When the header is too narrow to hold the actions and a readable text
|
|
89
|
+
* column, the whole end area wraps to its own row below the text.
|
|
90
|
+
*
|
|
84
91
|
* @example
|
|
85
92
|
* ```
|
|
86
93
|
* endContent={<Button label="Retry" variant="ghost" onClick={handleRetry} />}
|
|
@@ -159,7 +166,7 @@ export interface BannerProps extends BaseProps<HTMLDivElement> {
|
|
|
159
166
|
* </Banner>
|
|
160
167
|
* ```
|
|
161
168
|
*/
|
|
162
|
-
export declare function Banner({ status, title, description, icon, isDismissable, onDismiss, endContent, container, elevation, defaultIsExpanded, children, xstyle, className, style, ref, ...rest }: BannerProps): import("react").JSX.Element | null;
|
|
169
|
+
export declare function Banner({ status, title, description, icon, isDismissable, onDismiss, endContent, container, elevation, defaultIsExpanded, children, xstyle, className, style, ref, onFocusCapture, onPointerDownCapture, ...rest }: BannerProps): import("react").JSX.Element | null;
|
|
163
170
|
export declare namespace Banner {
|
|
164
171
|
var displayName: string;
|
|
165
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/Banner/Banner.tsx"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/Banner/Banner.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAA0B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAgB5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAI9C,OAAO,KAAK,EAAC,eAAe,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAMjE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmND;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EACN,KAAK,EACL,WAAW,EACX,IAAI,EACJ,aAAqB,EACrB,SAAS,EACT,UAAU,EACV,SAAkB,EAClB,SAAkB,EAClB,iBAAyB,EACzB,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,GAAG,IAAI,EACR,EAAE,WAAW,sCA+Mb;yBAlOe,MAAM"}
|
package/dist/Banner/Banner.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Banner.tsx
|
|
7
|
-
* @input Uses React useState, Button, Icon (with registry string names), StyleX
|
|
7
|
+
* @input Uses React useState/useRef/useId, Button, Icon (with registry string names), StyleX
|
|
8
8
|
* @output Exports Banner component, BannerProps, BannerStatus, BannerContainer types
|
|
9
9
|
* @position Core implementation; consumed by index.ts, tested by Banner.test.tsx
|
|
10
10
|
*
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
* - Each visual area owns its own border-radius (no overflow:clip on the container)
|
|
20
20
|
* - When children are provided, a collapse/expand toggle button appears in the end area
|
|
21
21
|
*
|
|
22
|
+
* A status added through `BannerStatusMap` augmentation has no entry in the
|
|
23
|
+
* status lookups, so it renders with no status fill, no default glyph and the
|
|
24
|
+
* polite `role="status"` rather than losing its ARIA role entirely.
|
|
25
|
+
*
|
|
22
26
|
* Title and description render as <div> (not <p>): they accept arbitrary
|
|
23
27
|
* ReactNode content, and <p> cannot legally contain block-level children
|
|
24
28
|
* (the HTML parser reparents them, desyncing SSR markup from the hydrated
|
|
@@ -33,13 +37,13 @@
|
|
|
33
37
|
* - /apps/storybook/stories/Banner.stories.tsx (storybook stories)
|
|
34
38
|
* - /packages/cli/assets/templates/blocks/components/Banner/ (showcase blocks)
|
|
35
39
|
*/
|
|
36
|
-
import { useId, useState } from 'react';
|
|
40
|
+
import { useId, useRef, useState } from 'react';
|
|
37
41
|
import * as stylex from '@stylexjs/stylex';
|
|
38
42
|
import { Button } from "../Button/index.js";
|
|
39
43
|
import { Icon } from "../Icon/index.js";
|
|
40
44
|
import "../theme/tokens.stylex.js";
|
|
41
45
|
import { colorVars, spacingVars, radiusVars, fontWeightVars, typeScaleVars, borderVars, durationVars, easeVars, shadowVars } from "../theme/tokens.stylex.js";
|
|
42
|
-
import { mergeProps } from "../utils/index.js";
|
|
46
|
+
import { composeEventHandlers, isRenderable, mergeProps } from "../utils/index.js";
|
|
43
47
|
import { edgeCompSlot } from "../Layout/edgeCompensation.stylex.js";
|
|
44
48
|
import { themeProps } from "../utils/themeProps.js";
|
|
45
49
|
import { useTranslator } from "../i18n/index.js";
|
|
@@ -62,20 +66,21 @@ import { useTranslator } from "../i18n/index.js";
|
|
|
62
66
|
*/
|
|
63
67
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
64
68
|
// =============================================================================
|
|
65
|
-
// Status
|
|
69
|
+
// Status lookups
|
|
66
70
|
// =============================================================================
|
|
67
71
|
|
|
72
|
+
// `BannerStatus` is `keyof BannerStatusMap`, and index.ts documents augmenting
|
|
73
|
+
// that interface to add a status. Every lookup below is therefore partial: an
|
|
74
|
+
// augmented status the library has never heard of falls through to the base
|
|
75
|
+
// treatment (no status fill, no glyph, the polite role) instead of resolving to
|
|
76
|
+
// `undefined` and dropping the ARIA role along with it.
|
|
77
|
+
|
|
68
78
|
const defaultIconNames = {
|
|
69
79
|
info: 'info',
|
|
70
80
|
warning: 'warning',
|
|
71
81
|
error: 'error',
|
|
72
82
|
success: 'success'
|
|
73
83
|
};
|
|
74
|
-
|
|
75
|
-
// =============================================================================
|
|
76
|
-
// Status → ARIA role mapping
|
|
77
|
-
// =============================================================================
|
|
78
|
-
|
|
79
84
|
const statusRole = {
|
|
80
85
|
info: 'status',
|
|
81
86
|
warning: 'alert',
|
|
@@ -83,10 +88,8 @@ const statusRole = {
|
|
|
83
88
|
success: 'status'
|
|
84
89
|
};
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// =============================================================================
|
|
89
|
-
|
|
91
|
+
/** An unknown status is not urgent by definition, so it announces politely. */
|
|
92
|
+
const FALLBACK_ROLE = 'status';
|
|
90
93
|
const statusIconColor = {
|
|
91
94
|
info: 'accent',
|
|
92
95
|
warning: 'warning',
|
|
@@ -106,24 +109,26 @@ const styles = {
|
|
|
106
109
|
$$css: true
|
|
107
110
|
},
|
|
108
111
|
rootElevatedCard: {
|
|
109
|
-
kaIpWk: "
|
|
112
|
+
kaIpWk: "x1vf2byn",
|
|
110
113
|
$$css: true
|
|
111
114
|
},
|
|
112
115
|
header: {
|
|
113
116
|
k1xSpc: "x78zum5",
|
|
114
117
|
kGNEyG: "x1cy8zhl",
|
|
115
|
-
|
|
118
|
+
kwnvtZ: "x1a02dak",
|
|
119
|
+
k1C7PZ: "xak3so",
|
|
120
|
+
khm7nJ: "x4olc9o",
|
|
116
121
|
k8WAf4: "x8o8v82",
|
|
117
122
|
kg3NbH: "x1pzlopt",
|
|
118
123
|
$$css: true
|
|
119
124
|
},
|
|
120
125
|
headerCardStandalone: {
|
|
121
|
-
kaIpWk: "
|
|
126
|
+
kaIpWk: "x1vf2byn",
|
|
122
127
|
$$css: true
|
|
123
128
|
},
|
|
124
129
|
headerCardWithContent: {
|
|
125
|
-
krdFHd: "
|
|
126
|
-
kfmiAY: "
|
|
130
|
+
krdFHd: "xwxb7qi",
|
|
131
|
+
kfmiAY: "x13nour3",
|
|
127
132
|
kVL7Gh: "xbiv7yw",
|
|
128
133
|
kT0f0o: "x16uus16",
|
|
129
134
|
$$css: true
|
|
@@ -135,8 +140,11 @@ const styles = {
|
|
|
135
140
|
endArea: {
|
|
136
141
|
k1xSpc: "x78zum5",
|
|
137
142
|
kGNEyG: "x6s0dn4",
|
|
143
|
+
kwnvtZ: "x1a02dak",
|
|
144
|
+
kjj79g: "x13a6bvl",
|
|
138
145
|
kOIVth: "x1txdalj",
|
|
139
146
|
kmuXW: "x2lah0s",
|
|
147
|
+
ks0D6T: "x193iq5w",
|
|
140
148
|
keTefX: "xvc5jky",
|
|
141
149
|
kqGvvJ: "x81ka23",
|
|
142
150
|
$$css: true
|
|
@@ -171,6 +179,15 @@ const statusStyles = {
|
|
|
171
179
|
}
|
|
172
180
|
};
|
|
173
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Narrows an augmented `BannerStatus` to one the style map actually carries,
|
|
184
|
+
* so an unknown status renders with no status fill rather than crashing the
|
|
185
|
+
* lookup. StyleX style maps cannot be declared `Partial`, hence the guard.
|
|
186
|
+
*/
|
|
187
|
+
function hasStatusStyle(status) {
|
|
188
|
+
return Object.prototype.hasOwnProperty.call(statusStyles, status);
|
|
189
|
+
}
|
|
190
|
+
|
|
174
191
|
// Resting elevation for the banner. Applied to the root so the shadow wraps
|
|
175
192
|
// the whole banner (header + optional content). 'none' is the default and
|
|
176
193
|
// leaves the layout-only root untouched. For the `card` container the root is
|
|
@@ -260,51 +277,80 @@ export function Banner({
|
|
|
260
277
|
className,
|
|
261
278
|
style,
|
|
262
279
|
ref,
|
|
280
|
+
onFocusCapture,
|
|
281
|
+
onPointerDownCapture,
|
|
263
282
|
...rest
|
|
264
283
|
}) {
|
|
265
284
|
const t = useTranslator();
|
|
266
285
|
const [isDismissed, setIsDismissed] = useState(false);
|
|
267
286
|
const [isExpanded, setIsExpanded] = useState(defaultIsExpanded);
|
|
287
|
+
// The element focus came from before it entered the banner. Dismissing
|
|
288
|
+
// unmounts the whole banner, dismiss button included, so without a handoff
|
|
289
|
+
// the browser drops focus to <body> and a keyboard user loses their place.
|
|
290
|
+
// ToastViewport makes the same handoff when a focused toast is dismissed.
|
|
291
|
+
const focusOriginRef = useRef(null);
|
|
268
292
|
// Links the expand/collapse toggle to the content region it shows/hides so
|
|
269
293
|
// assistive tech can move from the button to its controlled content
|
|
270
294
|
// (disclosure pattern). The region is conditionally rendered, so aria-controls
|
|
271
295
|
// below is set only while it's mounted to avoid a dangling reference.
|
|
272
296
|
const contentId = useId();
|
|
273
297
|
const defaultIconName = defaultIconNames[status];
|
|
274
|
-
const role = statusRole[status];
|
|
298
|
+
const role = statusRole[status] ?? FALLBACK_ROLE;
|
|
275
299
|
const iconColor = statusIconColor[status];
|
|
276
|
-
const hasChildren = children
|
|
300
|
+
const hasChildren = isRenderable(children);
|
|
277
301
|
if (isDismissed) {
|
|
278
302
|
return null;
|
|
279
303
|
}
|
|
304
|
+
|
|
305
|
+
// `focusin` reports the element focus came *from* as relatedTarget; on
|
|
306
|
+
// pointerdown focus has not moved yet, so document.activeElement is it.
|
|
307
|
+
const rememberFocusOrigin = (candidate, root) => {
|
|
308
|
+
if (candidate instanceof HTMLElement && candidate !== document.body && !root.contains(candidate)) {
|
|
309
|
+
focusOriginRef.current = candidate;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
const handleFocusCapture = event => {
|
|
313
|
+
rememberFocusOrigin(event.relatedTarget, event.currentTarget);
|
|
314
|
+
};
|
|
315
|
+
const handlePointerDownCapture = event => {
|
|
316
|
+
rememberFocusOrigin(document.activeElement, event.currentTarget);
|
|
317
|
+
};
|
|
280
318
|
const handleDismiss = () => {
|
|
319
|
+
const origin = focusOriginRef.current;
|
|
281
320
|
setIsDismissed(true);
|
|
282
321
|
onDismiss?.();
|
|
322
|
+
// Move focus before React removes the subtree, so the browser never has a
|
|
323
|
+
// frame where the focused node is gone.
|
|
324
|
+
if (origin?.isConnected) {
|
|
325
|
+
origin.focus();
|
|
326
|
+
}
|
|
283
327
|
};
|
|
284
328
|
const handleToggleExpand = () => {
|
|
285
329
|
setIsExpanded(prev => !prev);
|
|
286
330
|
};
|
|
287
331
|
|
|
288
332
|
// Show the end area if there are actions, dismiss, or a collapsible toggle
|
|
289
|
-
const showEndArea = endContent
|
|
333
|
+
const showEndArea = isRenderable(endContent) || isDismissable || hasChildren;
|
|
290
334
|
// Center items vertically when there's only a title (no description)
|
|
291
335
|
// and the banner has action buttons
|
|
292
|
-
const hasActions = endContent
|
|
293
|
-
const isSingleLine = description
|
|
336
|
+
const hasActions = isRenderable(endContent) || isDismissable;
|
|
337
|
+
const isSingleLine = !isRenderable(description) && hasActions;
|
|
294
338
|
const showContent = hasChildren && isExpanded;
|
|
295
339
|
const isCard = container === 'card';
|
|
296
340
|
return /*#__PURE__*/_jsxs("div", {
|
|
297
341
|
ref: ref,
|
|
298
342
|
role: role,
|
|
343
|
+
onFocusCapture: composeEventHandlers(onFocusCapture, handleFocusCapture),
|
|
344
|
+
onPointerDownCapture: composeEventHandlers(onPointerDownCapture, handlePointerDownCapture),
|
|
299
345
|
...mergeProps(stylex.props(styles.root, elevationStyles[elevation], isCard && elevation !== 'none' && styles.rootElevatedCard, xstyle), className, style),
|
|
300
346
|
...rest,
|
|
301
347
|
children: [/*#__PURE__*/_jsxs("div", {
|
|
302
348
|
...mergeProps(themeProps('banner', {
|
|
303
349
|
container,
|
|
304
350
|
status
|
|
305
|
-
}), stylex.props(styles.header, isSingleLine && styles.headerCentered, statusStyles[status], isCard && (showContent ? styles.headerCardWithContent : styles.headerCardStandalone))),
|
|
351
|
+
}), stylex.props(styles.header, isSingleLine && styles.headerCentered, hasStatusStyle(status) && statusStyles[status], isCard && (showContent ? styles.headerCardWithContent : styles.headerCardStandalone))),
|
|
306
352
|
children: [/*#__PURE__*/_jsx("div", {
|
|
307
|
-
...(icon
|
|
353
|
+
...(isRenderable(icon) ? mergeProps(themeProps('banner-icon', {
|
|
308
354
|
status
|
|
309
355
|
}), {
|
|
310
356
|
className: "x78zum5 x6s0dn4 x2lah0s"
|
|
@@ -312,7 +358,7 @@ export function Banner({
|
|
|
312
358
|
className: "x78zum5 x6s0dn4 x2lah0s"
|
|
313
359
|
}),
|
|
314
360
|
"aria-hidden": "true",
|
|
315
|
-
children: icon != null ?
|
|
361
|
+
children: isRenderable(icon) ? icon : defaultIconName != null ?
|
|
316
362
|
/*#__PURE__*/
|
|
317
363
|
// Applied to the status <Icon> itself rather than the wrapper, so
|
|
318
364
|
// the element that paints the glyph is the element a theme
|
|
@@ -325,19 +371,24 @@ export function Banner({
|
|
|
325
371
|
...themeProps('banner-icon', {
|
|
326
372
|
status
|
|
327
373
|
})
|
|
328
|
-
})
|
|
374
|
+
}) : null
|
|
329
375
|
}), /*#__PURE__*/_jsxs("div", {
|
|
330
376
|
...{
|
|
331
|
-
|
|
332
|
-
|
|
377
|
+
0: {
|
|
378
|
+
className: "x78zum5 xdt5ytf xxhr3t x98rzlu xeuugli"
|
|
379
|
+
},
|
|
380
|
+
1: {
|
|
381
|
+
className: "x78zum5 xdt5ytf xxhr3t x98rzlu xeuugli xgualba"
|
|
382
|
+
}
|
|
383
|
+
}[!!isRenderable(endContent) << 0],
|
|
333
384
|
children: [/*#__PURE__*/_jsx("div", {
|
|
334
385
|
...{
|
|
335
|
-
className: "x1ghz6dp xjb2p0i xcr08ib x2mo6ok x1kq96og x1tgivj0"
|
|
386
|
+
className: "x1ghz6dp xjb2p0i xcr08ib x2mo6ok x1kq96og x1tgivj0 xj0a0fe"
|
|
336
387
|
},
|
|
337
388
|
children: title
|
|
338
|
-
}), description
|
|
389
|
+
}), isRenderable(description) && /*#__PURE__*/_jsx("div", {
|
|
339
390
|
...{
|
|
340
|
-
className: "x1ghz6dp xjb2p0i x141an7d x1sodnla x1ltkj2j xv1l7n4"
|
|
391
|
+
className: "x1ghz6dp xjb2p0i x141an7d x1sodnla x1ltkj2j xv1l7n4 xj0a0fe"
|
|
341
392
|
},
|
|
342
393
|
children: description
|
|
343
394
|
})]
|
|
@@ -382,7 +433,7 @@ export function Banner({
|
|
|
382
433
|
className: "x1de1mus x8o8v82 x1pzlopt xgbv0en xw8tdv1 x92x3c3 x1t7ytsu x18b5jzi x1q0q8m5 x1j92z86 x1gejf6u xw8gpjh"
|
|
383
434
|
},
|
|
384
435
|
1: {
|
|
385
|
-
className: "x1de1mus x8o8v82 x1pzlopt xgbv0en xw8tdv1 x92x3c3 x1t7ytsu x18b5jzi x1q0q8m5 x1j92z86 x1gejf6u xw8gpjh
|
|
436
|
+
className: "x1de1mus x8o8v82 x1pzlopt xgbv0en xw8tdv1 x92x3c3 x1t7ytsu x18b5jzi x1q0q8m5 x1j92z86 x1gejf6u xw8gpjh x1k5g1gk x1eprgri"
|
|
386
437
|
}
|
|
387
438
|
}[!!isCard << 0]),
|
|
388
439
|
children: children
|
package/dist/Banner/index.d.ts
CHANGED
|
@@ -7,35 +7,37 @@
|
|
|
7
7
|
* SYNC: When modified, update this header and /packages/core/src/Banner/Banner.doc.mjs
|
|
8
8
|
*/
|
|
9
9
|
/**
|
|
10
|
-
* Extensible
|
|
10
|
+
* Extensible container map for Banner.
|
|
11
11
|
*
|
|
12
|
-
* Theme packages can add custom
|
|
12
|
+
* Theme packages can add custom container types via TypeScript module augmentation:
|
|
13
13
|
* @example
|
|
14
14
|
* ```
|
|
15
15
|
* declare module '@astryxdesign/core/Banner' {
|
|
16
|
-
* interface
|
|
17
|
-
* '
|
|
16
|
+
* interface BannerContainerMap {
|
|
17
|
+
* 'floating': true;
|
|
18
18
|
* }
|
|
19
19
|
* }
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
+
export interface BannerContainerMap {
|
|
23
|
+
card: true;
|
|
24
|
+
section: true;
|
|
25
|
+
}
|
|
22
26
|
/**
|
|
23
|
-
* Extensible
|
|
27
|
+
* Extensible status map for Banner.
|
|
24
28
|
*
|
|
25
|
-
* Theme packages can add custom
|
|
29
|
+
* Theme packages can add custom statuses via TypeScript module augmentation.
|
|
30
|
+
* A status the library does not know falls through to the base treatment: no
|
|
31
|
+
* status fill, no default glyph, and `role="status"`.
|
|
26
32
|
* @example
|
|
27
33
|
* ```
|
|
28
34
|
* declare module '@astryxdesign/core/Banner' {
|
|
29
|
-
* interface
|
|
30
|
-
* '
|
|
35
|
+
* interface BannerStatusMap {
|
|
36
|
+
* 'neutral': true;
|
|
31
37
|
* }
|
|
32
38
|
* }
|
|
33
39
|
* ```
|
|
34
40
|
*/
|
|
35
|
-
export interface BannerContainerMap {
|
|
36
|
-
card: true;
|
|
37
|
-
section: true;
|
|
38
|
-
}
|
|
39
41
|
export interface BannerStatusMap {
|
|
40
42
|
info: true;
|
|
41
43
|
warning: true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Banner/index.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AAEH;;;;;;;;;;;;GAYG;AACH
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Banner/index.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,IAAI,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,IAAI,CAAC;CACf;AAED,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,YAAY,EAAC,WAAW,EAAE,YAAY,EAAE,eAAe,EAAC,MAAM,UAAU,CAAC"}
|