@mortenbrudvik/waveui 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -17
- package/dist/components/input/Label.cjs +1 -1
- package/dist/components/input/Label.cjs.map +1 -1
- package/dist/components/input/Label.mjs +1 -0
- package/dist/components/input/Label.mjs.map +1 -1
- package/dist/components/portal/Portal.cjs +1 -1
- package/dist/components/portal/Portal.cjs.map +1 -1
- package/dist/components/portal/Portal.mjs +12 -11
- package/dist/components/portal/Portal.mjs.map +1 -1
- package/dist/components/provider/WaveProvider.cjs +1 -1
- package/dist/components/provider/WaveProvider.cjs.map +1 -1
- package/dist/components/provider/WaveProvider.mjs +61 -35
- package/dist/components/provider/WaveProvider.mjs.map +1 -1
- package/dist/components/typography/Text.cjs +1 -1
- package/dist/components/typography/Text.cjs.map +1 -1
- package/dist/components/typography/Text.mjs +11 -7
- package/dist/components/typography/Text.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +271 -10
- package/dist/index.d.ts +271 -10
- package/dist/index.mjs +143 -140
- package/dist/lib/brand.cjs +2 -0
- package/dist/lib/brand.cjs.map +1 -0
- package/dist/lib/brand.mjs +358 -0
- package/dist/lib/brand.mjs.map +1 -0
- package/dist/lib/cn.cjs +1 -1
- package/dist/lib/cn.cjs.map +1 -1
- package/dist/lib/cn.mjs +5 -1
- package/dist/lib/cn.mjs.map +1 -1
- package/dist/lib/tokens.cjs +3 -0
- package/dist/lib/tokens.cjs.map +1 -0
- package/dist/lib/tokens.mjs +55 -0
- package/dist/lib/tokens.mjs.map +1 -0
- package/dist/lib/waveTokens.cjs +2 -0
- package/dist/lib/waveTokens.cjs.map +1 -0
- package/dist/lib/waveTokens.mjs +24 -0
- package/dist/lib/waveTokens.mjs.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +3 -1
- package/src/styles/tokens.css +210 -10
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A React component library inspired by the Fluent UI 2 design language: 80 access
|
|
|
7
7
|
- [Requirements](#requirements) · [Installation](#installation) · [Quick start](#quick-start)
|
|
8
8
|
- [Styles](#styles) · [Theming](#theming) · [Global effects](#global-effects) · [Styling hooks](#styling-hooks)
|
|
9
9
|
- [React Server Components](#react-server-components) · [Components](#components) · [Usage notes](#usage-notes)
|
|
10
|
-
- [Large data](#large-data) · [Keyboard support](#keyboard-support) · [Hooks and utilities](#hooks-and-utilities) · [Upgrading from 0.15](#upgrading-from-015) · [Upgrading from 0.14](#upgrading-from-014) · [Upgrading from 0.13](#upgrading-from-013) · [Upgrading from 0.12](#upgrading-from-012) · [Upgrading from 0.11](#upgrading-from-011) · [Upgrading from 0.10](#upgrading-from-010) · [Upgrading from 0.9](#upgrading-from-09) · [Upgrading from 0.8](#upgrading-from-08) · [Upgrading from 0.7](#upgrading-from-07) · [Upgrading from 0.6](#upgrading-from-06) · [Upgrading from 0.5](#upgrading-from-05) · [Upgrading from 0.4](#upgrading-from-04) · [Development](#development)
|
|
10
|
+
- [Large data](#large-data) · [Keyboard support](#keyboard-support) · [Hooks and utilities](#hooks-and-utilities) · [Upgrading from 0.16](#upgrading-from-016) · [Upgrading from 0.15](#upgrading-from-015) · [Upgrading from 0.14](#upgrading-from-014) · [Upgrading from 0.13](#upgrading-from-013) · [Upgrading from 0.12](#upgrading-from-012) · [Upgrading from 0.11](#upgrading-from-011) · [Upgrading from 0.10](#upgrading-from-010) · [Upgrading from 0.9](#upgrading-from-09) · [Upgrading from 0.8](#upgrading-from-08) · [Upgrading from 0.7](#upgrading-from-07) · [Upgrading from 0.6](#upgrading-from-06) · [Upgrading from 0.5](#upgrading-from-05) · [Upgrading from 0.4](#upgrading-from-04) · [Development](#development)
|
|
11
11
|
|
|
12
12
|
## Requirements
|
|
13
13
|
|
|
@@ -117,11 +117,11 @@ export function Shell({ children }: { children: React.ReactNode }) {
|
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
- `theme`: `'light'` (default), `'dark'` or `'high-contrast'`. `dir`: `'ltr'` (default) or `'rtl'`. `portalContainer`: where overlays render (default `document.body`).
|
|
120
|
-
- A nested provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the enclosing provider, so the light panel above stays right-to-left. `portalContainer={null}` sends a nested subtree's overlays to `document.body`.
|
|
120
|
+
- A nested provider inherits every prop it omits (`theme`, `dir`, `portalContainer`, `motion`, `applyStylesToPortals`; `inputDefaults` and `tokens` merge key by key) from the enclosing provider, so the light panel above stays right-to-left. `portalContainer={null}` sends a nested subtree's overlays to `document.body`.
|
|
121
121
|
- The provider renders `<div class="wave-root wave-<theme>" dir data-wave-theme>` with `bg-background text-foreground font-wave text-body-1`; a `className` you pass wins.
|
|
122
|
-
- Portaled overlays (Dialog, Drawer, Popover, Menu, listboxes, Tooltip, Toaster) render in a `wave-portal` wrapper that carries the provider's theme classes, direction and font.
|
|
122
|
+
- Portaled overlays (Dialog, Drawer, Popover, Menu, listboxes, Tooltip, Toaster) render in a `wave-portal` wrapper that carries the provider's theme classes, direction and font, and its `tokens` overrides unless `applyStylesToPortals={false}` (see [Brand colors and token overrides](#brand-colors-and-token-overrides)).
|
|
123
123
|
- `inputDefaults`: `{ size?, appearance? }`, the default `size` and `appearance` of the text inputs and pickers below it (see [Sizes and appearances](#sizes-and-appearances)). A nested provider merges its own defined keys over the enclosing provider's, so `<WaveProvider inputDefaults={{ size: 'small' }}>` inside an app with `appearance: 'filled-darker'` gives small filled-darker fields; omitting the prop inherits the enclosing value.
|
|
124
|
-
- `useWaveTheme()` returns `{ theme, dir, themeClassName, portalContainer, inputDefaults, hasProvider }` (`inputDefaults`
|
|
124
|
+
- `useWaveTheme()` returns `{ theme, dir, themeClassName, portalContainer, inputDefaults, motion, tokens, tokenStyle, applyStylesToPortals, hasProvider }` (outside a provider `inputDefaults` and `tokens` are `{}`, `tokenStyle` is `undefined` and `applyStylesToPortals` is `true`).
|
|
125
125
|
|
|
126
126
|
**Theme classes.** The tokens are selected by `wave-light` (also `:root`), `wave-dark` and `wave-high-contrast`; `getThemeClassName(theme)` returns them. For dark and high contrast the provider also emits the deprecated 0.4 classes `dark` and `high-contrast`, so 0.4 `.dark` selectors in your CSS and class-based `dark:` variants (`@custom-variant dark (&:where(.dark, .dark *));`) keep working. Tailwind 4's default `dark:` variant follows `prefers-color-scheme`, not a class, and Wave does not define a class-based one. Wave's CSS still honours the two legacy classes, but they set only `--wave-*` variables. `color-scheme` is set on the three `wave-*` theme classes only, never on `:root`, so a page without a provider keeps its own scrollbar and form-control scheme.
|
|
127
127
|
|
|
@@ -164,11 +164,40 @@ export function Shell({ children }: { children: React.ReactNode }) {
|
|
|
164
164
|
|
|
165
165
|
A server cannot know the setting, so a server-rendered page paints light first and switches to dark after hydration on a dark system. `<meta name="color-scheme" content="light dark">` in the document head at least draws the scrollbar and canvas in the system's scheme from the start (the theme class on `<html>` takes over once it is set).
|
|
166
166
|
|
|
167
|
+
### Brand colors and token overrides
|
|
168
|
+
|
|
169
|
+
A brand color in three lines: `createBrandTokens` builds the 16 brand-ramp tokens from one color, and `WaveProvider tokens` applies them.
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { WaveProvider, createBrandTokens } from '@mortenbrudvik/waveui';
|
|
173
|
+
|
|
174
|
+
const brand = createBrandTokens('#c43e00'); // once, at module scope
|
|
175
|
+
export const App = ({ children }: { children: React.ReactNode }) => <WaveProvider tokens={brand}>{children}</WaveProvider>;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- **Contrast holds.** Each generated stop keeps the luminance of the default ramp's stop and takes its hue and colorfulness from the seed, so every text, fill, stroke and focus-ring pair of the light and dark themes stays above the floors the themes are tested against (4.5:1 for text, 3:1 for strokes and focus rings), whatever the seed. The cost: stop 80 (the light primary) is the seed's hue at the default's lightness, so a light seed gives a darker button. For exact brand colors pass a full ramp (`createBrandTokens({ 10: '#…', …, 160: '#…' })`, a `BrandRamp`), which is returned unchanged and checked in development: each pair the themes read from it that fails its contrast minimum logs a warning. The high-contrast theme ignores the brand.
|
|
179
|
+
- **Any token.** `tokens` takes any token by its camelCase name, the CSS variable without `--wave-` (`primaryForeground`, `brand80`, `paletteRedTint`, `shadow8`, `fontFamilyMonospace`, `zOverlay`); TypeScript refuses a misspelled one. A value is a finite number or a string without `;`, `{`, `}` or `url(` whose quotes, parentheses, square brackets and comments are balanced, with no `!` or backslash outside strings and comments (anything else is skipped with a development warning, so a value can neither add a declaration of its own to the server-rendered `style` nor swallow the declarations after it); it is trimmed, as React's server renderer trims it, so the server and the client render the same overrides. The provider writes them as inline custom properties on its root (also in the server HTML) and on every portal of its subtree, so its dialogs, menus, popovers, tooltips and toasts match it; `applyStylesToPortals={false}` keeps them off the portals, an enclosing provider's overrides (a brand ramp) included; theme and direction stay.
|
|
180
|
+
- **Nesting.** A nested provider merges its `tokens` over the enclosing ones key by key, also when it sets another `theme`: set a semantic override in the provider that sets the theme it was chosen for. `tokens={null}` clears the inherited overrides for the theme's own tokens and for the overlays, while the theme-independent ones (the ramps, fonts, stacking, motion) still reach the subtree's own elements through the CSS cascade. A nested accent:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<WaveProvider tokens={brand}>
|
|
184
|
+
<AppHeader />
|
|
185
|
+
{/* This panel, and the dialogs and menus it opens, use a crimson primary. */}
|
|
186
|
+
<WaveProvider theme="light" tokens={{ primary: '#c4314b', primaryHover: '#a9283f', primaryPressed: '#7a1c2d' }}>
|
|
187
|
+
<CampaignPanel />
|
|
188
|
+
</WaveProvider>
|
|
189
|
+
</WaveProvider>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- **Tokens in JavaScript.** `waveTokens` holds every token as the `var()` that reads it, typed (`waveTokens.primary` is `'var(--wave-primary)'`), for inline styles and charts; `chartPalette` lists the 12 palette fills in the order a chart gives them to its series. An SVG chart takes the values as they are: `<Bar dataKey="north" fill={waveTokens[chartPalette[0]]} />` is `var(--wave-palette-blue)` and follows the theme. A canvas chart resolves them with `getComputedStyle`; colors never tell series apart on their own, so give every chart a second encoding (a pattern, a marker, a label). The guide's "Charts with WaveUI" chapter has the recipes, and Storybook's `Guides/Charts` runs them with Recharts and Chart.js.
|
|
193
|
+
- **Server Components.** `createBrandTokens`, `getTokenStyle`, `waveTokens` and `chartPalette` are server-safe. `getTokenStyle(overrides)` returns the inline style the provider applies, for an element no provider renders: in a server-rendered layout, `<html style={getTokenStyle(createBrandTokens('#c43e00'))}>` brands the whole page, portals included, since the ramp is theme-independent (a semantic token such as `primary` belongs in `tokens`, because every theme class below `<html>` declares it again).
|
|
194
|
+
- **Strict CSP.** Under a Content Security Policy whose `style-src` blocks inline styles, the server-rendered overrides do not apply and hydration does not apply them again: ship them in a stylesheet there (below), and pass no `tokens` (nor `getTokenStyle`), since the overlays and the subtrees rendered in the client would still take them and leave the page half branded.
|
|
195
|
+
|
|
167
196
|
### Customizing tokens
|
|
168
197
|
|
|
169
198
|
Every color is a runtime CSS variable prefixed `--wave-`. The Tailwind color utilities use the same name without the prefix: `--wave-muted-foreground` is `text-muted-foreground`, `--wave-primary` is `bg-primary`, `border-primary`, `ring-primary`, and so on.
|
|
170
199
|
|
|
171
|
-
Override a token for one theme by targeting its theme selector. With `./styles`, load your CSS after Wave's (the selectors have the same specificity); on the Tailwind path any unlayered rule wins over Wave's `theme` layer.
|
|
200
|
+
Override a token for one theme by targeting its theme selector. With `./styles`, load your CSS after Wave's (the selectors have the same specificity); on the Tailwind path any unlayered rule wins over Wave's `theme` layer. A provider's `tokens` win over these rules on the elements that carry them (an inline style beats a stylesheet rule on the same element).
|
|
172
201
|
|
|
173
202
|
```css
|
|
174
203
|
/* All light surfaces */
|
|
@@ -183,7 +212,7 @@ Override a token for one theme by targeting its theme selector. With `./styles`,
|
|
|
183
212
|
}
|
|
184
213
|
```
|
|
185
214
|
|
|
186
|
-
**Re-branding.** The primary, hover, pressed, focus-ring and selected colors of the light and dark themes are built from the brand ramp (`--wave-brand-10` … `--wave-brand-160`, declared on `:root`), so overriding the ramp re-themes them: light uses brand-80/70/40 for primary/hover/pressed, brand-160 and brand-60 for selected
|
|
215
|
+
**Re-branding.** The primary, hover, pressed, focus-ring and selected colors of the light and dark themes are built from the brand ramp (`--wave-brand-10` … `--wave-brand-160`, declared on `:root`), so overriding the ramp re-themes them: light uses brand-80/70/40 for primary/hover/pressed, brand-80 for the focus ring, brand-160/150/140 for the selected surface and its hover and pressed states and brand-60 for selected text; dark uses brand-110/120/90, brand-100 for the focus ring, brand-20/40/10 and brand-110. High contrast uses fixed colors. `--wave-info` is a fixed color and does not follow the ramp. A ramp written in CSS is not checked: check its contrast against `--wave-primary-foreground` (white in light, black in dark) and the selection pairs, or generate it with `createBrandTokens` (above).
|
|
187
216
|
|
|
188
217
|
```css
|
|
189
218
|
:root {
|
|
@@ -284,13 +313,18 @@ Generated from `src/styles/tokens.css`. A value followed by a ramp or token name
|
|
|
284
313
|
|
|
285
314
|
</details>
|
|
286
315
|
|
|
287
|
-
|
|
316
|
+
Each theme also declares, beside the table's tokens:
|
|
317
|
+
|
|
318
|
+
- **The persona palette**: 12 hues (`red`, `pumpkin`, `gold`, `forest`, `teal`, `blue`, `navy`, `lavender`, `purple`, `magenta`, `brown`, `steel`) with five tokens each: `--wave-palette-<hue>` (the strong fill, 3:1 on the page), `-foreground` (its text), `-tint` (a surface: light in light, deep in dark), `-tint-foreground` (text on the tint and on the page) and `-border` (the hue's stroke), the last three Fluent's `Background2`, `Foreground2` and `BorderActive` persona roles. Utilities follow the names on the Tailwind path (`bg-palette-blue-tint`; the precompiled `./styles` holds only the fill and foreground utilities Avatar uses); high contrast paints every fill, tint foreground and border white, and every foreground and tint black. The guide's "Persona Palette" section lists the values.
|
|
319
|
+
- **The shadows** `--wave-shadow-2` … `--wave-shadow-64`, which the `shadow-N` utilities read: Fluent's values, ambient and key opacity 0.12 and 0.14 in light and the stronger 0.24 and 0.28 in dark and high contrast; on a dark page the border still carries most of a raised surface's separation.
|
|
320
|
+
|
|
321
|
+
Theme-independent variables on `:root`: `--wave-brand-10` … `--wave-brand-160` (also utilities such as `bg-brand-80`), `--wave-grey-2` … `--wave-grey-98`, the font families `--wave-font-family`, `--wave-font-family-monospace` and `--wave-font-family-numeric` (utilities `font-wave`, `font-wave-mono` and `font-wave-numeric`; `Text font="monospace"` and `font="numeric"` use the last two), the stacking layers `--wave-z-overlay` (1000), `--wave-z-toast` (1100) and `--wave-z-tooltip` (1200), and the motion durations `--wave-duration-*` and curves `--wave-curve-*` (see [Enter and exit motion](#enter-and-exit-motion)).
|
|
288
322
|
|
|
289
323
|
The three `input-filled-*` tokens (utilities `bg-input-filled-darker`, `bg-input-filled-lighter`, `border-input-filled-stroke`) draw the `filled-darker` and `filled-lighter` appearances of the text inputs and pickers (see [Sizes and appearances](#sizes-and-appearances)); `input-filled-lighter` follows `background`, and the stroke is transparent except in the high-contrast theme.
|
|
290
324
|
|
|
291
325
|
Contrast of every text and non-text pair is checked per theme by `src/styles/__tests__/tokens.test.ts`. One known limit: the `warning` fill (`#fde300` in light) is a background and accent color only; use `warning-tint-foreground` for warning text and icons.
|
|
292
326
|
|
|
293
|
-
Other Wave theme values: the type ramp `text-caption-2`, `text-caption-1`, `text-body-1`, `text-body-2`, `text-subtitle-2`, `text-subtitle-1`, `text-title-3`, `text-title-2`, `text-title-1`, `text-large-title`, `text-display`; shadows `shadow-2`, `shadow-4`, `shadow-8`, `shadow-16`, `shadow-28`, `shadow-64
|
|
327
|
+
Other Wave theme values: the type ramp `text-caption-2`, `text-caption-1`, `text-body-1`, `text-body-2`, `text-subtitle-2`, `text-subtitle-1`, `text-title-3`, `text-title-2`, `text-title-1`, `text-large-title`, `text-display`; shadows `shadow-2`, `shadow-4`, `shadow-8`, `shadow-16`, `shadow-28`, `shadow-64` (per theme, above); animations `animate-wave-spin`, `animate-wave-spin-slow`, `animate-wave-pulse`, `animate-wave-indeterminate`, `animate-wave-indeterminate-rtl`; on the Tailwind path, the motion utilities `duration-wave-ultra-fast` … `duration-wave-ultra-slow` and `ease-wave-accelerate-max` … `ease-wave-linear`. Spacing, border radius and stroke widths use Tailwind's default scales (Wave defines no tokens for them and no longer overrides the radius); the guide's "Spacing, Radius and Stroke Scales" maps Fluent's `spacing*`, `borderRadius*` and `strokeWidth*` tokens to the classes.
|
|
294
328
|
|
|
295
329
|
## Global effects
|
|
296
330
|
|
|
@@ -307,14 +341,14 @@ What adding Wave changes outside its own components:
|
|
|
307
341
|
There are no `html` or `body` rules, no global reduced-motion override (each component handles reduced motion itself), and no Preflight unless you import it. The only global rule is Tailwind's `@layer properties` fallback in `./styles`: in browsers without `@property` support (detected by an `@supports` query) it sets the initial values of Tailwind's `--tw-*` custom properties on `*`, `::before`, `::after` and `::backdrop`, and nothing else. It is also the only layered block in that otherwise unlayered file.
|
|
308
342
|
|
|
309
343
|
- **Utility classes (`./styles`).** The precompiled stylesheet contains the Tailwind utilities the components use (`.flex`, `.p-4`, `.bg-primary`, …) as ordinary class selectors, unlayered, plus Tailwind's `@property --tw-*` registrations. An element of yours with the same class name gets the same style. It contains no other utility: generic names that Tailwind would generate from words in Wave's comments and strings (`.container`, `.collapse`, `.table`, `.shadow`, `.ring`, …) are excluded, so they never restyle your own markup.
|
|
310
|
-
- **Theme and variant (`./tailwind`).** Wave adds its color names, `font-wave`, the type ramp, the shadow scale, the `animate-wave-*` animations and the `duration-wave-*` and `ease-wave-*` motion tokens to your Tailwind theme, and defines the `wave-rtl:` variant the components use to mirror glyphs (`./variants` defines it alone, for custom setups on `./tokens`). It matches by the element's own direction (`:dir(rtl)`, with a `[dir=rtl]` fallback for browsers without `:dir()`), so unlike Tailwind's `rtl:` it does not apply inside an LTR subtree of an RTL page; your build can use it too. Tailwind's `rtl:` is unchanged.
|
|
344
|
+
- **Theme and variant (`./tailwind`).** Wave adds its color names, the font families `font-wave`, `font-wave-mono` and `font-wave-numeric`, the type ramp, the shadow scale, the `animate-wave-*` animations and the `duration-wave-*` and `ease-wave-*` motion tokens to your Tailwind theme, and defines the `wave-rtl:` variant the components use to mirror glyphs (`./variants` defines it alone, for custom setups on `./tokens`). It matches by the element's own direction (`:dir(rtl)`, with a `[dir=rtl]` fallback for browsers without `:dir()`), so unlike Tailwind's `rtl:` it does not apply inside an LTR subtree of an RTL page; your build can use it too. Tailwind's `rtl:` is unchanged.
|
|
311
345
|
- The variant writes the direction as `:nth-child(n of :dir(rtl))`, which matches the same elements, so your app's CSS minifier keeps it. Vite's default minifier, Lightning CSS, rewrites a bare `:dir()` to a `:lang()` list for targets below Chrome 120, which never matches a page that sets `dir` without a right-to-left `lang`. Your own `:dir()` rules still go through that rewrite in a Vite build (Tailwind's `rtl:` keeps working through its `[dir=rtl]` alternatives). To keep them, set `build.cssTarget` to browsers with `:dir()` (for example `['chrome120', 'edge120', 'firefox128', 'safari16.4']`), or exclude the feature: `css: { lightningcss: { exclude: Features.DirSelector } }` with `import { Features } from 'lightningcss'`.
|
|
312
346
|
- postcss-preset-env at its default stage 2 enables `dir-pseudo-class`, which rewrites `:dir()` in a way that breaks Wave's mirroring (the 0.5 form of the variant too). Turn it off with `features: { 'dir-pseudo-class': false }`. Next.js's built-in PostCSS defaults (stage 3) are not affected.
|
|
313
347
|
|
|
314
348
|
**Colliding utility names.** Wave's Tailwind color names are unprefixed, because they are the utility vocabulary 0.4 documented and that consumer code uses in `className` overrides. In a shared Tailwind build, or next to another stylesheet that defines the same class names, each of the following names resolves to a single definition in every utility that takes a color (`bg-`, `text-`, `border-`, `ring-`, `outline-`, `fill-`, `stroke-`, `accent-`, `caret-`, `decoration-`, `divide-`, `placeholder-`, `shadow-`, `from-`/`via-`/`to-`, …):
|
|
315
349
|
|
|
316
350
|
- Shared with shadcn/ui: `background`, `foreground`, `card`, `card-foreground`, `primary`, `primary-foreground`, `secondary`, `secondary-foreground`, `muted`, `muted-foreground`, `accent`, `accent-foreground`, `destructive`, `destructive-foreground`, `border`, `input`, `ring`.
|
|
317
|
-
- Wave's other names: `primary-hover`, `primary-pressed`, `error`, `error-foreground`, `subtle`, `subtle-hover`, `subtle-pressed`, `subtle-selected`, `selected`, `selected-foreground`, `selected-hover`, `selected-pressed`, `muted-hover`, `muted-pressed`, `stroke`, `stroke-hover`, `stroke-accessible`, `success`, `success-foreground`, `success-tint`, `success-tint-foreground`, `warning`, `warning-foreground`, `warning-tint`, `warning-tint-foreground`, `error-tint`, `error-tint-foreground`, `severe`, `severe-foreground`, `severe-tint`, `severe-tint-foreground`, `info`, `info-foreground`, `info-tint`, `info-tint-foreground`, `inverted`, `inverted-foreground`, `inverted-border`, `input-filled-darker`, `input-filled-lighter`, `input-filled-stroke`, `track`, `skeleton`, `rating`, `presence-available`, `presence-busy`, `presence-away`, `presence-offline`, `presence-oof`, `presence-glyph`, `backdrop`, `brand-10` … `brand-160`.
|
|
351
|
+
- Wave's other names: `primary-hover`, `primary-pressed`, `error`, `error-foreground`, `subtle`, `subtle-hover`, `subtle-pressed`, `subtle-selected`, `selected`, `selected-foreground`, `selected-hover`, `selected-pressed`, `muted-hover`, `muted-pressed`, `stroke`, `stroke-hover`, `stroke-accessible`, `success`, `success-foreground`, `success-tint`, `success-tint-foreground`, `warning`, `warning-foreground`, `warning-tint`, `warning-tint-foreground`, `error-tint`, `error-tint-foreground`, `severe`, `severe-foreground`, `severe-tint`, `severe-tint-foreground`, `info`, `info-foreground`, `info-tint`, `info-tint-foreground`, `inverted`, `inverted-foreground`, `inverted-border`, `input-filled-darker`, `input-filled-lighter`, `input-filled-stroke`, `track`, `skeleton`, `rating`, `presence-available`, `presence-busy`, `presence-away`, `presence-offline`, `presence-oof`, `presence-glyph`, `backdrop`, `brand-10` … `brand-160`, and for each palette hue (`red`, `pumpkin`, … `steel`) `palette-<hue>`, `palette-<hue>-foreground`, `palette-<hue>-tint`, `palette-<hue>-tint-foreground` and `palette-<hue>-border`.
|
|
318
352
|
|
|
319
353
|
Workarounds when your app has its own design system:
|
|
320
354
|
|
|
@@ -370,7 +404,7 @@ export function checkedRows(tree: HTMLElement) {
|
|
|
370
404
|
|
|
371
405
|
## React Server Components
|
|
372
406
|
|
|
373
|
-
- Every component and hook module of the package starts with `"use client"`. The entry point, `cn`, the slot helpers, the class-name constants (`buttonClassNames`, …), `hsvToHex`, `hexToHsv`, `createToaster`, the date helpers (`addDays`, `addMonths`, `isSameDay`, `startOfWeek`, `getWeekNumber`), `getInitials`, `partitionAvatarGroupItems`, the breadcrumb helpers (`partitionBreadcrumbItems`, `truncateBreadcrumbLongName`, `truncateBreadcrumbLongTooltip`, `isTruncatableBreadcrumbContent`), `createTableColumn` and the types are server-safe, so a Server Component can import from `@mortenbrudvik/waveui`, read the constants, create a toaster handle, compute dates and render Wave components with serializable props. A DataGrid column with functions (`compare`, `renderCell`) is not serializable: define such columns in a client component.
|
|
407
|
+
- Every component and hook module of the package starts with `"use client"`. The entry point, `cn`, the slot helpers, the class-name constants (`buttonClassNames`, …), `hsvToHex`, `hexToHsv`, `createToaster`, the date helpers (`addDays`, `addMonths`, `isSameDay`, `startOfWeek`, `getWeekNumber`), `getInitials`, `partitionAvatarGroupItems`, the breadcrumb helpers (`partitionBreadcrumbItems`, `truncateBreadcrumbLongName`, `truncateBreadcrumbLongTooltip`, `isTruncatableBreadcrumbContent`), `createTableColumn`, the theming helpers (`createBrandTokens`, `getTokenStyle`, `waveTokens`, `chartPalette`, `getThemeClassName`) and the types are server-safe, so a Server Component can import from `@mortenbrudvik/waveui`, read the constants, create a toaster handle, compute dates, build a brand ramp and render Wave components with serializable props (a `tokens` object is one). A DataGrid column with functions (`compare`, `renderCell`) is not serializable: define such columns in a client component.
|
|
374
408
|
- A Server Component **cannot dot into a client component**: `Card.Header` throws "Cannot access Header on the server". Every sub-component is therefore also exported under a flat name. Use the flat names in Server Components and either form in client components.
|
|
375
409
|
- Every compound can be composed in a Server Component. Parts written there reach the client as lazy references, and the compounds recognise them, so the server HTML and the client behaviour are the same as in a client file: Accordion items, Carousel slides, the options of Combobox, Dropdown and Listbox, the parts and options of TagPicker, DataGrid parts (`DataGrid.SelectionCell` included), the Table parts, Overflow items and dividers, Breadcrumb items (collapsed into the overflow menu too), the TeachingPopover parts, Drawer and Menu triggers, the Drawer header, body and footer parts, the tags of a TagGroup and the halves of an InteractionTag, the header of a `Menu.Group`, the halves of a `Menu.SplitGroup`, List, TabList and Tree items, Nav items, categories, section headers, dividers and app items, the header, body and footer of a NavDrawer, the stars of a Rating or RatingDisplay, the parts of Toast and MessageBar, the bars of a MessageBarGroup, the ColorPicker parts, `SwatchPicker.Row` and the swatches (`ColorSwatch`, `ImageSwatch`, `EmptySwatch`, in rows of the grid layout or the implicit ones), `Tree.ItemPersonaLayout`, a `Label` or `InfoLabel` passed as the `label` of a Field, and a `Button` passed as a MessageBar, SearchBox or Tag dismiss slot.
|
|
376
410
|
- Event handlers and other functions cannot be passed from a Server Component; put interactive parts in a `'use client'` component. That includes every windowed or data-driven component: a `virtualWindow` (from the `useVirtualWindow` hook), `renderOption`, a FlatTree's model and `renderItem`, a windowed List's function child and `isSelectionDisabled` are client values. The server HTML of a windowed component holds its first window (the top of the list in the window's `initialViewportSize`), and that of a closed data-mode Combobox or Dropdown no options.
|
|
@@ -554,7 +588,7 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
554
588
|
|
|
555
589
|
| Component | Description |
|
|
556
590
|
| --------- | --------------------------------------------------------------------------- |
|
|
557
|
-
| `Text` | Type-ramp text (`variant
|
|
591
|
+
| `Text` | Type-ramp text (`variant`; `weight`: `regular`, `medium`, `semibold`, `bold`; `font`: `base`, `monospace`, `numeric`); polymorphic `as`; inherits its color. |
|
|
558
592
|
|
|
559
593
|
### Layout
|
|
560
594
|
|
|
@@ -615,9 +649,9 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
|
|
|
615
649
|
|
|
616
650
|
| Component | Description |
|
|
617
651
|
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
618
|
-
| `WaveProvider` | Theme (`light`, `dark`, `high-contrast`), direction, portal container and `inputDefaults` (the default `size` and `appearance` of the text inputs and pickers) and `motion` (`'none'` turns the default motion of every animated component below it off). |
|
|
652
|
+
| `WaveProvider` | Theme (`light`, `dark`, `high-contrast`), direction, portal container and `inputDefaults` (the default `size` and `appearance` of the text inputs and pickers) and `motion` (`'none'` turns the default motion of every animated component below it off); `tokens` overrides design tokens in the subtree and its overlays, and `applyStylesToPortals={false}` keeps them off the overlays (see [Brand colors and token overrides](#brand-colors-and-token-overrides)). |
|
|
619
653
|
|
|
620
|
-
`Portal` renders its children into `document.body` (or the provider's `portalContainer`) inside a themed `wave-portal` wrapper; use it for your own overlays.
|
|
654
|
+
`Portal` renders its children into `document.body` (or the provider's `portalContainer`) inside a themed `wave-portal` wrapper, which also carries the provider's token overrides; use it for your own overlays.
|
|
621
655
|
|
|
622
656
|
### Motion
|
|
623
657
|
|
|
@@ -2051,7 +2085,7 @@ export function Notices({ notices, onDismiss }: { notices: Notice[]; onDismiss:
|
|
|
2051
2085
|
|
|
2052
2086
|
- **Sizes and shape.** `size` takes the named sizes or a box in pixels (16, 20, 24, 28, 32, 36, 40, 48, 56, 64, 72, 96, 120, 128); `shape="square"` rounds the corners. An Avatar in a `Tag` with `shape="rounded"` is square.
|
|
2053
2087
|
- **Initials** come from `getInitials(name, { dir, firstInitialOnly })`: two letters for a full name, one for a single name, brackets and punctuation dropped, a hyphen or apostrophe joins words. A name with digits only, emoji or a script without letter initials (Han, Kana, Hangul, Thai, Lao, Khmer, Myanmar, Tibetan) shows none, so an icon or the `initials` slot is the better content. `initials` replaces the text. On a right-to-left page the two letters of a left-to-right name swap; Hebrew and Arabic names keep their order.
|
|
2054
|
-
- **Color.** `color` is `neutral` (the default), `brand`, `colorful` or one of `red`, `pumpkin`, `gold`, `forest`, `teal`, `blue`, `navy`, `lavender`, `purple`, `magenta`, `brown`, `steel`. `colorful` picks a named color from `idForColor`, else from `name`: the same input is always the same color, so a person keeps their color across pages. The palette is the `--wave-palette-<name>` and `--wave-palette-<name>-foreground` tokens; high contrast paints a white fill with black text.
|
|
2088
|
+
- **Color.** `color` is `neutral` (the default), `brand`, `colorful` or one of `red`, `pumpkin`, `gold`, `forest`, `teal`, `blue`, `navy`, `lavender`, `purple`, `magenta`, `brown`, `steel`. `colorful` picks a named color from `idForColor`, else from `name`: the same input is always the same color, so a person keeps their color across pages. The palette is the `--wave-palette-<name>` and `--wave-palette-<name>-foreground` tokens; high contrast paints a white fill with black text. Each hue also has a tint, a tint foreground and a border (`--wave-palette-<name>-tint`, `-tint-foreground`, `-border`) for tags, badges and charts of your own; Avatar does not use them.
|
|
2055
2089
|
- **`active`** is `active` (the `activeAppearance` around the avatar: `ring`, `ring-shadow`, `shadow` or `glow`), `inactive` (dimmed) or `unset`.
|
|
2056
2090
|
- **Presence.** `PresenceBadge` `status` is `available`, `busy`, `away`, `offline`, `do-not-disturb` or `out-of-office` (Fluent's names); `dnd` and `oof` still work and log a deprecation warning. Inside an Avatar the badge takes the size that fits the avatar.
|
|
2057
2091
|
- **`Persona`** shows `primaryText` (default: `name`), then `secondaryText`, `tertiaryText` and `quaternaryText`, after, before or below the avatar (`textPosition`; below, the text is centered unless `textAlignment="start"`). `presenceOnly` swaps the avatar for the presence badge. A Persona without a `name` or text needs an `aria-label`.
|
|
@@ -2866,7 +2900,7 @@ Typeahead (Menu, a selectable or navigable List, Tree, Dropdown, Listbox) accept
|
|
|
2866
2900
|
| `useListboxPopup(options)`, `ListboxSurface` | The popup of a custom picker: dismissal (`onDismiss(reason)`, a `DismissReason`: `'escape'`, `'outside-press'` or `'focus-outside'`) and positioning, and the surface that renders the list (`listClassName`, `surfaceClassName`). |
|
|
2867
2901
|
| `ListboxProvider` | Provides a listbox's context to its options when you render the list yourself. |
|
|
2868
2902
|
| `collectOptionLabels(children)`, `markListboxElement(component, kind)` | Read option labels from children before the options register (server HTML, first render); mark your own option and group components so they are read. |
|
|
2869
|
-
| `useWaveTheme()` | Current theme, direction, portal container, `inputDefaults
|
|
2903
|
+
| `useWaveTheme()` | Current theme, direction, portal container, `inputDefaults`, `motion` (`'default'` or `'none'`), the merged token overrides (`tokens`), the inline style that applies them (`tokenStyle`) and `applyStylesToPortals`. |
|
|
2870
2904
|
| `useToastController()` | `dispatchToast` (content or options) / `dismissToast` / `dismissAllToasts` inside `<Toaster>`. |
|
|
2871
2905
|
| `createToaster()` | A toast controller for code outside components; bind it with `<Toaster toaster={handle}>` (see [Toasts](#toasts)). Server-safe. |
|
|
2872
2906
|
| `useIsOverflowing(target)`, `useOverflowMenu()`, `useIsOverflowItemVisible(id)` | Overflow detection. |
|
|
@@ -2879,11 +2913,26 @@ Typeahead (Menu, a selectable or navigable List, Tree, Dropdown, Listbox) accept
|
|
|
2879
2913
|
| `partitionAvatarGroupItems({ items, maxInlineItems, layout })` | Split a list into the inline and overflow items of an AvatarGroup (`{ inlineItems, overflowItems }`); imports nothing. |
|
|
2880
2914
|
| `addDays(date, amount)`, `addMonths(date, amount)`, `isSameDay(a, b)`, `startOfWeek(date, firstDayOfWeek?)`, `getWeekNumber(date, options?)` | Date helpers on calendar days: every returned date is local midnight (Fluent's keep the time of day). `addMonths` clamps the day to the target month (January 31 plus a month is February 28 or 29); `getWeekNumber` takes `firstDayOfWeek` and `firstWeekOfYear` (`GetWeekNumberOptions`; ISO 8601 is `{ firstDayOfWeek: 1, firstWeekOfYear: 'first-four-day-week' }`). With the types `DayOfWeek` (`0`–`6`, Sunday first) and `FirstWeekOfYear`. Server-safe. |
|
|
2881
2915
|
| `buttonClassNames`, `toastTitleClassNames`, … | The stable class names of every component and part (see [Styling hooks](#styling-hooks)). Server-safe. |
|
|
2882
|
-
| `cn(...classes)` | clsx + tailwind-merge that knows Wave's type ramp and
|
|
2916
|
+
| `cn(...classes)` | clsx + tailwind-merge that knows Wave's type ramp, shadows and font families; the last class wins over a conflicting class of the same variant. |
|
|
2883
2917
|
| `composeEventHandlers(theirs, ours)` | Run a consumer handler, then yours unless it called `preventDefault()`. |
|
|
2884
2918
|
| `mergeRefs(...refs)` | Merge refs outside components. |
|
|
2885
2919
|
| `resolveSlot`, `renderSlot` | The slot helpers the components use. |
|
|
2886
2920
|
| `getThemeClassName(theme)` | The theme classes of a theme. |
|
|
2921
|
+
| `createBrandTokens(seed)` | The 16 brand tokens (`brand10` … `brand160`, a `BrandTokens`) of a `#rgb` or `#rrggbb` seed, each stop at the default ramp's luminance so the light and dark themes keep their contrast; or of a full `BrandRamp`, returned unchanged and checked for contrast in development. For `WaveProvider tokens` or `getTokenStyle` (see [Brand colors and token overrides](#brand-colors-and-token-overrides)). Server-safe. |
|
|
2922
|
+
| `waveTokens` | Every design token as the `var()` that reads it, typed as that literal (`waveTokens.primary` is `'var(--wave-primary)'`), for inline styles and SVG charts; with the types `WaveTokens` and `WaveTokenName`. Frozen and server-safe; no component imports it. |
|
|
2923
|
+
| `chartPalette` | The 12 palette fills as token names, in the order that keeps neighbouring chart series as far apart as the palette allows (read each with `waveTokens[name]`); colors still need a second encoding in every chart. Frozen and server-safe. |
|
|
2924
|
+
| `getTokenStyle(overrides)` | The inline style (`TokenStyle`) that applies token overrides (`WaveTokenOverrides`), the one `WaveProvider` puts on its root and portals; `undefined` when there is nothing to set. Server-safe. |
|
|
2925
|
+
|
|
2926
|
+
## Upgrading from 0.16
|
|
2927
|
+
|
|
2928
|
+
0.17 removes nothing public, and everything it adds is opt-in (`tokens` and `applyStylesToPortals`, `createBrandTokens`, `waveTokens`, `getTokenStyle`, `chartPalette`, `Text font`, the palette tints and the font families), but the dark and high-contrast shadows change, and so do two types:
|
|
2929
|
+
|
|
2930
|
+
1. **Stronger shadows in the dark and high-contrast themes.** The shadow levels are tokens of each theme (`--wave-shadow-2` … `--wave-shadow-64`): the light theme keeps the shadows of 0.16, and dark and high contrast take Fluent's, twice as dark (ambient opacity 0.24, key 0.28), so screenshots of those themes change. A shadow color utility or an opacity modifier (`shadow-black`, `shadow-4/50`) no longer changes Wave's levels: set the level's token for a theme instead (`.wave-dark { --wave-shadow-8: … }`, or `tokens={{ shadow8: … }}`). An `@theme` of yours that sets `--shadow-N` keeps its value.
|
|
2931
|
+
2. **`WaveContextValue` has three new members**, `tokens`, `tokenStyle` and `applyStylesToPortals` (`{}`, `undefined` and `true` outside a provider): a hand-built value (a test mock of `useWaveTheme`) adds them.
|
|
2932
|
+
3. **`TextWeight` has a fourth value, `'medium'`:** an exhaustive `switch` over it, or a `Record<TextWeight, …>` of your own, adds a case.
|
|
2933
|
+
4. **With `tokens`, the provider root and the portal wrappers carry the overrides** in their `style` attribute, a nested provider's root too. Without `tokens` the markup is 0.16's; a test that compares a whole `style` attribute changes when you adopt them.
|
|
2934
|
+
|
|
2935
|
+
On the `./tailwind` path your Tailwind theme gains the palette's new color names and the `font-wave-mono` and `font-wave-numeric` families (see [Global effects](#global-effects)). [Brand colors and token overrides](#brand-colors-and-token-overrides) shows the new API; the CHANGELOG's [0.17.0 section](CHANGELOG.md) lists the additions and the full upgrade notes.
|
|
2887
2936
|
|
|
2888
2937
|
## Upgrading from 0.15
|
|
2889
2938
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/slot.cjs"),r=require("../../lib/classNames.cjs"),i=require("./fieldLabel.cjs");let a=require("react");a=e.__toESM(a,1);let o=require("react/jsx-runtime");var s={small:`text-caption-1`,medium:`text-body-1`,large:`text-body-2`},c={regular:void 0,semibold:`font-semibold`,bold:`font-bold`},l=({required:e=!1,disabled:i=!1,size:a=`medium`,weight:l=`regular`,className:u,children:d,ref:f,...p})=>(0,o.jsxs)(`label`,{ref:f,className:t.cn(r.labelClassNames.root,i?`text-muted-foreground`:`text-foreground`,s[a],c[l],u),...p,children:[d,e===!0?(0,o.jsx)(`span`,{className:t.cn(r.labelClassNames.required,`ms-1 text-error`),"aria-hidden":`true`,children:`*`}):e!==!1&&n.slotRendersContent(e)?(0,o.jsx)(`span`,{className:t.cn(r.labelClassNames.required,`ms-1 text-error`),"aria-hidden":`true`,children:n.materialiseSlotContent(e)}):null]});l.displayName=`Label`,i.markFieldLabel(l),exports.Label=l;
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/slot.cjs"),r=require("../../lib/classNames.cjs"),i=require("./fieldLabel.cjs");let a=require("react");a=e.__toESM(a,1);let o=require("react/jsx-runtime");var s={small:`text-caption-1`,medium:`text-body-1`,large:`text-body-2`},c={regular:void 0,medium:`font-medium`,semibold:`font-semibold`,bold:`font-bold`},l=({required:e=!1,disabled:i=!1,size:a=`medium`,weight:l=`regular`,className:u,children:d,ref:f,...p})=>(0,o.jsxs)(`label`,{ref:f,className:t.cn(r.labelClassNames.root,i?`text-muted-foreground`:`text-foreground`,s[a],c[l],u),...p,children:[d,e===!0?(0,o.jsx)(`span`,{className:t.cn(r.labelClassNames.required,`ms-1 text-error`),"aria-hidden":`true`,children:`*`}):e!==!1&&n.slotRendersContent(e)?(0,o.jsx)(`span`,{className:t.cn(r.labelClassNames.required,`ms-1 text-error`),"aria-hidden":`true`,children:n.materialiseSlotContent(e)}):null]});l.displayName=`Label`,i.markFieldLabel(l),exports.Label=l;
|
|
2
2
|
//# sourceMappingURL=Label.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Label.cjs","names":["cn","labelClassNames","slotRendersContent","materialiseSlotContent","markFieldLabel","require_runtime","require_cn","require_slot","require_classNames","require_fieldLabel","react","react_jsx_runtime","sizeMap","weightMap","Label","rest","disabled","size","weight","className","children","required"],"sources":["../../../src/components/input/Label.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { labelClassNames } from '../../lib/classNames';\nimport { markFieldLabel } from './fieldLabel';\nimport { materialiseSlotContent, slotRendersContent } from '../../lib/slot';\nimport type { CoreSize, TextWeight } from '../../lib/types';\n\n/** Properties for the Label component. */\nexport interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {\n /** Whether to show a required indicator. `true` shows a decorative asterisk; other content\n * replaces it (for example a `(required)` text). Decorative (`aria-hidden`), so it must not be\n * focusable; mark the control itself `required`.\n * @default false\n */\n required?: boolean | React.ReactNode;\n /** Whether the label is visually dimmed.\n * @default false\n */\n disabled?: boolean;\n /** Text size of the label: `small` = caption-1, `medium` = body-1, `large` = body-2.\n * @default 'medium'\n */\n size?: CoreSize;\n /** Font weight of the label (the shared `TextWeight` vocabulary).\n * @default 'regular'\n */\n weight?: TextWeight;\n /** Ref to the `<label>` element. */\n ref?: React.Ref<HTMLLabelElement>;\n}\n\nconst sizeMap: Record<CoreSize, string> = {\n small: 'text-caption-1',\n medium: 'text-body-1',\n large: 'text-body-2',\n};\n\nconst weightMap: Record<TextWeight, string | undefined> = {\n regular: undefined,\n semibold: 'font-semibold',\n bold: 'font-bold',\n};\n\n/**\n * A text label for a form control (`htmlFor`), on the Fluent type ramp. The optional required\n * indicator is decorative (`aria-hidden`). `Field` renders its own label; use `Label` for custom\n * layouts.\n *\n * @example\n * <Label htmlFor=\"email\" required>Email</Label>\n * <Input id=\"email\" required />\n */\nexport const Label = ({\n required = false,\n disabled = false,\n size = 'medium',\n weight = 'regular',\n className,\n children,\n ref,\n ...rest\n}: LabelProps) => {\n return (\n <label\n ref={ref}\n className={cn(\n labelClassNames.root,\n disabled ? 'text-muted-foreground' : 'text-foreground',\n sizeMap[size],\n weightMap[weight],\n className,\n )}\n {...rest}\n >\n {children}\n {required === true ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n *\n </span>\n ) : required !== false && slotRendersContent(required) ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n {materialiseSlotContent(required)}\n </span>\n ) : null}\n </label>\n );\n};\n\nLabel.displayName = 'Label';\nmarkFieldLabel(Label);\n"],"mappings":"kRA+BA,IAAMY,EAAoC,CACxC,MAAO,iBACP,OAAQ,cACR,MAAO,aACT,EAEMC,EAAoD,CACxD,QAAS,IAAA,GACT,SAAU,gBACV,KAAM,WACR,EAWaC,GAAS,CACpB,WAAW,GACX,WAAW,GACX,OAAO,SACP,SAAS,UACT,YACA,WACA,MACA,GAAGC,MAGD,EAAAJ,EAAA,KAAA,CAAC,QAAD,CACO,MACL,UAAWX,EAAAA,GACTC,EAAAA,gBAAgB,KAChBe,EAAW,wBAA0B,kBACrCJ,EAAQK,GACRJ,EAAUK,GACVC,CACF,EACA,GAAIJ,EATN,SAAA,CAWGK,EACAC,IAAa,IACZ,EAAAV,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWX,EAAAA,GAAGC,EAAAA,gBAAgB,SAAU,iBAAiB,EAAG,cAAY,OAAO,SAAA,GAE/E,CAAA,EACJoB,IAAa,IAASnB,EAAAA,mBAAmBmB,CAAQ,GACnD,EAAAV,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWX,EAAAA,GAAGC,EAAAA,gBAAgB,SAAU,iBAAiB,EAAG,cAAY,OAC3E,SAAAE,EAAAA,uBAAuBkB,CAAQ,CAC5B,CAAA,EACJ,IACC,IAIX,EAAM,YAAc,QACpBjB,EAAAA,eAAeU,CAAK"}
|
|
1
|
+
{"version":3,"file":"Label.cjs","names":["cn","labelClassNames","slotRendersContent","materialiseSlotContent","markFieldLabel","require_runtime","require_cn","require_slot","require_classNames","require_fieldLabel","react","react_jsx_runtime","sizeMap","weightMap","Label","rest","disabled","size","weight","className","children","required"],"sources":["../../../src/components/input/Label.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { labelClassNames } from '../../lib/classNames';\nimport { markFieldLabel } from './fieldLabel';\nimport { materialiseSlotContent, slotRendersContent } from '../../lib/slot';\nimport type { CoreSize, TextWeight } from '../../lib/types';\n\n/** Properties for the Label component. */\nexport interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {\n /** Whether to show a required indicator. `true` shows a decorative asterisk; other content\n * replaces it (for example a `(required)` text). Decorative (`aria-hidden`), so it must not be\n * focusable; mark the control itself `required`.\n * @default false\n */\n required?: boolean | React.ReactNode;\n /** Whether the label is visually dimmed.\n * @default false\n */\n disabled?: boolean;\n /** Text size of the label: `small` = caption-1, `medium` = body-1, `large` = body-2.\n * @default 'medium'\n */\n size?: CoreSize;\n /** Font weight of the label (the shared `TextWeight` vocabulary).\n * @default 'regular'\n */\n weight?: TextWeight;\n /** Ref to the `<label>` element. */\n ref?: React.Ref<HTMLLabelElement>;\n}\n\nconst sizeMap: Record<CoreSize, string> = {\n small: 'text-caption-1',\n medium: 'text-body-1',\n large: 'text-body-2',\n};\n\nconst weightMap: Record<TextWeight, string | undefined> = {\n regular: undefined,\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n};\n\n/**\n * A text label for a form control (`htmlFor`), on the Fluent type ramp. The optional required\n * indicator is decorative (`aria-hidden`). `Field` renders its own label; use `Label` for custom\n * layouts.\n *\n * @example\n * <Label htmlFor=\"email\" required>Email</Label>\n * <Input id=\"email\" required />\n */\nexport const Label = ({\n required = false,\n disabled = false,\n size = 'medium',\n weight = 'regular',\n className,\n children,\n ref,\n ...rest\n}: LabelProps) => {\n return (\n <label\n ref={ref}\n className={cn(\n labelClassNames.root,\n disabled ? 'text-muted-foreground' : 'text-foreground',\n sizeMap[size],\n weightMap[weight],\n className,\n )}\n {...rest}\n >\n {children}\n {required === true ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n *\n </span>\n ) : required !== false && slotRendersContent(required) ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n {materialiseSlotContent(required)}\n </span>\n ) : null}\n </label>\n );\n};\n\nLabel.displayName = 'Label';\nmarkFieldLabel(Label);\n"],"mappings":"kRA+BA,IAAMY,EAAoC,CACxC,MAAO,iBACP,OAAQ,cACR,MAAO,aACT,EAEMC,EAAoD,CACxD,QAAS,IAAA,GACT,OAAQ,cACR,SAAU,gBACV,KAAM,WACR,EAWaC,GAAS,CACpB,WAAW,GACX,WAAW,GACX,OAAO,SACP,SAAS,UACT,YACA,WACA,MACA,GAAGC,MAGD,EAAAJ,EAAA,KAAA,CAAC,QAAD,CACO,MACL,UAAWX,EAAAA,GACTC,EAAAA,gBAAgB,KAChBe,EAAW,wBAA0B,kBACrCJ,EAAQK,GACRJ,EAAUK,GACVC,CACF,EACA,GAAIJ,EATN,SAAA,CAWGK,EACAC,IAAa,IACZ,EAAAV,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWX,EAAAA,GAAGC,EAAAA,gBAAgB,SAAU,iBAAiB,EAAG,cAAY,OAAO,SAAA,GAE/E,CAAA,EACJoB,IAAa,IAASnB,EAAAA,mBAAmBmB,CAAQ,GACnD,EAAAV,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWX,EAAAA,GAAGC,EAAAA,gBAAgB,SAAU,iBAAiB,EAAG,cAAY,OAC3E,SAAAE,EAAAA,uBAAuBkB,CAAQ,CAC5B,CAAA,EACJ,IACC,IAIX,EAAM,YAAc,QACpBjB,EAAAA,eAAeU,CAAK"}
|
|
@@ -12,6 +12,7 @@ var s = {
|
|
|
12
12
|
large: "text-body-2"
|
|
13
13
|
}, c = {
|
|
14
14
|
regular: void 0,
|
|
15
|
+
medium: "font-medium",
|
|
15
16
|
semibold: "font-semibold",
|
|
16
17
|
bold: "font-bold"
|
|
17
18
|
}, l = ({ required: i = !1, disabled: l = !1, size: u = "medium", weight: d = "regular", className: f, children: p, ref: m, ...h }) => /* @__PURE__ */ o("label", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Label.mjs","names":["sizeMap","weightMap","Label","rest","cn","labelClassNames","disabled","size","weight","className","children","required","slotRendersContent","materialiseSlotContent","markFieldLabel"],"sources":["../../../src/components/input/Label.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { labelClassNames } from '../../lib/classNames';\nimport { markFieldLabel } from './fieldLabel';\nimport { materialiseSlotContent, slotRendersContent } from '../../lib/slot';\nimport type { CoreSize, TextWeight } from '../../lib/types';\n\n/** Properties for the Label component. */\nexport interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {\n /** Whether to show a required indicator. `true` shows a decorative asterisk; other content\n * replaces it (for example a `(required)` text). Decorative (`aria-hidden`), so it must not be\n * focusable; mark the control itself `required`.\n * @default false\n */\n required?: boolean | React.ReactNode;\n /** Whether the label is visually dimmed.\n * @default false\n */\n disabled?: boolean;\n /** Text size of the label: `small` = caption-1, `medium` = body-1, `large` = body-2.\n * @default 'medium'\n */\n size?: CoreSize;\n /** Font weight of the label (the shared `TextWeight` vocabulary).\n * @default 'regular'\n */\n weight?: TextWeight;\n /** Ref to the `<label>` element. */\n ref?: React.Ref<HTMLLabelElement>;\n}\n\nconst sizeMap: Record<CoreSize, string> = {\n small: 'text-caption-1',\n medium: 'text-body-1',\n large: 'text-body-2',\n};\n\nconst weightMap: Record<TextWeight, string | undefined> = {\n regular: undefined,\n semibold: 'font-semibold',\n bold: 'font-bold',\n};\n\n/**\n * A text label for a form control (`htmlFor`), on the Fluent type ramp. The optional required\n * indicator is decorative (`aria-hidden`). `Field` renders its own label; use `Label` for custom\n * layouts.\n *\n * @example\n * <Label htmlFor=\"email\" required>Email</Label>\n * <Input id=\"email\" required />\n */\nexport const Label = ({\n required = false,\n disabled = false,\n size = 'medium',\n weight = 'regular',\n className,\n children,\n ref,\n ...rest\n}: LabelProps) => {\n return (\n <label\n ref={ref}\n className={cn(\n labelClassNames.root,\n disabled ? 'text-muted-foreground' : 'text-foreground',\n sizeMap[size],\n weightMap[weight],\n className,\n )}\n {...rest}\n >\n {children}\n {required === true ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n *\n </span>\n ) : required !== false && slotRendersContent(required) ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n {materialiseSlotContent(required)}\n </span>\n ) : null}\n </label>\n );\n};\n\nLabel.displayName = 'Label';\nmarkFieldLabel(Label);\n"],"mappings":";;;;;;;;AA+BA,IAAMA,IAAoC;CACxC,OAAO;CACP,QAAQ;CACR,OAAO;AACT,GAEMC,IAAoD;CACxD,SAAS,KAAA;CACT,UAAU;CACV,MAAM;AACR,GAWaC,KAAS,EACpB,cAAW,IACX,cAAW,IACX,UAAO,UACP,YAAS,WACT,cACA,aACA,QACA,GAAGC,QAGD,kBAAC,SAAD;CACO;CACL,WAAWC,EACTC,EAAgB,MAChBC,IAAW,0BAA0B,mBACrCN,EAAQO,IACRN,EAAUO,IACVC,CACF;CACA,GAAIN;CATN,UAAA,CAWGO,GACAC,MAAa,KACZ,kBAAC,QAAD;EAAM,WAAWP,EAAGC,EAAgB,UAAU,iBAAiB;EAAG,eAAY;EAAO,UAAA;CAE/E,CAAA,IACJM,MAAa,MAASC,EAAmBD,CAAQ,IACnD,kBAAC,QAAD;EAAM,WAAWP,EAAGC,EAAgB,UAAU,iBAAiB;EAAG,eAAY;EAC3E,UAAAQ,EAAuBF,CAAQ;CAC5B,CAAA,IACJ,IACC;;AAIX,EAAM,cAAc,SACpBG,EAAeZ,CAAK"}
|
|
1
|
+
{"version":3,"file":"Label.mjs","names":["sizeMap","weightMap","Label","rest","cn","labelClassNames","disabled","size","weight","className","children","required","slotRendersContent","materialiseSlotContent","markFieldLabel"],"sources":["../../../src/components/input/Label.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { labelClassNames } from '../../lib/classNames';\nimport { markFieldLabel } from './fieldLabel';\nimport { materialiseSlotContent, slotRendersContent } from '../../lib/slot';\nimport type { CoreSize, TextWeight } from '../../lib/types';\n\n/** Properties for the Label component. */\nexport interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {\n /** Whether to show a required indicator. `true` shows a decorative asterisk; other content\n * replaces it (for example a `(required)` text). Decorative (`aria-hidden`), so it must not be\n * focusable; mark the control itself `required`.\n * @default false\n */\n required?: boolean | React.ReactNode;\n /** Whether the label is visually dimmed.\n * @default false\n */\n disabled?: boolean;\n /** Text size of the label: `small` = caption-1, `medium` = body-1, `large` = body-2.\n * @default 'medium'\n */\n size?: CoreSize;\n /** Font weight of the label (the shared `TextWeight` vocabulary).\n * @default 'regular'\n */\n weight?: TextWeight;\n /** Ref to the `<label>` element. */\n ref?: React.Ref<HTMLLabelElement>;\n}\n\nconst sizeMap: Record<CoreSize, string> = {\n small: 'text-caption-1',\n medium: 'text-body-1',\n large: 'text-body-2',\n};\n\nconst weightMap: Record<TextWeight, string | undefined> = {\n regular: undefined,\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n};\n\n/**\n * A text label for a form control (`htmlFor`), on the Fluent type ramp. The optional required\n * indicator is decorative (`aria-hidden`). `Field` renders its own label; use `Label` for custom\n * layouts.\n *\n * @example\n * <Label htmlFor=\"email\" required>Email</Label>\n * <Input id=\"email\" required />\n */\nexport const Label = ({\n required = false,\n disabled = false,\n size = 'medium',\n weight = 'regular',\n className,\n children,\n ref,\n ...rest\n}: LabelProps) => {\n return (\n <label\n ref={ref}\n className={cn(\n labelClassNames.root,\n disabled ? 'text-muted-foreground' : 'text-foreground',\n sizeMap[size],\n weightMap[weight],\n className,\n )}\n {...rest}\n >\n {children}\n {required === true ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n *\n </span>\n ) : required !== false && slotRendersContent(required) ? (\n <span className={cn(labelClassNames.required, 'ms-1 text-error')} aria-hidden=\"true\">\n {materialiseSlotContent(required)}\n </span>\n ) : null}\n </label>\n );\n};\n\nLabel.displayName = 'Label';\nmarkFieldLabel(Label);\n"],"mappings":";;;;;;;;AA+BA,IAAMA,IAAoC;CACxC,OAAO;CACP,QAAQ;CACR,OAAO;AACT,GAEMC,IAAoD;CACxD,SAAS,KAAA;CACT,QAAQ;CACR,UAAU;CACV,MAAM;AACR,GAWaC,KAAS,EACpB,cAAW,IACX,cAAW,IACX,UAAO,UACP,YAAS,WACT,cACA,aACA,QACA,GAAGC,QAGD,kBAAC,SAAD;CACO;CACL,WAAWC,EACTC,EAAgB,MAChBC,IAAW,0BAA0B,mBACrCN,EAAQO,IACRN,EAAUO,IACVC,CACF;CACA,GAAIN;CATN,UAAA,CAWGO,GACAC,MAAa,KACZ,kBAAC,QAAD;EAAM,WAAWP,EAAGC,EAAgB,UAAU,iBAAiB;EAAG,eAAY;EAAO,UAAA;CAE/E,CAAA,IACJM,MAAa,MAASC,EAAmBD,CAAQ,IACnD,kBAAC,QAAD;EAAM,WAAWP,EAAGC,EAAgB,UAAU,iBAAiB;EAAG,eAAY;EAC3E,UAAAQ,EAAuBF,CAAQ;CAC5B,CAAA,IACJ,IACC;;AAIX,EAAM,cAAc,SACpBG,EAAeZ,CAAK"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/classNames.cjs"),r=require("../../hooks/useMergedRefs.cjs"),i=require("../../hooks/useIsClient.cjs"),a=require("../provider/WaveProvider.cjs"),o=require("../../hooks/useDirection.cjs"),s=require("../../lib/layers.cjs"),c=require("../../hooks/useDismiss.cjs");let l=require("react");l=e.__toESM(l,1);let u=require("react/jsx-runtime"),d=require("react-dom");var f=l.createContext(0);f.displayName=`PortalDepthContext`;var p=({children:e,container:p,layer:m=`overlay`,layerId:h,disabled:g=!1,className:_,ref:v})=>{let y=i.useIsClient(),{themeClassName:b,portalContainer:x}=a.useWaveTheme(),
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/classNames.cjs"),r=require("../../hooks/useMergedRefs.cjs"),i=require("../../hooks/useIsClient.cjs"),a=require("../provider/WaveProvider.cjs"),o=require("../../hooks/useDirection.cjs"),s=require("../../lib/layers.cjs"),c=require("../../hooks/useDismiss.cjs");let l=require("react");l=e.__toESM(l,1);let u=require("react/jsx-runtime"),d=require("react-dom");var f=l.createContext(0);f.displayName=`PortalDepthContext`;var p=({children:e,container:p,layer:m=`overlay`,layerId:h,disabled:g=!1,className:_,ref:v})=>{let y=i.useIsClient(),{themeClassName:b,portalContainer:x,tokenStyle:S,applyStylesToPortals:C}=a.useWaveTheme(),w=o.useDirection(),T=l.useContext(f),E=l.useContext(c.DismissLayerContext),D=l.useCallback(e=>{if(e&&E!==null)return s.registerLayerElement(E,e)},[E]),O=r.useMergedRefs(v,D),k=h===void 0?e:(0,u.jsx)(c.DismissLayerProvider,{layerId:h,children:e});if(g)return(0,u.jsx)(u.Fragment,{children:k});if(!y)return null;let A=p??x??document.body;return(0,d.createPortal)((0,u.jsx)(`div`,{ref:O,className:t.cn(n.portalClassNames.root,b,_),dir:w,"data-wave-portal":``,"data-layer":m,style:{...C?S:void 0,position:`relative`,zIndex:`calc(${s.Z_INDEX[m]} + ${T})`},children:(0,u.jsx)(f.Provider,{value:T+1,children:k})}),A)};p.displayName=`Portal`,exports.Portal=p,exports.PortalDepthContext=f;
|
|
2
2
|
//# sourceMappingURL=Portal.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Portal.cjs","names":["React","useIsClient","useWaveTheme","useDirection","DismissLayerContext","registerLayerElement","useMergedRefs","DismissLayerProvider","_Fragment","createPortal","cn","portalClassNames","Z_INDEX","require_runtime","require_cn","require_classNames","require_useMergedRefs","require_useIsClient","require_WaveProvider","require_useDirection","require_layers","require_useDismiss","react","react_jsx_runtime","react_dom","PortalDepthContext","Portal","isClient","dir","depth","parentLayerId","registerWithParent","el","wrapperRef","ref","content","layerId","children","disabled","target","container","portalContainer","themeClassName","className","layer"],"sources":["../../../src/components/portal/Portal.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createPortal } from 'react-dom';\nimport { cn } from '../../lib/cn';\nimport { portalClassNames } from '../../lib/classNames';\nimport { Z_INDEX, registerLayerElement } from '../../lib/layers';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useDirection } from '../../hooks/useDirection';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { DismissLayerContext, DismissLayerProvider } from '../../hooks/useDismiss';\nimport { useWaveTheme } from '../provider/WaveProvider';\n\n/** Props accepted by {@link Portal}. */\nexport interface PortalProps {\n /** Content rendered into the portal. */\n children: React.ReactNode;\n /** Element to render into. @default WaveProvider `portalContainer`, else `document.body` */\n container?: HTMLElement | null;\n /**\n * Stacking layer: `--wave-z-overlay` (dialogs, popovers, menus, listboxes), `--wave-z-toast`\n * (above overlays) or `--wave-z-tooltip`. The portal's nesting depth is added.\n * @default 'overlay'\n */\n layer?: 'overlay' | 'toast' | 'tooltip';\n /**\n * The dismiss layer of the content (from `useDismiss`). Provided to the children through\n * `DismissLayerProvider`, so layers opened inside become its descendants.\n */\n layerId?: string;\n /** Render the children inline instead (no portal, no wrapper). @default false */\n disabled?: boolean;\n /** Extra classes for the portal wrapper. */\n className?: string;\n /** Ref to the portal wrapper `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/**\n * Nesting depth of portals: `0` outside any portal; each `Portal` provides `depth + 1` to its\n * children. Portals add it to their z-index so a surface opened from inside another overlay always\n * stacks above it — also when both mount in the same commit.\n */\nexport const PortalDepthContext = React.createContext(0);\nPortalDepthContext.displayName = 'PortalDepthContext';\n\n/**\n * Renders its children into `document.body` (or the WaveProvider `portalContainer`), wrapped in a\n * `<div class=\"wave-portal …\">` that carries the provider's theme classes, direction and font, so\n * portaled overlays look like the rest of the themed tree. The wrapper paints no background.\n *\n * - **Client only, first commit**: nothing is rendered on the server or during hydration (no\n * mismatch); in every other client render the portal exists in the first commit, so layout\n * effects and ref callbacks of the content see connected elements.\n * - **Stacking**: `z-index: calc(<layer variable> + depth)`, where depth is the number of portals\n * it is rendered in, so a surface opened from inside another overlay always stacks above it,\n * also when both mount in the same commit.\n * - **Layers**: the wrapper is registered with the enclosing dismiss layer (the parent), so presses\n * and focus inside the portal count as inside that layer; `layerId` is provided only to the\n * children.\n *\n * Content that must exist on the server or before an overlay opens (tooltip descriptions, closed\n * listboxes) is rendered inline instead.\n */\nexport const Portal = ({\n children,\n container,\n layer = 'overlay',\n layerId,\n disabled = false,\n className,\n ref,\n}: PortalProps) => {\n const isClient = useIsClient();\n const { themeClassName, portalContainer } = useWaveTheme();\n const dir = useDirection();\n const depth = React.useContext(PortalDepthContext);\n const parentLayerId = React.useContext(DismissLayerContext);\n\n const registerWithParent = React.useCallback(\n (el: HTMLDivElement | null) => {\n if (!el || parentLayerId === null) return;\n return registerLayerElement(parentLayerId, el);\n },\n [parentLayerId],\n );\n const wrapperRef = useMergedRefs<HTMLDivElement>(ref, registerWithParent);\n\n const content =\n layerId === undefined ? (\n children\n ) : (\n <DismissLayerProvider layerId={layerId}>{children}</DismissLayerProvider>\n );\n\n if (disabled) return <>{content}</>;\n if (!isClient) return null;\n\n const target = container ?? portalContainer ?? document.body;\n\n return createPortal(\n <div\n ref={wrapperRef}\n className={cn(portalClassNames.root, themeClassName, className)}\n dir={dir}\n data-wave-portal=\"\"\n data-layer={layer}\n style={{ position: 'relative'
|
|
1
|
+
{"version":3,"file":"Portal.cjs","names":["React","useIsClient","useWaveTheme","useDirection","DismissLayerContext","registerLayerElement","useMergedRefs","DismissLayerProvider","_Fragment","createPortal","cn","portalClassNames","Z_INDEX","require_runtime","require_cn","require_classNames","require_useMergedRefs","require_useIsClient","require_WaveProvider","require_useDirection","require_layers","require_useDismiss","react","react_jsx_runtime","react_dom","PortalDepthContext","Portal","isClient","dir","depth","parentLayerId","registerWithParent","el","wrapperRef","ref","content","layerId","children","disabled","target","container","portalContainer","themeClassName","className","layer","applyStylesToPortals","tokenStyle"],"sources":["../../../src/components/portal/Portal.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createPortal } from 'react-dom';\nimport { cn } from '../../lib/cn';\nimport { portalClassNames } from '../../lib/classNames';\nimport { Z_INDEX, registerLayerElement } from '../../lib/layers';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useDirection } from '../../hooks/useDirection';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { DismissLayerContext, DismissLayerProvider } from '../../hooks/useDismiss';\nimport { useWaveTheme } from '../provider/WaveProvider';\n\n/** Props accepted by {@link Portal}. */\nexport interface PortalProps {\n /** Content rendered into the portal. */\n children: React.ReactNode;\n /** Element to render into. @default WaveProvider `portalContainer`, else `document.body` */\n container?: HTMLElement | null;\n /**\n * Stacking layer: `--wave-z-overlay` (dialogs, popovers, menus, listboxes), `--wave-z-toast`\n * (above overlays) or `--wave-z-tooltip`. The portal's nesting depth is added.\n * @default 'overlay'\n */\n layer?: 'overlay' | 'toast' | 'tooltip';\n /**\n * The dismiss layer of the content (from `useDismiss`). Provided to the children through\n * `DismissLayerProvider`, so layers opened inside become its descendants.\n */\n layerId?: string;\n /** Render the children inline instead (no portal, no wrapper). @default false */\n disabled?: boolean;\n /** Extra classes for the portal wrapper. */\n className?: string;\n /** Ref to the portal wrapper `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/**\n * Nesting depth of portals: `0` outside any portal; each `Portal` provides `depth + 1` to its\n * children. Portals add it to their z-index so a surface opened from inside another overlay always\n * stacks above it — also when both mount in the same commit.\n */\nexport const PortalDepthContext = React.createContext(0);\nPortalDepthContext.displayName = 'PortalDepthContext';\n\n/**\n * Renders its children into `document.body` (or the WaveProvider `portalContainer`), wrapped in a\n * `<div class=\"wave-portal …\">` that carries the provider's theme classes, direction and font, and\n * its token overrides (WaveProvider `tokens`, unless its `applyStylesToPortals` is `false`), so\n * portaled overlays look like the rest of the themed tree. The wrapper paints no background.\n *\n * - **Client only, first commit**: nothing is rendered on the server or during hydration (no\n * mismatch); in every other client render the portal exists in the first commit, so layout\n * effects and ref callbacks of the content see connected elements.\n * - **Stacking**: `z-index: calc(<layer variable> + depth)`, where depth is the number of portals\n * it is rendered in, so a surface opened from inside another overlay always stacks above it,\n * also when both mount in the same commit.\n * - **Layers**: the wrapper is registered with the enclosing dismiss layer (the parent), so presses\n * and focus inside the portal count as inside that layer; `layerId` is provided only to the\n * children.\n *\n * Content that must exist on the server or before an overlay opens (tooltip descriptions, closed\n * listboxes) is rendered inline instead.\n */\nexport const Portal = ({\n children,\n container,\n layer = 'overlay',\n layerId,\n disabled = false,\n className,\n ref,\n}: PortalProps) => {\n const isClient = useIsClient();\n const { themeClassName, portalContainer, tokenStyle, applyStylesToPortals } = useWaveTheme();\n const dir = useDirection();\n const depth = React.useContext(PortalDepthContext);\n const parentLayerId = React.useContext(DismissLayerContext);\n\n const registerWithParent = React.useCallback(\n (el: HTMLDivElement | null) => {\n if (!el || parentLayerId === null) return;\n return registerLayerElement(parentLayerId, el);\n },\n [parentLayerId],\n );\n const wrapperRef = useMergedRefs<HTMLDivElement>(ref, registerWithParent);\n\n const content =\n layerId === undefined ? (\n children\n ) : (\n <DismissLayerProvider layerId={layerId}>{children}</DismissLayerProvider>\n );\n\n if (disabled) return <>{content}</>;\n if (!isClient) return null;\n\n const target = container ?? portalContainer ?? document.body;\n\n return createPortal(\n <div\n ref={wrapperRef}\n className={cn(portalClassNames.root, themeClassName, className)}\n dir={dir}\n data-wave-portal=\"\"\n data-layer={layer}\n // The provider's token overrides first, so the wrapper's own two properties win.\n style={{\n ...(applyStylesToPortals ? tokenStyle : undefined),\n position: 'relative',\n zIndex: `calc(${Z_INDEX[layer]} + ${depth})`,\n }}\n >\n <PortalDepthContext.Provider value={depth + 1}>{content}</PortalDepthContext.Provider>\n </div>,\n target,\n );\n};\n\nPortal.displayName = 'Portal';\n"],"mappings":"6dAyCA,IAAayB,EAAqBzB,EAAM,cAAc,CAAC,EACvD,EAAmB,YAAc,qBAqBjC,IAAa0B,GAAU,CACrB,WACA,YACA,QAAQ,UACR,UACA,WAAW,GACX,YACA,SACiB,CACjB,IAAMC,EAAW1B,EAAAA,YAAY,EACvB,CAAE,iBAAgB,kBAAiB,aAAY,wBAAyBC,EAAAA,aAAa,EACrF0B,EAAMzB,EAAAA,aAAa,EACnB0B,EAAQ7B,EAAM,WAAWyB,CAAkB,EAC3CK,EAAgB9B,EAAM,WAAWI,EAAAA,mBAAmB,EAEpD2B,EAAqB/B,EAAM,YAC9B,GAA8B,CAC7B,GAAKgC,GAAMF,IAAkB,KAC7B,OAAOzB,EAAAA,qBAAqByB,EAAeE,CAAE,CAC/C,EACA,CAACF,CAAa,CAChB,EACMG,EAAa3B,EAAAA,cAA8B4B,EAAKH,CAAkB,EAElEI,EACJC,IAAY,IAAA,GACVC,GAEA,EAAAd,EAAA,IAAA,CAAChB,EAAAA,qBAAD,CAA+B,UAAU,UAA+B,CAAA,EAG5E,GAAI+B,EAAU,OAAO,EAAAf,EAAA,IAAA,CAAAf,EAAAA,SAAA,CAAA,SAAG2B,CAAU,CAAA,EAClC,GAAI,CAACR,EAAU,OAAO,KAEtB,IAAMY,EAASC,GAAaC,GAAmB,SAAS,KAExD,OAAA,EAAOhC,EAAAA,aAAAA,EACL,EAAAc,EAAA,IAAA,CAAC,MAAD,CACE,IAAKU,EACL,UAAWvB,EAAAA,GAAGC,EAAAA,iBAAiB,KAAM+B,EAAgBC,CAAS,EACzD,MACL,mBAAiB,GACjB,aAAYC,EAEZ,MAAO,CACL,GAAIC,EAAuBC,EAAa,IAAA,GACxC,SAAU,WACV,OAAQ,QAAQlC,EAAAA,QAAQgC,GAAO,KAAKf,EAAM,EAC5C,EAEA,UAAA,EAAAN,EAAA,IAAA,CAACE,EAAmB,SAApB,CAA6B,MAAOI,EAAQ,EAAI,SAAAM,CAAqC,CAAA,CAClF,CAAA,EACLI,CACF,CACF,EAEA,EAAO,YAAc"}
|
|
@@ -14,30 +14,31 @@ import { createPortal as p } from "react-dom";
|
|
|
14
14
|
var m = u.createContext(0);
|
|
15
15
|
m.displayName = "PortalDepthContext";
|
|
16
16
|
var h = ({ children: h, container: g, layer: _ = "overlay", layerId: v, disabled: y = !1, className: b, ref: x }) => {
|
|
17
|
-
let S = r(), { themeClassName: C, portalContainer: w } = i(),
|
|
18
|
-
if (e &&
|
|
19
|
-
}, [
|
|
17
|
+
let S = r(), { themeClassName: C, portalContainer: w, tokenStyle: T, applyStylesToPortals: E } = i(), D = a(), O = u.useContext(m), k = u.useContext(c), A = u.useCallback((e) => {
|
|
18
|
+
if (e && k !== null) return s(k, e);
|
|
19
|
+
}, [k]), j = n(x, A), M = v === void 0 ? h : /* @__PURE__ */ f(l, {
|
|
20
20
|
layerId: v,
|
|
21
21
|
children: h
|
|
22
22
|
});
|
|
23
|
-
if (y) return /* @__PURE__ */ f(d, { children:
|
|
23
|
+
if (y) return /* @__PURE__ */ f(d, { children: M });
|
|
24
24
|
if (!S) return null;
|
|
25
|
-
let
|
|
25
|
+
let N = g ?? w ?? document.body;
|
|
26
26
|
return p(/* @__PURE__ */ f("div", {
|
|
27
|
-
ref:
|
|
27
|
+
ref: j,
|
|
28
28
|
className: e(t.root, C, b),
|
|
29
|
-
dir:
|
|
29
|
+
dir: D,
|
|
30
30
|
"data-wave-portal": "",
|
|
31
31
|
"data-layer": _,
|
|
32
32
|
style: {
|
|
33
|
+
...E ? T : void 0,
|
|
33
34
|
position: "relative",
|
|
34
|
-
zIndex: `calc(${o[_]} + ${
|
|
35
|
+
zIndex: `calc(${o[_]} + ${O})`
|
|
35
36
|
},
|
|
36
37
|
children: /* @__PURE__ */ f(m.Provider, {
|
|
37
|
-
value:
|
|
38
|
-
children:
|
|
38
|
+
value: O + 1,
|
|
39
|
+
children: M
|
|
39
40
|
})
|
|
40
|
-
}),
|
|
41
|
+
}), N);
|
|
41
42
|
};
|
|
42
43
|
h.displayName = "Portal";
|
|
43
44
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Portal.mjs","names":["_Fragment","React","PortalDepthContext","Portal","isClient","useIsClient","useWaveTheme","dir","useDirection","depth","parentLayerId","DismissLayerContext","registerWithParent","el","registerLayerElement","wrapperRef","useMergedRefs","ref","content","layerId","children","DismissLayerProvider","disabled","Fragment","target","container","portalContainer","createPortal","cn","portalClassNames","themeClassName","className","layer","Z_INDEX"],"sources":["../../../src/components/portal/Portal.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createPortal } from 'react-dom';\nimport { cn } from '../../lib/cn';\nimport { portalClassNames } from '../../lib/classNames';\nimport { Z_INDEX, registerLayerElement } from '../../lib/layers';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useDirection } from '../../hooks/useDirection';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { DismissLayerContext, DismissLayerProvider } from '../../hooks/useDismiss';\nimport { useWaveTheme } from '../provider/WaveProvider';\n\n/** Props accepted by {@link Portal}. */\nexport interface PortalProps {\n /** Content rendered into the portal. */\n children: React.ReactNode;\n /** Element to render into. @default WaveProvider `portalContainer`, else `document.body` */\n container?: HTMLElement | null;\n /**\n * Stacking layer: `--wave-z-overlay` (dialogs, popovers, menus, listboxes), `--wave-z-toast`\n * (above overlays) or `--wave-z-tooltip`. The portal's nesting depth is added.\n * @default 'overlay'\n */\n layer?: 'overlay' | 'toast' | 'tooltip';\n /**\n * The dismiss layer of the content (from `useDismiss`). Provided to the children through\n * `DismissLayerProvider`, so layers opened inside become its descendants.\n */\n layerId?: string;\n /** Render the children inline instead (no portal, no wrapper). @default false */\n disabled?: boolean;\n /** Extra classes for the portal wrapper. */\n className?: string;\n /** Ref to the portal wrapper `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/**\n * Nesting depth of portals: `0` outside any portal; each `Portal` provides `depth + 1` to its\n * children. Portals add it to their z-index so a surface opened from inside another overlay always\n * stacks above it — also when both mount in the same commit.\n */\nexport const PortalDepthContext = React.createContext(0);\nPortalDepthContext.displayName = 'PortalDepthContext';\n\n/**\n * Renders its children into `document.body` (or the WaveProvider `portalContainer`), wrapped in a\n * `<div class=\"wave-portal …\">` that carries the provider's theme classes, direction and font, so\n * portaled overlays look like the rest of the themed tree. The wrapper paints no background.\n *\n * - **Client only, first commit**: nothing is rendered on the server or during hydration (no\n * mismatch); in every other client render the portal exists in the first commit, so layout\n * effects and ref callbacks of the content see connected elements.\n * - **Stacking**: `z-index: calc(<layer variable> + depth)`, where depth is the number of portals\n * it is rendered in, so a surface opened from inside another overlay always stacks above it,\n * also when both mount in the same commit.\n * - **Layers**: the wrapper is registered with the enclosing dismiss layer (the parent), so presses\n * and focus inside the portal count as inside that layer; `layerId` is provided only to the\n * children.\n *\n * Content that must exist on the server or before an overlay opens (tooltip descriptions, closed\n * listboxes) is rendered inline instead.\n */\nexport const Portal = ({\n children,\n container,\n layer = 'overlay',\n layerId,\n disabled = false,\n className,\n ref,\n}: PortalProps) => {\n const isClient = useIsClient();\n const { themeClassName, portalContainer } = useWaveTheme();\n const dir = useDirection();\n const depth = React.useContext(PortalDepthContext);\n const parentLayerId = React.useContext(DismissLayerContext);\n\n const registerWithParent = React.useCallback(\n (el: HTMLDivElement | null) => {\n if (!el || parentLayerId === null) return;\n return registerLayerElement(parentLayerId, el);\n },\n [parentLayerId],\n );\n const wrapperRef = useMergedRefs<HTMLDivElement>(ref, registerWithParent);\n\n const content =\n layerId === undefined ? (\n children\n ) : (\n <DismissLayerProvider layerId={layerId}>{children}</DismissLayerProvider>\n );\n\n if (disabled) return <>{content}</>;\n if (!isClient) return null;\n\n const target = container ?? portalContainer ?? document.body;\n\n return createPortal(\n <div\n ref={wrapperRef}\n className={cn(portalClassNames.root, themeClassName, className)}\n dir={dir}\n data-wave-portal=\"\"\n data-layer={layer}\n style={{ position: 'relative'
|
|
1
|
+
{"version":3,"file":"Portal.mjs","names":["_Fragment","React","PortalDepthContext","Portal","isClient","useIsClient","useWaveTheme","dir","useDirection","depth","parentLayerId","DismissLayerContext","registerWithParent","el","registerLayerElement","wrapperRef","useMergedRefs","ref","content","layerId","children","DismissLayerProvider","disabled","Fragment","target","container","portalContainer","createPortal","cn","portalClassNames","themeClassName","className","layer","applyStylesToPortals","tokenStyle","Z_INDEX"],"sources":["../../../src/components/portal/Portal.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createPortal } from 'react-dom';\nimport { cn } from '../../lib/cn';\nimport { portalClassNames } from '../../lib/classNames';\nimport { Z_INDEX, registerLayerElement } from '../../lib/layers';\nimport { useIsClient } from '../../hooks/useIsClient';\nimport { useDirection } from '../../hooks/useDirection';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { DismissLayerContext, DismissLayerProvider } from '../../hooks/useDismiss';\nimport { useWaveTheme } from '../provider/WaveProvider';\n\n/** Props accepted by {@link Portal}. */\nexport interface PortalProps {\n /** Content rendered into the portal. */\n children: React.ReactNode;\n /** Element to render into. @default WaveProvider `portalContainer`, else `document.body` */\n container?: HTMLElement | null;\n /**\n * Stacking layer: `--wave-z-overlay` (dialogs, popovers, menus, listboxes), `--wave-z-toast`\n * (above overlays) or `--wave-z-tooltip`. The portal's nesting depth is added.\n * @default 'overlay'\n */\n layer?: 'overlay' | 'toast' | 'tooltip';\n /**\n * The dismiss layer of the content (from `useDismiss`). Provided to the children through\n * `DismissLayerProvider`, so layers opened inside become its descendants.\n */\n layerId?: string;\n /** Render the children inline instead (no portal, no wrapper). @default false */\n disabled?: boolean;\n /** Extra classes for the portal wrapper. */\n className?: string;\n /** Ref to the portal wrapper `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/**\n * Nesting depth of portals: `0` outside any portal; each `Portal` provides `depth + 1` to its\n * children. Portals add it to their z-index so a surface opened from inside another overlay always\n * stacks above it — also when both mount in the same commit.\n */\nexport const PortalDepthContext = React.createContext(0);\nPortalDepthContext.displayName = 'PortalDepthContext';\n\n/**\n * Renders its children into `document.body` (or the WaveProvider `portalContainer`), wrapped in a\n * `<div class=\"wave-portal …\">` that carries the provider's theme classes, direction and font, and\n * its token overrides (WaveProvider `tokens`, unless its `applyStylesToPortals` is `false`), so\n * portaled overlays look like the rest of the themed tree. The wrapper paints no background.\n *\n * - **Client only, first commit**: nothing is rendered on the server or during hydration (no\n * mismatch); in every other client render the portal exists in the first commit, so layout\n * effects and ref callbacks of the content see connected elements.\n * - **Stacking**: `z-index: calc(<layer variable> + depth)`, where depth is the number of portals\n * it is rendered in, so a surface opened from inside another overlay always stacks above it,\n * also when both mount in the same commit.\n * - **Layers**: the wrapper is registered with the enclosing dismiss layer (the parent), so presses\n * and focus inside the portal count as inside that layer; `layerId` is provided only to the\n * children.\n *\n * Content that must exist on the server or before an overlay opens (tooltip descriptions, closed\n * listboxes) is rendered inline instead.\n */\nexport const Portal = ({\n children,\n container,\n layer = 'overlay',\n layerId,\n disabled = false,\n className,\n ref,\n}: PortalProps) => {\n const isClient = useIsClient();\n const { themeClassName, portalContainer, tokenStyle, applyStylesToPortals } = useWaveTheme();\n const dir = useDirection();\n const depth = React.useContext(PortalDepthContext);\n const parentLayerId = React.useContext(DismissLayerContext);\n\n const registerWithParent = React.useCallback(\n (el: HTMLDivElement | null) => {\n if (!el || parentLayerId === null) return;\n return registerLayerElement(parentLayerId, el);\n },\n [parentLayerId],\n );\n const wrapperRef = useMergedRefs<HTMLDivElement>(ref, registerWithParent);\n\n const content =\n layerId === undefined ? (\n children\n ) : (\n <DismissLayerProvider layerId={layerId}>{children}</DismissLayerProvider>\n );\n\n if (disabled) return <>{content}</>;\n if (!isClient) return null;\n\n const target = container ?? portalContainer ?? document.body;\n\n return createPortal(\n <div\n ref={wrapperRef}\n className={cn(portalClassNames.root, themeClassName, className)}\n dir={dir}\n data-wave-portal=\"\"\n data-layer={layer}\n // The provider's token overrides first, so the wrapper's own two properties win.\n style={{\n ...(applyStylesToPortals ? tokenStyle : undefined),\n position: 'relative',\n zIndex: `calc(${Z_INDEX[layer]} + ${depth})`,\n }}\n >\n <PortalDepthContext.Provider value={depth + 1}>{content}</PortalDepthContext.Provider>\n </div>,\n target,\n );\n};\n\nPortal.displayName = 'Portal';\n"],"mappings":";;;;;;;;;;;;;AAyCA,IAAaE,IAAqBD,EAAM,cAAc,CAAC;AACvD,EAAmB,cAAc;AAqBjC,IAAaE,KAAU,EACrB,aACA,cACA,WAAQ,WACR,YACA,cAAW,IACX,cACA,aACiB;CACjB,IAAMC,IAAWC,EAAY,GACvB,EAAE,mBAAgB,oBAAiB,eAAY,4BAAyBC,EAAa,GACrFC,IAAMC,EAAa,GACnBC,IAAQR,EAAM,WAAWC,CAAkB,GAC3CQ,IAAgBT,EAAM,WAAWU,CAAmB,GAEpDC,IAAqBX,EAAM,aAC9B,MAA8B;EAC7B,IAAKY,KAAMH,MAAkB,MAC7B,OAAOI,EAAqBJ,GAAeG,CAAE;CAC/C,GACA,CAACH,CAAa,CAChB,GACMK,IAAaC,EAA8BC,GAAKL,CAAkB,GAElEM,IACJC,MAAY,KAAA,IACVC,IAEA,kBAACC,GAAD;EAA+B;EAAU;CAA+B,CAAA;CAG5E,IAAIC,GAAU,OAAO,kBAAAtB,GAAA,EAAA,UAAGkB,EAAU,CAAA;CAClC,IAAI,CAACd,GAAU,OAAO;CAEtB,IAAMoB,IAASC,KAAaC,KAAmB,SAAS;CAExD,OAAOC,EACL,kBAAC,OAAD;EACE,KAAKZ;EACL,WAAWa,EAAGC,EAAiB,MAAMC,GAAgBC,CAAS;EACzD;EACL,oBAAiB;EACjB,cAAYC;EAEZ,OAAO;GACL,GAAIC,IAAuBC,IAAa,KAAA;GACxC,UAAU;GACV,QAAQ,QAAQC,EAAQH,GAAO,KAAKvB,EAAM;EAC5C;EAEA,UAAA,kBAACP,EAAmB,UAApB;GAA6B,OAAOO,IAAQ;GAAI,UAAAS;EAAqC,CAAA;CAClF,CAAA,GACLM,CACF;AACF;AAEA,EAAO,cAAc"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../lib/theme.cjs");let
|
|
1
|
+
"use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/dev.cjs"),r=require("../../lib/classNames.cjs"),i=require("../../lib/tokens.cjs"),a=require("../../lib/theme.cjs");let o=require("react");o=e.__toESM(o,1);let s=require("react/jsx-runtime");var c=o.createContext({theme:`light`,dir:`ltr`,themeClassName:``,portalContainer:null,inputDefaults:{},motion:`default`,tokens:{},tokenStyle:void 0,applyStylesToPortals:!0,hasProvider:!1});function l(e,t){let n=Object.keys(e),r=Object.keys(t);return n.length===r.length&&n.every((n,i)=>n===r[i]&&Object.is(e[n],t[n]))}function u(){return o.useContext(c)}var d=({theme:e,dir:u,portalContainer:d,inputDefaults:f,motion:p,tokens:m,applyStylesToPortals:h,children:g,className:_,style:v,ref:y,...b})=>{let x=o.useContext(c),S=e??x.theme,C=u??x.dir,w=p??x.motion,T=h??x.applyStylesToPortals,E=d===void 0?x.portalContainer:d,D=i.mergeTokenOverrides(x.tokens,m),[O,k]=o.useState(D);l(O,D)||k(D);let{style:A,invalid:j}=o.useMemo(()=>i.collectTokenStyle(O),[O]);o.useEffect(()=>{try{if(process.env.NODE_ENV!==`production`)for(let{key:e,reason:t}of j)n.warnOnce(`WaveProvider:tokens:${t}:${e}`,`WaveProvider: \`tokens\` skips \`${e}\`: `+(t===`name`?"not a token name (the CSS variable in camelCase without `--wave-`, such as `primaryForeground`).":"its value must be a finite number, or CSS text with balanced quotes, brackets and comments, without `;`, `{`, `}` or `url(`, and without `!` or a backslash outside strings and comments."))}catch{}},[j]);let M=f?.size??x.inputDefaults.size,N=f?.appearance??x.inputDefaults.appearance,P=o.useMemo(()=>{let e={};return M!==void 0&&(e.size=M),N!==void 0&&(e.appearance=N),e},[M,N]),F=a.isWaveTheme(S)?S:`light`,I=a.getThemeClassName(F);o.useEffect(()=>{a.isWaveTheme(S)||n.warnOnce(`WaveProvider:theme:${String(S)}`,`WaveProvider: unknown theme "${String(S)}". Valid themes: ${a.WAVE_THEMES.join(`, `)}. Using "light".`)},[S]);let L=o.useMemo(()=>({theme:F,dir:C,themeClassName:I,portalContainer:E,inputDefaults:P,motion:w,tokens:O,tokenStyle:A,applyStylesToPortals:T,hasProvider:!0}),[F,C,I,E,P,w,O,A,T]);return(0,s.jsx)(c.Provider,{value:L,children:(0,s.jsx)(`div`,{...b,style:A?{...A,...v}:v,ref:y,dir:C,"data-wave-theme":F,className:t.cn(r.waveProviderClassNames.root,I,`bg-background text-foreground font-wave text-body-1`,_),children:g})})};d.displayName=`WaveProvider`,exports.WaveProvider=d,exports.getThemeClassName=a.getThemeClassName,exports.useWaveTheme=u;
|
|
2
2
|
//# sourceMappingURL=WaveProvider.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WaveProvider.cjs","names":["React","isWaveTheme","getThemeClassName","WAVE_THEMES","cn","waveProviderClassNames","require_runtime","require_cn","require_dev","require_classNames","require_theme","react","react_jsx_runtime","WaveContext","useWaveTheme","WaveProvider","themeProp","dirProp","portalContainerProp","inputDefaultsProp","motionProp","rest","parent","theme","dir","motion","portalContainer","size","appearance","inputDefaults","merged","resolvedTheme","themeClassName","value","className"],"sources":["../../../src/components/provider/WaveProvider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { waveProviderClassNames } from '../../lib/classNames';\nimport { getThemeClassName, isWaveTheme, WAVE_THEMES, type WaveTheme } from '../../lib/theme';\nimport type { CoreSize, InputAppearance, MotionPreference } from '../../lib/types';\n\n// The theme helper lives in server-safe src/lib (no \"use client\"), so React Server Components can\n// call it; it stays exported from here too.\nexport { getThemeClassName } from '../../lib/theme';\nexport type { WaveTheme } from '../../lib/theme';\n\n/** Text direction for bidirectional layout support. */\nexport type WaveDir = 'ltr' | 'rtl';\n\n/** Default size and appearance of the text inputs and pickers in a subtree. */\nexport interface InputDefaults {\n /** Default `size` of Input, Textarea, Select, SearchBox, SpinButton and the pickers. */\n size?: CoreSize;\n /** Default `appearance` of the same controls. */\n appearance?: InputAppearance;\n}\n\n/** Props accepted by {@link WaveProvider}. */\nexport interface WaveProviderProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Visual theme applied to the subtree. Providers can be nested in any order: each root declares\n * its theme's tokens, so a light panel inside a dark app (and the reverse) renders correctly.\n * @default the enclosing WaveProvider's theme, else 'light'\n */\n theme?: WaveTheme;\n /**\n * Text direction for the subtree (also read by portaled overlays and keyboard navigation).\n * @default the enclosing WaveProvider's direction, else 'ltr'\n */\n dir?: WaveDir;\n /**\n * Element that portaled overlays (dialogs, popovers, menus, toasts) render into. `null` renders\n * them into `document.body`, also inside a provider that sets a container.\n * @default the enclosing WaveProvider's container, else document.body\n */\n portalContainer?: HTMLElement | null;\n /**\n * Default `size` and `appearance` of the text inputs and pickers in the subtree (Input,\n * Textarea, Select, SearchBox, SpinButton, Combobox, Dropdown, DatePicker, TimePicker,\n * TagPicker). Their own props and a Field's `size` win. A nested provider merges its keys over\n * the enclosing provider's; an omitted key is inherited.\n * @default the enclosing WaveProvider's defaults, else none (`medium`, `outline`)\n */\n inputDefaults?: InputDefaults;\n /**\n * Motion of the subtree. `'none'` makes every motion slot below it (a part's `motion`,\n * `backdropMotion`, `expandIconMotion` or `indeterminateMotion` left unset) behave as `null`,\n * which also stops the slide of TabList's selection line, and turns off Carousel's slide or fade\n * (a Carousel without its own `motion`) and the reveal of Nav item actions; a part's own value\n * still wins. Transitions that are no motion slot keep running: the turn of Accordion's and\n * Tree's chevrons, colour changes on hover and press. Fluent's `MotionBehaviourProvider` with `'skip'` is the counterpart, which\n * `@fluentui/react-components` does not export. A nested provider inherits an omitted `motion`\n * like `theme`.\n * @default the enclosing WaveProvider's motion, else 'default'\n */\n motion?: MotionPreference;\n /** Content rendered inside the themed container. */\n children: React.ReactNode;\n /** Ref to the themed root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Value provided by {@link WaveProvider} and returned by {@link useWaveTheme}. */\nexport interface WaveContextValue {\n /** The active theme. */\n theme: WaveTheme;\n /** The active text direction. */\n dir: WaveDir;\n /**\n * Theme classes of the nearest provider (see {@link getThemeClassName}); portals put them on\n * their wrapper so overlays inherit the theme. `''` outside a provider (the page's own theme).\n */\n themeClassName: string;\n /** Element portaled overlays render into; `null` means `document.body`. */\n portalContainer: HTMLElement | null;\n /** The merged input defaults of the providers above (`{}` outside a provider). */\n inputDefaults: InputDefaults;\n /** The motion preference of the providers above (`'default'` outside a provider). */\n motion: MotionPreference;\n /** `false` when no {@link WaveProvider} is above the caller (the defaults are returned). */\n hasProvider: boolean;\n}\n\nconst DEFAULT_CONTEXT: WaveContextValue = {\n theme: 'light',\n dir: 'ltr',\n themeClassName: '',\n portalContainer: null,\n inputDefaults: {},\n motion: 'default',\n hasProvider: false,\n};\n\nconst WaveContext = React.createContext<WaveContextValue>(DEFAULT_CONTEXT);\n\n/**\n * Reads the theme, direction, theme classes and portal container of the nearest\n * {@link WaveProvider}. Outside a provider it returns light/ltr defaults with `hasProvider: false`.\n *\n * @returns The {@link WaveContextValue} of the nearest provider.\n */\nexport function useWaveTheme(): WaveContextValue {\n return React.useContext(WaveContext);\n}\n\n/**\n * Applies Wave theming to an application or subtree.\n *\n * - Renders a `<div class=\"wave-root wave-<theme>\">` that declares the theme's tokens and paints\n * the themed background, text colour and font (`bg-background text-foreground font-wave\n * text-body-1`; a `className` passed by you wins). The precompiled `./styles` entry scopes its base\n * styles and native-element reset to this root, so it is required there.\n * - Sets `dir` and `data-wave-theme` on the root. An unknown `theme` value (from untyped code)\n * falls back to `'light'` — classes, `data-wave-theme` and the context value — and warns once in\n * development.\n * - Provides theme, direction, theme classes and the portal container through context, so\n * portaled overlays render with the same theme and direction.\n * - Nests: a provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the\n * enclosing provider, so `<WaveProvider theme=\"light\">` inside an RTL app is a light panel that\n * stays right-to-left and keeps the app's portal container. At the top level the defaults are\n * `'light'`, `'ltr'` and `document.body`.\n * - `inputDefaults` nests differently: a nested provider's defined keys override the enclosing\n * provider's, and an omitted key is inherited, so setting only `appearance` in a nested provider\n * keeps the enclosing provider's `size`.\n * - `motion=\"none\"` turns the default motion of every animated part below it off (its motion\n * slots, Carousel's slide, TabList's sliding line, the reveal of Nav item actions; a part's own\n * `motion` still wins), while transitions without a slot keep running (Accordion's and Tree's\n * chevron turns, colour changes); a nested provider inherits an omitted `motion` like `theme`.\n */\nexport const WaveProvider = ({\n theme: themeProp,\n dir: dirProp,\n portalContainer: portalContainerProp,\n inputDefaults: inputDefaultsProp,\n motion: motionProp,\n children,\n className,\n ref,\n ...rest\n}: WaveProviderProps) => {\n // Omitted props come from the enclosing provider; outside one, DEFAULT_CONTEXT supplies the\n // top-level defaults. An explicit `portalContainer={null}` means document.body, so only\n // `undefined` inherits.\n const parent = React.useContext(WaveContext);\n const theme = themeProp ?? parent.theme;\n const dir = dirProp ?? parent.dir;\n const motion = motionProp ?? parent.motion;\n const portalContainer =\n portalContainerProp === undefined ? parent.portalContainer : portalContainerProp;\n\n // Defined keys override the enclosing provider's; the merged object keeps its identity while the\n // two values are equal, so an inline `inputDefaults={{ … }}` does not re-render every input.\n const size = inputDefaultsProp?.size ?? parent.inputDefaults.size;\n const appearance = inputDefaultsProp?.appearance ?? parent.inputDefaults.appearance;\n const inputDefaults = React.useMemo<InputDefaults>(() => {\n const merged: InputDefaults = {};\n if (size !== undefined) merged.size = size;\n if (appearance !== undefined) merged.appearance = appearance;\n return merged;\n }, [size, appearance]);\n\n // An unknown value (untyped callers) renders, and is reported, as the light theme.\n const resolvedTheme: WaveTheme = isWaveTheme(theme) ? theme : 'light';\n const themeClassName = getThemeClassName(resolvedTheme);\n\n React.useEffect(() => {\n if (!isWaveTheme(theme)) {\n warnOnce(\n `WaveProvider:theme:${String(theme)}`,\n `WaveProvider: unknown theme \"${String(theme)}\". Valid themes: ${WAVE_THEMES.join(', ')}. Using \"light\".`,\n );\n }\n }, [theme]);\n\n const value = React.useMemo<WaveContextValue>(\n () => ({\n theme: resolvedTheme,\n dir,\n themeClassName,\n portalContainer,\n inputDefaults,\n motion,\n hasProvider: true,\n }),\n [resolvedTheme, dir, themeClassName, portalContainer, inputDefaults, motion],\n );\n\n return (\n <WaveContext.Provider value={value}>\n <div\n {...rest}\n ref={ref}\n dir={dir}\n data-wave-theme={resolvedTheme}\n className={cn(\n waveProviderClassNames.root,\n themeClassName,\n 'bg-background text-foreground font-wave text-body-1',\n className,\n )}\n >\n {children}\n </div>\n </WaveContext.Provider>\n );\n};\n\nWaveProvider.displayName = 'WaveProvider';\n"],"mappings":"oRAmGA,IAAMa,EAAcb,EAAM,cAAgC,CATxD,MAAO,QACP,IAAK,MACL,eAAgB,GAChB,gBAAiB,KACjB,cAAe,CAAC,EAChB,OAAQ,UACR,YAAa,EAG2C,CAAe,EAQzE,SAAgBc,GAAiC,CAC/C,OAAOd,EAAM,WAAWa,CAAW,CACrC,CA0BA,IAAaE,GAAgB,CAC3B,MAAOC,EACP,IAAKC,EACL,gBAAiBC,EACjB,cAAeC,EACf,OAAQC,EACR,WACA,YACA,MACA,GAAGC,KACoB,CAIvB,IAAMC,EAAStB,EAAM,WAAWa,CAAW,EACrCU,EAAQP,GAAaM,EAAO,MAC5BE,EAAMP,GAAWK,EAAO,IACxBG,EAASL,GAAcE,EAAO,OAC9BI,EACJR,IAAwB,IAAA,GAAYI,EAAO,gBAAkBJ,EAIzDS,EAAOR,GAAmB,MAAQG,EAAO,cAAc,KACvDM,EAAaT,GAAmB,YAAcG,EAAO,cAAc,WACnEO,EAAgB7B,EAAM,YAA6B,CACvD,IAAM8B,EAAwB,CAAC,EAG/B,OAFIH,IAAS,IAAA,KAAW,EAAO,KAAOA,GAClCC,IAAe,IAAA,KAAW,EAAO,WAAaA,GAC3CE,CACT,EAAG,CAACH,EAAMC,CAAU,CAAC,EAGfG,EAA2B9B,EAAAA,YAAYsB,CAAK,EAAIA,EAAQ,QACxDS,EAAiB9B,EAAAA,kBAAkB6B,CAAa,EAEtDpB,EAAM,cAAgB,CACfV,EAAAA,YAAYsB,CAAK,GACpBf,EAAA,SACE,sBAAsB,OAAOe,CAAK,IAClC,gCAAgC,OAAOA,CAAK,EAAE,mBAAmBpB,EAAAA,YAAY,KAAK,IAAI,EAAE,iBAC1F,CAEJ,EAAG,CAACoB,CAAK,CAAC,EAEV,IAAMU,EAAQjC,EAAM,aACX,CACL,MAAO+B,EACP,MACA,iBACA,kBACA,gBACA,SACA,YAAa,EACf,GACA,CAACA,EAAeP,EAAKQ,EAAgBN,EAAiBG,EAAeJ,CAAM,CAC7E,EAEA,OACE,EAAAb,EAAA,IAAA,CAACC,EAAY,SAAb,CAA6B,QAC3B,UAAA,EAAAD,EAAA,IAAA,CAAC,MAAD,CACE,GAAIS,EACC,MACA,MACL,kBAAiBU,EACjB,UAAW3B,EAAAA,GACTC,EAAAA,uBAAuB,KACvB2B,EACA,sDACAE,CACF,EAEC,UACE,CAAA,CACe,CAAA,CAE1B,EAEA,EAAa,YAAc"}
|
|
1
|
+
{"version":3,"file":"WaveProvider.cjs","names":["React","mergeTokenOverrides","collectTokenStyle","isWaveTheme","getThemeClassName","WAVE_THEMES","cn","waveProviderClassNames","require_runtime","require_cn","require_dev","require_classNames","require_tokens","require_theme","react","react_jsx_runtime","WaveContext","sameTokens","keys","a","others","b","key","index","useWaveTheme","WaveProvider","themeProp","dirProp","portalContainerProp","inputDefaultsProp","motionProp","tokensProp","applyStylesToPortalsProp","rest","parent","theme","dir","motion","applyStylesToPortals","portalContainer","mergedTokens","tokens","setTokens","tokenStyle","invalid","reason","size","appearance","inputDefaults","merged","resolvedTheme","themeClassName","value","style","className"],"sources":["../../../src/components/provider/WaveProvider.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { waveProviderClassNames } from '../../lib/classNames';\nimport { getThemeClassName, isWaveTheme, WAVE_THEMES, type WaveTheme } from '../../lib/theme';\nimport {\n collectTokenStyle,\n mergeTokenOverrides,\n type TokenStyle,\n type WaveTokenOverrides,\n} from '../../lib/tokens';\nimport type { CoreSize, InputAppearance, MotionPreference } from '../../lib/types';\n\n// The theme helper lives in server-safe src/lib (no \"use client\"), so React Server Components can\n// call it; it stays exported from here too.\nexport { getThemeClassName } from '../../lib/theme';\nexport type { WaveTheme } from '../../lib/theme';\n\n/** Text direction for bidirectional layout support. */\nexport type WaveDir = 'ltr' | 'rtl';\n\n/** Default size and appearance of the text inputs and pickers in a subtree. */\nexport interface InputDefaults {\n /** Default `size` of Input, Textarea, Select, SearchBox, SpinButton and the pickers. */\n size?: CoreSize;\n /** Default `appearance` of the same controls. */\n appearance?: InputAppearance;\n}\n\n/** Props accepted by {@link WaveProvider}. */\nexport interface WaveProviderProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Visual theme applied to the subtree. Providers can be nested in any order: each root declares\n * its theme's tokens, so a light panel inside a dark app (and the reverse) renders correctly.\n * @default the enclosing WaveProvider's theme, else 'light'\n */\n theme?: WaveTheme;\n /**\n * Text direction for the subtree (also read by portaled overlays and keyboard navigation).\n * @default the enclosing WaveProvider's direction, else 'ltr'\n */\n dir?: WaveDir;\n /**\n * Element that portaled overlays (dialogs, popovers, menus, toasts) render into. `null` renders\n * them into `document.body`, also inside a provider that sets a container.\n * @default the enclosing WaveProvider's container, else document.body\n */\n portalContainer?: HTMLElement | null;\n /**\n * Default `size` and `appearance` of the text inputs and pickers in the subtree (Input,\n * Textarea, Select, SearchBox, SpinButton, Combobox, Dropdown, DatePicker, TimePicker,\n * TagPicker). Their own props and a Field's `size` win. A nested provider merges its keys over\n * the enclosing provider's; an omitted key is inherited.\n * @default the enclosing WaveProvider's defaults, else none (`medium`, `outline`)\n */\n inputDefaults?: InputDefaults;\n /**\n * Motion of the subtree. `'none'` makes every motion slot below it (a part's `motion`,\n * `backdropMotion`, `expandIconMotion` or `indeterminateMotion` left unset) behave as `null`,\n * which also stops the slide of TabList's selection line, and turns off Carousel's slide or fade\n * (a Carousel without its own `motion`) and the reveal of Nav item actions; a part's own value\n * still wins. Transitions that are no motion slot keep running: the turn of Accordion's and\n * Tree's chevrons, colour changes on hover and press. Fluent's `MotionBehaviourProvider` with `'skip'` is the counterpart, which\n * `@fluentui/react-components` does not export. A nested provider inherits an omitted `motion`\n * like `theme`.\n * @default the enclosing WaveProvider's motion, else 'default'\n */\n motion?: MotionPreference;\n /**\n * Design token overrides for the subtree: CSS values by token name, the CSS variable in camelCase\n * without its `--wave-` prefix (`brand80` for `--wave-brand-80`, `primaryForeground` for\n * `--wave-primary-foreground`; a number is written as its string). `theme` still names one of\n * the three themes, and the overrides apply over it as inline custom properties on the root and\n * on every portal of the subtree (see `applyStylesToPortals`), so its dialogs, menus, popovers,\n * tooltips and toasts take them too. Fluent's `theme` prop with a partial theme is the\n * counterpart.\n *\n * A nested provider merges its overrides over the enclosing provider's, key by key: an omitted\n * `tokens` inherits them all, and a key whose value is `undefined` inherits that key. `null`\n * clears the inherited overrides of the tokens the theme declares (the semantic colours, shadows,\n * palette) and of the subtree's overlays; theme-independent tokens (the brand and grey ramps,\n * font families, stacking order, motion durations and curves) of an enclosing provider still\n * reach the subtree's own elements through the CSS cascade, and the theme's colours built on the\n * brand ramp follow them there. A nested provider that sets another `theme` inherits the\n * overrides too, so set an override of a semantic colour (`primary`) in the provider that sets\n * the theme it was chosen for. A whole brand ramp is better made with `createBrandTokens` than\n * written by hand.\n * A key that is not a camelCase identifier is skipped with a development warning, and so is a\n * value unless it is a finite number or CSS text without `;`, `{`, `}` or `url(` whose quotes,\n * parentheses, square brackets and comments are balanced, with no `!` or backslash outside\n * strings and comments; a well-formed name is not checked against the list of tokens\n * (TypeScript refuses a misspelled one). In the server-rendered `style` attribute a refused\n * character could add a declaration of its own, and an open string, comment or bracket, or an\n * escape that spells `url(` or takes the semicolon after the value, would swallow the\n * declarations after it (though not in the client), while `!important` would be a priority the\n * client refuses. A value is trimmed as React's server renderer trims it, so the values that pass\n * are written and read alike by the server and the client.\n *\n * The overrides are rendered on the server too. Under a Content Security Policy that blocks\n * inline style attributes (`style-src` without `'unsafe-inline'`), the server-rendered overrides\n * do not apply and hydration does not apply them again: ship them in a stylesheet there, and pass\n * no `tokens` (nor `getTokenStyle`), since the overlays and the subtrees rendered in the client\n * would still take them and leave the page half branded.\n *\n * @example\n * <WaveProvider tokens={{ fontFamily: 'Inter, sans-serif', zOverlay: 2000 }}>\n * @default the enclosing WaveProvider's overrides, else none\n */\n tokens?: WaveTokenOverrides | null;\n /**\n * Whether the portals of the subtree (dialogs, popovers, menus, tooltips, toasts) carry the\n * overrides of `tokens`. `false` leaves them out: an overlay then shows its theme's own values\n * for the tokens the theme declares, and keeps the surrounding cascade (that of the element it\n * renders into) for the theme-independent ones. The theme classes and the direction are set on\n * every portal either way, so an overlay of a dark provider is still dark. Fluent's\n * `applyStylesToPortals` is the counterpart; unlike Fluent's, a nested provider inherits an\n * omitted value, like `theme`.\n * @default the enclosing WaveProvider's value, else true\n */\n applyStylesToPortals?: boolean;\n /** Content rendered inside the themed container. */\n children: React.ReactNode;\n /** Ref to the themed root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Value provided by {@link WaveProvider} and returned by {@link useWaveTheme}. */\nexport interface WaveContextValue {\n /** The active theme. */\n theme: WaveTheme;\n /** The active text direction. */\n dir: WaveDir;\n /**\n * Theme classes of the nearest provider (see {@link getThemeClassName}); portals put them on\n * their wrapper so overlays inherit the theme. `''` outside a provider (the page's own theme).\n */\n themeClassName: string;\n /** Element portaled overlays render into; `null` means `document.body`. */\n portalContainer: HTMLElement | null;\n /** The merged input defaults of the providers above (`{}` outside a provider). */\n inputDefaults: InputDefaults;\n /** The motion preference of the providers above (`'default'` outside a provider). */\n motion: MotionPreference;\n /**\n * The merged token overrides of the providers above (`{}` outside a provider), as given: an\n * entry skipped for a malformed key or value stays in it, and only `tokenStyle` leaves it out.\n */\n tokens: WaveTokenOverrides;\n /**\n * The inline style that applies `tokens`, as the provider's root and, unless\n * `applyStylesToPortals` is `false`, its portals carry it; `undefined` when there is nothing to\n * set (also outside a provider).\n */\n tokenStyle: TokenStyle | undefined;\n /** Whether portals carry `tokenStyle` (`true` outside a provider). */\n applyStylesToPortals: boolean;\n /** `false` when no {@link WaveProvider} is above the caller (the defaults are returned). */\n hasProvider: boolean;\n}\n\nconst DEFAULT_CONTEXT: WaveContextValue = {\n theme: 'light',\n dir: 'ltr',\n themeClassName: '',\n portalContainer: null,\n inputDefaults: {},\n motion: 'default',\n tokens: {},\n tokenStyle: undefined,\n applyStylesToPortals: true,\n hasProvider: false,\n};\n\nconst WaveContext = React.createContext<WaveContextValue>(DEFAULT_CONTEXT);\n\n/** Whether two override maps have the same keys in the same order, with the same values. */\nfunction sameTokens(a: WaveTokenOverrides, b: WaveTokenOverrides): boolean {\n const keys = Object.keys(a) as (keyof WaveTokenOverrides)[];\n const others = Object.keys(b);\n return (\n keys.length === others.length &&\n keys.every((key, index) => key === others[index] && Object.is(a[key], b[key]))\n );\n}\n\n/**\n * Reads the theme, direction, theme classes, portal container and token overrides of the nearest\n * {@link WaveProvider}. Outside a provider it returns light/ltr defaults with `hasProvider: false`.\n *\n * @returns The {@link WaveContextValue} of the nearest provider.\n */\nexport function useWaveTheme(): WaveContextValue {\n return React.useContext(WaveContext);\n}\n\n/**\n * Applies Wave theming to an application or subtree.\n *\n * - Renders a `<div class=\"wave-root wave-<theme>\">` that declares the theme's tokens and paints\n * the themed background, text colour and font (`bg-background text-foreground font-wave\n * text-body-1`; a `className` passed by you wins). The precompiled `./styles` entry scopes its base\n * styles and native-element reset to this root, so it is required there.\n * - Sets `dir` and `data-wave-theme` on the root. An unknown `theme` value (from untyped code)\n * falls back to `'light'` — classes, `data-wave-theme` and the context value — and warns once in\n * development.\n * - Provides theme, direction, theme classes and the portal container through context, so\n * portaled overlays render with the same theme and direction.\n * - Nests: a provider inherits every prop it omits (`theme`, `dir`, `portalContainer`) from the\n * enclosing provider, so `<WaveProvider theme=\"light\">` inside an RTL app is a light panel that\n * stays right-to-left and keeps the app's portal container. At the top level the defaults are\n * `'light'`, `'ltr'` and `document.body`.\n * - `inputDefaults` nests differently: a nested provider's defined keys override the enclosing\n * provider's, and an omitted key is inherited, so setting only `appearance` in a nested provider\n * keeps the enclosing provider's `size`.\n * - `motion=\"none\"` turns the default motion of every animated part below it off (its motion\n * slots, Carousel's slide, TabList's sliding line, the reveal of Nav item actions; a part's own\n * `motion` still wins), while transitions without a slot keep running (Accordion's and Tree's\n * chevron turns, colour changes); a nested provider inherits an omitted `motion` like `theme`.\n * - `tokens` overrides design tokens in the subtree with inline custom properties on the root\n * (rendered on the server too) and on every portal of the subtree, unless\n * `applyStylesToPortals` is `false`. A nested provider merges its overrides over the enclosing\n * provider's key by key, also when it sets another `theme`; `tokens={null}` clears them for the\n * tokens the theme declares and for the overlays, while the theme-independent ones of an\n * enclosing provider still reach the subtree through the CSS cascade. An override whose key is\n * not a camelCase identifier is skipped with a development warning, and so is a value unless it\n * is a finite number or CSS text without `;`, `{`, `}` or `url(` with balanced quotes,\n * parentheses, square brackets and comments and no `!` or backslash outside strings and\n * comments; a value is trimmed as React's server renderer trims it, so the server and the client\n * write the same overrides. Without overrides the root renders no `style` attribute unless you\n * pass `style`.\n */\nexport const WaveProvider = ({\n theme: themeProp,\n dir: dirProp,\n portalContainer: portalContainerProp,\n inputDefaults: inputDefaultsProp,\n motion: motionProp,\n tokens: tokensProp,\n applyStylesToPortals: applyStylesToPortalsProp,\n children,\n className,\n style,\n ref,\n ...rest\n}: WaveProviderProps) => {\n // Omitted props come from the enclosing provider; outside one, DEFAULT_CONTEXT supplies the\n // top-level defaults. An explicit `portalContainer={null}` means document.body, so only\n // `undefined` inherits.\n const parent = React.useContext(WaveContext);\n const theme = themeProp ?? parent.theme;\n const dir = dirProp ?? parent.dir;\n const motion = motionProp ?? parent.motion;\n const applyStylesToPortals = applyStylesToPortalsProp ?? parent.applyStylesToPortals;\n const portalContainer =\n portalContainerProp === undefined ? parent.portalContainer : portalContainerProp;\n\n // The own overrides merged over the enclosing provider's. The merged map keeps its identity while\n // its entries are equal (a previous-value state, C-HOOKS), so an inline `tokens={{ … }}` neither\n // re-renders the consumers nor rebuilds the style.\n const mergedTokens = mergeTokenOverrides(parent.tokens, tokensProp);\n const [tokens, setTokens] = React.useState(mergedTokens);\n if (!sameTokens(tokens, mergedTokens)) setTokens(mergedTokens);\n // Collected without warnings during render; the skipped entries are reported from an effect.\n const { style: tokenStyle, invalid } = React.useMemo(() => collectTokenStyle(tokens), [tokens]);\n\n React.useEffect(() => {\n // The mode is read here as `lib/dev` reads it, written out so a bundler replaces it: a\n // production bundle then drops the warnings and their text. Without a bundler and without\n // `process` (an unbundled import in a browser), the read throws and the warnings are skipped.\n try {\n if (process.env.NODE_ENV !== 'production') {\n for (const { key, reason } of invalid) {\n warnOnce(\n `WaveProvider:tokens:${reason}:${key}`,\n `WaveProvider: \\`tokens\\` skips \\`${key}\\`: ` +\n (reason === 'name'\n ? 'not a token name (the CSS variable in camelCase without `--wave-`, such as `primaryForeground`).'\n : 'its value must be a finite number, or CSS text with balanced quotes, brackets and comments, without `;`, `{`, `}` or `url(`, and without `!` or a backslash outside strings and comments.'),\n );\n }\n }\n } catch {\n // No `process`: see above.\n }\n }, [invalid]);\n\n // Defined keys override the enclosing provider's; the merged object keeps its identity while the\n // two values are equal, so an inline `inputDefaults={{ … }}` does not re-render every input.\n const size = inputDefaultsProp?.size ?? parent.inputDefaults.size;\n const appearance = inputDefaultsProp?.appearance ?? parent.inputDefaults.appearance;\n const inputDefaults = React.useMemo<InputDefaults>(() => {\n const merged: InputDefaults = {};\n if (size !== undefined) merged.size = size;\n if (appearance !== undefined) merged.appearance = appearance;\n return merged;\n }, [size, appearance]);\n\n // An unknown value (untyped callers) renders, and is reported, as the light theme.\n const resolvedTheme: WaveTheme = isWaveTheme(theme) ? theme : 'light';\n const themeClassName = getThemeClassName(resolvedTheme);\n\n React.useEffect(() => {\n if (!isWaveTheme(theme)) {\n warnOnce(\n `WaveProvider:theme:${String(theme)}`,\n `WaveProvider: unknown theme \"${String(theme)}\". Valid themes: ${WAVE_THEMES.join(', ')}. Using \"light\".`,\n );\n }\n }, [theme]);\n\n const value = React.useMemo<WaveContextValue>(\n () => ({\n theme: resolvedTheme,\n dir,\n themeClassName,\n portalContainer,\n inputDefaults,\n motion,\n tokens,\n tokenStyle,\n applyStylesToPortals,\n hasProvider: true,\n }),\n [\n resolvedTheme,\n dir,\n themeClassName,\n portalContainer,\n inputDefaults,\n motion,\n tokens,\n tokenStyle,\n applyStylesToPortals,\n ],\n );\n\n return (\n <WaveContext.Provider value={value}>\n <div\n {...rest}\n // The merged overrides (a nested root re-declares what its theme class would shadow), then\n // the consumer's `style`, which wins. Without overrides the consumer's `style` as given, so\n // the root has no style attribute unless the consumer passes one.\n style={tokenStyle ? { ...tokenStyle, ...style } : style}\n ref={ref}\n dir={dir}\n data-wave-theme={resolvedTheme}\n className={cn(\n waveProviderClassNames.root,\n themeClassName,\n 'bg-background text-foreground font-wave text-body-1',\n className,\n )}\n >\n {children}\n </div>\n </WaveContext.Provider>\n );\n};\n\nWaveProvider.displayName = 'WaveProvider';\n"],"mappings":"sTA6KA,IAAMgB,EAAchB,EAAM,cAAgC,CAZxD,MAAO,QACP,IAAK,MACL,eAAgB,GAChB,gBAAiB,KACjB,cAAe,CAAC,EAChB,OAAQ,UACR,OAAQ,CAAC,EACT,WAAY,IAAA,GACZ,qBAAsB,GACtB,YAAa,EAG2C,CAAe,EAGzE,SAASiB,EAAW,EAAuB,EAAgC,CACzE,IAAMC,EAAO,OAAO,KAAKC,CAAC,EACpBC,EAAS,OAAO,KAAKC,CAAC,EAC5B,OACEH,EAAK,SAAWE,EAAO,QACvBF,EAAK,OAAO,EAAK,IAAUI,IAAQF,EAAOG,IAAU,OAAO,GAAGJ,EAAEG,GAAMD,EAAEC,EAAI,CAAC,CAEjF,CAQA,SAAgBE,GAAiC,CAC/C,OAAOxB,EAAM,WAAWgB,CAAW,CACrC,CAsCA,IAAaS,GAAgB,CAC3B,MAAOC,EACP,IAAKC,EACL,gBAAiBC,EACjB,cAAeC,EACf,OAAQC,EACR,OAAQC,EACR,qBAAsBC,EACtB,WACA,YACA,QACA,MACA,GAAGC,KACoB,CAIvB,IAAMC,EAASlC,EAAM,WAAWgB,CAAW,EACrCmB,EAAQT,GAAaQ,EAAO,MAC5BE,EAAMT,GAAWO,EAAO,IACxBG,EAASP,GAAcI,EAAO,OAC9BI,EAAuBN,GAA4BE,EAAO,qBAC1DK,EACJX,IAAwB,IAAA,GAAYM,EAAO,gBAAkBN,EAKzDY,EAAevC,EAAAA,oBAAoBiC,EAAO,OAAQH,CAAU,EAC5D,CAACU,EAAQC,GAAa1C,EAAM,SAASwC,CAAY,EAClDvB,EAAWwB,EAAQD,CAAY,GAAGE,EAAUF,CAAY,EAE7D,GAAM,CAAE,MAAOG,EAAY,WAAY3C,EAAM,YAAcE,EAAAA,kBAAkBuC,CAAM,EAAG,CAACA,CAAM,CAAC,EAE9F3B,EAAM,cAAgB,CAIpB,GAAI,CACF,GAAA,QAAA,IAAA,WAA6B,aAC3B,IAAK,GAAM,CAAE,MAAK,YAAY8B,EAC5BlC,EAAA,SACE,uBAAuBmC,EAAO,GAAGvB,IACjC,oCAAoCA,EAAI,OACrCuB,IAAW,OACR,mGACA,4LACR,CAGN,MAAQ,CAER,CACF,EAAG,CAACD,CAAO,CAAC,EAIZ,IAAME,EAAOjB,GAAmB,MAAQK,EAAO,cAAc,KACvDa,EAAalB,GAAmB,YAAcK,EAAO,cAAc,WACnEc,EAAgBhD,EAAM,YAA6B,CACvD,IAAMiD,EAAwB,CAAC,EAG/B,OAFIH,IAAS,IAAA,KAAW,EAAO,KAAOA,GAClCC,IAAe,IAAA,KAAW,EAAO,WAAaA,GAC3CE,CACT,EAAG,CAACH,EAAMC,CAAU,CAAC,EAGfG,EAA2B/C,EAAAA,YAAYgC,CAAK,EAAIA,EAAQ,QACxDgB,EAAiB/C,EAAAA,kBAAkB8C,CAAa,EAEtDpC,EAAM,cAAgB,CACfX,EAAAA,YAAYgC,CAAK,GACpBzB,EAAA,SACE,sBAAsB,OAAOyB,CAAK,IAClC,gCAAgC,OAAOA,CAAK,EAAE,mBAAmB9B,EAAAA,YAAY,KAAK,IAAI,EAAE,iBAC1F,CAEJ,EAAG,CAAC8B,CAAK,CAAC,EAEV,IAAMiB,EAAQpD,EAAM,aACX,CACL,MAAOkD,EACP,MACA,iBACA,kBACA,gBACA,SACA,SACA,aACA,uBACA,YAAa,EACf,GACA,CACEA,EACAd,EACAe,EACAZ,EACAS,EACAX,EACAI,EACAE,EACAL,CACF,CACF,EAEA,OACE,EAAAvB,EAAA,IAAA,CAACC,EAAY,SAAb,CAA6B,QAC3B,UAAA,EAAAD,EAAA,IAAA,CAAC,MAAD,CACE,GAAIkB,EAIJ,MAAOU,EAAa,CAAE,GAAGA,EAAY,GAAGU,CAAM,EAAIA,EAC7C,MACA,MACL,kBAAiBH,EACjB,UAAW5C,EAAAA,GACTC,EAAAA,uBAAuB,KACvB4C,EACA,sDACAG,CACF,EAEC,UACE,CAAA,CACe,CAAA,CAE1B,EAEA,EAAa,YAAc"}
|