@mortenbrudvik/waveui 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +60 -11
  2. package/dist/components/data-display/Avatar.cjs +1 -1
  3. package/dist/components/data-display/Avatar.cjs.map +1 -1
  4. package/dist/components/data-display/Avatar.colors.cjs +2 -0
  5. package/dist/components/data-display/Avatar.colors.cjs.map +1 -0
  6. package/dist/components/data-display/Avatar.colors.mjs +45 -0
  7. package/dist/components/data-display/Avatar.colors.mjs.map +1 -0
  8. package/dist/components/data-display/Avatar.context.cjs +1 -1
  9. package/dist/components/data-display/Avatar.context.cjs.map +1 -1
  10. package/dist/components/data-display/Avatar.context.mjs +4 -2
  11. package/dist/components/data-display/Avatar.context.mjs.map +1 -1
  12. package/dist/components/data-display/Avatar.mjs +103 -96
  13. package/dist/components/data-display/Avatar.mjs.map +1 -1
  14. package/dist/components/data-display/Avatar.sizes.cjs +2 -0
  15. package/dist/components/data-display/Avatar.sizes.cjs.map +1 -0
  16. package/dist/components/data-display/Avatar.sizes.mjs +41 -0
  17. package/dist/components/data-display/Avatar.sizes.mjs.map +1 -0
  18. package/dist/components/data-display/AvatarGroup.cjs +1 -1
  19. package/dist/components/data-display/AvatarGroup.cjs.map +1 -1
  20. package/dist/components/data-display/AvatarGroup.context.cjs +2 -0
  21. package/dist/components/data-display/AvatarGroup.context.cjs.map +1 -0
  22. package/dist/components/data-display/AvatarGroup.context.mjs +9 -0
  23. package/dist/components/data-display/AvatarGroup.context.mjs.map +1 -0
  24. package/dist/components/data-display/AvatarGroup.mjs +65 -122
  25. package/dist/components/data-display/AvatarGroup.mjs.map +1 -1
  26. package/dist/components/data-display/AvatarGroup.partition.cjs +2 -0
  27. package/dist/components/data-display/AvatarGroup.partition.cjs.map +1 -0
  28. package/dist/components/data-display/AvatarGroup.partition.mjs +14 -0
  29. package/dist/components/data-display/AvatarGroup.partition.mjs.map +1 -0
  30. package/dist/components/data-display/AvatarGroup.parts.cjs +2 -0
  31. package/dist/components/data-display/AvatarGroup.parts.cjs.map +1 -0
  32. package/dist/components/data-display/AvatarGroup.parts.mjs +178 -0
  33. package/dist/components/data-display/AvatarGroup.parts.mjs.map +1 -0
  34. package/dist/components/data-display/InteractionTag.cjs +1 -1
  35. package/dist/components/data-display/InteractionTag.cjs.map +1 -1
  36. package/dist/components/data-display/InteractionTag.mjs +36 -33
  37. package/dist/components/data-display/InteractionTag.mjs.map +1 -1
  38. package/dist/components/data-display/Persona.cjs +1 -1
  39. package/dist/components/data-display/Persona.cjs.map +1 -1
  40. package/dist/components/data-display/Persona.mjs +96 -37
  41. package/dist/components/data-display/Persona.mjs.map +1 -1
  42. package/dist/components/data-display/PresenceBadge.cjs +1 -1
  43. package/dist/components/data-display/PresenceBadge.cjs.map +1 -1
  44. package/dist/components/data-display/PresenceBadge.mjs +42 -28
  45. package/dist/components/data-display/PresenceBadge.mjs.map +1 -1
  46. package/dist/components/data-display/Tag.cjs +1 -1
  47. package/dist/components/data-display/Tag.cjs.map +1 -1
  48. package/dist/components/data-display/Tag.mjs +111 -108
  49. package/dist/components/data-display/Tag.mjs.map +1 -1
  50. package/dist/components/layout/Card.cjs +1 -1
  51. package/dist/components/layout/Card.cjs.map +1 -1
  52. package/dist/components/layout/Card.context.cjs +2 -0
  53. package/dist/components/layout/Card.context.cjs.map +1 -0
  54. package/dist/components/layout/Card.context.mjs +14 -0
  55. package/dist/components/layout/Card.context.mjs.map +1 -0
  56. package/dist/components/layout/Card.mjs +138 -92
  57. package/dist/components/layout/Card.mjs.map +1 -1
  58. package/dist/components/layout/Card.preview.cjs +2 -0
  59. package/dist/components/layout/Card.preview.cjs.map +1 -0
  60. package/dist/components/layout/Card.preview.mjs +22 -0
  61. package/dist/components/layout/Card.preview.mjs.map +1 -0
  62. package/dist/components/layout/Carousel.button.cjs +2 -0
  63. package/dist/components/layout/Carousel.button.cjs.map +1 -0
  64. package/dist/components/layout/Carousel.button.mjs +34 -0
  65. package/dist/components/layout/Carousel.button.mjs.map +1 -0
  66. package/dist/components/layout/Carousel.cjs +1 -1
  67. package/dist/components/layout/Carousel.cjs.map +1 -1
  68. package/dist/components/layout/Carousel.context.cjs +2 -0
  69. package/dist/components/layout/Carousel.context.cjs.map +1 -0
  70. package/dist/components/layout/Carousel.context.mjs +24 -0
  71. package/dist/components/layout/Carousel.context.mjs.map +1 -0
  72. package/dist/components/layout/Carousel.drag.cjs +2 -0
  73. package/dist/components/layout/Carousel.drag.cjs.map +1 -0
  74. package/dist/components/layout/Carousel.drag.mjs +79 -0
  75. package/dist/components/layout/Carousel.drag.mjs.map +1 -0
  76. package/dist/components/layout/Carousel.groups.cjs +2 -0
  77. package/dist/components/layout/Carousel.groups.cjs.map +1 -0
  78. package/dist/components/layout/Carousel.groups.mjs +23 -0
  79. package/dist/components/layout/Carousel.groups.mjs.map +1 -0
  80. package/dist/components/layout/Carousel.mjs +288 -150
  81. package/dist/components/layout/Carousel.mjs.map +1 -1
  82. package/dist/components/layout/Carousel.styles.cjs +2 -0
  83. package/dist/components/layout/Carousel.styles.cjs.map +1 -0
  84. package/dist/components/layout/Carousel.styles.mjs +30 -0
  85. package/dist/components/layout/Carousel.styles.mjs.map +1 -0
  86. package/dist/components/layout/TabList.cjs +1 -1
  87. package/dist/components/layout/TabList.cjs.map +1 -1
  88. package/dist/components/layout/TabList.mjs +163 -124
  89. package/dist/components/layout/TabList.mjs.map +1 -1
  90. package/dist/index.cjs +1 -1
  91. package/dist/index.d.cts +550 -48
  92. package/dist/index.d.ts +550 -48
  93. package/dist/index.mjs +132 -127
  94. package/dist/lib/classNames.cjs +1 -1
  95. package/dist/lib/classNames.cjs.map +1 -1
  96. package/dist/lib/classNames.mjs +112 -92
  97. package/dist/lib/classNames.mjs.map +1 -1
  98. package/dist/lib/initials.cjs +2 -0
  99. package/dist/lib/initials.cjs.map +1 -0
  100. package/dist/lib/initials.mjs +21 -0
  101. package/dist/lib/initials.mjs.map +1 -0
  102. package/dist/styles.css +1 -1
  103. package/package.json +1 -1
  104. package/src/styles/tokens.css +102 -0
package/README.md CHANGED
@@ -447,8 +447,9 @@ export function SaveArea() {
447
447
  | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
448
448
  | `Accordion` | `AccordionItem`, `AccordionTrigger`, `AccordionPanel` |
449
449
  | `Breadcrumb` | `BreadcrumbItem` |
450
- | `Card` | `CardHeader`, `CardBody`, `CardFooter` |
451
- | `Carousel` | `CarouselItem` |
450
+ | `AvatarGroup` | `AvatarGroupItem`, `AvatarGroupPopover` |
451
+ | `Card` | `CardHeader`, `CardPreview`, `CardBody`, `CardFooter` |
452
+ | `Carousel` | `CarouselItem`, `CarouselButton` |
452
453
  | `ColorPicker` | `ColorPickerHexField`, `ColorPickerSwatches`, `ColorPickerPreview` |
453
454
  | `Combobox` | `ComboboxOption`, `ComboboxOptionGroup` (also `Option`, `OptionGroup`) |
454
455
  | `DataGrid` | `DataGridHeader`, `DataGridHeaderCell`, `DataGridBody`, `DataGridRow`, `DataGridCell` |
@@ -531,17 +532,17 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
531
532
 
532
533
  | Component | Description |
533
534
  | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
534
- | `Avatar` | Image, initials or icon, with an image-failure fallback and an optional `badge`. |
535
- | `AvatarGroup` | Overlapping avatars; `max` adds an overflow button that lists the hidden members. |
535
+ | `Avatar` | Image, initials or icon, with an image-failure fallback and an optional `badge`; `size` (named or 16 to 128 px), `shape`, `color` (`neutral`, `brand`, `colorful` or one of twelve named colors), `active` and `activeAppearance`, an `initials` slot (see [Avatars and people](#avatars-and-people)). |
536
+ | `AvatarGroup` | Avatars in a `stack`, `spread` or `pie` `layout`; `max` adds an overflow button (`99+` past 99) that lists the hidden members. Compound form: `AvatarGroup.Item` and `AvatarGroup.Popover`. |
536
537
  | `Badge` | Status or category label: `appearance` `filled`, `tint`, `outline`; semantic `color` (see [Badges](#badges)). |
537
538
  | `CounterBadge` | Count pill (`99+` above `overflowCount`) or, with `dot`, an unread dot; Badge's `color` palette; `showZero`. |
538
- | `PresenceBadge` | Availability badge with a distinct shape per status. |
539
+ | `PresenceBadge` | Availability badge with a distinct shape per status (`available`, `busy`, `away`, `offline`, `do-not-disturb`, `out-of-office`; `dnd` and `oof` are deprecated). |
539
540
  | `Tag` | Chip: `appearance` (`filled`, `outline`, `brand`), `size` (`medium` 28px, `small`, `extra-small`), `shape` (`circular`, `rounded`), `disabled`, a look-only `selected`, `value` (its key in a TagGroup) and the `media` (an Avatar), `icon` and `secondaryText` slots; an optional Dismiss button (`dismissible`, `onDismiss`, `dismissLabel`) that also dismisses on Delete and Backspace. `onDismiss` and `dismissIcon` need `dismissible` (a development warning says so). In a TagGroup focus moves for you when a tag is removed; on its own, move it yourself (see [Tags](#tags)). |
540
541
  | `TagGroup` | A set of tags: a toolbar (one Tab stop, arrow keys) or, with `role="list"`, a list; default `size`, `appearance`, `shape`, `dismissible` and `disabled` for its tags; `onDismiss(value)`, focus to the next tag (else the previous one, else `fallbackFocusRef`) when the focused tag is removed; selectable InteractionTags through `value`/`defaultValue`/`onValueChange`, with `name`, `form` and `required`. |
541
542
  | `InteractionTag` | A tag with an action: `InteractionTag.Primary` (a button, or a toggle in a selecting TagGroup; `media`, `icon`, `secondaryText`) and an optional `InteractionTag.Secondary` (a separate Dismiss button named "Dismiss <text>"); `size`, `appearance`, `shape`, `disabled`, `selected`, `value`. |
542
543
  | `InfoLabel` | A real `<label>` (`children`, `htmlFor`, `required`, `size`, `weight`, `disabled`) with an info button after it that shows `info` (text, links or other content) in a note; `openOnHover`, `infoButtonLabel`. Usable as the `label` of a Field. |
543
544
  | `InfoButton` | A button with the info glyph that shows `info` in a `role="note"` next to it, without moving focus: opens on click, on mouse hover and on keyboard focus (`openOnHover={false}`: click only); Tab from the button enters the note; `size`. Named "Information" (`aria-label`). |
544
- | `Persona` | Avatar with name, secondary text and presence. |
545
+ | `Persona` | Avatar with up to four lines of text (`primaryText` to `quaternaryText`), `textPosition` (`after`, `before`, `below`), `presenceOnly` and presence; sizes up to `huge`. |
545
546
  | `Divider` | Horizontal or vertical separator, optionally labelled. |
546
547
  | `Image` | `<img>` with `fit`, `shape`, `shadow`, `bordered`; warns in development when `alt` is missing. Never wider than its parent; with the default `fit` its height follows its width. `fit="none"` keeps the top-left corner in view, `"center"` the middle. |
547
548
  | `List` | Plain list, selectable listbox (single or multiple) or, with item `action`s, a grid; in a selectable List item values are unique (development warning). |
@@ -556,11 +557,11 @@ Your `className` is merged last with `cn()`, so it replaces a conflicting class
556
557
 
557
558
  | Component | Description |
558
559
  | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
559
- | `Card` | Surface with `Card.Header`, `Card.Body`, `Card.Footer`; optionally selectable (`onSelect`, `selected`, `selectionControl`). |
560
+ | `Card` | Surface with `Card.Header` (`image`, `action`), `Card.Preview`, `Card.Body`, `Card.Footer`; `appearance`, `size`, `orientation`; optionally selectable (`onSelect`, `selected`, `selectionControl`, `checkbox`) with a `floatingAction` (see [Cards, tabs and carousels](#cards-tabs-and-carousels)). |
560
561
  | `Accordion` | Disclosure sections (`Accordion.Item`, `.Trigger`, `.Panel`); single (default) or `type="multiple"`; `headingLevel`; item values are unique (development warning). |
561
- | `TabList` | Tabs (`TabList.Tab`, `.Panel`, `.Panels`) with automatic activation, or manual with `selectTabOnFocus={false}`; `orientation`; tab values are unique (development warning). |
562
+ | `TabList` | Tabs (`TabList.Tab`, `.Panel`, `.Panels`) with automatic activation, or manual with `selectTabOnFocus={false}`; `orientation`, `appearance` (`transparent`, `subtle`, `subtle-circular`, `filled-circular`), `size`, a `Tab` `icon` (alone: icon-only); tab values are unique (development warning). |
562
563
  | `Tree` | Hierarchical tree (`Tree.Item`) with expand/collapse and typeahead; single selection, or checkboxes with a cascading mixed state (`selectionMode="multiple"`); row `actions`, `aside` and `Tree.ItemPersonaLayout`; `navigationMode="treegrid"` (see [Trees](#trees)). Nested items are `Tree.Item` elements in their parent's children (directly, in Fragments or from a render function), not rendered by a recursive component; values are unique. |
563
- | `Carousel` | One slide at a time with previous/next, a slide picker and optional auto-rotation with a pause control. Slides are `Carousel.Item` children, directly or in Fragments (a component that renders `Carousel.Item` is not a slide). |
564
+ | `Carousel` | Slides (`Carousel.Item` children, directly or in Fragments; a component that renders `Carousel.Item` is not a slide) with previous/next, a slide picker and optional auto-rotation with a pause control. `groupSize` (a number or `auto`) shows several at once, `draggable` drags them, `Carousel.Item` `thumbnail` shows thumbnails, `controlsLayout` places the buttons and `Carousel.Button` replaces one. |
564
565
  | `Overflow` | Hides items that do not fit in one row and renders an overflow button (`useOverflowMenu` lists the hidden items); item ids are unique (development warning). The row has a 4px padding, so its items' focus rings are not clipped. |
565
566
  | `Grid` | CSS grid with a column count and token gaps. |
566
567
  | `Stack` | Vertical or horizontal stack (`orientation`, `gap`). |
@@ -1853,6 +1854,29 @@ export function UpdateNotice({ onUpdate, onLater }: { onUpdate: () => void; onLa
1853
1854
  - A badge whose text alone does not say what it means ("3") can be named: with `aria-label` or `aria-labelledby` a Badge or CounterBadge gets `role="img"`, so the name is announced (a `role` you pass wins; an empty or whitespace-only name counts as absent). `<CounterBadge dot aria-label="Unread messages" />` is a named unread dot.
1854
1855
  - The root carries `data-color` and `data-appearance` with the resolved values (and `data-dot` on a dot), for your own selectors.
1855
1856
 
1857
+ ### Avatars and people
1858
+
1859
+ - **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.
1860
+ - **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.
1861
+ - **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.
1862
+ - **`active`** is `active` (the `activeAppearance` around the avatar: `ring`, `ring-shadow`, `shadow` or `glow`), `inactive` (dimmed) or `unset`.
1863
+ - **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.
1864
+ - **`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`.
1865
+ - **`AvatarGroup`** `layout` is `stack` (overlapping), `spread` (a gap) or `pie` (up to three wedges of a circle). Members take the group's `size`; one with a size of its own keeps it. `max` splits the members; to write the split yourself, use `partitionAvatarGroupItems({ items, maxInlineItems, layout })` and render `AvatarGroup.Item`s with an `AvatarGroup.Popover` for the rest:
1866
+
1867
+ ```tsx
1868
+ const { inlineItems, overflowItems } = partitionAvatarGroupItems({ items: people, maxInlineItems: 3 });
1869
+
1870
+ <AvatarGroup aria-label="Project team">
1871
+ {inlineItems.map((name) => <AvatarGroup.Item key={name} name={name} />)}
1872
+ {overflowItems && (
1873
+ <AvatarGroup.Popover>
1874
+ {overflowItems.map((name) => <AvatarGroup.Item key={name} name={name} />)}
1875
+ </AvatarGroup.Popover>
1876
+ )}
1877
+ </AvatarGroup>;
1878
+ ```
1879
+
1856
1880
  ### Tags
1857
1881
 
1858
1882
  Put tags that belong together in a `TagGroup`. By default it is a toolbar: one Tab stop, Left/Right between the tags (mirrored in RTL), Delete or Backspace to dismiss. When the focused tag is removed, focus moves to the next tag, else the previous one, else `fallbackFocusRef`, so it never falls to the page:
@@ -2121,6 +2145,15 @@ export function Shell({ narrow }: { narrow: boolean }) {
2121
2145
  - **Width.** `size` defaults to `small` (320px), Drawer's scale; Fluent's navigation pane is 260px wide, `className="w-65"` on your Tailwind build (or a class of your own with `width: 260px`). `position` defaults to `start` (Drawer's to `end`), and `separator` to on.
2122
2146
  - **Server rendering.** An inline NavDrawer renders in the server HTML (open, or hidden when kept mounted); with `focusMode="arrow"` its Tab stop is set once it has mounted.
2123
2147
 
2148
+ ### Cards, tabs and carousels
2149
+
2150
+ - **Card** `appearance` is `filled` (the default), `filled-alternative`, `outline` or `subtle`; `size` (`small`, `medium`, `large`) sets the padding of its parts through `--wave-card-padding`; `orientation="horizontal"` puts a `Card.Preview` beside the text. `Card.Preview` takes a `logo` over its bottom start corner. `Card.Header` takes an `image` (an Avatar, say) and an `action` (a menu button). With `selectionControl="checkbox"`, `checkbox` merges onto the built-in checkbox (an object), replaces it (an element) or removes it (`null`, when you render the control elsewhere); `floatingAction` sits over the top end corner and moves the checkbox to the start corner.
2151
+ - **TabList** `appearance` is `transparent` (the default), `subtle`, `subtle-circular` or `filled-circular`, and `size` `small`, `medium` or `large`. A `TabList.Tab` with an `icon` and no text is icon-only: give it an `aria-label` (development warning `TabList.Tab:icon-only-name`).
2152
+ - **Carousel groups.** `groupSize={n}` shows `n` slides per page and `"auto"` as many as fit (the slides keep their width, which must be equal; `align` places a page that does not fill the width). `value`, `onValueChange` and `defaultValue` are the index of the first slide of the page; previous, next, the picker (one button per page), looping and autoplay move by page. `labels.slides(start, end, total)` names a page.
2153
+ - **Dragging.** `draggable` follows the pointer (mouse, touch, pen); a drag past a quarter of the width, or a release faster than 0.5 px/ms, changes the page, else the track snaps back. The click that ends a drag is dropped, a vertical swipe still scrolls the page, and autoplay pauses while dragging.
2154
+ - **Thumbnails.** `<Carousel.Item thumbnail="/a.png">` (or an object of `<img>` attributes) turns the picker into a row of thumbnails; each button keeps its slide name.
2155
+ - **Controls.** `controlsLayout` is `overlay` (the default, round buttons over the slides), `overlay-wide` (full-height edge strips), `inline` (buttons beside the slides) or `inline-wide`. `Carousel.Button` (`direction="previous"` or `"next"`) replaces the built-in button of that direction and keeps its name, `aria-controls` and disabled behaviour; its children replace the chevron. `appearance="elevated"` rounds the carousel and adds a shadow.
2156
+
2124
2157
  ### Slots
2125
2158
 
2126
2159
  Props such as `icon`, `contentBefore` and `dismiss` are slots. A slot accepts content (rendered inside the slot's element) or an object with `as`, `className`, `children` and any attribute of the slot's element:
@@ -2161,7 +2194,7 @@ The names and hidden texts that components render themselves are English by defa
2161
2194
  | `MessageBar`, `Toast` | `statusLabel` (the hidden severity text; also an option of both `dispatchToast` forms); `Toast` also `dismissLabel` (also an option of the text form) |
2162
2195
  | `Tag` | `dismissLabel` ("Dismiss", with the tag's text) |
2163
2196
  | `InteractionTag.Secondary` | `dismissLabel` ("Dismiss", with the primary text: "Dismiss Cherry") |
2164
- | `Carousel` | `labels` (`previous`, `next`, `picker`, `slide(index, total)` with a zero-based index, `carouselRoleDescription`, `slideRoleDescription`), `autoPlayLabels` |
2197
+ | `Carousel` | `labels` (`previous`, `next`, `picker`, `slide(index, total)` with a zero-based index, `slides(start, end, total)` with one-based numbers for a page of several slides, `carouselRoleDescription`, `slideRoleDescription`), `autoPlayLabels` |
2165
2198
  | `Stepper` | `statusLabels` (`completed`, `error`; `''` omits the text) |
2166
2199
  | `Pagination` | `getItemAriaLabel(type, page, selected)` |
2167
2200
  | `ColorPicker` | `labels` (the 0.7 strings, plus `area` (the name of a bound area's group, "Saturation and brightness"), `saturation`, `brightness`, `roleDescription`, `hue`, `transparency`, `colorName(color)` and `valueText(color, channel)` for bound parts) |
@@ -2233,7 +2266,7 @@ As implemented in 0.11. Buttons, links, checkboxes, switches and the trigger but
2233
2266
  | ColorArea | One Tab stop. Left/Right change saturation (mirrored in RTL), Up/Down brightness; Shift with an arrow by 10; PageUp/PageDown brightness by 10; Home/End saturation to 0/100. |
2234
2267
  | ColorSlider, AlphaSlider | The arrows of their axis by 1 (mirrored in RTL when horizontal), Shift with an arrow by 10, PageUp/PageDown by 10, Home/End to the minimum/maximum. |
2235
2268
 
2236
- Accordion and Carousel have no arrow-key navigation: their triggers and controls are Tab stops.
2269
+ Accordion and Carousel have no arrow-key navigation between their controls: their triggers and controls are Tab stops. With `groupSize` above 1 the slides of the shown page are one arrow-key group (the slide that last had focus is its Tab stop; an arrow past the end of the page shows the next one).
2237
2270
 
2238
2271
  Typeahead (Menu, selectable List, Tree, Dropdown, Listbox) accepts characters typed with AltGr, which Windows reports as Ctrl+Alt (Polish `ł`, Romanian `ș`); Ctrl+Alt with an arrow, Home, End or Space is left to the browser. An item that cannot take focus is passed over for the next match. Menu typeahead matches the item labels, not icons or shortcuts. Options and option groups with `hidden` are skipped like a native `<option hidden>`.
2239
2272
 
@@ -2269,6 +2302,8 @@ Typeahead (Menu, selectable List, Tree, Dropdown, Listbox) accepts characters ty
2269
2302
  | `createToaster()` | A toast controller for code outside components; bind it with `<Toaster toaster={handle}>` (see [Toasts](#toasts)). Server-safe. |
2270
2303
  | `useIsOverflowing(target)`, `useOverflowMenu()`, `useIsOverflowItemVisible(id)` | Overflow detection. |
2271
2304
  | `hsvToHex(color)`, `hexToHsv(hex)` | Convert between an `HsvColor` and a hex string (`#rrggbb`, or `#rrggbbaa` below full opacity); `hexToHsv` returns `null` for other text. Server-safe. |
2305
+ | `getInitials(name, options?)` | The initials Avatar shows (`dir`, `firstInitialOnly`); none for digits, emoji or scripts without letter initials. Server-safe. |
2306
+ | `partitionAvatarGroupItems({ items, maxInlineItems, layout })` | Split a list into the inline and overflow items of an AvatarGroup (`{ inlineItems, overflowItems }`); imports nothing. |
2272
2307
  | `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. |
2273
2308
  | `buttonClassNames`, `toastTitleClassNames`, … | The stable class names of every component and part (see [Styling hooks](#styling-hooks)). Server-safe. |
2274
2309
  | `cn(...classes)` | clsx + tailwind-merge that knows Wave's type ramp and shadows; the last class wins over a conflicting class of the same variant. |
@@ -2277,6 +2312,20 @@ Typeahead (Menu, selectable List, Tree, Dropdown, Listbox) accepts characters ty
2277
2312
  | `resolveSlot`, `renderSlot` | The slot helpers the components use. |
2278
2313
  | `getThemeClassName(theme)` | The theme classes of a theme. |
2279
2314
 
2315
+ ## Upgrading from 0.12
2316
+
2317
+ 0.13 removes nothing public; these changes can still affect your types, tests and styles:
2318
+
2319
+ 1. **`PresenceStatus` gains `'do-not-disturb'` and `'out-of-office'`** (`dnd` and `oof` stay, deprecated): an exhaustive `switch` or a `Record` over it needs the cases. `PresenceBadge` `status` is optional.
2320
+ 2. **`Avatar` `size` also takes a number and `Persona` `size` gains `'huge'`:** code that narrows these unions needs the new members.
2321
+ 3. **An Avatar in a rounded Tag is square** (`Tag` and `InteractionTag` pass their shape to their `media`).
2322
+ 4. **Initials come from `getInitials`:** punctuation is dropped, a hyphen or apostrophe joins words, and names without letter initials show none.
2323
+ 5. **AvatarGroup's popup is `AvatarGroup.Popover`:** the count reads `99+` past 99 (the name keeps the real number).
2324
+ 6. **Card parts take their padding from `--wave-card-padding`** instead of a fixed `p-4`.
2325
+ 7. **Carousel has a viewport element** (`.wave-carousel__viewport`) that clips the track; the default layout's buttons sit inside it. Reach the track by `.wave-carousel__track`.
2326
+
2327
+ The CHANGELOG's [0.13.0 section](CHANGELOG.md) lists the additions.
2328
+
2280
2329
  ## Upgrading from 0.11
2281
2330
 
2282
2331
  0.12 removes nothing public; these changes can still affect your types and tests:
@@ -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/composeEventHandlers.cjs"),i=require("../../lib/classNames.cjs"),a=require("../../hooks/useMergedRefs.cjs"),o=require("../../lib/aria.cjs"),ee=require("../../lib/icons.cjs"),te=require("./Avatar.context.cjs");let s=require("react");s=e.__toESM(s,1);let c=require("react/jsx-runtime");var l={"extra-small":`w-6 h-6 text-[10px]`,small:`w-8 h-8 text-xs`,medium:`w-10 h-10 text-sm`,large:`w-12 h-12 text-base`,"extra-large":`w-14 h-14 text-lg`},ne={24:l[`extra-small`],20:`w-5 h-5 text-[10px]`,16:`w-4 h-4 text-[10px]`},u=`w-full h-full object-cover`;function re(e){let t=e.trim().split(/\s+/).filter(Boolean);return t.length===0?``:((Array.from(t[0])[0]??``)+(t.length>1?Array.from(t[t.length-1])[0]??``:``)).toUpperCase()}function d(e){return e?.trim().replace(/\s+/g,` `)||void 0}function ie(e){if(s.isValidElement(e))return e.props;if(typeof e==`object`&&e&&!(Symbol.iterator in e))return e}function ae(e,t,n){if(e===void 0)return[void 0,void 0,n||void 0,void 0];if(typeof e==`string`)return[void 0,void 0,e,void 0];let r=s.isValidElement(e)?e:void 0;return[r?.type,r?.key,t?.src,t?.srcSet]}function f(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function p(e,t){if(Object.is(e,t))return!0;if(!f(e)||!f(t))return!1;let n=Object.keys(e);return n.length===Object.keys(t).length&&n.every(n=>Object.prototype.hasOwnProperty.call(t,n)&&Object.is(e[n],t[n]))}function oe(e,t){return e.every((e,n)=>p(e,t[n]))}var m=({src:e,name:f,size:p,icon:m,image:h,badge:g,decorative:_=!1,className:v,ref:y,role:b,"aria-label":x,"aria-labelledby":S,"aria-describedby":C,"aria-description":se,"aria-details":ce,...w})=>{let T=s.useContext(te.AvatarSizeContext),E=h==null||typeof h==`boolean`||h===``?void 0:h,D=ie(E),O=ae(E,D,e),[le,k]=s.useState(!1),[ue,A]=s.useState(O);oe(ue,O)||(A(O),k(!1));let j=s.useRef(null);s.useLayoutEffect(()=>{let e=j.current?.querySelector(`img`);if(!e||!e.complete||e.naturalWidth!==0)return;let t=!0,n=e.getAttribute(`src`),r=e.getAttribute(`srcset`),i=()=>{t&&e.isConnected&&e.getAttribute(`src`)===n&&e.getAttribute(`srcset`)===r&&k(!0)};return typeof e.decode==`function`?e.decode().then(void 0,i):queueMicrotask(i),()=>{t=!1}},[]);let M=a.useMergedRefs(y,j),N=x!==void 0||S!==void 0,P=b!==void 0&&b!==`presentation`&&b!==`none`,F=d(f),I=_||N||P?``:F??``,L=()=>k(!0),R=D?.alt,z=R!=null,B=z?R:I,V={"aria-describedby":C,"aria-description":se,"aria-details":ce},H=!_&&!N&&!P&&typeof B==`string`&&B.trim()!==``,U=H?V:{},W=null;if(!le){if(typeof E==`string`)W=(0,c.jsx)(`img`,{src:E,alt:I,...U,className:t.cn(i.avatarClassNames.image,u),onError:L});else if(E!==void 0){let e={onError:r.composeEventHandlers(D?.onError,L)};if(z||(e.alt=I),H){let t=D?.[`aria-describedby`];e[`aria-describedby`]=o.joinIds(C,typeof t==`string`?t:void 0)}let a=s.isValidElement(E)?s.cloneElement(E,e):D?{...E,...e}:E;W=n.renderSlot(a,`img`,t.cn(i.avatarClassNames.image,u),{alt:I,...U})}else e&&(W=(0,c.jsx)(`img`,{src:e,alt:I,...U,className:t.cn(i.avatarClassNames.image,u),onError:L}))}let G=W!==null&&H?{}:V,K=W===null&&m&&n.slotRendersContent(m)?n.renderSlot(m,`span`,t.cn(i.avatarClassNames.icon,`inline-flex`),{"aria-hidden":!0}):null,q=W===null&&K===null&&f?re(f):``,J,Y;W===null?K===null?q?(J=(0,c.jsx)(`span`,{"aria-hidden":`true`,className:i.avatarClassNames.initials,children:q}),Y=`bg-primary text-primary-foreground`):(J=(0,c.jsx)(ee.PersonIcon,{size:`60%`,className:i.avatarClassNames.icon}),Y=`bg-muted text-muted-foreground`):(J=K,Y=`bg-muted text-muted-foreground`):J=W;let de=W!==null&&P&&typeof B==`string`?d(B):void 0,fe=typeof R==`string`?d(R):void 0,X=x??(W===null?F??fe:P?de??F:void 0),Z;Z=_?{"aria-hidden":!0,...G}:X!==void 0||S!==void 0?{role:b??`img`,"aria-label":X,"aria-labelledby":S,...G}:W!==null||b!==void 0?{role:b,...G}:{"aria-hidden":!0,...G};let Q=g&&n.slotRendersContent(g)?n.renderSlot(g,`span`,t.cn(i.avatarClassNames.badge,`absolute bottom-0 end-0 inline-flex`)):null,pe=Q!==null,me=p===void 0?T?ne[T.size]:l.medium:l[p],$=t.cn(i.avatarClassNames.visual,`inline-flex items-center justify-center rounded-full overflow-hidden shrink-0 font-bold`,me,Y);return pe?(0,c.jsxs)(`span`,{ref:y,...w,className:t.cn(i.avatarClassNames.root,`relative inline-flex shrink-0`,v),children:[(0,c.jsx)(`span`,{ref:j,...Z,className:$,children:J}),Q]}):(0,c.jsx)(`span`,{ref:M,...Z,...w,className:t.cn(i.avatarClassNames.root,$,v),children:J})};m.displayName=`Avatar`,exports.Avatar=m,exports.avatarSizeClasses=l;
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/slot.cjs"),r=require("../../lib/composeEventHandlers.cjs"),i=require("../../lib/classNames.cjs"),a=require("../../lib/initials.cjs"),ee=require("../../hooks/useMergedRefs.cjs"),te=require("../../lib/aria.cjs"),ne=require("../../hooks/useDirection.cjs"),re=require("../../lib/icons.cjs"),o=require("./Avatar.context.cjs"),ie=require("./AvatarGroup.context.cjs"),s=require("./Avatar.colors.cjs"),c=require("./Avatar.sizes.cjs");let l=require("react");l=e.__toESM(l,1);let u=require("react/jsx-runtime");c.avatarBoxClasses[24],c.avatarBoxClasses[32],c.avatarBoxClasses[40],c.avatarBoxClasses[48],c.avatarBoxClasses[56];var d=`w-full h-full object-cover`,ae={ring:`ring-2 ring-primary ring-offset-2 ring-offset-background`,"ring-shadow":`ring-2 ring-primary ring-offset-2 ring-offset-background shadow-8`,shadow:`shadow-8`,glow:`shadow-[0_0_0.5rem_0.125rem_var(--wave-primary)]`};function f(e){return e?.trim().replace(/\s+/g,` `)||void 0}function oe(e){if(l.isValidElement(e))return e.props;if(typeof e==`object`&&e&&!(Symbol.iterator in e))return e}function se(e,t,n){if(e===void 0)return[void 0,void 0,n||void 0,void 0];if(typeof e==`string`)return[void 0,void 0,e,void 0];let r=l.isValidElement(e)?e:void 0;return[r?.type,r?.key,t?.src,t?.srcSet]}function p(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function m(e,t){if(Object.is(e,t))return!0;if(!p(e)||!p(t))return!1;let n=Object.keys(e);return n.length===Object.keys(t).length&&n.every(n=>Object.prototype.hasOwnProperty.call(t,n)&&Object.is(e[n],t[n]))}function ce(e,t){return e.every((e,n)=>m(e,t[n]))}var h=({src:e,name:p,size:m,shape:h,color:le=`brand`,idForColor:ue,active:g=`unset`,activeAppearance:_=`ring`,initials:v,icon:y,image:b,badge:x,decorative:S=!1,className:C,ref:w,role:T,"aria-label":de,"aria-labelledby":E,"aria-describedby":fe,"aria-description":pe,"aria-details":me,...he})=>{let D=l.useContext(o.AvatarSizeContext),O=b==null||typeof b==`boolean`||b===``?void 0:b,k=oe(O),A=se(O,k,e),[ge,j]=l.useState(!1),[_e,ve]=l.useState(A);ce(_e,A)||(ve(A),j(!1));let M=l.useRef(null);l.useLayoutEffect(()=>{let e=M.current?.querySelector(`img`);if(!e||!e.complete||e.naturalWidth!==0)return;let t=!0,n=e.getAttribute(`src`),r=e.getAttribute(`srcset`),i=()=>{t&&e.isConnected&&e.getAttribute(`src`)===n&&e.getAttribute(`srcset`)===r&&j(!0)};return typeof e.decode==`function`?e.decode().then(void 0,i):queueMicrotask(i),()=>{t=!1}},[]);let ye=ee.useMergedRefs(w,M),N=de!==void 0||E!==void 0,P=T!==void 0&&T!==`presentation`&&T!==`none`,F=f(p),I=S||N||P?``:F??``,L=()=>j(!0),R=k?.alt,z=R!=null,B=z?R:I,V={"aria-describedby":fe,"aria-description":pe,"aria-details":me},H=!S&&!N&&!P&&typeof B==`string`&&B.trim()!==``,U=H?V:{},W=null;if(!ge){if(typeof O==`string`)W=(0,u.jsx)(`img`,{src:O,alt:I,...U,className:t.cn(i.avatarClassNames.image,d),onError:L});else if(O!==void 0){let e={onError:r.composeEventHandlers(k?.onError,L)};if(z||(e.alt=I),H){let t=k?.[`aria-describedby`];e[`aria-describedby`]=te.joinIds(fe,typeof t==`string`?t:void 0)}let a=l.isValidElement(O)?l.cloneElement(O,e):k?{...O,...e}:O;W=n.renderSlot(a,`img`,t.cn(i.avatarClassNames.image,d),{alt:I,...U})}else e&&(W=(0,u.jsx)(`img`,{src:e,alt:I,...U,className:t.cn(i.avatarClassNames.image,d),onError:L}))}let G=W!==null&&H?{}:V,K=W===null&&y&&n.slotRendersContent(y)?n.renderSlot(y,`span`,t.cn(i.avatarClassNames.icon,`inline-flex`),{"aria-hidden":!0}):null,be=ne.useDirection(),q=l.useContext(ie.AvatarGroupContext),xe=t.cn(i.avatarClassNames.initials,q?.layout===`pie`&&`invisible`),J=s.resolveAvatarColor(le,ue,p),Se=W===null&&K===null,Ce=Se&&p?a.getInitials(p,{dir:be}):``,we=Se?v&&n.slotRendersContent(v)?n.renderSlot(v,`span`,xe,{"aria-hidden":!0}):Ce?(0,u.jsx)(`span`,{"aria-hidden":`true`,className:xe,children:Ce}):null:null,Y,X;W===null?K===null?we?(Y=we,X=s.avatarColorClasses[J]):(Y=(0,u.jsx)(re.PersonIcon,{size:`60%`,className:i.avatarClassNames.icon}),X=`bg-muted text-muted-foreground`):(Y=K,X=`bg-muted text-muted-foreground`):Y=W;let Te=W!==null&&P&&typeof B==`string`?f(B):void 0,Ee=typeof R==`string`?f(R):void 0,De=de??(W===null?F??Ee:P?Te??F:void 0),Z;Z=S?{"aria-hidden":!0,...G}:De!==void 0||E!==void 0?{role:T??`img`,"aria-label":De,"aria-labelledby":E,...G}:W!==null||T!==void 0?{role:T,...G}:{"aria-hidden":!0,...G};let Oe=x&&n.slotRendersContent(x)?n.renderSlot(x,`span`,t.cn(i.avatarClassNames.badge,`absolute bottom-0 end-0 inline-flex`)):null,ke=Oe!==null,Q=m??q?.size??(D?D.size:`medium`),Ae=h??D?.shape??`circular`,je=c.avatarPixelSize(Q),Me={"data-size":String(Q),"data-shape":Ae,"data-color":J,"data-active":g,"data-active-appearance":_},$=t.cn(i.avatarClassNames.visual,`inline-flex items-center justify-center overflow-hidden shrink-0 font-bold`,c.avatarShapeClasses(Ae,je),c.avatarSizeClassesFor(Q),X,g===`active`&&ae[_],g===`inactive`&&`opacity-60 transition-opacity motion-reduce:transition-none`);return ke?(0,u.jsxs)(`span`,{ref:w,...Me,...he,className:t.cn(i.avatarClassNames.root,`relative inline-flex shrink-0`,C),children:[(0,u.jsx)(`span`,{ref:M,...Z,className:$,children:Y}),(0,u.jsx)(o.AvatarBadgeSizeContext.Provider,{value:c.avatarBadgeSize(je),children:Oe})]}):(0,u.jsx)(`span`,{ref:ye,...Z,...Me,...he,className:t.cn(i.avatarClassNames.root,$,C),children:Y})};h.displayName=`Avatar`,exports.Avatar=h;
2
2
  //# sourceMappingURL=Avatar.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.cjs","names":["React","AvatarSizeContext","useMergedRefs","cn","avatarClassNames","composeEventHandlers","joinIds","renderSlot","slotRendersContent","PersonIcon","require_runtime","require_cn","require_slot","require_composeEventHandlers","require_classNames","require_useMergedRefs","require_aria","require_icons","require_Avatar_context","react","react_jsx_runtime","avatarSizeClasses","AVATAR_BOX_CLASSES","IMAGE_CLASS","getInitials","parts","name","normaliseName","readSlotProps","slot","getImageKey","imageSlot","src","element","slotProps","isPlainObject","value","proto","sameKeyPart","a","b","keys","k","sameImageKey","part","i","Avatar","sizeProp","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaDescription","ariaDetails","rest","box","image","imageKey","failed","setFailed","prevImageKey","setPrevImageKey","visualRef","img","active","checkedSrc","checkedSrcSet","markFailed","rootVisualRef","ref","hasConsumerName","roleCarriesName","role","normalisedName","imageAlt","decorative","handleError","ownAlt","hasOwnAlt","renderedAlt","description","imageIsNamed","imageDescription","imageNode","overrides","ownIds","visualDescription","iconNode","icon","initials","content","colorClass","imageName","slotAltName","label","a11yProps","badgeNode","badge","hasBadge","sizeClasses","visualClassName","className"],"sources":["../../../src/components/data-display/Avatar.tsx"],"sourcesContent":["import * as React from 'react';\nimport { joinIds } from '../../lib/aria';\nimport { cn } from '../../lib/cn';\nimport { avatarClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { PersonIcon } from '../../lib/icons';\nimport type { Size, Slot } from '../../lib/types';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport { AvatarSizeContext, type AvatarSizeContextValue } from './Avatar.context';\n\n/** Properties for the Avatar component. */\nexport interface AvatarProps extends React.HTMLAttributes<HTMLSpanElement> {\n /**\n * URL of the avatar image. When it fails to load (also before hydration, for server-rendered\n * markup), the avatar falls back to its icon or initials; a new `src` is tried again.\n */\n src?: string;\n /**\n * Full name of the person. It names the avatar for assistive technology (the image `alt`; in\n * initials and icon mode, or with a consumer `role`, the avatar's `aria-label`, with\n * `role=\"img\"` unless `role` is given) and generates the initials shown when there is no image.\n */\n name?: string;\n /**\n * Size of the avatar. Without it, an Avatar in the `media` of a tag (Tag, InteractionTag) takes\n * the tag's media box instead: 24, 20 or 16px by the tag's size.\n * @default 'medium'\n */\n size?: Size;\n /**\n * Icon shown instead of the initials when there is no image. Rendered in an `aria-hidden`\n * `<span>` (a slot object can override its attributes). A falsy value (`null`, `false`, `''`,\n * `0`, `NaN`, e.g. `icon={count && <Icon />}`) counts as no icon, as in 0.4, and so does an\n * array, `Set` or generator whose items render nothing. Without `icon` and without a usable\n * `name`, a generic person glyph is shown.\n */\n icon?: Slot<'span'>;\n /**\n * Custom image. Accepts a URL string (treated like `src`), an element (rendered as the image\n * itself, e.g. `<img>` or a framework image component) or an object of `<img>` attributes\n * (`{ src, alt, … }`). Takes precedence over `src`; its `alt` defaults to `name` (an `alt` that\n * is `undefined` counts as not given). When its alt names the avatar, the avatar's\n * `aria-describedby` ids are joined with the slot's own, while the slot's own `aria-description`\n * and `aria-details` win over the avatar's. When the image fails to load, an avatar without a\n * `name` keeps the name of the slot's own `alt`. After a load failure, a new image is tried\n * again: another `src` or `srcSet` of any type (an equal inline object is the same image), or\n * for an element slot another component type or `key`.\n */\n image?: Slot<'img'>;\n /**\n * Badge shown at the bottom end corner, e.g. `<PresenceBadge status=\"busy\" />`. With a badge\n * the avatar is wrapped in an outer `<span>` that receives `ref`, `className` and the other\n * props; `role`, `aria-label`, `aria-labelledby`, `aria-describedby`, `aria-description` and\n * `aria-details` stay on the avatar visual, so the badge keeps its own accessible name. A falsy\n * value (`null`, `false`, `''`, `0`, `NaN`, e.g. `badge={count && <CounterBadge count={count} />}`)\n * counts as no badge, as in 0.4, and so does an array, `Set` or generator whose items render\n * nothing: the avatar then renders without the wrapper.\n */\n badge?: Slot<'span'>;\n /**\n * Hides the avatar visual from assistive technology (`aria-hidden`, empty image `alt`, no\n * `role=\"img\"`). Use it when the name is already shown next to the avatar (Persona). A badge\n * stays exposed.\n * @default false\n */\n decorative?: boolean;\n /** Ref to the root `<span>`: the avatar itself, or the badge wrapper when `badge` is set. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * Width, height and text size classes of the avatar per `size`. Module-only (not in the barrel):\n * AvatarGroup sizes its overflow button with the same classes, so the two always match.\n */\nexport const avatarSizeClasses: Readonly<Record<Size, string>> = {\n 'extra-small': 'w-6 h-6 text-[10px]', // 24px\n small: 'w-8 h-8 text-xs', // 32px\n medium: 'w-10 h-10 text-sm', // 40px\n large: 'w-12 h-12 text-base', // 48px\n 'extra-large': 'w-14 h-14 text-lg', // 56px\n};\n\n/**\n * Width, height and text size classes of each box of {@link AvatarSizeContext}, for an Avatar\n * that sets no `size`: 24px is the `extra-small` size, 20px and 16px are internal boxes.\n */\nconst AVATAR_BOX_CLASSES: Readonly<Record<AvatarSizeContextValue['size'], string>> = {\n 24: avatarSizeClasses['extra-small'],\n 20: 'w-5 h-5 text-[10px]',\n 16: 'w-4 h-4 text-[10px]',\n};\n\nconst IMAGE_CLASS = 'w-full h-full object-cover';\n\n/** First letter of the first and last word, upper-cased; `''` for a blank name. */\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n const first = Array.from(parts[0]!)[0] ?? '';\n const last = parts.length > 1 ? (Array.from(parts[parts.length - 1]!)[0] ?? '') : '';\n return (first + last).toUpperCase();\n}\n\n/** A name with its whitespace collapsed, or `undefined` when it is blank. */\nfunction normaliseName(name: string | undefined): string | undefined {\n return name?.trim().replace(/\\s+/g, ' ') || undefined;\n}\n\ntype ImageErrorHandler = React.ReactEventHandler<HTMLImageElement>;\n\n/** Description props that go to the element carrying the avatar's name (data-display#19). */\ntype DescriptionProps = Pick<\n React.HTMLAttributes<HTMLElement>,\n 'aria-describedby' | 'aria-description' | 'aria-details'\n>;\n\n/** Props the avatar sets on an element or object image slot, over the slot's own. */\ninterface SlotOverrides {\n onError: ImageErrorHandler;\n alt?: string;\n 'aria-describedby'?: string;\n}\n\n/** The props of an element or object image slot that the avatar reads. */\ninterface ImageSlotProps {\n src?: unknown;\n srcSet?: unknown;\n alt?: unknown;\n onError?: unknown;\n 'aria-describedby'?: unknown;\n}\n\n/** The props of an element or object image slot (to read its `src`, `alt` and handlers). */\nfunction readSlotProps(slot: unknown): ImageSlotProps | undefined {\n if (React.isValidElement(slot)) return slot.props as ImageSlotProps;\n if (typeof slot === 'object' && slot !== null && !(Symbol.iterator in slot)) {\n return slot as ImageSlotProps;\n }\n return undefined;\n}\n\n/**\n * What identifies the image, so a load failure is kept for it and a new image is tried again\n * (data-display#4): the `src` and `srcSet`, of any type (a framework image component may take a\n * static-import object), and for an element slot also its type and `key`.\n */\ntype ImageKey = readonly [type: unknown, key: unknown, src: unknown, srcSet: unknown];\n\nfunction getImageKey(\n imageSlot: unknown,\n slotProps: ImageSlotProps | undefined,\n src: string | undefined,\n): ImageKey {\n if (imageSlot === undefined) return [undefined, undefined, src || undefined, undefined];\n if (typeof imageSlot === 'string') return [undefined, undefined, imageSlot, undefined];\n const element = React.isValidElement(imageSlot) ? imageSlot : undefined;\n return [element?.type, element?.key, slotProps?.src, slotProps?.srcSet];\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n/** Same value, or plain objects with the same entries (an inline `src={{ … }}` on each render). */\nfunction sameKeyPart(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n const keys = Object.keys(a);\n return (\n keys.length === Object.keys(b).length &&\n keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && Object.is(a[k], b[k]))\n );\n}\n\nfunction sameImageKey(a: ImageKey, b: ImageKey): boolean {\n return a.every((part, i) => sameKeyPart(part, b[i]));\n}\n\n/**\n * A circular picture of a person: their image, an icon, or the initials of their `name`.\n *\n * - **Image**: `image` (URL string, element or `{ src, alt }` object) or `src`. If the image fails\n * to load, the avatar falls back to `icon`, the initials or a person glyph, with their\n * background; a new image is tried again. A server-rendered image that failed before hydration\n * is detected after mount (settled without a natural size, confirmed by `decode()`).\n * - **Accessible name**: an image uses `name` (whitespace collapsed) as its `alt`, or `''` when the\n * name is blank. Without an image (none given, or it failed to load) the avatar is `role=\"img\"`\n * with `aria-label={name}` (without a name: the image slot's own `alt`) and the initials/icon\n * are hidden; without either (and without `aria-label`/`aria-labelledby`) it is decorative\n * (`aria-hidden`). A consumer `aria-label`, `aria-labelledby` or `role` (other than\n * `presentation`/`none`) puts the name on the avatar visual, and an image inside it gets\n * `alt=\"\"`. `decorative` hides it in every mode. `aria-describedby`, `aria-description` and\n * `aria-details` go to the element that carries the name: the `<img>` when a non-blank `alt`\n * names it, otherwise the avatar visual (never an `alt=\"\"` image).\n * - **Badge**: `badge` renders at the bottom end corner (logical, RTL-safe) as a sibling of the\n * avatar visual, so a `PresenceBadge` keeps its own name. With a badge, `ref`, `className` and\n * the rest props go to the outer wrapper.\n *\n * @example\n * <Avatar name=\"Jane Doe\" src=\"/jane.jpg\" badge={<PresenceBadge status=\"available\" />} />\n */\nexport const Avatar = ({\n src,\n name,\n size: sizeProp,\n icon,\n image,\n badge,\n decorative = false,\n className,\n ref,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n ...rest\n}: AvatarProps) => {\n // The media box of a surrounding Tag or InteractionTag, for an Avatar without its own `size`.\n const box = React.useContext(AvatarSizeContext);\n const imageSlot =\n image === null || image === undefined || typeof image === 'boolean' || image === ''\n ? undefined\n : image;\n const slotProps = readSlotProps(imageSlot);\n const imageKey = getImageKey(imageSlot, slotProps, src);\n\n // Load failure of the current image; a new image is tried again (C-HOOKS: adjust state during\n // render on prop change).\n const [failed, setFailed] = React.useState(false);\n const [prevImageKey, setPrevImageKey] = React.useState(imageKey);\n if (!sameImageKey(prevImageKey, imageKey)) {\n setPrevImageKey(imageKey);\n setFailed(false);\n }\n\n // An image that failed before hydration (a fast 404 of server-rendered markup) never reaches\n // `onError`: React attaches the listener while hydrating and does not replay the event. After\n // mount, a settled image without a natural size is broken, unless it is an SVG without one, so\n // `decode()` (which rejects for a broken image) confirms it where the browser supports it. Later\n // images, rendered by the client, report their errors through `onError`.\n const visualRef = React.useRef<HTMLSpanElement>(null);\n React.useLayoutEffect(() => {\n const img = visualRef.current?.querySelector('img');\n if (!img || !img.complete || img.naturalWidth !== 0) return;\n let active = true;\n const checkedSrc = img.getAttribute('src');\n const checkedSrcSet = img.getAttribute('srcset');\n const markFailed = () => {\n // Deferred (C-HOOKS); skipped if the image was replaced or got another source meanwhile.\n if (\n active &&\n img.isConnected &&\n img.getAttribute('src') === checkedSrc &&\n img.getAttribute('srcset') === checkedSrcSet\n ) {\n setFailed(true);\n }\n };\n if (typeof img.decode === 'function') img.decode().then(undefined, markFailed);\n else queueMicrotask(markFailed);\n return () => {\n active = false;\n };\n }, []);\n const rootVisualRef = useMergedRefs(ref, visualRef);\n\n const hasConsumerName = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n // A consumer role goes to the visual span together with the name (data-display#19), so the\n // image inside it is presentational (`alt=\"\"`), as with a consumer `aria-label`: the children\n // of `role=\"img\"` are not exposed. A presentational role (`presentation`, `none`) cannot carry\n // a name, so the image keeps it.\n const roleCarriesName = role !== undefined && role !== 'presentation' && role !== 'none';\n const normalisedName = normaliseName(name);\n const imageAlt = decorative || hasConsumerName || roleCarriesName ? '' : (normalisedName ?? '');\n const handleError: ImageErrorHandler = () => setFailed(true);\n\n // The alt text the image renders with: an element or object slot's own `alt` wins over ours. An\n // `alt` that is `undefined` (an optional `alt: user.photoAlt`, a wrapper forwarding `alt={alt}`)\n // or `null` is not given, so ours applies (the slot's own value would otherwise override it).\n const ownAlt = slotProps?.alt;\n const hasOwnAlt = ownAlt !== undefined && ownAlt !== null;\n const renderedAlt: unknown = hasOwnAlt ? ownAlt : imageAlt;\n\n // The description goes to the element that carries the name: the image itself when a non-blank\n // `alt` names it (the visual span then has no role, or a presentational one), otherwise the\n // visual span. An image with an empty `alt` is presentational, and a description on it would\n // conflict with that role.\n const description: DescriptionProps = {\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n };\n const imageIsNamed =\n !decorative &&\n !hasConsumerName &&\n !roleCarriesName &&\n typeof renderedAlt === 'string' &&\n renderedAlt.trim() !== '';\n const imageDescription: DescriptionProps = imageIsNamed ? description : {};\n\n let imageNode: React.ReactNode = null;\n if (!failed) {\n if (typeof imageSlot === 'string') {\n imageNode = (\n <img\n src={imageSlot}\n alt={imageAlt}\n {...imageDescription}\n className={cn(avatarClassNames.image, IMAGE_CLASS)}\n onError={handleError}\n />\n );\n } else if (imageSlot !== undefined) {\n // The slot's own props win over renderSlot's defaults, so the composed `onError`, the\n // default `alt` (when the slot gives none) and the joined `aria-describedby` (the avatar's\n // ids, then the slot's) are set on the slot itself. A slot's own `aria-description` and\n // `aria-details` win over the avatar's.\n const overrides: SlotOverrides = {\n onError: composeEventHandlers(\n slotProps?.onError as ImageErrorHandler | undefined,\n handleError,\n ),\n };\n if (!hasOwnAlt) overrides.alt = imageAlt;\n if (imageIsNamed) {\n const ownIds = slotProps?.['aria-describedby'];\n overrides['aria-describedby'] = joinIds(\n ariaDescribedBy,\n typeof ownIds === 'string' ? ownIds : undefined,\n );\n }\n const slot = React.isValidElement(imageSlot)\n ? React.cloneElement(imageSlot as React.ReactElement<SlotOverrides>, overrides)\n : slotProps\n ? { ...(imageSlot as object), ...overrides }\n : imageSlot;\n imageNode = renderSlot(slot as Slot<'img'>, 'img', cn(avatarClassNames.image, IMAGE_CLASS), {\n alt: imageAlt,\n ...imageDescription,\n });\n } else if (src) {\n imageNode = (\n <img\n src={src}\n alt={imageAlt}\n {...imageDescription}\n className={cn(avatarClassNames.image, IMAGE_CLASS)}\n onError={handleError}\n />\n );\n }\n }\n const visualDescription = imageNode !== null && imageIsNamed ? {} : description;\n\n // A falsy icon (`icon={count && <Icon />}` with count 0) is no icon, as in 0.4, and so is a\n // collection whose items render nothing (`icon={items.map(…)}` mapping to nothing). The check\n // does not consume a generator: renderSlot still renders its items.\n const iconNode =\n imageNode === null && icon && slotRendersContent(icon)\n ? renderSlot(icon, 'span', cn(avatarClassNames.icon, 'inline-flex'), { 'aria-hidden': true })\n : null;\n const initials = imageNode === null && iconNode === null && name ? getInitials(name) : '';\n\n let content: React.ReactNode;\n let colorClass: string | undefined;\n if (imageNode !== null) {\n content = imageNode;\n } else if (iconNode !== null) {\n content = iconNode;\n colorClass = 'bg-muted text-muted-foreground';\n } else if (initials) {\n content = (\n <span aria-hidden=\"true\" className={avatarClassNames.initials}>\n {initials}\n </span>\n );\n colorClass = 'bg-primary text-primary-foreground';\n } else {\n content = <PersonIcon size=\"60%\" className={avatarClassNames.icon} />;\n colorClass = 'bg-muted text-muted-foreground';\n }\n\n // Accessible name of the avatar visual (data-display#19, #27). With a consumer role and an\n // image it is the name the image would carry: an element or object slot's own alt, else `name`.\n // Without an image (none given, or it failed to load) it is `name`, else the slot's own alt: an\n // avatar named only by its image alt keeps that name when the image fails.\n const imageName =\n imageNode !== null && roleCarriesName && typeof renderedAlt === 'string'\n ? normaliseName(renderedAlt)\n : undefined;\n const slotAltName = typeof ownAlt === 'string' ? normaliseName(ownAlt) : undefined;\n const label =\n ariaLabel ??\n (imageNode === null\n ? (normalisedName ?? slotAltName)\n : roleCarriesName\n ? (imageName ?? normalisedName)\n : undefined);\n let a11yProps: React.HTMLAttributes<HTMLSpanElement>;\n if (decorative) {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n } else if (label !== undefined || ariaLabelledBy !== undefined) {\n a11yProps = {\n role: role ?? 'img',\n 'aria-label': label,\n 'aria-labelledby': ariaLabelledBy,\n ...visualDescription,\n };\n } else if (imageNode !== null || role !== undefined) {\n a11yProps = { role, ...visualDescription };\n } else {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n }\n\n // A falsy or empty badge (`badge={count && <CounterBadge … />}` with count 0) is no badge, as in\n // 0.4 and like `icon`: no stray \"0\" in the corner, and no wrapper taking the root props.\n const badgeNode =\n badge && slotRendersContent(badge)\n ? renderSlot(badge, 'span', cn(avatarClassNames.badge, 'absolute bottom-0 end-0 inline-flex'))\n : null;\n const hasBadge = badgeNode !== null;\n\n // An own `size` wins; without one, the box of a surrounding tag's media, else medium. With a\n // badge the visual is sized, not the wrapper.\n const sizeClasses =\n sizeProp !== undefined\n ? avatarSizeClasses[sizeProp]\n : box\n ? AVATAR_BOX_CLASSES[box.size]\n : avatarSizeClasses.medium;\n const visualClassName = cn(\n avatarClassNames.visual,\n 'inline-flex items-center justify-center rounded-full overflow-hidden shrink-0 font-bold',\n sizeClasses,\n colorClass,\n );\n\n if (!hasBadge) {\n return (\n <span\n ref={rootVisualRef}\n {...a11yProps}\n {...rest}\n className={cn(avatarClassNames.root, visualClassName, className)}\n >\n {content}\n </span>\n );\n }\n\n return (\n <span\n ref={ref}\n {...rest}\n className={cn(avatarClassNames.root, 'relative inline-flex shrink-0', className)}\n >\n <span ref={visualRef} {...a11yProps} className={visualClassName}>\n {content}\n </span>\n {badgeNode}\n </span>\n );\n};\n\nAvatar.displayName = 'Avatar';\n"],"mappings":"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"}
1
+ {"version":3,"file":"Avatar.cjs","names":["avatarBoxClasses","React","AvatarSizeContext","useMergedRefs","cn","avatarClassNames","composeEventHandlers","joinIds","renderSlot","slotRendersContent","useDirection","AvatarGroupContext","resolveAvatarColor","getInitials","avatarColorClasses","PersonIcon","avatarPixelSize","avatarShapeClasses","avatarSizeClassesFor","AvatarBadgeSizeContext","avatarBadgeSize","require_runtime","require_cn","require_slot","require_composeEventHandlers","require_classNames","require_initials","require_useMergedRefs","require_aria","require_useDirection","require_icons","require_Avatar_context","require_AvatarGroup_context","require_Avatar_colors","require_Avatar_sizes","react","react_jsx_runtime","IMAGE_CLASS","activeClasses","normaliseName","name","readSlotProps","slot","getImageKey","imageSlot","src","element","slotProps","isPlainObject","value","proto","sameKeyPart","a","b","keys","k","sameImageKey","part","i","Avatar","sizeProp","shapeProp","colorProp","initialsSlot","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaDescription","ariaDetails","rest","box","image","imageKey","failed","setFailed","prevImageKey","setPrevImageKey","visualRef","img","active","checkedSrc","checkedSrcSet","markFailed","rootVisualRef","ref","hasConsumerName","roleCarriesName","role","normalisedName","imageAlt","decorative","handleError","ownAlt","hasOwnAlt","renderedAlt","description","imageIsNamed","imageDescription","imageNode","overrides","ownIds","visualDescription","iconNode","icon","dir","group","initialsClass","resolvedColor","idForColor","showsInitials","computedInitials","initialsNode","content","colorClass","imageName","slotAltName","label","a11yProps","badgeNode","badge","hasBadge","resolvedSize","resolvedShape","pixels","stateProps","activeAppearance","visualClassName","className"],"sources":["../../../src/components/data-display/Avatar.tsx"],"sourcesContent":["import * as React from 'react';\nimport { joinIds } from '../../lib/aria';\nimport { cn } from '../../lib/cn';\nimport { avatarClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { useDirection } from '../../hooks/useDirection';\nimport { getInitials } from '../../lib/initials';\nimport { PersonIcon } from '../../lib/icons';\nimport type { Size, Slot } from '../../lib/types';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\nimport { AvatarBadgeSizeContext, AvatarSizeContext } from './Avatar.context';\nimport { AvatarGroupContext } from './AvatarGroup.context';\nimport { avatarColorClasses, resolveAvatarColor, type AvatarColor } from './Avatar.colors';\nimport {\n avatarBadgeSize,\n avatarBoxClasses,\n avatarPixelSize,\n avatarShapeClasses,\n avatarSizeClassesFor,\n type AvatarSize,\n} from './Avatar.sizes';\n\nexport type { AvatarNumericSize, AvatarSize } from './Avatar.sizes';\nexport type { AvatarColor, AvatarNamedColor } from './Avatar.colors';\n\n/** Properties for the Avatar component. */\nexport interface AvatarProps extends React.HTMLAttributes<HTMLSpanElement> {\n /**\n * URL of the avatar image. When it fails to load (also before hydration, for server-rendered\n * markup), the avatar falls back to its icon or initials; a new `src` is tried again.\n */\n src?: string;\n /**\n * Full name of the person. It names the avatar for assistive technology (the image `alt`; in\n * initials and icon mode, or with a consumer `role`, the avatar's `aria-label`, with\n * `role=\"img\"` unless `role` is given) and generates the initials shown when there is no image.\n */\n name?: string;\n /**\n * Size of the avatar: a named size (`extra-small` 24px to `extra-large` 56px) or a pixel size from\n * 16 to 128. Without it, an Avatar in the `media` of a tag (Tag, InteractionTag) takes the tag's\n * media box instead: 24, 20 or 16px by the tag's size.\n * @default 'medium'\n */\n size?: AvatarSize;\n /**\n * Shape of the avatar. Without it, an Avatar in the `media` of a rounded tag is square.\n * @default 'circular'\n */\n shape?: 'circular' | 'square';\n /**\n * Color of the avatar when it shows initials: `brand` (the primary color), `neutral` (muted),\n * `colorful` (one of the named colors, chosen from `idForColor`, else from `name`; `brand`\n * without either) or a named color of the persona palette. An image, or an icon, keeps its own\n * look.\n * @default 'brand'\n */\n color?: AvatarColor;\n /**\n * Whether the person is active: `active` draws the `activeAppearance` around the avatar, `inactive`\n * dims it, `unset` shows neither.\n * @default 'unset'\n */\n active?: 'active' | 'inactive' | 'unset';\n /**\n * How an `active` avatar is marked: a `ring`, a `shadow`, both (`ring-shadow`) or a `glow` in the\n * primary color.\n * @default 'ring'\n */\n activeAppearance?: 'ring' | 'ring-shadow' | 'shadow' | 'glow';\n /**\n * A stable id (a user id, say) that picks the color of `color=\"colorful\"`, so the same person has\n * the same color wherever they are shown, whatever their name looks like.\n */\n idForColor?: string;\n /**\n * The initials shown when there is no image and no icon, instead of the ones computed from\n * `name` (see `getInitials`): a string, an element or an object of `<span>` attributes. A value\n * that renders nothing (`''`, `[]`) keeps the computed initials. Rendered in an `aria-hidden`\n * `<span>`.\n */\n initials?: Slot<'span'>;\n /**\n * Icon shown instead of the initials when there is no image. Rendered in an `aria-hidden`\n * `<span>` (a slot object can override its attributes). A falsy value (`null`, `false`, `''`,\n * `0`, `NaN`, e.g. `icon={count && <Icon />}`) counts as no icon, as in 0.4, and so does an\n * array, `Set` or generator whose items render nothing. Without `icon` and without a usable\n * `name`, a generic person glyph is shown.\n */\n icon?: Slot<'span'>;\n /**\n * Custom image. Accepts a URL string (treated like `src`), an element (rendered as the image\n * itself, e.g. `<img>` or a framework image component) or an object of `<img>` attributes\n * (`{ src, alt, … }`). Takes precedence over `src`; its `alt` defaults to `name` (an `alt` that\n * is `undefined` counts as not given). When its alt names the avatar, the avatar's\n * `aria-describedby` ids are joined with the slot's own, while the slot's own `aria-description`\n * and `aria-details` win over the avatar's. When the image fails to load, an avatar without a\n * `name` keeps the name of the slot's own `alt`. After a load failure, a new image is tried\n * again: another `src` or `srcSet` of any type (an equal inline object is the same image), or\n * for an element slot another component type or `key`.\n */\n image?: Slot<'img'>;\n /**\n * Badge shown at the bottom end corner, e.g. `<PresenceBadge status=\"busy\" />`. With a badge\n * the avatar is wrapped in an outer `<span>` that receives `ref`, `className` and the other\n * props; `role`, `aria-label`, `aria-labelledby`, `aria-describedby`, `aria-description` and\n * `aria-details` stay on the avatar visual, so the badge keeps its own accessible name. A falsy\n * value (`null`, `false`, `''`, `0`, `NaN`, e.g. `badge={count && <CounterBadge count={count} />}`)\n * counts as no badge, as in 0.4, and so does an array, `Set` or generator whose items render\n * nothing: the avatar then renders without the wrapper.\n */\n badge?: Slot<'span'>;\n /**\n * Hides the avatar visual from assistive technology (`aria-hidden`, empty image `alt`, no\n * `role=\"img\"`). Use it when the name is already shown next to the avatar (Persona). A badge\n * stays exposed.\n * @default false\n */\n decorative?: boolean;\n /** Ref to the root `<span>`: the avatar itself, or the badge wrapper when `badge` is set. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * Width, height and text size classes of the avatar per `size`. Module-only (not in the barrel):\n * AvatarGroup sizes its overflow button with the same classes, so the two always match.\n */\nexport const avatarSizeClasses: Readonly<Record<Size, string>> = {\n 'extra-small': avatarBoxClasses[24],\n small: avatarBoxClasses[32],\n medium: avatarBoxClasses[40],\n large: avatarBoxClasses[48],\n 'extra-large': avatarBoxClasses[56],\n};\n\nconst IMAGE_CLASS = 'w-full h-full object-cover';\n\n/** How an `active` avatar is marked. Literal strings only. */\nconst activeClasses: Readonly<Record<'ring' | 'ring-shadow' | 'shadow' | 'glow', string>> = {\n ring: 'ring-2 ring-primary ring-offset-2 ring-offset-background',\n 'ring-shadow': 'ring-2 ring-primary ring-offset-2 ring-offset-background shadow-8',\n shadow: 'shadow-8',\n glow: 'shadow-[0_0_0.5rem_0.125rem_var(--wave-primary)]',\n};\n\n/** A name with its whitespace collapsed, or `undefined` when it is blank. */\nfunction normaliseName(name: string | undefined): string | undefined {\n return name?.trim().replace(/\\s+/g, ' ') || undefined;\n}\n\ntype ImageErrorHandler = React.ReactEventHandler<HTMLImageElement>;\n\n/** Description props that go to the element carrying the avatar's name (data-display#19). */\ntype DescriptionProps = Pick<\n React.HTMLAttributes<HTMLElement>,\n 'aria-describedby' | 'aria-description' | 'aria-details'\n>;\n\n/** Props the avatar sets on an element or object image slot, over the slot's own. */\ninterface SlotOverrides {\n onError: ImageErrorHandler;\n alt?: string;\n 'aria-describedby'?: string;\n}\n\n/** The props of an element or object image slot that the avatar reads. */\ninterface ImageSlotProps {\n src?: unknown;\n srcSet?: unknown;\n alt?: unknown;\n onError?: unknown;\n 'aria-describedby'?: unknown;\n}\n\n/** The props of an element or object image slot (to read its `src`, `alt` and handlers). */\nfunction readSlotProps(slot: unknown): ImageSlotProps | undefined {\n if (React.isValidElement(slot)) return slot.props as ImageSlotProps;\n if (typeof slot === 'object' && slot !== null && !(Symbol.iterator in slot)) {\n return slot as ImageSlotProps;\n }\n return undefined;\n}\n\n/**\n * What identifies the image, so a load failure is kept for it and a new image is tried again\n * (data-display#4): the `src` and `srcSet`, of any type (a framework image component may take a\n * static-import object), and for an element slot also its type and `key`.\n */\ntype ImageKey = readonly [type: unknown, key: unknown, src: unknown, srcSet: unknown];\n\nfunction getImageKey(\n imageSlot: unknown,\n slotProps: ImageSlotProps | undefined,\n src: string | undefined,\n): ImageKey {\n if (imageSlot === undefined) return [undefined, undefined, src || undefined, undefined];\n if (typeof imageSlot === 'string') return [undefined, undefined, imageSlot, undefined];\n const element = React.isValidElement(imageSlot) ? imageSlot : undefined;\n return [element?.type, element?.key, slotProps?.src, slotProps?.srcSet];\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n/** Same value, or plain objects with the same entries (an inline `src={{ … }}` on each render). */\nfunction sameKeyPart(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n const keys = Object.keys(a);\n return (\n keys.length === Object.keys(b).length &&\n keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && Object.is(a[k], b[k]))\n );\n}\n\nfunction sameImageKey(a: ImageKey, b: ImageKey): boolean {\n return a.every((part, i) => sameKeyPart(part, b[i]));\n}\n\n/**\n * A circular picture of a person: their image, an icon, or the initials of their `name`.\n *\n * - **Image**: `image` (URL string, element or `{ src, alt }` object) or `src`. If the image fails\n * to load, the avatar falls back to `icon`, the initials or a person glyph, with their\n * background; a new image is tried again. A server-rendered image that failed before hydration\n * is detected after mount (settled without a natural size, confirmed by `decode()`).\n * - **Accessible name**: an image uses `name` (whitespace collapsed) as its `alt`, or `''` when the\n * name is blank. Without an image (none given, or it failed to load) the avatar is `role=\"img\"`\n * with `aria-label={name}` (without a name: the image slot's own `alt`) and the initials/icon\n * are hidden; without either (and without `aria-label`/`aria-labelledby`) it is decorative\n * (`aria-hidden`). A consumer `aria-label`, `aria-labelledby` or `role` (other than\n * `presentation`/`none`) puts the name on the avatar visual, and an image inside it gets\n * `alt=\"\"`. `decorative` hides it in every mode. `aria-describedby`, `aria-description` and\n * `aria-details` go to the element that carries the name: the `<img>` when a non-blank `alt`\n * names it, otherwise the avatar visual (never an `alt=\"\"` image).\n * - **Badge**: `badge` renders at the bottom end corner (logical, RTL-safe) as a sibling of the\n * avatar visual, so a `PresenceBadge` keeps its own name. With a badge, `ref`, `className` and\n * the rest props go to the outer wrapper.\n *\n * @example\n * <Avatar name=\"Jane Doe\" src=\"/jane.jpg\" badge={<PresenceBadge status=\"available\" />} />\n */\nexport const Avatar = ({\n src,\n name,\n size: sizeProp,\n shape: shapeProp,\n color: colorProp = 'brand',\n idForColor,\n active = 'unset',\n activeAppearance = 'ring',\n initials: initialsSlot,\n icon,\n image,\n badge,\n decorative = false,\n className,\n ref,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n ...rest\n}: AvatarProps) => {\n // The media box of a surrounding Tag or InteractionTag, for an Avatar without its own `size`.\n const box = React.useContext(AvatarSizeContext);\n const imageSlot =\n image === null || image === undefined || typeof image === 'boolean' || image === ''\n ? undefined\n : image;\n const slotProps = readSlotProps(imageSlot);\n const imageKey = getImageKey(imageSlot, slotProps, src);\n\n // Load failure of the current image; a new image is tried again (C-HOOKS: adjust state during\n // render on prop change).\n const [failed, setFailed] = React.useState(false);\n const [prevImageKey, setPrevImageKey] = React.useState(imageKey);\n if (!sameImageKey(prevImageKey, imageKey)) {\n setPrevImageKey(imageKey);\n setFailed(false);\n }\n\n // An image that failed before hydration (a fast 404 of server-rendered markup) never reaches\n // `onError`: React attaches the listener while hydrating and does not replay the event. After\n // mount, a settled image without a natural size is broken, unless it is an SVG without one, so\n // `decode()` (which rejects for a broken image) confirms it where the browser supports it. Later\n // images, rendered by the client, report their errors through `onError`.\n const visualRef = React.useRef<HTMLSpanElement>(null);\n React.useLayoutEffect(() => {\n const img = visualRef.current?.querySelector('img');\n if (!img || !img.complete || img.naturalWidth !== 0) return;\n let active = true;\n const checkedSrc = img.getAttribute('src');\n const checkedSrcSet = img.getAttribute('srcset');\n const markFailed = () => {\n // Deferred (C-HOOKS); skipped if the image was replaced or got another source meanwhile.\n if (\n active &&\n img.isConnected &&\n img.getAttribute('src') === checkedSrc &&\n img.getAttribute('srcset') === checkedSrcSet\n ) {\n setFailed(true);\n }\n };\n if (typeof img.decode === 'function') img.decode().then(undefined, markFailed);\n else queueMicrotask(markFailed);\n return () => {\n active = false;\n };\n }, []);\n const rootVisualRef = useMergedRefs(ref, visualRef);\n\n const hasConsumerName = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n // A consumer role goes to the visual span together with the name (data-display#19), so the\n // image inside it is presentational (`alt=\"\"`), as with a consumer `aria-label`: the children\n // of `role=\"img\"` are not exposed. A presentational role (`presentation`, `none`) cannot carry\n // a name, so the image keeps it.\n const roleCarriesName = role !== undefined && role !== 'presentation' && role !== 'none';\n const normalisedName = normaliseName(name);\n const imageAlt = decorative || hasConsumerName || roleCarriesName ? '' : (normalisedName ?? '');\n const handleError: ImageErrorHandler = () => setFailed(true);\n\n // The alt text the image renders with: an element or object slot's own `alt` wins over ours. An\n // `alt` that is `undefined` (an optional `alt: user.photoAlt`, a wrapper forwarding `alt={alt}`)\n // or `null` is not given, so ours applies (the slot's own value would otherwise override it).\n const ownAlt = slotProps?.alt;\n const hasOwnAlt = ownAlt !== undefined && ownAlt !== null;\n const renderedAlt: unknown = hasOwnAlt ? ownAlt : imageAlt;\n\n // The description goes to the element that carries the name: the image itself when a non-blank\n // `alt` names it (the visual span then has no role, or a presentational one), otherwise the\n // visual span. An image with an empty `alt` is presentational, and a description on it would\n // conflict with that role.\n const description: DescriptionProps = {\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n };\n const imageIsNamed =\n !decorative &&\n !hasConsumerName &&\n !roleCarriesName &&\n typeof renderedAlt === 'string' &&\n renderedAlt.trim() !== '';\n const imageDescription: DescriptionProps = imageIsNamed ? description : {};\n\n let imageNode: React.ReactNode = null;\n if (!failed) {\n if (typeof imageSlot === 'string') {\n imageNode = (\n <img\n src={imageSlot}\n alt={imageAlt}\n {...imageDescription}\n className={cn(avatarClassNames.image, IMAGE_CLASS)}\n onError={handleError}\n />\n );\n } else if (imageSlot !== undefined) {\n // The slot's own props win over renderSlot's defaults, so the composed `onError`, the\n // default `alt` (when the slot gives none) and the joined `aria-describedby` (the avatar's\n // ids, then the slot's) are set on the slot itself. A slot's own `aria-description` and\n // `aria-details` win over the avatar's.\n const overrides: SlotOverrides = {\n onError: composeEventHandlers(\n slotProps?.onError as ImageErrorHandler | undefined,\n handleError,\n ),\n };\n if (!hasOwnAlt) overrides.alt = imageAlt;\n if (imageIsNamed) {\n const ownIds = slotProps?.['aria-describedby'];\n overrides['aria-describedby'] = joinIds(\n ariaDescribedBy,\n typeof ownIds === 'string' ? ownIds : undefined,\n );\n }\n const slot = React.isValidElement(imageSlot)\n ? React.cloneElement(imageSlot as React.ReactElement<SlotOverrides>, overrides)\n : slotProps\n ? { ...(imageSlot as object), ...overrides }\n : imageSlot;\n imageNode = renderSlot(slot as Slot<'img'>, 'img', cn(avatarClassNames.image, IMAGE_CLASS), {\n alt: imageAlt,\n ...imageDescription,\n });\n } else if (src) {\n imageNode = (\n <img\n src={src}\n alt={imageAlt}\n {...imageDescription}\n className={cn(avatarClassNames.image, IMAGE_CLASS)}\n onError={handleError}\n />\n );\n }\n }\n const visualDescription = imageNode !== null && imageIsNamed ? {} : description;\n\n // A falsy icon (`icon={count && <Icon />}` with count 0) is no icon, as in 0.4, and so is a\n // collection whose items render nothing (`icon={items.map(…)}` mapping to nothing). The check\n // does not consume a generator: renderSlot still renders its items.\n const iconNode =\n imageNode === null && icon && slotRendersContent(icon)\n ? renderSlot(icon, 'span', cn(avatarClassNames.icon, 'inline-flex'), { 'aria-hidden': true })\n : null;\n const dir = useDirection();\n const group = React.useContext(AvatarGroupContext);\n // The wedges of a pie overlap at its centre: their initials would print over each other.\n const initialsClass = cn(avatarClassNames.initials, group?.layout === 'pie' && 'invisible');\n const resolvedColor = resolveAvatarColor(colorProp, idForColor, name);\n const showsInitials = imageNode === null && iconNode === null;\n const computedInitials = showsInitials && name ? getInitials(name, { dir }) : '';\n const initialsNode = !showsInitials ? null : initialsSlot && slotRendersContent(initialsSlot) ? (\n renderSlot(initialsSlot, 'span', initialsClass, { 'aria-hidden': true })\n ) : computedInitials ? (\n <span aria-hidden=\"true\" className={initialsClass}>\n {computedInitials}\n </span>\n ) : null;\n\n let content: React.ReactNode;\n let colorClass: string | undefined;\n if (imageNode !== null) {\n content = imageNode;\n } else if (iconNode !== null) {\n content = iconNode;\n colorClass = 'bg-muted text-muted-foreground';\n } else if (initialsNode) {\n content = initialsNode;\n colorClass = avatarColorClasses[resolvedColor];\n } else {\n content = <PersonIcon size=\"60%\" className={avatarClassNames.icon} />;\n colorClass = 'bg-muted text-muted-foreground';\n }\n\n // Accessible name of the avatar visual (data-display#19, #27). With a consumer role and an\n // image it is the name the image would carry: an element or object slot's own alt, else `name`.\n // Without an image (none given, or it failed to load) it is `name`, else the slot's own alt: an\n // avatar named only by its image alt keeps that name when the image fails.\n const imageName =\n imageNode !== null && roleCarriesName && typeof renderedAlt === 'string'\n ? normaliseName(renderedAlt)\n : undefined;\n const slotAltName = typeof ownAlt === 'string' ? normaliseName(ownAlt) : undefined;\n const label =\n ariaLabel ??\n (imageNode === null\n ? (normalisedName ?? slotAltName)\n : roleCarriesName\n ? (imageName ?? normalisedName)\n : undefined);\n let a11yProps: React.HTMLAttributes<HTMLSpanElement>;\n if (decorative) {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n } else if (label !== undefined || ariaLabelledBy !== undefined) {\n a11yProps = {\n role: role ?? 'img',\n 'aria-label': label,\n 'aria-labelledby': ariaLabelledBy,\n ...visualDescription,\n };\n } else if (imageNode !== null || role !== undefined) {\n a11yProps = { role, ...visualDescription };\n } else {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n }\n\n // A falsy or empty badge (`badge={count && <CounterBadge … />}` with count 0) is no badge, as in\n // 0.4 and like `icon`: no stray \"0\" in the corner, and no wrapper taking the root props.\n const badgeNode =\n badge && slotRendersContent(badge)\n ? renderSlot(badge, 'span', cn(avatarClassNames.badge, 'absolute bottom-0 end-0 inline-flex'))\n : null;\n const hasBadge = badgeNode !== null;\n\n // An own `size` wins; without one, the box of a surrounding tag's media, else medium. With a\n // badge the visual is sized, not the wrapper.\n const resolvedSize: AvatarSize =\n sizeProp ?? group?.size ?? (box ? (box.size as AvatarSize) : 'medium');\n const resolvedShape = shapeProp ?? box?.shape ?? 'circular';\n const pixels = avatarPixelSize(resolvedSize);\n const stateProps = {\n 'data-size': String(resolvedSize),\n 'data-shape': resolvedShape,\n 'data-color': resolvedColor,\n 'data-active': active,\n 'data-active-appearance': activeAppearance,\n };\n const visualClassName = cn(\n avatarClassNames.visual,\n 'inline-flex items-center justify-center overflow-hidden shrink-0 font-bold',\n avatarShapeClasses(resolvedShape, pixels),\n avatarSizeClassesFor(resolvedSize),\n colorClass,\n active === 'active' && activeClasses[activeAppearance],\n active === 'inactive' && 'opacity-60 transition-opacity motion-reduce:transition-none',\n );\n\n if (!hasBadge) {\n return (\n <span\n ref={rootVisualRef}\n {...a11yProps}\n {...stateProps}\n {...rest}\n className={cn(avatarClassNames.root, visualClassName, className)}\n >\n {content}\n </span>\n );\n }\n\n return (\n <span\n ref={ref}\n {...stateProps}\n {...rest}\n className={cn(avatarClassNames.root, 'relative inline-flex shrink-0', className)}\n >\n <span ref={visualRef} {...a11yProps} className={visualClassName}>\n {content}\n </span>\n <AvatarBadgeSizeContext.Provider value={avatarBadgeSize(pixels)}>\n {badgeNode}\n </AvatarBadgeSizeContext.Provider>\n </span>\n );\n};\n\nAvatar.displayName = 'Avatar';\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use client";var e=[`red`,`pumpkin`,`gold`,`forest`,`teal`,`blue`,`navy`,`lavender`,`purple`,`magenta`,`brown`,`steel`],t={neutral:`bg-muted text-muted-foreground`,brand:`bg-primary text-primary-foreground`,red:`bg-palette-red text-palette-red-foreground`,pumpkin:`bg-palette-pumpkin text-palette-pumpkin-foreground`,gold:`bg-palette-gold text-palette-gold-foreground`,forest:`bg-palette-forest text-palette-forest-foreground`,teal:`bg-palette-teal text-palette-teal-foreground`,blue:`bg-palette-blue text-palette-blue-foreground`,navy:`bg-palette-navy text-palette-navy-foreground`,lavender:`bg-palette-lavender text-palette-lavender-foreground`,purple:`bg-palette-purple text-palette-purple-foreground`,magenta:`bg-palette-magenta text-palette-magenta-foreground`,brown:`bg-palette-brown text-palette-brown-foreground`,steel:`bg-palette-steel text-palette-steel-foreground`};function n(t){let n=2166136261;for(let e=0;e<t.length;e+=1)n^=t.charCodeAt(e),n=Math.imul(n,16777619);return e[(n>>>0)%e.length]}function r(e,t,r){if(e!==`colorful`)return e;let i=t?.trim()||r?.trim();return i?n(i):`brand`}exports.AVATAR_NAMED_COLORS=e,exports.avatarColorClasses=t,exports.hashToNamedColor=n,exports.resolveAvatarColor=r;
2
+ //# sourceMappingURL=Avatar.colors.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.colors.cjs","names":["AVATAR_NAMED_COLORS","avatarColorClasses","hashToNamedColor","hash","index","key","resolveAvatarColor","color","idForColor","name"],"sources":["../../../src/components/data-display/Avatar.colors.ts"],"sourcesContent":["/** The named colors of an Avatar: the persona palette (`--wave-palette-<name>` tokens). */\nexport type AvatarNamedColor =\n | 'red'\n | 'pumpkin'\n | 'gold'\n | 'forest'\n | 'teal'\n | 'blue'\n | 'navy'\n | 'lavender'\n | 'purple'\n | 'magenta'\n | 'brown'\n | 'steel';\n\n/** The color of an Avatar that shows initials: a look, `colorful` (hashed), or a named color. */\nexport type AvatarColor = 'neutral' | 'brand' | 'colorful' | AvatarNamedColor;\n\n/** The named colors in a fixed order: `colorful` indexes into it, so the order never changes. */\nexport const AVATAR_NAMED_COLORS: readonly AvatarNamedColor[] = [\n 'red',\n 'pumpkin',\n 'gold',\n 'forest',\n 'teal',\n 'blue',\n 'navy',\n 'lavender',\n 'purple',\n 'magenta',\n 'brown',\n 'steel',\n];\n\n/** Fill and text classes of each resolved color. Literal strings only (Tailwind scans them). */\nexport const avatarColorClasses: Readonly<Record<Exclude<AvatarColor, 'colorful'>, string>> = {\n neutral: 'bg-muted text-muted-foreground',\n brand: 'bg-primary text-primary-foreground',\n red: 'bg-palette-red text-palette-red-foreground',\n pumpkin: 'bg-palette-pumpkin text-palette-pumpkin-foreground',\n gold: 'bg-palette-gold text-palette-gold-foreground',\n forest: 'bg-palette-forest text-palette-forest-foreground',\n teal: 'bg-palette-teal text-palette-teal-foreground',\n blue: 'bg-palette-blue text-palette-blue-foreground',\n navy: 'bg-palette-navy text-palette-navy-foreground',\n lavender: 'bg-palette-lavender text-palette-lavender-foreground',\n purple: 'bg-palette-purple text-palette-purple-foreground',\n magenta: 'bg-palette-magenta text-palette-magenta-foreground',\n brown: 'bg-palette-brown text-palette-brown-foreground',\n steel: 'bg-palette-steel text-palette-steel-foreground',\n};\n\n/**\n * The named color a string stands for: a 32-bit FNV-1a hash of its UTF-16 code units, modulo the\n * number of named colors. Stable across runs, the server and the client, and releases.\n */\nexport function hashToNamedColor(key: string): AvatarNamedColor {\n let hash = 0x811c9dc5;\n for (let index = 0; index < key.length; index += 1) {\n hash ^= key.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193);\n }\n return AVATAR_NAMED_COLORS[(hash >>> 0) % AVATAR_NAMED_COLORS.length]!;\n}\n\n/**\n * Resolves the `color` prop: `colorful` becomes the named color of `idForColor`, else of `name`,\n * else `brand`.\n */\nexport function resolveAvatarColor(\n color: AvatarColor,\n idForColor: string | undefined,\n name: string | undefined,\n): Exclude<AvatarColor, 'colorful'> {\n if (color !== 'colorful') return color;\n const key = idForColor?.trim() || name?.trim();\n return key ? hashToNamedColor(key) : 'brand';\n}\n"],"mappings":"aAmBA,IAAaA,EAAmD,CAC9D,MACA,UACA,OACA,SACA,OACA,OACA,OACA,WACA,SACA,UACA,QACA,OACF,EAGaC,EAAiF,CAC5F,QAAS,iCACT,MAAO,qCACP,IAAK,6CACL,QAAS,qDACT,KAAM,+CACN,OAAQ,mDACR,KAAM,+CACN,KAAM,+CACN,KAAM,+CACN,SAAU,uDACV,OAAQ,mDACR,QAAS,qDACT,MAAO,iDACP,MAAO,gDACT,EAMA,SAAgBC,EAAiB,EAA+B,CAC9D,IAAIC,EAAO,WACX,IAAK,IAAIC,EAAQ,EAAGA,EAAQC,EAAI,OAAQ,GAAS,EAC/C,GAAQA,EAAI,WAAWD,CAAK,EAC5B,EAAO,KAAK,KAAKD,EAAM,QAAU,EAEnC,OAAOH,GAAqBG,IAAS,GAAKH,EAAoB,OAChE,CAMA,SAAgBM,EACd,EACA,EACA,EACkC,CAClC,GAAIC,IAAU,WAAY,OAAOA,EACjC,IAAMF,EAAMG,GAAY,KAAK,GAAKC,GAAM,KAAK,EAC7C,OAAOJ,EAAMH,EAAiBG,CAAG,EAAI,OACvC"}
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ //#region src/components/data-display/Avatar.colors.ts
3
+ var e = [
4
+ "red",
5
+ "pumpkin",
6
+ "gold",
7
+ "forest",
8
+ "teal",
9
+ "blue",
10
+ "navy",
11
+ "lavender",
12
+ "purple",
13
+ "magenta",
14
+ "brown",
15
+ "steel"
16
+ ], t = {
17
+ neutral: "bg-muted text-muted-foreground",
18
+ brand: "bg-primary text-primary-foreground",
19
+ red: "bg-palette-red text-palette-red-foreground",
20
+ pumpkin: "bg-palette-pumpkin text-palette-pumpkin-foreground",
21
+ gold: "bg-palette-gold text-palette-gold-foreground",
22
+ forest: "bg-palette-forest text-palette-forest-foreground",
23
+ teal: "bg-palette-teal text-palette-teal-foreground",
24
+ blue: "bg-palette-blue text-palette-blue-foreground",
25
+ navy: "bg-palette-navy text-palette-navy-foreground",
26
+ lavender: "bg-palette-lavender text-palette-lavender-foreground",
27
+ purple: "bg-palette-purple text-palette-purple-foreground",
28
+ magenta: "bg-palette-magenta text-palette-magenta-foreground",
29
+ brown: "bg-palette-brown text-palette-brown-foreground",
30
+ steel: "bg-palette-steel text-palette-steel-foreground"
31
+ };
32
+ function n(t) {
33
+ let n = 2166136261;
34
+ for (let e = 0; e < t.length; e += 1) n ^= t.charCodeAt(e), n = Math.imul(n, 16777619);
35
+ return e[(n >>> 0) % e.length];
36
+ }
37
+ function r(e, t, r) {
38
+ if (e !== "colorful") return e;
39
+ let i = t?.trim() || r?.trim();
40
+ return i ? n(i) : "brand";
41
+ }
42
+ //#endregion
43
+ export { e as AVATAR_NAMED_COLORS, t as avatarColorClasses, n as hashToNamedColor, r as resolveAvatarColor };
44
+
45
+ //# sourceMappingURL=Avatar.colors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.colors.mjs","names":["AVATAR_NAMED_COLORS","avatarColorClasses","hashToNamedColor","hash","index","key","resolveAvatarColor","color","idForColor","name"],"sources":["../../../src/components/data-display/Avatar.colors.ts"],"sourcesContent":["/** The named colors of an Avatar: the persona palette (`--wave-palette-<name>` tokens). */\nexport type AvatarNamedColor =\n | 'red'\n | 'pumpkin'\n | 'gold'\n | 'forest'\n | 'teal'\n | 'blue'\n | 'navy'\n | 'lavender'\n | 'purple'\n | 'magenta'\n | 'brown'\n | 'steel';\n\n/** The color of an Avatar that shows initials: a look, `colorful` (hashed), or a named color. */\nexport type AvatarColor = 'neutral' | 'brand' | 'colorful' | AvatarNamedColor;\n\n/** The named colors in a fixed order: `colorful` indexes into it, so the order never changes. */\nexport const AVATAR_NAMED_COLORS: readonly AvatarNamedColor[] = [\n 'red',\n 'pumpkin',\n 'gold',\n 'forest',\n 'teal',\n 'blue',\n 'navy',\n 'lavender',\n 'purple',\n 'magenta',\n 'brown',\n 'steel',\n];\n\n/** Fill and text classes of each resolved color. Literal strings only (Tailwind scans them). */\nexport const avatarColorClasses: Readonly<Record<Exclude<AvatarColor, 'colorful'>, string>> = {\n neutral: 'bg-muted text-muted-foreground',\n brand: 'bg-primary text-primary-foreground',\n red: 'bg-palette-red text-palette-red-foreground',\n pumpkin: 'bg-palette-pumpkin text-palette-pumpkin-foreground',\n gold: 'bg-palette-gold text-palette-gold-foreground',\n forest: 'bg-palette-forest text-palette-forest-foreground',\n teal: 'bg-palette-teal text-palette-teal-foreground',\n blue: 'bg-palette-blue text-palette-blue-foreground',\n navy: 'bg-palette-navy text-palette-navy-foreground',\n lavender: 'bg-palette-lavender text-palette-lavender-foreground',\n purple: 'bg-palette-purple text-palette-purple-foreground',\n magenta: 'bg-palette-magenta text-palette-magenta-foreground',\n brown: 'bg-palette-brown text-palette-brown-foreground',\n steel: 'bg-palette-steel text-palette-steel-foreground',\n};\n\n/**\n * The named color a string stands for: a 32-bit FNV-1a hash of its UTF-16 code units, modulo the\n * number of named colors. Stable across runs, the server and the client, and releases.\n */\nexport function hashToNamedColor(key: string): AvatarNamedColor {\n let hash = 0x811c9dc5;\n for (let index = 0; index < key.length; index += 1) {\n hash ^= key.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193);\n }\n return AVATAR_NAMED_COLORS[(hash >>> 0) % AVATAR_NAMED_COLORS.length]!;\n}\n\n/**\n * Resolves the `color` prop: `colorful` becomes the named color of `idForColor`, else of `name`,\n * else `brand`.\n */\nexport function resolveAvatarColor(\n color: AvatarColor,\n idForColor: string | undefined,\n name: string | undefined,\n): Exclude<AvatarColor, 'colorful'> {\n if (color !== 'colorful') return color;\n const key = idForColor?.trim() || name?.trim();\n return key ? hashToNamedColor(key) : 'brand';\n}\n"],"mappings":";;AAmBA,IAAaA,IAAmD;CAC9D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GAGaC,IAAiF;CAC5F,SAAS;CACT,OAAO;CACP,KAAK;CACL,SAAS;CACT,MAAM;CACN,QAAQ;CACR,MAAM;CACN,MAAM;CACN,MAAM;CACN,UAAU;CACV,QAAQ;CACR,SAAS;CACT,OAAO;CACP,OAAO;AACT;AAMA,SAAgBC,EAAiB,GAA+B;CAC9D,IAAIC,IAAO;CACX,KAAK,IAAIC,IAAQ,GAAGA,IAAQC,EAAI,QAAQ,KAAS,GAE/C,AADA,KAAQA,EAAI,WAAWD,CAAK,GAC5B,IAAO,KAAK,KAAKD,GAAM,QAAU;CAEnC,OAAOH,GAAqBG,MAAS,KAAKH,EAAoB;AAChE;AAMA,SAAgBM,EACd,GACA,GACA,GACkC;CAClC,IAAIC,MAAU,YAAY,OAAOA;CACjC,IAAMF,IAAMG,GAAY,KAAK,KAAKC,GAAM,KAAK;CAC7C,OAAOJ,IAAMH,EAAiBG,CAAG,IAAI;AACvC"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("react");t=e.__toESM(t,1);var n=t.createContext(null);n.displayName=`AvatarSizeContext`,exports.AvatarSizeContext=n;
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("react");t=e.__toESM(t,1);var n=t.createContext(null);n.displayName=`AvatarBadgeSizeContext`;var r=t.createContext(null);r.displayName=`AvatarSizeContext`,exports.AvatarBadgeSizeContext=n,exports.AvatarSizeContext=r;
2
2
  //# sourceMappingURL=Avatar.context.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.context.cjs","names":["React","require_runtime","react","AvatarSizeContext"],"sources":["../../../src/components/data-display/Avatar.context.ts"],"sourcesContent":["import * as React from 'react';\n\n/*\n * The box a container gives the Avatars inside it that set no `size`: Tag and\n * InteractionTag.Primary provide it around their `media`, so a person's Avatar there matches the\n * tag's size. Internal (never exported from a barrel); imports no component, so a Tag that reads\n * it holds no Avatar code.\n */\n\n/**\n * The box a container gives the Avatars inside it that set no `size` (Tag and InteractionTag\n * media). Internal.\n */\nexport interface AvatarSizeContextValue {\n /** The box: 24px (Avatar's `extra-small`), or the internal 20px and 16px boxes. */\n size: 16 | 20 | 24;\n}\n\n/** Internal: provided by Tag and InteractionTag.Primary around their `media`; `null` elsewhere. */\nexport const AvatarSizeContext = React.createContext<AvatarSizeContextValue | null>(null);\nAvatarSizeContext.displayName = 'AvatarSizeContext';\n"],"mappings":"6GAmBA,IAAaG,EAAoBH,EAAM,cAA6C,IAAI,EACxF,EAAkB,YAAc"}
1
+ {"version":3,"file":"Avatar.context.cjs","names":["React","require_runtime","react","AvatarBadgeSizeContext","AvatarSizeContext"],"sources":["../../../src/components/data-display/Avatar.context.ts"],"sourcesContent":["import * as React from 'react';\nimport type { Size } from '../../lib/types';\n\n/*\n * The box a container gives the Avatars inside it that set no `size`: Tag and\n * InteractionTag.Primary provide it around their `media`, so a person's Avatar there matches the\n * tag's size. Internal (never exported from a barrel); imports no component, so a Tag that reads\n * it holds no Avatar code.\n */\n\n/**\n * The box a container gives the Avatars inside it that set no `size` (Tag and InteractionTag\n * media). Internal.\n */\nexport interface AvatarSizeContextValue {\n /** The box in pixels: a tag's 24px (Avatar's `extra-small`), 20px or 16px box, or a group's size. */\n size: number;\n /** The shape the container gives its Avatars: a rounded Tag gives `square`. */\n shape?: 'circular' | 'square';\n}\n\n/**\n * The size a badge in an Avatar's `badge` slot takes when it sets none (PresenceBadge): derived from\n * the avatar's pixel size. Internal.\n */\nexport const AvatarBadgeSizeContext = React.createContext<Size | null>(null);\nAvatarBadgeSizeContext.displayName = 'AvatarBadgeSizeContext';\n\n/** Internal: provided by Tag and InteractionTag.Primary around their `media`; `null` elsewhere. */\nexport const AvatarSizeContext = React.createContext<AvatarSizeContextValue | null>(null);\nAvatarSizeContext.displayName = 'AvatarSizeContext';\n"],"mappings":"6GAyBA,IAAaG,EAAyBH,EAAM,cAA2B,IAAI,EAC3E,EAAuB,YAAc,yBAGrC,IAAaI,EAAoBJ,EAAM,cAA6C,IAAI,EACxF,EAAkB,YAAc"}
@@ -2,8 +2,10 @@
2
2
  import * as e from "react";
3
3
  //#region src/components/data-display/Avatar.context.ts
4
4
  var t = e.createContext(null);
5
- t.displayName = "AvatarSizeContext";
5
+ t.displayName = "AvatarBadgeSizeContext";
6
+ var n = e.createContext(null);
7
+ n.displayName = "AvatarSizeContext";
6
8
  //#endregion
7
- export { t as AvatarSizeContext };
9
+ export { t as AvatarBadgeSizeContext, n as AvatarSizeContext };
8
10
 
9
11
  //# sourceMappingURL=Avatar.context.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.context.mjs","names":["React","AvatarSizeContext"],"sources":["../../../src/components/data-display/Avatar.context.ts"],"sourcesContent":["import * as React from 'react';\n\n/*\n * The box a container gives the Avatars inside it that set no `size`: Tag and\n * InteractionTag.Primary provide it around their `media`, so a person's Avatar there matches the\n * tag's size. Internal (never exported from a barrel); imports no component, so a Tag that reads\n * it holds no Avatar code.\n */\n\n/**\n * The box a container gives the Avatars inside it that set no `size` (Tag and InteractionTag\n * media). Internal.\n */\nexport interface AvatarSizeContextValue {\n /** The box: 24px (Avatar's `extra-small`), or the internal 20px and 16px boxes. */\n size: 16 | 20 | 24;\n}\n\n/** Internal: provided by Tag and InteractionTag.Primary around their `media`; `null` elsewhere. */\nexport const AvatarSizeContext = React.createContext<AvatarSizeContextValue | null>(null);\nAvatarSizeContext.displayName = 'AvatarSizeContext';\n"],"mappings":";;;AAmBA,IAAaC,IAAoBD,EAAM,cAA6C,IAAI;AACxF,EAAkB,cAAc"}
1
+ {"version":3,"file":"Avatar.context.mjs","names":["React","AvatarBadgeSizeContext","AvatarSizeContext"],"sources":["../../../src/components/data-display/Avatar.context.ts"],"sourcesContent":["import * as React from 'react';\nimport type { Size } from '../../lib/types';\n\n/*\n * The box a container gives the Avatars inside it that set no `size`: Tag and\n * InteractionTag.Primary provide it around their `media`, so a person's Avatar there matches the\n * tag's size. Internal (never exported from a barrel); imports no component, so a Tag that reads\n * it holds no Avatar code.\n */\n\n/**\n * The box a container gives the Avatars inside it that set no `size` (Tag and InteractionTag\n * media). Internal.\n */\nexport interface AvatarSizeContextValue {\n /** The box in pixels: a tag's 24px (Avatar's `extra-small`), 20px or 16px box, or a group's size. */\n size: number;\n /** The shape the container gives its Avatars: a rounded Tag gives `square`. */\n shape?: 'circular' | 'square';\n}\n\n/**\n * The size a badge in an Avatar's `badge` slot takes when it sets none (PresenceBadge): derived from\n * the avatar's pixel size. Internal.\n */\nexport const AvatarBadgeSizeContext = React.createContext<Size | null>(null);\nAvatarBadgeSizeContext.displayName = 'AvatarBadgeSizeContext';\n\n/** Internal: provided by Tag and InteractionTag.Primary around their `media`; `null` elsewhere. */\nexport const AvatarSizeContext = React.createContext<AvatarSizeContextValue | null>(null);\nAvatarSizeContext.displayName = 'AvatarSizeContext';\n"],"mappings":";;;AAyBA,IAAaC,IAAyBD,EAAM,cAA2B,IAAI;AAC3E,EAAuB,cAAc;AAGrC,IAAaE,IAAoBF,EAAM,cAA6C,IAAI;AACxF,EAAkB,cAAc"}