@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.84 → 2.0.0-next.86

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.
@@ -8,6 +8,25 @@ import '../../icons/icon-close-google.js';
8
8
  import '../badge/badge.js';
9
9
  import {BadgeSize, BadgeType} from '../badge/badge.js';
10
10
 
11
+ /**
12
+ * Configuration for a single badge rendered by `<obc-tab-item>` via the `badges` property.
13
+ */
14
+ export interface TabItemBadge {
15
+ /** Visual style/type of the badge. See `BadgeType` for available options. */
16
+ type: BadgeType;
17
+ /** Size of the badge. See `BadgeSize` for available options. */
18
+ size: BadgeSize;
19
+ /** The numeric value to display in the badge. */
20
+ count?: number;
21
+ /** Whether to show an icon inside the badge. Default: false. */
22
+ showIcon?: boolean;
23
+ /**
24
+ * When set, a `<slot>` with this name is rendered inside the badge so a custom
25
+ * icon can be projected (used for badge types without a built-in icon).
26
+ */
27
+ iconSlotName?: string;
28
+ }
29
+
11
30
  /**
12
31
  * `<obc-tab-item>` – A selectable tab component for navigation menus and tabbed interfaces.
13
32
  *
@@ -150,6 +169,20 @@ export class ObcTabItem extends LitElement {
150
169
  @property({type: Boolean, attribute: 'has-divider'}) hasDivider = false;
151
170
 
152
171
  /**
172
+ * One or more badges (count/status) to display on the tab.
173
+ *
174
+ * When this array is non-empty it takes precedence over the deprecated
175
+ * single-badge props (`hasBadge`, `badgeType`, `badgeSize`, `badgeCount`,
176
+ * `badgeShowNumber`, `showLeadingBadgeIcon`). When empty, the deprecated
177
+ * props are used instead (gated by `hasBadge`).
178
+ *
179
+ * Default: []
180
+ */
181
+ @property({type: Array, attribute: false}) badges: TabItemBadge[] = [];
182
+
183
+ /**
184
+ * @deprecated Use the `badges` array instead.
185
+ *
153
186
  * Displays a badge (count/status) on the tab.
154
187
  * Configure badge appearance via `badgeCount`, `badgeType`, `badgeSize`, etc.
155
188
  *
@@ -195,6 +228,8 @@ export class ObcTabItem extends LitElement {
195
228
  @property({type: Boolean}) disabled = false;
196
229
 
197
230
  /**
231
+ * @deprecated Use the `badges` array instead.
232
+ *
198
233
  * Type of badge to display (e.g., 'regular', 'alarm', 'warning', etc.).
199
234
  * See `BadgeType` enum for available options.
200
235
  *
@@ -204,6 +239,8 @@ export class ObcTabItem extends LitElement {
204
239
  @property({type: String}) badgeType: string = BadgeType.regular;
205
240
 
206
241
  /**
242
+ * @deprecated Use the `badges` array instead.
243
+ *
207
244
  * Size of the badge ('regular' or 'large').
208
245
  * See `BadgeSize` enum for available options.
209
246
  *
@@ -213,6 +250,8 @@ export class ObcTabItem extends LitElement {
213
250
  @property({type: String}) badgeSize: string = BadgeSize.regular;
214
251
 
215
252
  /**
253
+ * @deprecated Use the `badges` array instead.
254
+ *
216
255
  * Shows the badge's numeric value. When false, only the badge background is rendered (for status-only badges).
217
256
  *
218
257
  * Default: true
@@ -221,6 +260,8 @@ export class ObcTabItem extends LitElement {
221
260
  @property({type: Boolean, attribute: false}) badgeShowNumber: boolean = true;
222
261
 
223
262
  /**
263
+ * @deprecated Use the `badges` array instead.
264
+ *
224
265
  * The numeric value to display in the badge (e.g., count of notifications).
225
266
  *
226
267
  * Default: 0
@@ -229,6 +270,8 @@ export class ObcTabItem extends LitElement {
229
270
  @property({type: Number}) badgeCount = 0;
230
271
 
231
272
  /**
273
+ * @deprecated Use the `badges` array instead.
274
+ *
232
275
  * Shows an icon inside the badge.
233
276
  * Supply icon content via the `badge-icon` slot.
234
277
  *
@@ -267,7 +310,44 @@ export class ObcTabItem extends LitElement {
267
310
  }
268
311
  }
269
312
 
313
+ private get effectiveBadges(): TabItemBadge[] {
314
+ if (this.badges.length > 0) {
315
+ return this.badges;
316
+ }
317
+ if (!this.hasBadge) {
318
+ return [];
319
+ }
320
+ return [
321
+ {
322
+ type: (this.badgeType as BadgeType) || BadgeType.regular,
323
+ size: (this.badgeSize as BadgeSize) || BadgeSize.regular,
324
+ count: this.badgeShowNumber ? this.badgeCount : undefined,
325
+ showIcon: this.showLeadingBadgeIcon,
326
+ iconSlotName: this.showLeadingBadgeIcon ? 'badge-icon' : undefined,
327
+ },
328
+ ];
329
+ }
330
+
331
+ private renderBadge(badge: TabItemBadge) {
332
+ return html`
333
+ <obc-badge
334
+ class="badge"
335
+ .number=${badge.count ?? 0}
336
+ .type=${badge.type || BadgeType.regular}
337
+ .size=${badge.size || BadgeSize.regular}
338
+ .showNumber=${badge.count !== undefined}
339
+ .showIcon=${badge.showIcon ?? false}
340
+ >
341
+ ${badge.iconSlotName
342
+ ? html`<slot name=${badge.iconSlotName} slot="badge-icon"></slot>`
343
+ : nothing}
344
+ </obc-badge>
345
+ `;
346
+ }
347
+
270
348
  override render() {
349
+ const badges = this.effectiveBadges;
350
+ const hasBadge = badges.length > 0;
271
351
  const wrapperClasses = {
272
352
  wrapper: true,
273
353
  hug: this.hug,
@@ -275,7 +355,7 @@ export class ObcTabItem extends LitElement {
275
355
  'has-leading-icon': this.hasLeadingIcon,
276
356
  'has-title': this.hasTitle,
277
357
  'has-divider': this.hasDivider && !this.checked,
278
- 'has-badge': this.hasBadge,
358
+ 'has-badge': hasBadge,
279
359
  'has-subtitle': this.showSubtitle,
280
360
  disabled: this.disabled,
281
361
  'center-content': this.centerContent,
@@ -309,38 +389,16 @@ export class ObcTabItem extends LitElement {
309
389
  </div>
310
390
  `
311
391
  : nothing}
312
- ${this.centerContent && this.hasBadge
313
- ? html`
314
- <obc-badge
315
- class="badge"
316
- .number=${this.badgeCount}
317
- .type=${this.badgeType || BadgeType.regular}
318
- .size=${this.badgeSize || BadgeSize.regular}
319
- .showNumber=${this.badgeShowNumber}
320
- .showIcon=${this.showLeadingBadgeIcon}
321
- >
322
- ${this.showLeadingBadgeIcon
323
- ? html`<slot name="badge-icon" slot="badge-icon"></slot>`
324
- : nothing}
325
- </obc-badge>
326
- `
392
+ ${this.centerContent && hasBadge
393
+ ? html`<div class="badges">
394
+ ${badges.map((badge) => this.renderBadge(badge))}
395
+ </div>`
327
396
  : nothing}
328
397
  </div>
329
- ${!this.centerContent && this.hasBadge
330
- ? html`
331
- <obc-badge
332
- class="badge"
333
- .number=${this.badgeCount}
334
- .type=${this.badgeType || BadgeType.regular}
335
- .size=${this.badgeSize || BadgeSize.regular}
336
- .showNumber=${this.badgeShowNumber}
337
- .showIcon=${this.showLeadingBadgeIcon}
338
- >
339
- ${this.showLeadingBadgeIcon
340
- ? html`<slot name="badge-icon" slot="badge-icon"></slot>`
341
- : nothing}
342
- </obc-badge>
343
- `
398
+ ${!this.centerContent && hasBadge
399
+ ? html`<div class="badges">
400
+ ${badges.map((badge) => this.renderBadge(badge))}
401
+ </div>`
344
402
  : nothing}
345
403
  ${this.hasClose
346
404
  ? html`
@@ -41,6 +41,7 @@ interface TabRowStoryArgs {
41
41
  hug?: boolean;
42
42
  showSubtitle?: boolean;
43
43
  hasAddNewTab?: boolean;
44
+ centerContent?: boolean;
44
45
  }
45
46
 
46
47
  function InteractiveTabRow(args: TabRowStoryArgs) {
@@ -58,6 +59,7 @@ function InteractiveTabRow(args: TabRowStoryArgs) {
58
59
  tabRow.hug = args.hug ?? false;
59
60
  tabRow.showSubtitle = args.showSubtitle ?? false;
60
61
  tabRow.hasAddNewTab = args.hasAddNewTab ?? true;
62
+ tabRow.centerContent = args.centerContent ?? false;
61
63
 
62
64
  tabRow.addEventListener('tab-selected', (e: Event) => {
63
65
  const detail = (e as CustomEvent<{id: string}>).detail;
@@ -109,6 +111,18 @@ export const HugMode: Story = {
109
111
  render: InteractiveTabRow,
110
112
  };
111
113
 
114
+ export const CenterContent: Story = {
115
+ args: {
116
+ tabs: defaultTabs,
117
+ hug: false,
118
+ hasClose: true,
119
+ hasAddNewTab: true,
120
+ selectedTabId: 'tab2',
121
+ centerContent: true,
122
+ },
123
+ render: InteractiveTabRow,
124
+ };
125
+
112
126
  export const NoClose: Story = {
113
127
  args: {
114
128
  tabs: defaultTabs,
@@ -148,24 +162,20 @@ export const WithBadges: Story = {
148
162
  {
149
163
  id: 'tab1',
150
164
  title: 'Inbox',
151
- hasBadge: true,
152
- badgeCount: 12,
153
- badgeType: BadgeType.notification,
165
+ badges: [{type: BadgeType.warning, size: BadgeSize.regular, count: 12}],
154
166
  },
155
167
  {
156
168
  id: 'tab2',
157
169
  title: 'Notifications',
158
- hasBadge: true,
159
- badgeCount: 3,
160
- badgeType: BadgeType.alarm,
170
+ badges: [
171
+ {type: BadgeType.alarm, size: BadgeSize.regular, count: 3},
172
+ {type: BadgeType.warning, size: BadgeSize.regular, count: 7},
173
+ ],
161
174
  },
162
175
  {
163
176
  id: 'tab3',
164
177
  title: 'Updates',
165
- hasBadge: true,
166
- badgeCount: 99,
167
- badgeType: BadgeType.enhance,
168
- badgeSize: BadgeSize.large,
178
+ badges: [{type: BadgeType.caution, size: BadgeSize.regular, count: 99}],
169
179
  },
170
180
  {id: 'tab4', title: 'Messages'},
171
181
  ],
@@ -3,6 +3,7 @@ import {property} from 'lit/decorators.js';
3
3
  import {repeat} from 'lit/directives/repeat.js';
4
4
  import compentStyle from './tab-row.css?inline';
5
5
  import '../tab-item/tab-item.js';
6
+ import type {TabItemBadge} from '../tab-item/tab-item.js';
6
7
  import '../icon-button/icon-button.js';
7
8
  import '../../icons/icon-placeholder.js';
8
9
  import {customElement} from '../../decorator.js';
@@ -15,11 +16,22 @@ export interface TabData {
15
16
  subtitle?: string;
16
17
  showSubtitle?: boolean;
17
18
  hasLeadingIcon?: boolean;
19
+ /**
20
+ * One or more badges to display on the tab. When non-empty this takes
21
+ * precedence over the deprecated single-badge fields below.
22
+ */
23
+ badges?: TabItemBadge[];
24
+ /** @deprecated Use `badges` instead. */
18
25
  hasBadge?: boolean;
26
+ /** @deprecated Use `badges` instead. */
19
27
  badgeCount?: number;
28
+ /** @deprecated Use `badges` instead. */
20
29
  badgeType?: BadgeType;
30
+ /** @deprecated Use `badges` instead. */
21
31
  badgeSize?: BadgeSize;
32
+ /** @deprecated Use `badges` instead. */
22
33
  badgeShowNumber?: boolean;
34
+ /** @deprecated Use `badges` instead. */
23
35
  showLeadingBadgeIcon?: boolean;
24
36
  disabled?: boolean;
25
37
  }
@@ -80,8 +92,8 @@ export interface TabData {
80
92
  *
81
93
  * | Slot Name | Renders When... | Purpose |
82
94
  * |--------------------------|------------------------------------|------------------------------------------------------|
83
- * | `tab-<id>-icon` | If `hasLeadingIcon` is true | Leading icon for a specific tab (replaceable) |
84
- * | `tab-<id>-badge-icon` | If `showLeadingBadgeIcon` is true | Badge icon for a specific tab (replaceable) |
95
+ * | `tab-<id>-icon` | If `hasLeadingIcon` is true | Leading icon for a specific tab (replaceable) |
96
+ * | `tab-<id>-<iconSlotName>` | For each badge that declares an `iconSlotName` | Custom icon for a specific tab's badge (replaceable). The deprecated single-badge path uses `tab-<id>-badge-icon`. |
85
97
  *
86
98
  * ---
87
99
  *
@@ -100,7 +112,7 @@ export interface TabData {
100
112
  * - If using custom icons, provide them via the appropriate named slot for each tab.
101
113
  *
102
114
  * @slot tab-<id>-icon - Leading icon slot for each tab (shown when `hasLeadingIcon` is true for that tab)
103
- * @slot tab-<id>-badge-icon - Badge icon slot for each tab (shown when `showLeadingBadgeIcon` is true for that tab)
115
+ * @slot tab-<id>-<iconSlotName> - Custom badge icon slot for each tab, one per badge that declares an `iconSlotName`. The deprecated single-badge path uses `tab-<id>-badge-icon`.
104
116
  * @fires tab-selected {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab is selected.
105
117
  * @fires tab-closed {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab's close button is clicked.
106
118
  * @fires add-new-tab {CustomEvent<void>} Fired when the "add new tab" button is clicked.
@@ -115,12 +127,13 @@ export class ObcTabRow extends LitElement {
115
127
  * - `subtitle` (string): Contextual text shown below the title when subtitle display is enabled.
116
128
  * - `showSubtitle` (boolean): Optional per-tab override for displaying the subtitle.
117
129
  * - `hasLeadingIcon` (boolean): Whether to show a leading icon (default: true).
118
- * - `hasBadge` (boolean): Whether to show a badge on the tab.
119
- * - `badgeCount` (number): Number to display in the badge.
120
- * - `badgeType` (BadgeType): Visual style of the badge (e.g., notification, alarm, enhance).
121
- * - `badgeSize` (BadgeSize): Size of the badge (e.g., regular, large).
122
- * - `badgeShowNumber` (boolean): If true, shows the badge number.
123
- * - `showLeadingBadgeIcon` (boolean): If true, shows a badge icon.
130
+ * - `badges` (TabItemBadge[]): One or more badges to display on the tab. Takes precedence over the deprecated single-badge fields below.
131
+ * - `hasBadge` (boolean, deprecated): Whether to show a badge on the tab.
132
+ * - `badgeCount` (number, deprecated): Number to display in the badge.
133
+ * - `badgeType` (BadgeType, deprecated): Visual style of the badge (e.g., notification, alarm, enhance).
134
+ * - `badgeSize` (BadgeSize, deprecated): Size of the badge (e.g., regular, large).
135
+ * - `badgeShowNumber` (boolean, deprecated): If true, shows the badge number.
136
+ * - `showLeadingBadgeIcon` (boolean, deprecated): If true, shows a badge icon.
124
137
  * - `disabled` (boolean): If true, disables the tab.
125
138
  */
126
139
  @property({type: Array}) tabs: TabData[] = [];
@@ -139,6 +152,8 @@ export class ObcTabRow extends LitElement {
139
152
  */
140
153
  @property({type: Boolean, attribute: 'has-close'}) hasClose = false;
141
154
 
155
+ @property({type: Boolean}) centerContent = false;
156
+
142
157
  /**
143
158
  * Enables "hug" mode for a more compact tab layout with reduced padding.
144
159
  *
@@ -203,8 +218,27 @@ export class ObcTabRow extends LitElement {
203
218
 
204
219
  private renderTab(tab: TabData, index: number) {
205
220
  const isFirst = index === 0;
221
+ const previousTabSelected = this.selectedTabId === this.tabs[index - 1]?.id;
206
222
  const isChecked = tab.id === this.selectedTabId;
207
223
  const showSubtitle = tab.showSubtitle ?? this.showSubtitle;
224
+ // Forward a light-DOM slot for every distinct `iconSlotName` declared by the
225
+ // tab's badges (new `badges` array), plus the deprecated single badge-icon
226
+ // slot. obc-tab-item renders `<slot name=${iconSlotName} slot="badge-icon">`
227
+ // inside each badge, so obc-tab-row must project matching content into that
228
+ // slot. Slots are namespaced by tab id to stay unique across the row.
229
+ const badgeIconSlots = [
230
+ ...new Set(
231
+ (tab.badges ?? [])
232
+ .map((badge) => badge.iconSlotName)
233
+ .filter((slotName): slotName is string => slotName !== undefined)
234
+ ),
235
+ ];
236
+ if (
237
+ (tab.showLeadingBadgeIcon ?? false) &&
238
+ !badgeIconSlots.includes('badge-icon')
239
+ ) {
240
+ badgeIconSlots.push('badge-icon');
241
+ }
208
242
  return html`
209
243
  <obc-tab-item
210
244
  .title=${tab.title}
@@ -214,11 +248,12 @@ export class ObcTabRow extends LitElement {
214
248
  .hasClose=${this.hasClose}
215
249
  .hasLeadingIcon=${tab.hasLeadingIcon ?? true}
216
250
  .hasTitle=${true}
217
- .hasDivider=${!isFirst}
251
+ .hasDivider=${!isFirst && !previousTabSelected}
218
252
  .hug=${this.hug}
253
+ .centerContent=${this.centerContent}
219
254
  .disabled=${tab.disabled || false}
220
- .hasBadge=${tab.hasBadge ||
221
- (tab.badgeCount !== undefined && tab.badgeCount > 0)}
255
+ .badges=${tab.badges ?? []}
256
+ .hasBadge=${tab.hasBadge || false}
222
257
  .badgeCount=${tab.badgeCount || 0}
223
258
  .badgeType=${tab.badgeType ?? BadgeType.regular}
224
259
  .badgeSize=${tab.badgeSize ?? BadgeSize.regular}
@@ -235,13 +270,13 @@ export class ObcTabRow extends LitElement {
235
270
  `
236
271
  : ''}
237
272
  <span slot="title">${tab.title}</span>
238
- ${tab.showLeadingBadgeIcon
239
- ? html`
240
- <slot name="tab-${tab.id}-badge-icon" slot="badge-icon">
241
- <obi-placeholder></obi-placeholder>
242
- </slot>
243
- `
244
- : ''}
273
+ ${badgeIconSlots.map(
274
+ (slotName) => html`
275
+ <slot name="tab-${tab.id}-${slotName}" slot=${slotName}>
276
+ <obi-placeholder></obi-placeholder>
277
+ </slot>
278
+ `
279
+ )}
245
280
  </obc-tab-item>
246
281
  `;
247
282
  }
@@ -436,7 +436,6 @@ export class ObcTopBar extends LitElement {
436
436
  </obc-icon-button>
437
437
  </div>`
438
438
  );
439
- leftGroup.push(html`<div class="divider"></div>`);
440
439
  leftGroup.push(
441
440
  html`<obc-icon-button
442
441
  variant="flat"