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

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.
@@ -1,4 +1,5 @@
1
1
  import { LitElement } from 'lit';
2
+ import { TabItemBadge } from '../tab-item/tab-item.js';
2
3
  import { BadgeType, BadgeSize } from '../badge/badge.js';
3
4
  import '../tab-item/tab-item.js';
4
5
  import '../icon-button/icon-button.js';
@@ -10,11 +11,22 @@ export interface TabData {
10
11
  subtitle?: string;
11
12
  showSubtitle?: boolean;
12
13
  hasLeadingIcon?: boolean;
14
+ /**
15
+ * One or more badges to display on the tab. When non-empty this takes
16
+ * precedence over the deprecated single-badge fields below.
17
+ */
18
+ badges?: TabItemBadge[];
19
+ /** @deprecated Use `badges` instead. */
13
20
  hasBadge?: boolean;
21
+ /** @deprecated Use `badges` instead. */
14
22
  badgeCount?: number;
23
+ /** @deprecated Use `badges` instead. */
15
24
  badgeType?: BadgeType;
25
+ /** @deprecated Use `badges` instead. */
16
26
  badgeSize?: BadgeSize;
27
+ /** @deprecated Use `badges` instead. */
17
28
  badgeShowNumber?: boolean;
29
+ /** @deprecated Use `badges` instead. */
18
30
  showLeadingBadgeIcon?: boolean;
19
31
  disabled?: boolean;
20
32
  }
@@ -74,8 +86,8 @@ export interface TabData {
74
86
  *
75
87
  * | Slot Name | Renders When... | Purpose |
76
88
  * |--------------------------|------------------------------------|------------------------------------------------------|
77
- * | `tab-<id>-icon` | If `hasLeadingIcon` is true | Leading icon for a specific tab (replaceable) |
78
- * | `tab-<id>-badge-icon` | If `showLeadingBadgeIcon` is true | Badge icon for a specific tab (replaceable) |
89
+ * | `tab-<id>-icon` | If `hasLeadingIcon` is true | Leading icon for a specific tab (replaceable) |
90
+ * | `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`. |
79
91
  *
80
92
  * ---
81
93
  *
@@ -94,7 +106,7 @@ export interface TabData {
94
106
  * - If using custom icons, provide them via the appropriate named slot for each tab.
95
107
  *
96
108
  * @slot tab-<id>-icon - Leading icon slot for each tab (shown when `hasLeadingIcon` is true for that tab)
97
- * @slot tab-<id>-badge-icon - Badge icon slot for each tab (shown when `showLeadingBadgeIcon` is true for that tab)
109
+ * @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`.
98
110
  * @fires tab-selected {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab is selected.
99
111
  * @fires tab-closed {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab's close button is clicked.
100
112
  * @fires add-new-tab {CustomEvent<void>} Fired when the "add new tab" button is clicked.
@@ -108,12 +120,13 @@ export declare class ObcTabRow extends LitElement {
108
120
  * - `subtitle` (string): Contextual text shown below the title when subtitle display is enabled.
109
121
  * - `showSubtitle` (boolean): Optional per-tab override for displaying the subtitle.
110
122
  * - `hasLeadingIcon` (boolean): Whether to show a leading icon (default: true).
111
- * - `hasBadge` (boolean): Whether to show a badge on the tab.
112
- * - `badgeCount` (number): Number to display in the badge.
113
- * - `badgeType` (BadgeType): Visual style of the badge (e.g., notification, alarm, enhance).
114
- * - `badgeSize` (BadgeSize): Size of the badge (e.g., regular, large).
115
- * - `badgeShowNumber` (boolean): If true, shows the badge number.
116
- * - `showLeadingBadgeIcon` (boolean): If true, shows a badge icon.
123
+ * - `badges` (TabItemBadge[]): One or more badges to display on the tab. Takes precedence over the deprecated single-badge fields below.
124
+ * - `hasBadge` (boolean, deprecated): Whether to show a badge on the tab.
125
+ * - `badgeCount` (number, deprecated): Number to display in the badge.
126
+ * - `badgeType` (BadgeType, deprecated): Visual style of the badge (e.g., notification, alarm, enhance).
127
+ * - `badgeSize` (BadgeSize, deprecated): Size of the badge (e.g., regular, large).
128
+ * - `badgeShowNumber` (boolean, deprecated): If true, shows the badge number.
129
+ * - `showLeadingBadgeIcon` (boolean, deprecated): If true, shows a badge icon.
117
130
  * - `disabled` (boolean): If true, disables the tab.
118
131
  */
119
132
  tabs: TabData[];
@@ -129,6 +142,7 @@ export declare class ObcTabRow extends LitElement {
129
142
  * Default: `false`.
130
143
  */
131
144
  hasClose: boolean;
145
+ centerContent: boolean;
132
146
  /**
133
147
  * Enables "hug" mode for a more compact tab layout with reduced padding.
134
148
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tab-row.d.ts","sourceRoot":"","sources":["../../../src/components/tab-row/tab-row.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAkB,MAAM,KAAK,CAAC;AAIhD,OAAO,yBAAyB,CAAC;AACjC,OAAO,+BAA+B,CAAC;AACvC,OAAO,iCAAiC,CAAC;AAEzC,OAAO,4BAA4B,CAAC;AACpC,OAAO,EAAC,SAAS,EAAE,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,qBACa,SAAU,SAAQ,UAAU;IACvC;;;;;;;;;;;;;;;OAeG;IACsB,IAAI,EAAE,OAAO,EAAE,CAAM;IAE9C;;;;OAIG;IACqD,aAAa,SAAM;IAE3E;;;;OAIG;IACgD,QAAQ,UAAS;IAEpE;;;;OAIG;IACwB,GAAG,UAAS;IAEvC;;;;OAIG;IACoD,YAAY,UAAS;IAE5E;;;;OAIG;IACsD,YAAY,UAAS;IAE9E,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,SAAS;IA6CR,MAAM;IAwBf,OAAgB,MAAM,0BAA2B;CAClD"}
1
+ {"version":3,"file":"tab-row.d.ts","sourceRoot":"","sources":["../../../src/components/tab-row/tab-row.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAkB,MAAM,KAAK,CAAC;AAIhD,OAAO,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,yBAAyB,CAAC;AAC1D,OAAO,+BAA+B,CAAC;AACvC,OAAO,iCAAiC,CAAC;AAEzC,OAAO,4BAA4B,CAAC;AACpC,OAAO,EAAC,SAAS,EAAE,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,wCAAwC;IACxC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,wCAAwC;IACxC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,qBACa,SAAU,SAAQ,UAAU;IACvC;;;;;;;;;;;;;;;;OAgBG;IACsB,IAAI,EAAE,OAAO,EAAE,CAAM;IAE9C;;;;OAIG;IACqD,aAAa,SAAM;IAE3E;;;;OAIG;IACgD,QAAQ,UAAS;IAEzC,aAAa,UAAS;IAEjD;;;;OAIG;IACwB,GAAG,UAAS;IAEvC;;;;OAIG;IACoD,YAAY,UAAS;IAE5E;;;;OAIG;IACsD,YAAY,UAAS;IAE9E,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,SAAS;IAiER,MAAM;IAwBf,OAAgB,MAAM,0BAA2B;CAClD"}
@@ -24,6 +24,7 @@ let ObcTabRow = class extends LitElement {
24
24
  this.tabs = [];
25
25
  this.selectedTabId = "";
26
26
  this.hasClose = false;
27
+ this.centerContent = false;
27
28
  this.hug = false;
28
29
  this.showSubtitle = false;
29
30
  this.hasAddNewTab = false;
@@ -68,8 +69,17 @@ let ObcTabRow = class extends LitElement {
68
69
  }
69
70
  renderTab(tab, index) {
70
71
  const isFirst = index === 0;
72
+ const previousTabSelected = this.selectedTabId === this.tabs[index - 1]?.id;
71
73
  const isChecked = tab.id === this.selectedTabId;
72
74
  const showSubtitle = tab.showSubtitle ?? this.showSubtitle;
75
+ const badgeIconSlots = [
76
+ ...new Set(
77
+ (tab.badges ?? []).map((badge) => badge.iconSlotName).filter((slotName) => slotName !== void 0)
78
+ )
79
+ ];
80
+ if ((tab.showLeadingBadgeIcon ?? false) && !badgeIconSlots.includes("badge-icon")) {
81
+ badgeIconSlots.push("badge-icon");
82
+ }
73
83
  return html`
74
84
  <obc-tab-item
75
85
  .title=${tab.title}
@@ -79,10 +89,12 @@ let ObcTabRow = class extends LitElement {
79
89
  .hasClose=${this.hasClose}
80
90
  .hasLeadingIcon=${tab.hasLeadingIcon ?? true}
81
91
  .hasTitle=${true}
82
- .hasDivider=${!isFirst}
92
+ .hasDivider=${!isFirst && !previousTabSelected}
83
93
  .hug=${this.hug}
94
+ .centerContent=${this.centerContent}
84
95
  .disabled=${tab.disabled || false}
85
- .hasBadge=${tab.hasBadge || tab.badgeCount !== void 0 && tab.badgeCount > 0}
96
+ .badges=${tab.badges ?? []}
97
+ .hasBadge=${tab.hasBadge || false}
86
98
  .badgeCount=${tab.badgeCount || 0}
87
99
  .badgeType=${tab.badgeType ?? BadgeType.regular}
88
100
  .badgeSize=${tab.badgeSize ?? BadgeSize.regular}
@@ -97,11 +109,13 @@ let ObcTabRow = class extends LitElement {
97
109
  </slot>
98
110
  ` : ""}
99
111
  <span slot="title">${tab.title}</span>
100
- ${tab.showLeadingBadgeIcon ? html`
101
- <slot name="tab-${tab.id}-badge-icon" slot="badge-icon">
102
- <obi-placeholder></obi-placeholder>
103
- </slot>
104
- ` : ""}
112
+ ${badgeIconSlots.map(
113
+ (slotName) => html`
114
+ <slot name="tab-${tab.id}-${slotName}" slot=${slotName}>
115
+ <obi-placeholder></obi-placeholder>
116
+ </slot>
117
+ `
118
+ )}
105
119
  </obc-tab-item>
106
120
  `;
107
121
  }
@@ -137,6 +151,9 @@ __decorateClass([
137
151
  __decorateClass([
138
152
  property({ type: Boolean, attribute: "has-close" })
139
153
  ], ObcTabRow.prototype, "hasClose", 2);
154
+ __decorateClass([
155
+ property({ type: Boolean })
156
+ ], ObcTabRow.prototype, "centerContent", 2);
140
157
  __decorateClass([
141
158
  property({ type: Boolean })
142
159
  ], ObcTabRow.prototype, "hug", 2);
@@ -1 +1 @@
1
- {"version":3,"file":"tab-row.js","sources":["../../../src/components/tab-row/tab-row.ts"],"sourcesContent":["import {LitElement, html, unsafeCSS} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {repeat} from 'lit/directives/repeat.js';\nimport compentStyle from './tab-row.css?inline';\nimport '../tab-item/tab-item.js';\nimport '../icon-button/icon-button.js';\nimport '../../icons/icon-placeholder.js';\nimport {customElement} from '../../decorator.js';\nimport '../../icons/icon-up-iec.js';\nimport {BadgeType, BadgeSize} from '../badge/badge.js';\n\nexport interface TabData {\n id: string;\n title: string;\n subtitle?: string;\n showSubtitle?: boolean;\n hasLeadingIcon?: boolean;\n hasBadge?: boolean;\n badgeCount?: number;\n badgeType?: BadgeType;\n badgeSize?: BadgeSize;\n badgeShowNumber?: boolean;\n showLeadingBadgeIcon?: boolean;\n disabled?: boolean;\n}\n\n/**\n * `<obc-tab-row>` – A horizontal tab navigation bar for switching between multiple content sections.\n *\n * Displays a row of interactive tabs, each representing a distinct view or section. Supports subtitles, badges, icons, close buttons, and an optional \"add new tab\" action. Designed for use cases where users need to quickly switch between related content areas, such as dashboards, settings, or document editors.\n *\n * ---\n *\n * ### Features\n * - **Tab Selection:** Only one tab can be selected at a time; selection is managed via the `selectedTabId` property.\n * - **Closeable Tabs:** Optionally display a close button on each tab (`hasClose`), allowing users to remove tabs dynamically.\n * - **Add New Tab:** Optionally show an \"add new tab\" button at the end of the row (`hasAddNewTab`), emitting an event when clicked.\n * - **Subtitles:** Optionally show secondary contextual text below each tab title (`showSubtitle` and `subtitle`).\n * - **Badges:** Tabs can display badges with counts and types (e.g., notification, alarm, enhance), supporting different badge sizes and optional hiding of the number.\n * - **Icons:** Each tab can show a leading icon (customizable via slot), and optionally a badge icon.\n * - **Disabled Tabs:** Individual tabs can be disabled to prevent user interaction.\n * - **Hug Mode:** When `hug` is enabled, tabs use a compact layout with reduced padding.\n * - **Responsive Layout:** Tabs are arranged horizontally and adapt to the available width.\n *\n * ---\n *\n * ### Usage Guidelines\n * Use `<obc-tab-row>` to organize content into logical sections that users can switch between without leaving the current page. Ideal for navigation in multi-view interfaces, such as settings panels, dashboards, or editors. Each tab should represent a distinct, high-level section. Avoid using tabs for navigation between unrelated pages or for single-action toggles.\n *\n * - Use badges to highlight new activity or counts (e.g., unread messages).\n * - Use the close button for tabs that represent removable or temporary content (e.g., documents, chat sessions).\n * - The \"add new tab\" action is suitable for interfaces where users can create new sections dynamically.\n * - For best accessibility, ensure each tab has a unique `id` and descriptive `title`.\n *\n * **TODO(designer):** Confirm if there are recommended limits on the number of tabs, or guidance for handling overflow (e.g., scrolling, collapsing).\n *\n * ---\n *\n * ### Example\n * ```html\n * <obc-tab-row\n * .tabs=${[\n * {id: 'tab1', title: 'Inbox', subtitle: 'Monitoring', hasBadge: true, badgeCount: 5, badgeType: 'notification'},\n * {id: 'tab2', title: 'Updates'},\n * {id: 'tab3', title: 'Settings', hasLeadingIcon: true}\n * ]}\n * selectedTabId=\"tab1\"\n * show-subtitle\n * hasClose\n * hasAddNewTab\n * >\n * <obi-placeholder slot=\"tab-tab1-icon\"></obi-placeholder>\n * <obi-placeholder slot=\"tab-tab3-icon\"></obi-placeholder>\n * </obc-tab-row>\n * ```\n *\n * ---\n *\n * ### Slots\n *\n * | Slot Name | Renders When... | Purpose |\n * |--------------------------|------------------------------------|------------------------------------------------------|\n * | `tab-<id>-icon` | If `hasLeadingIcon` is true | Leading icon for a specific tab (replaceable) |\n * | `tab-<id>-badge-icon` | If `showLeadingBadgeIcon` is true | Badge icon for a specific tab (replaceable) |\n *\n * ---\n *\n * ### Events\n * - `tab-selected` – Fired when a tab is selected. Detail: `{tab, id, index}`.\n * - `tab-closed` – Fired when a tab's close button is clicked. Detail: `{tab, id, index}`.\n * - `add-new-tab` – Fired when the \"add new tab\" button is clicked. No detail.\n *\n * ---\n *\n * ### Best Practices\n * - Only one tab should be selected at a time.\n * - Use badges sparingly to avoid overwhelming the user.\n * - Avoid using the close button for tabs that should always be present.\n * - For accessibility, ensure tab titles are clear and concise.\n * - If using custom icons, provide them via the appropriate named slot for each tab.\n *\n * @slot tab-<id>-icon - Leading icon slot for each tab (shown when `hasLeadingIcon` is true for that tab)\n * @slot tab-<id>-badge-icon - Badge icon slot for each tab (shown when `showLeadingBadgeIcon` is true for that tab)\n * @fires tab-selected {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab is selected.\n * @fires tab-closed {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab's close button is clicked.\n * @fires add-new-tab {CustomEvent<void>} Fired when the \"add new tab\" button is clicked.\n */\n@customElement('obc-tab-row')\nexport class ObcTabRow extends LitElement {\n /**\n * The list of tabs to display. Each tab is defined by an object with properties such as `id`, `title`, `subtitle`, `showSubtitle`, `hasLeadingIcon`, `hasBadge`, `badgeCount`, `badgeType`, `badgeSize`, `badgeShowNumber`, `showLeadingBadgeIcon`, and `disabled`.\n *\n * - `id` (string): Unique identifier for the tab.\n * - `title` (string): Display label for the tab.\n * - `subtitle` (string): Contextual text shown below the title when subtitle display is enabled.\n * - `showSubtitle` (boolean): Optional per-tab override for displaying the subtitle.\n * - `hasLeadingIcon` (boolean): Whether to show a leading icon (default: true).\n * - `hasBadge` (boolean): Whether to show a badge on the tab.\n * - `badgeCount` (number): Number to display in the badge.\n * - `badgeType` (BadgeType): Visual style of the badge (e.g., notification, alarm, enhance).\n * - `badgeSize` (BadgeSize): Size of the badge (e.g., regular, large).\n * - `badgeShowNumber` (boolean): If true, shows the badge number.\n * - `showLeadingBadgeIcon` (boolean): If true, shows a badge icon.\n * - `disabled` (boolean): If true, disables the tab.\n */\n @property({type: Array}) tabs: TabData[] = [];\n\n /**\n * The `id` of the currently selected tab. Only one tab can be selected at a time.\n *\n * Changing this property updates the selected tab visually and emits the `tab-selected` event when changed by user interaction.\n */\n @property({type: String, attribute: 'selected-tab-id'}) selectedTabId = '';\n\n /**\n * Whether to display a close button on each tab. When enabled, users can remove tabs individually.\n *\n * Default: `false`.\n */\n @property({type: Boolean, attribute: 'has-close'}) hasClose = false;\n\n /**\n * Enables \"hug\" mode for a more compact tab layout with reduced padding.\n *\n * Default: `false`.\n */\n @property({type: Boolean}) hug = false;\n\n /**\n * Whether to display subtitle text for each tab. Individual tabs can override this with `tab.showSubtitle`.\n *\n * Default: `false`.\n */\n @property({type: Boolean, attribute: 'show-subtitle'}) showSubtitle = false;\n\n /**\n * Whether to display an \"add new tab\" button at the end of the tab row. When clicked, emits the `add-new-tab` event.\n *\n * Default: `false`.\n */\n @property({type: Boolean, attribute: 'has-add-new-tab'}) hasAddNewTab = false;\n\n private handleTabClick(_: Event, tabId: string) {\n const tabIndex = this.tabs.findIndex((t) => t.id === tabId);\n if (tabIndex === -1) return;\n this.selectedTabId = this.tabs[tabIndex].id;\n this.dispatchEvent(\n new CustomEvent('tab-selected', {\n detail: {tab: this.tabs[tabIndex], id: tabId, index: tabIndex},\n bubbles: true,\n composed: true,\n })\n );\n }\n\n private handleTabClose(event: Event, tabId: string) {\n event.stopPropagation();\n const tabIndex = this.tabs.findIndex((t) => t.id === tabId);\n if (tabIndex === -1) return;\n const removedTab = this.tabs[tabIndex];\n this.tabs = [\n ...this.tabs.slice(0, tabIndex),\n ...this.tabs.slice(tabIndex + 1),\n ];\n if (removedTab.id === this.selectedTabId && this.tabs.length) {\n const newIdx = Math.min(tabIndex, this.tabs.length - 1);\n this.selectedTabId = this.tabs[newIdx].id;\n }\n this.dispatchEvent(\n new CustomEvent('tab-closed', {\n detail: {tab: removedTab, id: removedTab.id, index: tabIndex},\n bubbles: true,\n composed: true,\n })\n );\n }\n\n private handleAddNewTab() {\n this.dispatchEvent(\n new CustomEvent('add-new-tab', {bubbles: true, composed: true})\n );\n }\n\n private renderTab(tab: TabData, index: number) {\n const isFirst = index === 0;\n const isChecked = tab.id === this.selectedTabId;\n const showSubtitle = tab.showSubtitle ?? this.showSubtitle;\n return html`\n <obc-tab-item\n .title=${tab.title}\n .subtitle=${tab.subtitle ?? ''}\n .showSubtitle=${showSubtitle}\n .checked=${isChecked}\n .hasClose=${this.hasClose}\n .hasLeadingIcon=${tab.hasLeadingIcon ?? true}\n .hasTitle=${true}\n .hasDivider=${!isFirst}\n .hug=${this.hug}\n .disabled=${tab.disabled || false}\n .hasBadge=${tab.hasBadge ||\n (tab.badgeCount !== undefined && tab.badgeCount > 0)}\n .badgeCount=${tab.badgeCount || 0}\n .badgeType=${tab.badgeType ?? BadgeType.regular}\n .badgeSize=${tab.badgeSize ?? BadgeSize.regular}\n .badgeShowNumber=${tab.badgeShowNumber ?? true}\n .showLeadingBadgeIcon=${tab.showLeadingBadgeIcon || false}\n @tab-click=${(e: Event) => this.handleTabClick(e, tab.id)}\n @tab-close=${(e: Event) => this.handleTabClose(e, tab.id)}\n >\n ${tab.hasLeadingIcon !== false\n ? html`\n <slot name=\"tab-${tab.id}-icon\" slot=\"leading-icon\">\n <obi-placeholder></obi-placeholder>\n </slot>\n `\n : ''}\n <span slot=\"title\">${tab.title}</span>\n ${tab.showLeadingBadgeIcon\n ? html`\n <slot name=\"tab-${tab.id}-badge-icon\" slot=\"badge-icon\">\n <obi-placeholder></obi-placeholder>\n </slot>\n `\n : ''}\n </obc-tab-item>\n `;\n }\n\n override render() {\n return html`\n <div class=\"wrapper\" role=\"tablist\">\n ${repeat(\n this.tabs,\n (t) => t.id,\n (t, i) => this.renderTab(t, i)\n )}\n ${this.hasAddNewTab\n ? html`\n <obc-icon-button\n class=\"add-new-tab\"\n variant=\"flat\"\n @click=${this.handleAddNewTab}\n aria-label=\"Add new tab\"\n >\n <obi-up-iec></obi-up-iec>\n </obc-icon-button>\n `\n : ''}\n </div>\n `;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tab-row.js","sources":["../../../src/components/tab-row/tab-row.ts"],"sourcesContent":["import {LitElement, html, unsafeCSS} from 'lit';\nimport {property} from 'lit/decorators.js';\nimport {repeat} from 'lit/directives/repeat.js';\nimport compentStyle from './tab-row.css?inline';\nimport '../tab-item/tab-item.js';\nimport type {TabItemBadge} from '../tab-item/tab-item.js';\nimport '../icon-button/icon-button.js';\nimport '../../icons/icon-placeholder.js';\nimport {customElement} from '../../decorator.js';\nimport '../../icons/icon-up-iec.js';\nimport {BadgeType, BadgeSize} from '../badge/badge.js';\n\nexport interface TabData {\n id: string;\n title: string;\n subtitle?: string;\n showSubtitle?: boolean;\n hasLeadingIcon?: boolean;\n /**\n * One or more badges to display on the tab. When non-empty this takes\n * precedence over the deprecated single-badge fields below.\n */\n badges?: TabItemBadge[];\n /** @deprecated Use `badges` instead. */\n hasBadge?: boolean;\n /** @deprecated Use `badges` instead. */\n badgeCount?: number;\n /** @deprecated Use `badges` instead. */\n badgeType?: BadgeType;\n /** @deprecated Use `badges` instead. */\n badgeSize?: BadgeSize;\n /** @deprecated Use `badges` instead. */\n badgeShowNumber?: boolean;\n /** @deprecated Use `badges` instead. */\n showLeadingBadgeIcon?: boolean;\n disabled?: boolean;\n}\n\n/**\n * `<obc-tab-row>` – A horizontal tab navigation bar for switching between multiple content sections.\n *\n * Displays a row of interactive tabs, each representing a distinct view or section. Supports subtitles, badges, icons, close buttons, and an optional \"add new tab\" action. Designed for use cases where users need to quickly switch between related content areas, such as dashboards, settings, or document editors.\n *\n * ---\n *\n * ### Features\n * - **Tab Selection:** Only one tab can be selected at a time; selection is managed via the `selectedTabId` property.\n * - **Closeable Tabs:** Optionally display a close button on each tab (`hasClose`), allowing users to remove tabs dynamically.\n * - **Add New Tab:** Optionally show an \"add new tab\" button at the end of the row (`hasAddNewTab`), emitting an event when clicked.\n * - **Subtitles:** Optionally show secondary contextual text below each tab title (`showSubtitle` and `subtitle`).\n * - **Badges:** Tabs can display badges with counts and types (e.g., notification, alarm, enhance), supporting different badge sizes and optional hiding of the number.\n * - **Icons:** Each tab can show a leading icon (customizable via slot), and optionally a badge icon.\n * - **Disabled Tabs:** Individual tabs can be disabled to prevent user interaction.\n * - **Hug Mode:** When `hug` is enabled, tabs use a compact layout with reduced padding.\n * - **Responsive Layout:** Tabs are arranged horizontally and adapt to the available width.\n *\n * ---\n *\n * ### Usage Guidelines\n * Use `<obc-tab-row>` to organize content into logical sections that users can switch between without leaving the current page. Ideal for navigation in multi-view interfaces, such as settings panels, dashboards, or editors. Each tab should represent a distinct, high-level section. Avoid using tabs for navigation between unrelated pages or for single-action toggles.\n *\n * - Use badges to highlight new activity or counts (e.g., unread messages).\n * - Use the close button for tabs that represent removable or temporary content (e.g., documents, chat sessions).\n * - The \"add new tab\" action is suitable for interfaces where users can create new sections dynamically.\n * - For best accessibility, ensure each tab has a unique `id` and descriptive `title`.\n *\n * **TODO(designer):** Confirm if there are recommended limits on the number of tabs, or guidance for handling overflow (e.g., scrolling, collapsing).\n *\n * ---\n *\n * ### Example\n * ```html\n * <obc-tab-row\n * .tabs=${[\n * {id: 'tab1', title: 'Inbox', subtitle: 'Monitoring', hasBadge: true, badgeCount: 5, badgeType: 'notification'},\n * {id: 'tab2', title: 'Updates'},\n * {id: 'tab3', title: 'Settings', hasLeadingIcon: true}\n * ]}\n * selectedTabId=\"tab1\"\n * show-subtitle\n * hasClose\n * hasAddNewTab\n * >\n * <obi-placeholder slot=\"tab-tab1-icon\"></obi-placeholder>\n * <obi-placeholder slot=\"tab-tab3-icon\"></obi-placeholder>\n * </obc-tab-row>\n * ```\n *\n * ---\n *\n * ### Slots\n *\n * | Slot Name | Renders When... | Purpose |\n * |--------------------------|------------------------------------|------------------------------------------------------|\n * | `tab-<id>-icon` | If `hasLeadingIcon` is true | Leading icon for a specific tab (replaceable) |\n * | `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`. |\n *\n * ---\n *\n * ### Events\n * - `tab-selected` – Fired when a tab is selected. Detail: `{tab, id, index}`.\n * - `tab-closed` – Fired when a tab's close button is clicked. Detail: `{tab, id, index}`.\n * - `add-new-tab` – Fired when the \"add new tab\" button is clicked. No detail.\n *\n * ---\n *\n * ### Best Practices\n * - Only one tab should be selected at a time.\n * - Use badges sparingly to avoid overwhelming the user.\n * - Avoid using the close button for tabs that should always be present.\n * - For accessibility, ensure tab titles are clear and concise.\n * - If using custom icons, provide them via the appropriate named slot for each tab.\n *\n * @slot tab-<id>-icon - Leading icon slot for each tab (shown when `hasLeadingIcon` is true for that tab)\n * @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`.\n * @fires tab-selected {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab is selected.\n * @fires tab-closed {CustomEvent<{tab: TabData, id: string, index: number}>} Fired when a tab's close button is clicked.\n * @fires add-new-tab {CustomEvent<void>} Fired when the \"add new tab\" button is clicked.\n */\n@customElement('obc-tab-row')\nexport class ObcTabRow extends LitElement {\n /**\n * The list of tabs to display. Each tab is defined by an object with properties such as `id`, `title`, `subtitle`, `showSubtitle`, `hasLeadingIcon`, `hasBadge`, `badgeCount`, `badgeType`, `badgeSize`, `badgeShowNumber`, `showLeadingBadgeIcon`, and `disabled`.\n *\n * - `id` (string): Unique identifier for the tab.\n * - `title` (string): Display label for the tab.\n * - `subtitle` (string): Contextual text shown below the title when subtitle display is enabled.\n * - `showSubtitle` (boolean): Optional per-tab override for displaying the subtitle.\n * - `hasLeadingIcon` (boolean): Whether to show a leading icon (default: true).\n * - `badges` (TabItemBadge[]): One or more badges to display on the tab. Takes precedence over the deprecated single-badge fields below.\n * - `hasBadge` (boolean, deprecated): Whether to show a badge on the tab.\n * - `badgeCount` (number, deprecated): Number to display in the badge.\n * - `badgeType` (BadgeType, deprecated): Visual style of the badge (e.g., notification, alarm, enhance).\n * - `badgeSize` (BadgeSize, deprecated): Size of the badge (e.g., regular, large).\n * - `badgeShowNumber` (boolean, deprecated): If true, shows the badge number.\n * - `showLeadingBadgeIcon` (boolean, deprecated): If true, shows a badge icon.\n * - `disabled` (boolean): If true, disables the tab.\n */\n @property({type: Array}) tabs: TabData[] = [];\n\n /**\n * The `id` of the currently selected tab. Only one tab can be selected at a time.\n *\n * Changing this property updates the selected tab visually and emits the `tab-selected` event when changed by user interaction.\n */\n @property({type: String, attribute: 'selected-tab-id'}) selectedTabId = '';\n\n /**\n * Whether to display a close button on each tab. When enabled, users can remove tabs individually.\n *\n * Default: `false`.\n */\n @property({type: Boolean, attribute: 'has-close'}) hasClose = false;\n\n @property({type: Boolean}) centerContent = false;\n\n /**\n * Enables \"hug\" mode for a more compact tab layout with reduced padding.\n *\n * Default: `false`.\n */\n @property({type: Boolean}) hug = false;\n\n /**\n * Whether to display subtitle text for each tab. Individual tabs can override this with `tab.showSubtitle`.\n *\n * Default: `false`.\n */\n @property({type: Boolean, attribute: 'show-subtitle'}) showSubtitle = false;\n\n /**\n * Whether to display an \"add new tab\" button at the end of the tab row. When clicked, emits the `add-new-tab` event.\n *\n * Default: `false`.\n */\n @property({type: Boolean, attribute: 'has-add-new-tab'}) hasAddNewTab = false;\n\n private handleTabClick(_: Event, tabId: string) {\n const tabIndex = this.tabs.findIndex((t) => t.id === tabId);\n if (tabIndex === -1) return;\n this.selectedTabId = this.tabs[tabIndex].id;\n this.dispatchEvent(\n new CustomEvent('tab-selected', {\n detail: {tab: this.tabs[tabIndex], id: tabId, index: tabIndex},\n bubbles: true,\n composed: true,\n })\n );\n }\n\n private handleTabClose(event: Event, tabId: string) {\n event.stopPropagation();\n const tabIndex = this.tabs.findIndex((t) => t.id === tabId);\n if (tabIndex === -1) return;\n const removedTab = this.tabs[tabIndex];\n this.tabs = [\n ...this.tabs.slice(0, tabIndex),\n ...this.tabs.slice(tabIndex + 1),\n ];\n if (removedTab.id === this.selectedTabId && this.tabs.length) {\n const newIdx = Math.min(tabIndex, this.tabs.length - 1);\n this.selectedTabId = this.tabs[newIdx].id;\n }\n this.dispatchEvent(\n new CustomEvent('tab-closed', {\n detail: {tab: removedTab, id: removedTab.id, index: tabIndex},\n bubbles: true,\n composed: true,\n })\n );\n }\n\n private handleAddNewTab() {\n this.dispatchEvent(\n new CustomEvent('add-new-tab', {bubbles: true, composed: true})\n );\n }\n\n private renderTab(tab: TabData, index: number) {\n const isFirst = index === 0;\n const previousTabSelected = this.selectedTabId === this.tabs[index - 1]?.id;\n const isChecked = tab.id === this.selectedTabId;\n const showSubtitle = tab.showSubtitle ?? this.showSubtitle;\n // Forward a light-DOM slot for every distinct `iconSlotName` declared by the\n // tab's badges (new `badges` array), plus the deprecated single badge-icon\n // slot. obc-tab-item renders `<slot name=${iconSlotName} slot=\"badge-icon\">`\n // inside each badge, so obc-tab-row must project matching content into that\n // slot. Slots are namespaced by tab id to stay unique across the row.\n const badgeIconSlots = [\n ...new Set(\n (tab.badges ?? [])\n .map((badge) => badge.iconSlotName)\n .filter((slotName): slotName is string => slotName !== undefined)\n ),\n ];\n if (\n (tab.showLeadingBadgeIcon ?? false) &&\n !badgeIconSlots.includes('badge-icon')\n ) {\n badgeIconSlots.push('badge-icon');\n }\n return html`\n <obc-tab-item\n .title=${tab.title}\n .subtitle=${tab.subtitle ?? ''}\n .showSubtitle=${showSubtitle}\n .checked=${isChecked}\n .hasClose=${this.hasClose}\n .hasLeadingIcon=${tab.hasLeadingIcon ?? true}\n .hasTitle=${true}\n .hasDivider=${!isFirst && !previousTabSelected}\n .hug=${this.hug}\n .centerContent=${this.centerContent}\n .disabled=${tab.disabled || false}\n .badges=${tab.badges ?? []}\n .hasBadge=${tab.hasBadge || false}\n .badgeCount=${tab.badgeCount || 0}\n .badgeType=${tab.badgeType ?? BadgeType.regular}\n .badgeSize=${tab.badgeSize ?? BadgeSize.regular}\n .badgeShowNumber=${tab.badgeShowNumber ?? true}\n .showLeadingBadgeIcon=${tab.showLeadingBadgeIcon || false}\n @tab-click=${(e: Event) => this.handleTabClick(e, tab.id)}\n @tab-close=${(e: Event) => this.handleTabClose(e, tab.id)}\n >\n ${tab.hasLeadingIcon !== false\n ? html`\n <slot name=\"tab-${tab.id}-icon\" slot=\"leading-icon\">\n <obi-placeholder></obi-placeholder>\n </slot>\n `\n : ''}\n <span slot=\"title\">${tab.title}</span>\n ${badgeIconSlots.map(\n (slotName) => html`\n <slot name=\"tab-${tab.id}-${slotName}\" slot=${slotName}>\n <obi-placeholder></obi-placeholder>\n </slot>\n `\n )}\n </obc-tab-item>\n `;\n }\n\n override render() {\n return html`\n <div class=\"wrapper\" role=\"tablist\">\n ${repeat(\n this.tabs,\n (t) => t.id,\n (t, i) => this.renderTab(t, i)\n )}\n ${this.hasAddNewTab\n ? html`\n <obc-icon-button\n class=\"add-new-tab\"\n variant=\"flat\"\n @click=${this.handleAddNewTab}\n aria-label=\"Add new tab\"\n >\n <obi-up-iec></obi-up-iec>\n </obc-icon-button>\n `\n : ''}\n </div>\n `;\n }\n\n static override styles = unsafeCSS(compentStyle);\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oicl/openbridge-webcomponents-full-bundle",
3
- "version": "2.0.0-next.84",
3
+ "version": "2.0.0-next.85",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -61,11 +61,18 @@
61
61
  color: var(--on-flat-disabled-color);
62
62
  }
63
63
 
64
+ .badges {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 4px;
68
+ position: relative;
69
+ }
70
+
64
71
  .badge {
65
72
  position: relative;
66
73
  }
67
74
 
68
- &:not(.hug) .badge {
75
+ &:not(.hug) .badges {
69
76
  right: 0;
70
77
  }
71
78
 
@@ -28,6 +28,7 @@ const meta: Meta<ObcTabItem> = {
28
28
  .badgeShowNumber=${args.badgeShowNumber}
29
29
  .showLeadingBadgeIcon=${args.showLeadingBadgeIcon}
30
30
  .centerContent=${args.centerContent}
31
+ .badges=${args.badges ?? []}
31
32
  >
32
33
  ${args.hasLeadingIcon
33
34
  ? html`<obi-placeholder slot="leading-icon"></obi-placeholder>`
@@ -35,6 +36,14 @@ const meta: Meta<ObcTabItem> = {
35
36
  ${args.hasBadge
36
37
  ? html`<obi-placeholder slot="badge-icon"></obi-placeholder>`
37
38
  : null}
39
+ ${(args.badges ?? [])
40
+ .filter((badge) => badge.iconSlotName)
41
+ .map(
42
+ (badge) =>
43
+ html`<obi-placeholder
44
+ slot=${badge.iconSlotName!}
45
+ ></obi-placeholder>`
46
+ )}
38
47
  </obc-tab-item>
39
48
  `,
40
49
  args: {
@@ -56,6 +65,7 @@ const meta: Meta<ObcTabItem> = {
56
65
  badgeShowNumber: true,
57
66
  showLeadingBadgeIcon: false,
58
67
  centerContent: false,
68
+ badges: [],
59
69
  },
60
70
  } satisfies Meta<ObcTabItem>;
61
71
 
@@ -311,6 +321,26 @@ export const HighBadgeCount = {
311
321
  title: 'High Alarm Count',
312
322
  },
313
323
  };
324
+ export const MultipleBadges = {
325
+ args: {
326
+ hug: false,
327
+ hasLeadingIcon: true,
328
+ hasClose: false,
329
+ centerContent: false,
330
+ title: 'Multiple Badges',
331
+ badges: [
332
+ {type: BadgeType.alarm, size: BadgeSize.regular, count: 3},
333
+ {type: BadgeType.warning, size: BadgeSize.regular, count: 12},
334
+ {
335
+ type: BadgeType.notification,
336
+ size: BadgeSize.regular,
337
+ showIcon: true,
338
+ iconSlotName: 'notification-icon',
339
+ },
340
+ ],
341
+ },
342
+ };
343
+
314
344
  export const BadgeHideNumber = {
315
345
  args: {
316
346
  hug: false,
@@ -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
  ],