@norconsult-digital-public/isypixel 2.0.2-canary.5 → 2.0.2-canary.6

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 (43) hide show
  1. package/dist/docs/web-components/ip-avatar/llm.md +27 -21
  2. package/dist/docs/web-components/ip-avatar-stack/llm.md +169 -0
  3. package/dist/llm.md +1 -0
  4. package/dist/types/components/ip-avatar-stack/ip-avatar-stack.d.ts +96 -0
  5. package/dist/types/components.d.ts +144 -0
  6. package/dist/types/index.d.ts +1 -0
  7. package/dist/web-components/components/index.js +1 -1
  8. package/dist/web-components/components/ip-avatar-stack.d.ts +11 -0
  9. package/dist/web-components/components/ip-avatar-stack.js +1 -0
  10. package/dist/web-components/components/ip-avatar.js +1 -1
  11. package/dist/web-components/components/ip-button.js +1 -1
  12. package/dist/web-components/components/ip-icon-button.js +1 -1
  13. package/dist/web-components/components/ip-main-menu-group.js +1 -1
  14. package/dist/web-components/components/ip-main-menu-item.js +1 -1
  15. package/dist/web-components/components/ip-main-menu-section.js +1 -1
  16. package/dist/web-components/components/ip-main-menu-toggle.js +1 -1
  17. package/dist/web-components/components/ip-main-menu.js +1 -1
  18. package/dist/web-components/components/ip-tooltip.js +1 -1
  19. package/dist/web-components/components/{p-DrSqAb5O.js → p-6Z45CK1I.js} +1 -1
  20. package/dist/web-components/components/{p-uroVS1GL.js → p-BYKtWIr5.js} +1 -1
  21. package/dist/web-components/components/{p-CSHQqNXM.js → p-BkayA00u.js} +1 -1
  22. package/dist/web-components/components/{p-B7DtfC4M.js → p-C1tFWpS0.js} +1 -1
  23. package/dist/web-components/components/{p-DpQ72I9n.js → p-CXMRqnYG.js} +1 -1
  24. package/dist/web-components/components/p-D7rK08OB.js +1 -0
  25. package/dist/web-components/components/{p-CPd0stBB.js → p-DWpUb5rd.js} +1 -1
  26. package/dist/web-components/components/{p-C7Kf2aED.js → p-MvV7276a.js} +1 -1
  27. package/dist/web-components/components/{p-CWbwcDHB.js → p-VLgeZTve.js} +1 -1
  28. package/dist/web-components/components/{p-B1x6AHw9.js → p-msgdnxTK.js} +1 -1
  29. package/dist/web-components/generated/angular/components.d.ts +54 -0
  30. package/dist/web-components/generated/angular/components.js +83 -0
  31. package/dist/web-components/generated/angular/components.ts +71 -0
  32. package/dist/web-components/generated/angular/index.d.ts +1 -1
  33. package/dist/web-components/generated/angular/index.js +1 -0
  34. package/dist/web-components/generated/angular/index.ts +1 -0
  35. package/dist/web-components/generated/react/components.d.ts +6 -1
  36. package/dist/web-components/generated/react/components.js +9 -0
  37. package/dist/web-components/generated/react/components.ts +13 -1
  38. package/dist/web-components/generated/svelte/components.d.ts +2 -0
  39. package/dist/web-components/generated/vue/components.d.ts +1 -0
  40. package/dist/web-components/generated/vue/components.js +16 -0
  41. package/dist/web-components/generated/vue/components.ts +18 -0
  42. package/llms.txt +1 -0
  43. package/package.json +1 -1
@@ -72,27 +72,33 @@ A theme-aware user avatar. It shows a profile image with a fallback chain (prima
72
72
 
73
73
  > The avatar reuses generic semantic tokens as theme-aware stand-ins; a dedicated avatar token family (background, foreground, ring, container hover/selected) does not exist yet. Avatar sizes are literal pixel values for the same reason. These are marked `//TODO` in the SCSS and will be replaced once design adds the avatar tokens.
74
74
 
75
- | SCSS variable | Role |
76
- | ------------------------------------------------------------ | ----------------------------------------------------------------- |
77
- | `$ip_color_surface_primary_default` | Ring layer background behind the ring gap |
78
- | `$ip_color_text_primary_blue` | Foreground colour: initials, silhouette, loader, and default ring |
79
- | `$ip_color_border_opaque_15` | No-image background behind initials / silhouette |
80
- | `$ip_color_border_opaque_10` | Clickable container hover background and the stripe overlay |
81
- | `$ip_color_border_opaque_20` | Selected container background |
82
- | `$ip_color_button_focus_default` | Focus ring when `clickable` |
83
- | `$ip_color_brand_accent_primary_default` | Ring colour for `ring-variant="brand"` |
84
- | `$ip_color_extended_green-500` | Ring colour for `ring-variant="success"` |
85
- | `$ip_color_extended_yellow-500` | Ring colour for `ring-variant="warning"` |
86
- | `$ip_color_extended_red-500` | Ring colour for `ring-variant="error"` |
87
- | `$ip_color_text_primary_black` | Label (name) text colour |
88
- | `$ip_radius_size_small` | Framed container and focus-ring radius |
89
- | `$ip_spacing_space_050` / `$ip_spacing_space_200` | Framed container padding |
90
- | `$ip_spacing_space_100` / `_200` / `_300` | Avatar↔label gap defaults for small / medium / large |
91
- | `$ip_typography_body_font_size_small` / `_medium` / `_large` | Initials and label font size per size |
92
- | `$ip_typography_body_line_height_compact` | Line height for the name and caption lines |
93
- | `$ip_typography_font_family_primary` | Font family for initials and label |
94
- | `$ip_typography_font_weight_bold_700` | Initials font weight |
95
- | `$ip_typography_font_weight_light_300` | Caption and placeholder font weight (italic) |
75
+ | SCSS variable | Role |
76
+ | ------------------------------------------------------------ | ------------------------------------------------------------------------ |
77
+ | `$ip_color_surface_primary_default` | Ring layer background behind the ring gap |
78
+ | `$ip_color_text_primary_blue` | Foreground colour: initials, silhouette, loader, and default ring |
79
+ | `$ip_color_border_opaque_15` | No-image background behind initials / silhouette |
80
+ | `$ip_color_border_opaque_10` | Clickable container hover background and the stripe overlay |
81
+ | `$ip_color_border_opaque_20` | Selected container background |
82
+ | `$ip_color_button_focus_default` | Focus ring when `clickable` |
83
+ | `$ip_color_brand_accent_primary_default` | Ring colour for `ring-variant="brand"` |
84
+ | `$ip_color_extended_green-500` | Ring colour for `ring-variant="success"` |
85
+ | `$ip_color_extended_yellow-500` | Ring colour for `ring-variant="warning"` |
86
+ | `$ip_color_extended_red-500` | Ring colour for `ring-variant="error"` |
87
+ | `$ip_color_text_primary_black` | Default label (name) text colour; override with `--ip-avatar-text-color` |
88
+ | `$ip_radius_size_small` | Framed container and focus-ring radius |
89
+ | `$ip_spacing_space_050` / `$ip_spacing_space_200` | Framed container padding |
90
+ | `$ip_spacing_space_100` / `_200` / `_300` | Avatar↔label gap defaults for small / medium / large |
91
+ | `$ip_typography_body_font_size_small` / `_medium` / `_large` | Initials and label font size per size |
92
+ | `$ip_typography_body_line_height_compact` | Line height for the name and caption lines |
93
+ | `$ip_typography_font_family_primary` | Font family for initials and label |
94
+ | `$ip_typography_font_weight_bold_700` | Initials font weight |
95
+ | `$ip_typography_font_weight_light_300` | Caption and placeholder font weight (italic) |
96
+
97
+ ## CSS custom properties
98
+
99
+ | Property | Default | Role |
100
+ | ------------------------ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | `--ip-avatar-text-color` | `$ip_color_text_primary_black` | Label (name and caption) text colour. Set to `currentColor` so the label inherits its surroundings — e.g. on a dark surface such as inside a tooltip. |
96
102
 
97
103
  ## Usage
98
104
 
@@ -0,0 +1,169 @@
1
+ # ip-avatar-stack
2
+
3
+ A stacked-avatar component that overlaps multiple `ip-avatar` instances and shows a combined names label (the first person's name plus a `+N` count for the rest). Consumers place `ip-avatar` elements in the default slot; the stack manages the overlap, the overflow collapse into a `+N` counter, and the label. It reuses each avatar's own ring to keep the overlapping circles visually distinct.
4
+
5
+ **Storybook:** https://pixel.isy.no/storybook/?path=/docs/core-ui-ip-avatar-stack-usage--docs
6
+
7
+ > This component requires ISY Pixel global styles to render correctly. Always remind the user to verify that styles are imported in their global stylesheet. See `llm.md` in the package root for style import options.
8
+
9
+ > Place `ip-avatar` elements in the default slot. The stack forces each slotted avatar to `image-only`, so you do not need to set `mode` yourself. In `label-mode="name"` the stack reads the first avatar's `name` for the text label, so set `name` on at least the first avatar. Match each avatar's `size` to the stack's `size` so the circles align.
10
+
11
+ ## Attributes
12
+
13
+ | Attribute | Type | Default | Values | Description |
14
+ | ----------------- | ------- | ----------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | `max` | number | `3` | Any integer ≥ 1 | Maximum number of avatars rendered (the hard cap). Surplus avatars are hidden; in `label-mode="counter"` they collapse into a `+N` counter circle (N = total − visible). |
16
+ | `auto-fit` | boolean | `true` | `true`, `false` | Reduces the visible avatars to fit the container width, collapsing the rest like `max` does (`max` stays the cap). Set `false` for a fixed stack that always shows up to `max`. |
17
+ | `size` | string | `"medium"` | `"small"`, `"medium"`, `"large"`, `"custom"` | Stack size: drives the overflow counter size (small 40px, medium 50px, large 64px) and the default overlap. |
18
+ | `custom-size` | string | `undefined` | Any CSS length | Diameter of the overflow counter when `size="custom"`, applied to both width and height. |
19
+ | `overlap` | string | `undefined` | Any CSS length | Overrides the overlap offset between circles. Defaults scale with `size` (small 20px, medium 24px, large 30px). |
20
+ | `label-mode` | string | `"name"` | `"name"`, `"counter"`, `"none"` | How the group is summarised. `name`: first name + `+N` rest text label. `counter`: a `+N` counter circle in the stack. `none`: nothing. The counter circle and the text label are mutually exclusive. |
21
+ | `gap` | string | `undefined` | Any CSS length | Overrides the gap between the stack and the label. Defaults scale with `size`. |
22
+ | `preview-avatars` | boolean | `true` | `true`, `false` | Whether the tooltip previews (the counter preview and the names-label tooltip) show a small avatar beside each person. Set `false` for a compact, name-only list. |
23
+ | `clickable` | boolean | `false` | `true`, `false` | Makes the `+N` overflow counter (in `label-mode="counter"`) focusable, keyboard-activatable, and emits `ipOverflowClick`. |
24
+ | `disabled` | boolean | `false` | `true`, `false` | Greys out the stack and, when `clickable`, prevents activation of the overflow counter. |
25
+
26
+ **Counter mode** — In `label-mode="counter"`, when more avatars are slotted than are shown, the visible avatars are shown (up to `max`, or fewer with `auto-fit`) and the remainder collapse into a single `+N` counter circle appended to the stack, where N = total − visible. No text label is shown. The collapsed avatars are hidden from the layout and the accessibility tree. Hovering or focusing the counter opens a tooltip previewing the collapsed people. The preview reads `name`, `src`, and `caption` from the collapsed `ip-avatar` elements; by default each row shows a small `ip-avatar` with its name and caption, or just the name and caption when `preview-avatars="false"`.
27
+
28
+ **Auto-fit** — On by default. The stack measures its container width and shows as many avatars as fit, collapsing the rest into the `+N` counter (counter mode) or reflecting them in the `+N` text (name mode). `max` remains the upper cap, so the visible count is never more than `max` and adapts down as the container narrows. It needs a width-constrained ancestor to measure against; place the stack in its own container for best results. Set `auto-fit="false"` for a fixed stack that always shows up to `max`.
29
+
30
+ **Names label** — In `label-mode="name"` the text label reads the first slotted avatar's `name` and appends `+N`, where N is the number of remaining avatars (total − 1), e.g. `Markus Halvorsen +2` for three avatars. This mode shows no counter circle. When more than one avatar is slotted, hovering the label opens a tooltip listing every slotted person (not just the overflow), using the same preview rows as counter mode and honouring `preview-avatars`.
31
+
32
+ ## Slots
33
+
34
+ | Slot | Description |
35
+ | ---------- | ---------------------------------------------------------------------------------- |
36
+ | `default` | The `ip-avatar` elements to stack. Non-`ip-avatar` nodes are ignored. |
37
+ | `label` | Overrides the auto-generated text label. Shown in `label-mode="name"`. |
38
+ | `overflow` | Overrides the content of the `+N` counter circle. Shown in `label-mode="counter"`. |
39
+
40
+ ## Events
41
+
42
+ | Event | Payload | When |
43
+ | ----------------- | ------- | ---------------------------------------------------------------------------------------------- |
44
+ | `ipOverflowClick` | `void` | A `clickable` overflow counter is activated by mouse or keyboard. Not emitted when `disabled`. |
45
+
46
+ ## Accessibility
47
+
48
+ - The overlap and paint order are presentational only; each slotted `ip-avatar` keeps its own accessible name and tooltip.
49
+ - Avatars collapsed into the `+N` counter are hidden from the accessibility tree; their names are surfaced through the counter's tooltip preview instead.
50
+ - When `clickable`, the overflow counter exposes `role="button"`, is focusable (`tabindex="0"`), shows a `:focus-visible` ring, is activated with Enter or Space, and carries an `aria-label` describing how many more there are.
51
+ - When `disabled`, a clickable counter sets `aria-disabled="true"` and blocks `ipOverflowClick`.
52
+
53
+ ## Design tokens
54
+
55
+ > The stack reuses generic semantic tokens as theme-aware stand-ins; a dedicated avatar token family (background, foreground, ring, sizes, overlap) does not exist yet. Counter sizes and overlap offsets are literal pixel values for the same reason. These are marked `//TODO` in the SCSS and will be replaced once design adds the avatar tokens.
56
+
57
+ | SCSS variable | Role |
58
+ | ------------------------------------------------------------ | ---------------------------------------------------------- |
59
+ | `$ip_color_surface_primary_default` | Overflow counter ring layer background behind the ring gap |
60
+ | `$ip_color_text_primary_blue` | Overflow counter ring colour and `+N` text colour |
61
+ | `$ip_color_border_opaque_15` | Overflow counter inner background behind the `+N` text |
62
+ | `$ip_color_button_focus_default` | Focus ring of the overflow counter when `clickable` |
63
+ | `$ip_color_text_tertiary_default` | Names label text colour |
64
+ | `$ip_spacing_space_100` / `_200` / `_300` | Stack↔label gap defaults for small / medium / large |
65
+ | `$ip_typography_body_font_size_small` / `_medium` / `_large` | Counter and label font size per size |
66
+ | `$ip_typography_body_line_height_compact` | Label line height |
67
+ | `$ip_typography_font_family_primary` | Font family for the counter and label |
68
+ | `$ip_typography_font_weight_bold_700` | `+N` counter font weight |
69
+
70
+ ## Usage
71
+
72
+ ### HTML
73
+
74
+ > Attribute names must be **kebab-case** in HTML: use `label-mode`, not `labelMode`.
75
+
76
+ ```js
77
+ import '@norconsult-digital-public/isypixel/web-components/ip-avatar-stack.js';
78
+ import '@norconsult-digital-public/isypixel/web-components/ip-avatar.js';
79
+ ```
80
+
81
+ ```html
82
+ <ip-avatar-stack max="3" size="medium" label-mode="name">
83
+ <ip-avatar name="Markus Halvorsen" src="/users/markus.jpg"></ip-avatar>
84
+ <ip-avatar name="Mia Berg" src="/users/mia.jpg"></ip-avatar>
85
+ <ip-avatar name="Jonas Lie" src="/users/jonas.jpg"></ip-avatar>
86
+ <ip-avatar name="Sara Nilsen"></ip-avatar>
87
+ </ip-avatar-stack>
88
+ ```
89
+
90
+ ### React
91
+
92
+ ```tsx
93
+ import { IpAvatarStack, IpAvatar } from '@norconsult-digital-public/isypixel/web-components/react';
94
+
95
+ <IpAvatarStack max={3} size='medium' labelMode='name' clickable onIpOverflowClick={() => {}}>
96
+ <IpAvatar name='Markus Halvorsen' src='/users/markus.jpg' />
97
+ <IpAvatar name='Mia Berg' src='/users/mia.jpg' />
98
+ <IpAvatar name='Jonas Lie' src='/users/jonas.jpg' />
99
+ <IpAvatar name='Sara Nilsen' />
100
+ </IpAvatarStack>;
101
+ ```
102
+
103
+ ### Vue
104
+
105
+ ```vue
106
+ <script setup lang="ts">
107
+ import { IpAvatarStack, IpAvatar } from '@norconsult-digital-public/isypixel/web-components/vue';
108
+ </script>
109
+
110
+ <template>
111
+ <IpAvatarStack :max="3" size="medium" label-mode="name">
112
+ <IpAvatar name="Markus Halvorsen" src="/users/markus.jpg" />
113
+ <IpAvatar name="Mia Berg" src="/users/mia.jpg" />
114
+ <IpAvatar name="Jonas Lie" src="/users/jonas.jpg" />
115
+ <IpAvatar name="Sara Nilsen" />
116
+ </IpAvatarStack>
117
+ </template>
118
+ ```
119
+
120
+ ### Angular
121
+
122
+ ```ts
123
+ import { IpAvatarStack, IpAvatar } from '@norconsult-digital-public/isypixel/web-components/angular';
124
+ ```
125
+
126
+ ```html
127
+ <ip-avatar-stack [max]="3" size="medium" label-mode="name" clickable (ipOverflowClick)="onShowAll()">
128
+ <ip-avatar name="Markus Halvorsen" [src]="markusUrl"></ip-avatar>
129
+ <ip-avatar name="Mia Berg" [src]="miaUrl"></ip-avatar>
130
+ <ip-avatar name="Jonas Lie" [src]="jonasUrl"></ip-avatar>
131
+ <ip-avatar name="Sara Nilsen"></ip-avatar>
132
+ </ip-avatar-stack>
133
+ ```
134
+
135
+ ### Svelte
136
+
137
+ ```svelte
138
+ <script lang="ts">
139
+ import '@norconsult-digital-public/isypixel/web-components/ip-avatar-stack.js';
140
+ import '@norconsult-digital-public/isypixel/web-components/ip-avatar.js';
141
+ </script>
142
+
143
+ <ip-avatar-stack max="3" size="medium" label-mode="name">
144
+ <ip-avatar name="Markus Halvorsen" src="/users/markus.jpg"></ip-avatar>
145
+ <ip-avatar name="Mia Berg" src="/users/mia.jpg"></ip-avatar>
146
+ <ip-avatar name="Jonas Lie" src="/users/jonas.jpg"></ip-avatar>
147
+ <ip-avatar name="Sara Nilsen"></ip-avatar>
148
+ </ip-avatar-stack>
149
+ ```
150
+
151
+ ### Blazor
152
+
153
+ > Attribute names must be **kebab-case** in Blazor HTML.
154
+
155
+ Copy `ip-avatar-stack.js` and `ip-avatar.js` to `wwwroot/lib/isypixel/components/` at build time. See `llm.md` in the package root for the MSBuild target.
156
+
157
+ ```html
158
+ <script type="module" src="lib/isypixel/components/ip-avatar-stack.js"></script>
159
+ <script type="module" src="lib/isypixel/components/ip-avatar.js"></script>
160
+
161
+ <ip-avatar-stack max="3" size="medium" label-mode="name">
162
+ <ip-avatar name="Markus Halvorsen" src="/users/markus.jpg"></ip-avatar>
163
+ <ip-avatar name="Mia Berg" src="/users/mia.jpg"></ip-avatar>
164
+ <ip-avatar name="Jonas Lie" src="/users/jonas.jpg"></ip-avatar>
165
+ <ip-avatar name="Sara Nilsen"></ip-avatar>
166
+ </ip-avatar-stack>
167
+ ```
168
+
169
+ - Boolean attributes such as `clickable` and `disabled` must be set as `clickable="true"` (string value) — Blazor does not reliably render bare boolean attributes on custom elements.
package/dist/llm.md CHANGED
@@ -175,6 +175,7 @@ In `index.html` `<body>`:
175
175
  | `ip-icon-button` | Circular icon-only button with CSS ripple, visual variants, and loading state | `docs/web-components/ip-icon-button/llm.md` |
176
176
  | `ip-tooltip` | Tooltip that wraps a trigger and shows non-interactive content on hover, focus, and Escape | `docs/web-components/ip-tooltip/llm.md` |
177
177
  | `ip-avatar` | Theme-aware avatar with image/fallback/initials/silhouette chain, label, ring, loader, corner slots, and tooltip | `docs/web-components/ip-avatar/llm.md` |
178
+ | `ip-avatar-stack` | Overlapping stack of `ip-avatar` elements with overflow collapse into a `+N` counter and a combined names label | `docs/web-components/ip-avatar-stack/llm.md` |
178
179
  | `ip-main-menu` | Sidebar navigation wrapper with expanded/auto-hide/collapsed desktop modes and mobile drawer | `docs/web-components/ip-main-menu/llm.md` |
179
180
  | `ip-main-menu-toggle` | Hamburger button for the top bar that opens the `ip-main-menu` mobile drawer via a shared store | `docs/web-components/ip-main-menu-toggle/llm.md` |
180
181
  | `ip-main-menu-section` | Semantic section wrapper that groups navigation items with an optional visible label | `docs/web-components/ip-main-menu-section/llm.md` |
@@ -0,0 +1,96 @@
1
+ import { EventEmitter, JSX } from '../../stencil-public-runtime';
2
+ export declare class IpAvatarStack {
3
+ /** Host element, used to keep the stacking layout in sync when layout props change. */
4
+ private host;
5
+ /** Maximum number of avatars shown before the rest collapse into a `+N` counter. Acts as the hard cap; with `auto-fit` on, fewer may show. */
6
+ max: number;
7
+ /** Reduces the visible avatars to fit the container width, collapsing the rest like `max` does (`max` stays the cap). Set `auto-fit="false"` for a fixed stack that always shows up to `max`. */
8
+ autoFit: boolean;
9
+ /** Stack size. Drives the overflow counter size and the default overlap; slotted avatars should match. */
10
+ size: 'small' | 'medium' | 'large' | 'custom';
11
+ /** Diameter of the overflow counter when `size="custom"`. Any CSS length, applied to both width and height. */
12
+ customSize: string | undefined;
13
+ /** Overrides the overlap offset between circles. Any CSS length; defaults scale with `size`. */
14
+ overlap: string | undefined;
15
+ /** How the group is summarised: the first name with a `+N` rest count, a `+N` overflow counter circle in the stack, or nothing. The counter circle and the text label are mutually exclusive. */
16
+ labelMode: 'name' | 'counter' | 'none';
17
+ /** Overrides the gap between the stack and the label. Any CSS length; defaults scale with `size`. */
18
+ gap: string | undefined;
19
+ /** Whether the tooltip previews (the counter and the names label) show a small avatar beside each person, or just the name and caption. */
20
+ previewAvatars: boolean;
21
+ /** Makes the `+N` overflow counter focusable, keyboard-activatable, and emits `ipOverflowClick`. */
22
+ clickable: boolean;
23
+ /** Greys out the stack and, when `clickable`, prevents activation of the overflow counter. */
24
+ disabled: boolean;
25
+ /** Identity of every slotted avatar, in order. Drives the count, the names label, the counter, and both tooltip previews. */
26
+ private people;
27
+ /** Width-fitted cap computed by the width observer when `autoFit` is on; `null` until first measured (then `effectiveMax` falls back to `max`). */
28
+ private fittedMax;
29
+ /** Emitted when a clickable, non-disabled overflow counter is activated by mouse or keyboard. */
30
+ overflowClick: EventEmitter<void>;
31
+ /** Active width observer driving auto-fit; null when auto-fit is off or the element is detached. */
32
+ private resizeObserver;
33
+ /** True once the first render has measured; gates restart-on-reconnect so the very first connect defers to `componentDidLoad`. */
34
+ private hasLoaded;
35
+ /** Re-reads the slot and re-applies the layout when the collapse threshold changes. */
36
+ relayout(): void;
37
+ /** Re-fits when a dimension-affecting prop changes — the count that fits depends on avatar size and overlap. */
38
+ onDimensionChange(): void;
39
+ /** Starts or stops width observation when auto-fit is toggled, resetting the fitted cap. */
40
+ onAutoFitChange(): void;
41
+ /** Restarts width observation when the element is re-attached after a move; the first connect defers to `componentDidLoad`. */
42
+ connectedCallback(): void;
43
+ /** Begins observing the container width once the element and its slotted avatars exist. */
44
+ componentDidLoad(): void;
45
+ /** Tears down the width observer when the element leaves the DOM. */
46
+ disconnectedCallback(): void;
47
+ /** The `ip-avatar` elements currently assigned to the default slot. */
48
+ private assignedAvatars;
49
+ /** Reads the first avatar's name from the `name` property, falling back to the attribute for plain-HTML consumers. */
50
+ private nameOf;
51
+ /** Reads an avatar's image source from the `src` property, falling back to the attribute for plain-HTML consumers. */
52
+ private srcOf;
53
+ /** Reads an avatar's caption from the `caption` property, falling back to the attribute for plain-HTML consumers. */
54
+ private captionOf;
55
+ /** Records every slotted avatar's identity, then applies the stacking layout. */
56
+ private syncFromSlot;
57
+ /** Re-reads the slotted avatars whenever the assigned set changes. */
58
+ private handleSlotChange;
59
+ /** Forces each avatar to image-only, paints each above the one before it (last on top), and collapses avatars beyond `effectiveMax` into the overflow counter. */
60
+ private applyStacking;
61
+ /** Avatars to show before collapsing: the width-fitted cap (never above `max`) in auto-fit mode, otherwise `max`. */
62
+ private get effectiveMax();
63
+ /** Observes the parent's width (container resize) and the window (ambient resize) so the fitted cap stays in sync. */
64
+ private startObserving;
65
+ /** Disconnects the width observer and the window listener. */
66
+ private stopObserving;
67
+ /** Measures the container and updates the fitted cap, re-applying the stacking layout when it changes. */
68
+ private recomputeFit;
69
+ /** Content-box width available to the stack, measured from its parent. */
70
+ private availableWidth;
71
+ /** Current overlap offset in px, read from the computed custom property (set per size or via the `overlap` prop). */
72
+ private measuredOverlap;
73
+ /** Current stack↔label gap in px, read from the computed custom property (set per size or via the `gap` prop). */
74
+ private measuredGap;
75
+ /** Natural width of the rendered names label, reserved so it is not clipped before avatars collapse in name mode. */
76
+ private measuredLabelWidth;
77
+ /** Pixel width the `+N` counter occupies, matching the per-size circle in the SCSS. */
78
+ private counterWidth;
79
+ /** Largest avatar count that fits the available width, reserving room for the counter when one will show. At least 1. */
80
+ private computeFittedMax;
81
+ /** Emits ipOverflowClick when the counter is clickable and not disabled. */
82
+ private handleOverflowActivate;
83
+ /** Activates the overflow counter on Enter or Space, mirroring native button keyboard behaviour. */
84
+ private handleOverflowKeyDown;
85
+ /** Auto-generated names label: the first name plus a `+N` count of the remaining avatars. */
86
+ private get labelText();
87
+ /** Renders the people preview: ip-avatar's built-in label per row, or a compact name-only list when `previewAvatars` is off. Shared by the counter preview and the names-label tooltip. */
88
+ private renderPeopleList;
89
+ /** A preview row using ip-avatar's own circle, name, and caption. The class makes its label inherit the tooltip bubble's colour. */
90
+ private renderPersonRow;
91
+ /** A compact, avatar-less preview row — the name with an optional caption beneath. Used when `previewAvatars` is off, where ip-avatar's circle-bound label cannot apply. */
92
+ private renderPersonName;
93
+ /** Inline custom properties for the overlap and label gap, set only when the consumer overrides the defaults. */
94
+ private get hostStyle();
95
+ render(): JSX.Element;
96
+ }
@@ -148,6 +148,58 @@ export namespace Components {
148
148
  */
149
149
  "tooltipVariant": 'default' | 'onLight' | 'onDark';
150
150
  }
151
+ interface IpAvatarStack {
152
+ /**
153
+ * Reduces the visible avatars to fit the container width, collapsing the rest like `max` does (`max` stays the cap). Set `auto-fit="false"` for a fixed stack that always shows up to `max`.
154
+ * @default true
155
+ */
156
+ "autoFit": boolean;
157
+ /**
158
+ * Makes the `+N` overflow counter focusable, keyboard-activatable, and emits `ipOverflowClick`.
159
+ * @default false
160
+ */
161
+ "clickable": boolean;
162
+ /**
163
+ * Diameter of the overflow counter when `size="custom"`. Any CSS length, applied to both width and height.
164
+ * @default undefined
165
+ */
166
+ "customSize": string | undefined;
167
+ /**
168
+ * Greys out the stack and, when `clickable`, prevents activation of the overflow counter.
169
+ * @default false
170
+ */
171
+ "disabled": boolean;
172
+ /**
173
+ * Overrides the gap between the stack and the label. Any CSS length; defaults scale with `size`.
174
+ * @default undefined
175
+ */
176
+ "gap": string | undefined;
177
+ /**
178
+ * How the group is summarised: the first name with a `+N` rest count, a `+N` overflow counter circle in the stack, or nothing. The counter circle and the text label are mutually exclusive.
179
+ * @default 'name'
180
+ */
181
+ "labelMode": 'name' | 'counter' | 'none';
182
+ /**
183
+ * Maximum number of avatars shown before the rest collapse into a `+N` counter. Acts as the hard cap; with `auto-fit` on, fewer may show.
184
+ * @default 3
185
+ */
186
+ "max": number;
187
+ /**
188
+ * Overrides the overlap offset between circles. Any CSS length; defaults scale with `size`.
189
+ * @default undefined
190
+ */
191
+ "overlap": string | undefined;
192
+ /**
193
+ * Whether the tooltip previews (the counter and the names label) show a small avatar beside each person, or just the name and caption.
194
+ * @default true
195
+ */
196
+ "previewAvatars": boolean;
197
+ /**
198
+ * Stack size. Drives the overflow counter size and the default overlap; slotted avatars should match.
199
+ * @default 'medium'
200
+ */
201
+ "size": 'small' | 'medium' | 'large' | 'custom';
202
+ }
151
203
  interface IpButton {
152
204
  /**
153
205
  * Position of the icon slot — "start" places it before the label, "end" places it after
@@ -406,6 +458,10 @@ export interface IpAvatarCustomEvent<T> extends CustomEvent<T> {
406
458
  detail: T;
407
459
  target: HTMLIpAvatarElement;
408
460
  }
461
+ export interface IpAvatarStackCustomEvent<T> extends CustomEvent<T> {
462
+ detail: T;
463
+ target: HTMLIpAvatarStackElement;
464
+ }
409
465
  export interface IpMainMenuCustomEvent<T> extends CustomEvent<T> {
410
466
  detail: T;
411
467
  target: HTMLIpMainMenuElement;
@@ -437,6 +493,23 @@ declare global {
437
493
  prototype: HTMLIpAvatarElement;
438
494
  new (): HTMLIpAvatarElement;
439
495
  };
496
+ interface HTMLIpAvatarStackElementEventMap {
497
+ "ipOverflowClick": void;
498
+ }
499
+ interface HTMLIpAvatarStackElement extends Components.IpAvatarStack, HTMLStencilElement {
500
+ addEventListener<K extends keyof HTMLIpAvatarStackElementEventMap>(type: K, listener: (this: HTMLIpAvatarStackElement, ev: IpAvatarStackCustomEvent<HTMLIpAvatarStackElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
501
+ addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
502
+ addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
503
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
504
+ removeEventListener<K extends keyof HTMLIpAvatarStackElementEventMap>(type: K, listener: (this: HTMLIpAvatarStackElement, ev: IpAvatarStackCustomEvent<HTMLIpAvatarStackElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
505
+ removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
506
+ removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
507
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
508
+ }
509
+ var HTMLIpAvatarStackElement: {
510
+ prototype: HTMLIpAvatarStackElement;
511
+ new (): HTMLIpAvatarStackElement;
512
+ };
440
513
  interface HTMLIpButtonElement extends Components.IpButton, HTMLStencilElement {
441
514
  }
442
515
  var HTMLIpButtonElement: {
@@ -541,6 +614,7 @@ declare global {
541
614
  };
542
615
  interface HTMLElementTagNameMap {
543
616
  "ip-avatar": HTMLIpAvatarElement;
617
+ "ip-avatar-stack": HTMLIpAvatarStackElement;
544
618
  "ip-button": HTMLIpButtonElement;
545
619
  "ip-icon-button": HTMLIpIconButtonElement;
546
620
  "ip-main-menu": HTMLIpMainMenuElement;
@@ -704,6 +778,62 @@ declare namespace LocalJSX {
704
778
  */
705
779
  "tooltipVariant"?: 'default' | 'onLight' | 'onDark';
706
780
  }
781
+ interface IpAvatarStack {
782
+ /**
783
+ * Reduces the visible avatars to fit the container width, collapsing the rest like `max` does (`max` stays the cap). Set `auto-fit="false"` for a fixed stack that always shows up to `max`.
784
+ * @default true
785
+ */
786
+ "autoFit"?: boolean;
787
+ /**
788
+ * Makes the `+N` overflow counter focusable, keyboard-activatable, and emits `ipOverflowClick`.
789
+ * @default false
790
+ */
791
+ "clickable"?: boolean;
792
+ /**
793
+ * Diameter of the overflow counter when `size="custom"`. Any CSS length, applied to both width and height.
794
+ * @default undefined
795
+ */
796
+ "customSize"?: string | undefined;
797
+ /**
798
+ * Greys out the stack and, when `clickable`, prevents activation of the overflow counter.
799
+ * @default false
800
+ */
801
+ "disabled"?: boolean;
802
+ /**
803
+ * Overrides the gap between the stack and the label. Any CSS length; defaults scale with `size`.
804
+ * @default undefined
805
+ */
806
+ "gap"?: string | undefined;
807
+ /**
808
+ * How the group is summarised: the first name with a `+N` rest count, a `+N` overflow counter circle in the stack, or nothing. The counter circle and the text label are mutually exclusive.
809
+ * @default 'name'
810
+ */
811
+ "labelMode"?: 'name' | 'counter' | 'none';
812
+ /**
813
+ * Maximum number of avatars shown before the rest collapse into a `+N` counter. Acts as the hard cap; with `auto-fit` on, fewer may show.
814
+ * @default 3
815
+ */
816
+ "max"?: number;
817
+ /**
818
+ * Emitted when a clickable, non-disabled overflow counter is activated by mouse or keyboard.
819
+ */
820
+ "onIpOverflowClick"?: (event: IpAvatarStackCustomEvent<void>) => void;
821
+ /**
822
+ * Overrides the overlap offset between circles. Any CSS length; defaults scale with `size`.
823
+ * @default undefined
824
+ */
825
+ "overlap"?: string | undefined;
826
+ /**
827
+ * Whether the tooltip previews (the counter and the names label) show a small avatar beside each person, or just the name and caption.
828
+ * @default true
829
+ */
830
+ "previewAvatars"?: boolean;
831
+ /**
832
+ * Stack size. Drives the overflow counter size and the default overlap; slotted avatars should match.
833
+ * @default 'medium'
834
+ */
835
+ "size"?: 'small' | 'medium' | 'large' | 'custom';
836
+ }
707
837
  interface IpButton {
708
838
  /**
709
839
  * Position of the icon slot — "start" places it before the label, "end" places it after
@@ -1028,6 +1158,18 @@ declare namespace LocalJSX {
1028
1158
  "tooltipSize": 'small' | 'medium' | 'large';
1029
1159
  "tooltipVariant": 'default' | 'onLight' | 'onDark';
1030
1160
  }
1161
+ interface IpAvatarStackAttributes {
1162
+ "max": number;
1163
+ "autoFit": boolean;
1164
+ "size": 'small' | 'medium' | 'large' | 'custom';
1165
+ "customSize": string | undefined;
1166
+ "overlap": string | undefined;
1167
+ "labelMode": 'name' | 'counter' | 'none';
1168
+ "gap": string | undefined;
1169
+ "previewAvatars": boolean;
1170
+ "clickable": boolean;
1171
+ "disabled": boolean;
1172
+ }
1031
1173
  interface IpButtonAttributes {
1032
1174
  "ariaLabel": string | null;
1033
1175
  "variant": 'primary' | 'secondary' | 'tertiary' | 'brand' | 'destructive';
@@ -1092,6 +1234,7 @@ declare namespace LocalJSX {
1092
1234
 
1093
1235
  interface IntrinsicElements {
1094
1236
  "ip-avatar": Omit<IpAvatar, keyof IpAvatarAttributes> & { [K in keyof IpAvatar & keyof IpAvatarAttributes]?: IpAvatar[K] } & { [K in keyof IpAvatar & keyof IpAvatarAttributes as `attr:${K}`]?: IpAvatarAttributes[K] } & { [K in keyof IpAvatar & keyof IpAvatarAttributes as `prop:${K}`]?: IpAvatar[K] };
1237
+ "ip-avatar-stack": Omit<IpAvatarStack, keyof IpAvatarStackAttributes> & { [K in keyof IpAvatarStack & keyof IpAvatarStackAttributes]?: IpAvatarStack[K] } & { [K in keyof IpAvatarStack & keyof IpAvatarStackAttributes as `attr:${K}`]?: IpAvatarStackAttributes[K] } & { [K in keyof IpAvatarStack & keyof IpAvatarStackAttributes as `prop:${K}`]?: IpAvatarStack[K] };
1095
1238
  "ip-button": Omit<IpButton, keyof IpButtonAttributes> & { [K in keyof IpButton & keyof IpButtonAttributes]?: IpButton[K] } & { [K in keyof IpButton & keyof IpButtonAttributes as `attr:${K}`]?: IpButtonAttributes[K] } & { [K in keyof IpButton & keyof IpButtonAttributes as `prop:${K}`]?: IpButton[K] };
1096
1239
  "ip-icon-button": Omit<IpIconButton, keyof IpIconButtonAttributes> & { [K in keyof IpIconButton & keyof IpIconButtonAttributes]?: IpIconButton[K] } & { [K in keyof IpIconButton & keyof IpIconButtonAttributes as `attr:${K}`]?: IpIconButtonAttributes[K] } & { [K in keyof IpIconButton & keyof IpIconButtonAttributes as `prop:${K}`]?: IpIconButton[K] };
1097
1240
  "ip-main-menu": Omit<IpMainMenu, keyof IpMainMenuAttributes> & { [K in keyof IpMainMenu & keyof IpMainMenuAttributes]?: IpMainMenu[K] } & { [K in keyof IpMainMenu & keyof IpMainMenuAttributes as `attr:${K}`]?: IpMainMenuAttributes[K] } & { [K in keyof IpMainMenu & keyof IpMainMenuAttributes as `prop:${K}`]?: IpMainMenu[K] } & OneOf<"heading", IpMainMenu["heading"], IpMainMenuAttributes["heading"]>;
@@ -1107,6 +1250,7 @@ declare module "@stencil/core" {
1107
1250
  export namespace JSX {
1108
1251
  interface IntrinsicElements {
1109
1252
  "ip-avatar": LocalJSX.IntrinsicElements["ip-avatar"] & JSXBase.HTMLAttributes<HTMLIpAvatarElement>;
1253
+ "ip-avatar-stack": LocalJSX.IntrinsicElements["ip-avatar-stack"] & JSXBase.HTMLAttributes<HTMLIpAvatarStackElement>;
1110
1254
  "ip-button": LocalJSX.IntrinsicElements["ip-button"] & JSXBase.HTMLAttributes<HTMLIpButtonElement>;
1111
1255
  /**
1112
1256
  * Circular icon-only button with tooltip, five variants, three sizes, and form participation.
@@ -11,3 +11,4 @@ export { IpMainMenuItem } from './components/ip-main-menu-item/ip-main-menu-item
11
11
  export { IpIconButton } from './components/ip-icon-button/ip-icon-button';
12
12
  export { IpTooltip } from './components/ip-tooltip/ip-tooltip';
13
13
  export { IpAvatar } from './components/ip-avatar/ip-avatar';
14
+ export { IpAvatarStack } from './components/ip-avatar-stack/ip-avatar-stack';
@@ -1 +1 @@
1
- export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-BB9-AZXH.js";export{I as IpButton}from"./p-DrSqAb5O.js";export{P as PixelI18nService}from"./p-DHUR9EVo.js";export{I as IpMainMenu}from"./p-CWbwcDHB.js";export{I as IpMainMenuToggle}from"./p-B7DtfC4M.js";export{I as IpMainMenuSection}from"./p-uroVS1GL.js";export{I as IpMainMenuGroup}from"./p-CSHQqNXM.js";export{I as IpMainMenuItem}from"./p-CPd0stBB.js";export{I as IpIconButton}from"./p-C7Kf2aED.js";export{I as IpTooltip}from"./p-B1x6AHw9.js";export{I as IpAvatar}from"./p-DpQ72I9n.js";
1
+ export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-BB9-AZXH.js";export{I as IpButton}from"./p-6Z45CK1I.js";export{P as PixelI18nService}from"./p-DHUR9EVo.js";export{I as IpMainMenu}from"./p-VLgeZTve.js";export{I as IpMainMenuToggle}from"./p-C1tFWpS0.js";export{I as IpMainMenuSection}from"./p-BYKtWIr5.js";export{I as IpMainMenuGroup}from"./p-BkayA00u.js";export{I as IpMainMenuItem}from"./p-DWpUb5rd.js";export{I as IpIconButton}from"./p-MvV7276a.js";export{I as IpTooltip}from"./p-msgdnxTK.js";export{I as IpAvatar}from"./p-CXMRqnYG.js";export{I as IpAvatarStack}from"./p-D7rK08OB.js";
@@ -0,0 +1,11 @@
1
+ import type { Components, JSX } from "../../types/components";
2
+
3
+ interface IpAvatarStack extends Components.IpAvatarStack, HTMLElement {}
4
+ export const IpAvatarStack: {
5
+ prototype: IpAvatarStack;
6
+ new (): IpAvatarStack;
7
+ };
8
+ /**
9
+ * Used to define this component and all nested components recursively.
10
+ */
11
+ export const defineCustomElement: () => void;
@@ -0,0 +1 @@
1
+ import{I as o,d as r}from"./p-D7rK08OB.js";const s=o,p=r;export{s as IpAvatarStack,p as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as p}from"./p-DpQ72I9n.js";const s=o,r=p;export{s as IpAvatar,r as defineCustomElement}
1
+ import{I as o,d as s}from"./p-CXMRqnYG.js";const p=o,r=s;export{p as IpAvatar,r as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as r}from"./p-DrSqAb5O.js";const s=o,p=r;export{s as IpButton,p as defineCustomElement}
1
+ import{I as o,d as s}from"./p-6Z45CK1I.js";const p=o,r=s;export{p as IpButton,r as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as s}from"./p-C7Kf2aED.js";const a=o,p=s;export{a as IpIconButton,p as defineCustomElement}
1
+ import{I as o,d as s}from"./p-MvV7276a.js";const a=o,p=s;export{a as IpIconButton,p as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as s}from"./p-CSHQqNXM.js";const p=o,r=s;export{p as IpMainMenuGroup,r as defineCustomElement}
1
+ import{I as o,d as s}from"./p-BkayA00u.js";const a=o,p=s;export{a as IpMainMenuGroup,p as defineCustomElement}
@@ -1 +1 @@
1
- import{I as s,d as o}from"./p-CPd0stBB.js";const t=s,p=o;export{t as IpMainMenuItem,p as defineCustomElement}
1
+ import{I as o,d as p}from"./p-DWpUb5rd.js";const r=o,s=p;export{r as IpMainMenuItem,s as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as r}from"./p-uroVS1GL.js";const s=o,p=r;export{s as IpMainMenuSection,p as defineCustomElement}
1
+ import{I as o,d as r}from"./p-BYKtWIr5.js";const s=o,t=r;export{s as IpMainMenuSection,t as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as s}from"./p-B7DtfC4M.js";const t=o,p=s;export{t as IpMainMenuToggle,p as defineCustomElement}
1
+ import{I as o,d as p}from"./p-C1tFWpS0.js";const s=o,t=p;export{s as IpMainMenuToggle,t as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as s}from"./p-CWbwcDHB.js";const p=o,r=s;export{p as IpMainMenu,r as defineCustomElement}
1
+ import{I as o,d as s}from"./p-VLgeZTve.js";const e=o,p=s;export{e as IpMainMenu,p as defineCustomElement}
@@ -1 +1 @@
1
- import{I as o,d as s}from"./p-B1x6AHw9.js";const p=o,r=s;export{p as IpTooltip,r as defineCustomElement}
1
+ import{I as s,d as o}from"./p-msgdnxTK.js";const m=s,p=o;export{m as IpTooltip,p as defineCustomElement}