@repobit/dex-system-design 0.23.70 → 0.23.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.23.72](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.71...@repobit/dex-system-design@0.23.72) (2026-07-08)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **DEX-1014:** tabs component family
11
+ * **DEX-1014:** tabs component family
12
+
13
+
14
+ ## [0.23.71](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.70...@repobit/dex-system-design@0.23.71) (2026-07-07)
15
+
16
+ ### Bug Fixes
17
+
18
+ * **DEX-1014:** card adjustments css
19
+ * **DEX-1014:** card adjustments css
20
+
21
+
6
22
  ## [0.23.70](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.69...@repobit/dex-system-design@0.23.70) (2026-07-07)
7
23
 
8
24
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repobit/dex-system-design",
3
- "version": "0.23.70",
3
+ "version": "0.23.72",
4
4
  "description": "Design system based on Web Components.",
5
5
  "author": "Iordache Matei Cezar <miordache@bitdefender.com>",
6
6
  "homepage": "https://github.com/bitdefender/dex-core#readme",
@@ -52,6 +52,8 @@
52
52
  "./pricing-cards": "./src/components/pricing-cards/pricing-card.js",
53
53
  "./radio": "./src/components/radio/radio.js",
54
54
  "./tabs": "./src/components/tabs/tabs.js",
55
+ "./tabsComponent": "./src/components/tabsComponent/tabsComponent.js",
56
+ "./tabsComponentFamily": "./src/components/tabsComponentFamily/tabsComponentFamily.js",
55
57
  "./terms": "./src/components/termsOfUse/terms.js"
56
58
  },
57
59
  "publishConfig": {
@@ -72,8 +74,8 @@
72
74
  "url": "https://github.com/bitdefender/dex-core/issues"
73
75
  },
74
76
  "dependencies": {
75
- "@repobit/dex-store": "1.3.66",
76
- "@repobit/dex-store-elements": "1.4.58",
77
+ "@repobit/dex-store": "1.3.68",
78
+ "@repobit/dex-store-elements": "1.4.60",
77
79
  "lit": "^3.3.2"
78
80
  },
79
81
  "devDependencies": {
@@ -90,5 +92,5 @@
90
92
  "volta": {
91
93
  "node": "24.14.0"
92
94
  },
93
- "gitHead": "345b65276e5c71e0106f563d644b713645b98221"
95
+ "gitHead": "7f992fcac9ee0c02c66190d68b6713b10b7589a8"
94
96
  }
@@ -5,8 +5,6 @@ export default css`
5
5
  display: block;
6
6
  --background-card-grey: var(--color-neutral-25);
7
7
  --border-card-grey: var(--color-neutral-50);
8
- padding-top: var(--spacing-64);
9
- padding-bottom: var(--spacing-64);
10
8
  max-width: 1290px;
11
9
  margin: 0 auto;
12
10
  }
@@ -187,15 +187,29 @@ class CardItem extends LitElement {
187
187
  clone.style.height = "auto";
188
188
  clone.style.objectFit = "contain";
189
189
  clone.style.display = "block";
190
+
191
+ // Aplica si pe img din interior (ex: picture > img)
192
+ const innerImg = clone.querySelector("img");
193
+ if (innerImg) {
194
+ innerImg.style.maxWidth = "120px";
195
+ innerImg.style.maxHeight = "120px";
196
+ innerImg.style.width = "auto";
197
+ innerImg.style.height = "auto";
198
+ innerImg.style.objectFit = "contain";
199
+ innerImg.style.display = "block";
200
+ innerImg.removeAttribute("width");
201
+ innerImg.removeAttribute("height");
202
+ }
203
+
190
204
  return clone;
191
205
  }
192
206
 
193
207
  if (this.icon) {
194
208
  return html`<img
195
- src="${this.icon}"
196
- alt="icon"
197
- class="bd-light-icon-s"
198
- />`;
209
+ src="${this.icon}"
210
+ alt="icon"
211
+ class="bd-light-icon-s"
212
+ />`;
199
213
  }
200
214
 
201
215
  return "";
@@ -0,0 +1,181 @@
1
+ import { css } from "lit";
2
+
3
+ export default css`
4
+ :host {
5
+ --bd-accessibility-focus: var(--color-blue-500);
6
+ display: block;
7
+ font-family: var(--typography-fontFamily-sans);
8
+ font-size: 100%;
9
+ padding-top: var(--spacing-64);
10
+ padding-bottom: var(--spacing-64);
11
+ }
12
+
13
+ bd-highlight {
14
+ max-width: 660px;
15
+ padding-top: var(--spacing-32);
16
+ }
17
+
18
+ .bd-tabs-component {
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ max-width: 990px;
23
+ margin-inline: auto;
24
+ padding-inline: var(--layout-ensemble-inline-padding, var(--spacing-24));
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ .bd-tabs-subtitle {
29
+ font-size: var(--typography-body-regular-fontSize);
30
+ max-width: 560px;
31
+ text-align: center;
32
+ margin-top: var(--spacing-32);
33
+ margin-bottom: var(--spacing-32);
34
+ }
35
+
36
+ .bd-tabs-container {
37
+ display: inline-flex;
38
+ justify-content: center;
39
+ flex-wrap: wrap;
40
+ border-bottom: 1px solid var(--color-neutral-100);
41
+ width: 100%;
42
+ margin-bottom: var(--spacing-32);
43
+ }
44
+
45
+ .bd-tab-button {
46
+ display: inline-flex;
47
+ align-items: center;
48
+ gap: var(--spacing-8);
49
+ background: none;
50
+ border: none;
51
+ padding: var(--spacing-10) var(--spacing-32);
52
+ cursor: pointer;
53
+ color: var(--color-blue-500);
54
+ position: relative;
55
+ font-family: var(--typography-fontFamily-sans);
56
+ font-size: var(--typography-body-large-fontSize);
57
+ font-weight: var(--typography-fontWeight-semibold);
58
+ transition: color var(--transition-duration-fast, 150ms) ease;
59
+ }
60
+
61
+ .bd-tab-button::after {
62
+ content: "";
63
+ height: 3px;
64
+ background: var(--color-blue-500);
65
+ position: absolute;
66
+ left: 0;
67
+ bottom: 0;
68
+ width: 0;
69
+ transition: width var(--transition-duration-slow, 300ms) ease;
70
+ }
71
+
72
+ .bd-tab-button.bd-selected::after {
73
+ width: 100%;
74
+ }
75
+
76
+ .bd-tab-button.bd-selected {
77
+ color: var(--color-neutral-900);
78
+ }
79
+
80
+ .bd-tab-button:focus-visible {
81
+ outline: 2px solid var(--bd-accessibility-focus);
82
+ outline-offset: 2px;
83
+ border-radius: var(--radius-sm);
84
+ }
85
+
86
+ .bd-tab-icon {
87
+ width: 28px;
88
+ height: 28px;
89
+ object-fit: contain;
90
+ }
91
+
92
+ .bd-tab-label {
93
+ pointer-events: none;
94
+ }
95
+
96
+ .bd-panel-wrapper {
97
+ width: 100%;
98
+ box-sizing: border-box;
99
+ }
100
+
101
+ .bd-card-container {
102
+ display: flex;
103
+ align-items: center;
104
+ gap: var(--spacing-64);
105
+ width: 100%;
106
+ animation: bd-fade-slide-right 350ms ease forwards;
107
+ }
108
+
109
+ .bd-left {
110
+ flex: 1;
111
+ display: flex;
112
+ flex-direction: column;
113
+ align-items: flex-start;
114
+ gap: var(--spacing-24);
115
+ }
116
+
117
+ .bd-panel-content {
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: var(--spacing-8);
121
+ }
122
+
123
+ .bd-right {
124
+ flex: 1;
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ }
129
+
130
+ .bd-panel-image {
131
+ width: 100%;
132
+ max-width: 520px;
133
+ height: auto;
134
+ border-radius: var(--radius-lg, 16px);
135
+ object-fit: cover;
136
+ display: block;
137
+ }
138
+
139
+ @keyframes bd-fade-slide-right {
140
+ from {
141
+ opacity: 0;
142
+ transform: translateX(24px);
143
+ }
144
+ to {
145
+ opacity: 1;
146
+ transform: translateX(0);
147
+ }
148
+ }
149
+
150
+ @media (max-width: 992px) {
151
+ .bd-card-container {
152
+ flex-direction: column-reverse;
153
+ gap: var(--spacing-32);
154
+ }
155
+
156
+ .bd-left {
157
+ align-items: center;
158
+ text-align: center;
159
+ }
160
+
161
+ .bd-panel-image {
162
+ max-width: 100%;
163
+ }
164
+ }
165
+
166
+ @media (max-width: 600px) {
167
+ .bd-tabs-container {
168
+ gap: var(--spacing-4);
169
+ }
170
+
171
+ .bd-tab-button {
172
+ padding: var(--spacing-8) var(--spacing-12);
173
+ font-size: var(--typography-fontSize-sm);
174
+ }
175
+
176
+ .bd-tab-icon {
177
+ width: 20px;
178
+ height: 20px;
179
+ }
180
+ }
181
+ `;
@@ -0,0 +1,180 @@
1
+ import { LitElement, html } from "lit";
2
+ import { keyed } from "lit/directives/keyed.js";
3
+ import { tokens } from "../../tokens/tokens.js";
4
+ import "../button/button.js";
5
+ import "../heading/heading.js";
6
+ import "../highlight/highlight.js";
7
+ import "../paragraph/paragraph.js";
8
+ import tabsChildCSS from "./tabsComponentFamily.css.js";
9
+
10
+ class BdTabContentPanelFamily extends HTMLElement {
11
+ connectedCallback() {
12
+ this.style.display = "none";
13
+ }
14
+ }
15
+ customElements.define("bd-tab-content-panel-family", BdTabContentPanelFamily);
16
+
17
+ class TabsComponentFamily extends LitElement {
18
+ static properties = {
19
+ title : { type: String },
20
+ subtitle : { type: String },
21
+ highlightText : { type: String, attribute: "highlight-text" },
22
+ selectedTab : { type: Number, state: true },
23
+ _panelMinHeight: { type: Number, state: true }
24
+ };
25
+
26
+ static styles = [tokens, tabsChildCSS];
27
+
28
+ constructor() {
29
+ super();
30
+ this.title = "";
31
+ this.subtitle = "";
32
+ this.highlightText = "";
33
+ this.selectedTab = 0;
34
+ this._panelMinHeight = 0;
35
+ }
36
+
37
+ _getPanels() {
38
+ return Array.from(this.querySelectorAll(":scope > bd-tab-content-panel-family"));
39
+ }
40
+
41
+ selectTab(index) {
42
+ this.selectedTab = index;
43
+ }
44
+
45
+ async firstUpdated() {
46
+ await this.updateComplete;
47
+ await this._measureAllPanels();
48
+ }
49
+
50
+ async _measureAllPanels() {
51
+ const panels = this._getPanels();
52
+ const wrapper = this.renderRoot.querySelector(".bd-panel-wrapper");
53
+ if (!wrapper || !panels.length) return;
54
+
55
+ const originalTab = this.selectedTab;
56
+ let maxHeight = 0;
57
+
58
+ for (let i = 0; i < panels.length; i++) {
59
+ this.selectedTab = i;
60
+ await this.updateComplete;
61
+ await new Promise(r => requestAnimationFrame(r));
62
+ await new Promise(r => requestAnimationFrame(r));
63
+
64
+ const h = wrapper.scrollHeight;
65
+ if (h > maxHeight) maxHeight = h;
66
+ }
67
+
68
+ this.selectedTab = originalTab;
69
+ await this.updateComplete;
70
+ this._panelMinHeight = maxHeight;
71
+ }
72
+
73
+ _renderPanel(panel) {
74
+ const headingEl = panel.querySelector("[slot='heading']");
75
+ const contentEl = panel.querySelector("[slot='content']");
76
+ const imageEl = panel.querySelector("[slot='image']");
77
+ const ctaEl = panel.querySelector("[slot='cta']");
78
+
79
+ const heading = panel.getAttribute("heading");
80
+ const imageSrc = panel.getAttribute("image-src");
81
+ const imageAlt = panel.getAttribute("image-alt") || "";
82
+ const ctaLabel = panel.getAttribute("cta-label");
83
+ const ctaHref = panel.getAttribute("cta-href") || "#";
84
+ const ctaKind = panel.getAttribute("cta-kind") || "danger";
85
+ const ctaSize = panel.getAttribute("cta-size") || "md";
86
+
87
+ return html`
88
+ <div class="bd-card-container">
89
+
90
+ <div class="bd-left">
91
+ ${headingEl
92
+ ? headingEl.cloneNode(true)
93
+ : heading
94
+ ? html`<bd-h as="h3" color="var(--color-blue-600)">${heading}</bd-h>`
95
+ : ""}
96
+
97
+ ${contentEl
98
+ ? html`<div class="bd-panel-content">${contentEl.cloneNode(true)}</div>`
99
+ : ""}
100
+
101
+ ${ctaEl
102
+ ? ctaEl.cloneNode(true)
103
+ : ctaLabel
104
+ ? html`
105
+ <bd-button-link
106
+ href="${ctaHref}"
107
+ kind="${ctaKind}"
108
+ size="${ctaSize}"
109
+ label="${ctaLabel}"
110
+ strong="true"
111
+ >
112
+ ${ctaLabel}
113
+ </bd-button-link>`
114
+ : ""}
115
+ </div>
116
+
117
+ <div class="bd-right">
118
+ ${imageEl
119
+ ? imageEl.cloneNode(true)
120
+ : imageSrc
121
+ ? html`<img src="${imageSrc}" alt="${imageAlt}" class="bd-panel-image" loading="lazy" />`
122
+ : ""}
123
+ </div>
124
+
125
+ </div>
126
+ `;
127
+ }
128
+
129
+ render() {
130
+ const panels = this._getPanels();
131
+ const activePanel = panels[this.selectedTab];
132
+
133
+ return html`
134
+ <div class="bd-tabs-component">
135
+
136
+ ${this.title
137
+ ? html`<bd-highlight highlight-text="${this.highlightText}">${this.title}</bd-highlight>`
138
+ : ""}
139
+
140
+ ${this.subtitle
141
+ ? html`<bd-p kind="regular" class="bd-tabs-subtitle">${this.subtitle}</bd-p>`
142
+ : ""}
143
+
144
+ <div class="bd-tabs-container" role="tablist">
145
+ ${panels.map((panel, index) => {
146
+ const icon = panel.getAttribute("icon");
147
+ return html`
148
+ <button
149
+ role="tab"
150
+ aria-selected="${this.selectedTab === index ? "true" : "false"}"
151
+ aria-controls="tab-panel-${index}"
152
+ id="tab-btn-${index}"
153
+ class="bd-tab-button ${this.selectedTab === index ? "bd-selected" : ""}"
154
+ @click=${() => this.selectTab(index)}
155
+ >
156
+ ${icon ? html`<img src="${icon}" alt="" class="bd-tab-icon" />` : ""}
157
+ <span class="bd-tab-label">${panel.getAttribute("title")}</span>
158
+ </button>
159
+ `;
160
+ })}
161
+ </div>
162
+
163
+ <div
164
+ id="tab-panel-${this.selectedTab}"
165
+ role="tabpanel"
166
+ aria-labelledby="tab-btn-${this.selectedTab}"
167
+ class="bd-panel-wrapper"
168
+ style="${this._panelMinHeight ? `min-height: ${this._panelMinHeight}px;` : ""}"
169
+ >
170
+ ${activePanel
171
+ ? keyed(this.selectedTab, this._renderPanel(activePanel))
172
+ : ""}
173
+ </div>
174
+
175
+ </div>
176
+ `;
177
+ }
178
+ }
179
+
180
+ customElements.define("bd-tabs-component-family", TabsComponentFamily);
@@ -0,0 +1,319 @@
1
+ import { html } from 'lit';
2
+ import './tabsComponentFamily.js';
3
+
4
+ export default {
5
+ title : 'Components/TabsComponentFamily',
6
+ tags : ['autodocs'],
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: `
11
+ **TabsComponentFamily** is a fully slotted Lit component for a tab-based content showcase with left content and right image.
12
+
13
+ ### Usage
14
+ \`\`\`html
15
+ <bd-tabs-component-family
16
+ title="Customized online protection for each family member"
17
+ highlight-text="family member"
18
+ subtitle="Your subtitle here."
19
+ >
20
+ <bd-tab-content-panel-family
21
+ title="Admin"
22
+ image-alt="Description"
23
+ cta-label="Stay worry free"
24
+ cta-href="#"
25
+ >
26
+ <bd-h as="h3" slot="heading">Your heading here.</bd-h>
27
+ <div slot="content">
28
+ <bd-p kind="small">Your content here.</bd-p>
29
+ </div>
30
+ <img slot="image" src="/assets/image.png" alt="Description" loading="lazy" />
31
+ </bd-tab-content-panel-family>
32
+ </bd-tabs-component-family>
33
+ \`\`\`
34
+
35
+ ### Atribute \`bd-tab-content-panel-family\`
36
+ | Atribut | Tip | Descriere |
37
+ |---|---|---|
38
+ | \`title\` | String | Label-ul butonului de tab |
39
+ | \`icon\` | String | Cale imagine icon lângă label (opțional) |
40
+ | \`image-src\` | String | Imaginea din dreapta (fallback când nu există slot="image") |
41
+ | \`image-alt\` | String | Alt text imagine |
42
+ | \`cta-label\` | String | Text buton CTA (alternativă la slot) |
43
+ | \`cta-href\` | String | Href buton CTA |
44
+ | \`cta-kind\` | String | Kind buton CTA (default: danger) |
45
+ | \`cta-size\` | String | Size buton CTA (default: md) |
46
+
47
+ ### Slot-uri \`bd-tab-content-panel-family\`
48
+ | Slot | Descriere |
49
+ |---|---|
50
+ | \`heading\` | Heading custom |
51
+ | \`content\` | Orice conținut: paragrafe, liste, etc. |
52
+ | \`image\` | Imagine custom — suprascrie \`image-src\` |
53
+ | \`cta\` | Buton/link custom — suprascrie \`cta-label\` |
54
+ `
55
+ }
56
+ }
57
+ },
58
+ argTypes: {
59
+ title: {
60
+ control : 'text',
61
+ description: 'Titlul principal al componentei',
62
+ table : { type: { summary: 'string' }, category: 'TabsComponentFamily' }
63
+ },
64
+ subtitle: {
65
+ control : 'text',
66
+ description: 'Subtitlul componentei',
67
+ table : { type: { summary: 'string' }, category: 'TabsComponentFamily' }
68
+ },
69
+ highlightText: {
70
+ control : 'text',
71
+ description: 'Textul care se highlightează în titlu',
72
+ table : { type: { summary: 'string' }, category: 'TabsComponentFamily' }
73
+ }
74
+ }
75
+ };
76
+
77
+ const panels = [
78
+ {
79
+ title : 'Admin',
80
+ imageSrc: '/assets/tabs-img1.png',
81
+ imageAlt: 'Admin using laptop',
82
+ heading : 'Keep your loved ones protected.',
83
+ content : 'As the Subscription Manager, you can oversee and adjust online safety through an intuitive dashboard. Effortlessly manage settings, monitor real-time alerts, and maintain control without compromising simplicity or security.',
84
+ ctaLabel: 'Stay worry free',
85
+ ctaHref : '#admin'
86
+ },
87
+ {
88
+ title : 'Adult',
89
+ imageSrc: '/assets/tabs-img2.png',
90
+ imageAlt: 'Adult protection',
91
+ heading : 'Stay safe online, every day.',
92
+ content : 'Advanced privacy and security tools tailored for adults. Browse safely, protect your identity, and keep your data private at all times.',
93
+ ctaLabel: 'Learn more',
94
+ ctaHref : '#adult'
95
+ },
96
+ {
97
+ title : 'Teen',
98
+ imageSrc: '/assets/tabs-img3.png',
99
+ imageAlt: 'Teen protection',
100
+ heading : 'Safe browsing for teens.',
101
+ content : 'Give teens the freedom to explore online while keeping them protected from inappropriate content, cyberbullying, and online threats.',
102
+ ctaLabel: 'Explore features',
103
+ ctaHref : '#teen'
104
+ },
105
+ {
106
+ title : 'Child',
107
+ imageSrc: '/assets/tabs-img3.png',
108
+ imageAlt: 'Child protection',
109
+ heading : 'A safe digital world for children.',
110
+ content : 'Comprehensive parental controls and content filtering ensure your child has a safe, age-appropriate online experience every time they connect.',
111
+ ctaLabel: 'Protect your child',
112
+ ctaHref : '#child'
113
+ }
114
+ ];
115
+
116
+ const renderPanels = () => panels.map(p => html`
117
+ <bd-tab-content-panel-family
118
+ title="${p.title}"
119
+ image-src="${p.imageSrc}"
120
+ image-alt="${p.imageAlt}"
121
+ cta-label="${p.ctaLabel}"
122
+ cta-href="${p.ctaHref}"
123
+ >
124
+ <bd-h as="h3" slot="heading" style="color: var(--color-blue-600);">${p.heading}</bd-h>
125
+ <div slot="content">
126
+ <bd-p kind="small">${p.content}</bd-p>
127
+ </div>
128
+ </bd-tab-content-panel-family>
129
+ `);
130
+
131
+ export const Default = {
132
+ name : 'Default (4 tabs)',
133
+ render: () => html`
134
+ <bd-tabs-component-family
135
+ title="Customized online protection for each family member – Smart, Private & Safe"
136
+ highlight-text="family member"
137
+ subtitle="Your family is unique and so should be your online security. Our family protection plan adapts to each member's needs, offering the ideal mix of privacy, freedom, and safety."
138
+ >
139
+ ${renderPanels()}
140
+ </bd-tabs-component-family>
141
+ `,
142
+ parameters: { docs: { description: { story: 'Default cu 4 tab-uri și conținut complet.' } } }
143
+ };
144
+
145
+ export const WithImageSlot = {
146
+ name : 'Cu slot="image" (picture element)',
147
+ render: () => html`
148
+ <bd-tabs-component-family
149
+ title="Customized online protection for each family member – Smart, Private & Safe"
150
+ highlight-text="family member"
151
+ subtitle="Folosind slot='image' pentru control complet asupra imaginii."
152
+ >
153
+ ${panels.map(p => html`
154
+ <bd-tab-content-panel-family
155
+ title="${p.title}"
156
+ cta-label="${p.ctaLabel}"
157
+ cta-href="${p.ctaHref}"
158
+ >
159
+ <bd-h as="h3" slot="heading" style="color: var(--color-blue-600);">${p.heading}</bd-h>
160
+ <div slot="content">
161
+ <bd-p kind="small">${p.content}</bd-p>
162
+ </div>
163
+ <picture slot="image" style="display:block;">
164
+ <source type="image/webp" srcset="${p.imageSrc}?width=2000&format=webp&optimize=medium" media="(min-width: 600px)" />
165
+ <source type="image/webp" srcset="${p.imageSrc}?width=750&format=webp&optimize=medium" />
166
+ <img
167
+ src="${p.imageSrc}"
168
+ alt="${p.imageAlt}"
169
+ loading="lazy"
170
+ style="width:100%;max-width:520px;height:auto;border-radius:var(--radius-lg,16px);object-fit:cover;display:block;"
171
+ />
172
+ </picture>
173
+ </bd-tab-content-panel-family>
174
+ `)}
175
+ </bd-tabs-component-family>
176
+ `,
177
+ parameters: { docs: { description: { story: 'Imaginea furnizată via slot="image" cu element picture complet.' } } }
178
+ };
179
+
180
+ export const NoTitleNoSubtitle = {
181
+ name : 'No Title / No Subtitle',
182
+ render: () => html`
183
+ <bd-tabs-component-family>
184
+ ${renderPanels()}
185
+ </bd-tabs-component-family>
186
+ `,
187
+ parameters: { docs: { description: { story: 'Fără titlu și subtitlu.' } } }
188
+ };
189
+
190
+ export const SingleTab = {
191
+ name : 'Single Tab (edge case)',
192
+ render: () => html`
193
+ <bd-tabs-component-family
194
+ title="Protection for your family"
195
+ highlight-text="family"
196
+ subtitle="Stay protected."
197
+ >
198
+ <bd-tab-content-panel-family
199
+ title="Admin"
200
+ image-src="/assets/tabs-img1.png"
201
+ image-alt="Admin"
202
+ cta-label="Get started"
203
+ cta-href="#"
204
+ >
205
+ <bd-h as="h3" slot="heading" style="color: var(--color-blue-600);">Keep your loved ones protected.</bd-h>
206
+ <div slot="content">
207
+ <bd-p kind="small">Single tab edge case — only one panel available.</bd-p>
208
+ </div>
209
+ </bd-tab-content-panel-family>
210
+ </bd-tabs-component-family>
211
+ `,
212
+ parameters: { docs: { description: { story: 'Edge case cu un singur tab.' } } }
213
+ };
214
+
215
+ export const NoCTA = {
216
+ name : 'No CTA Button',
217
+ render: () => html`
218
+ <bd-tabs-component-family
219
+ title="Customized online protection for each family member"
220
+ highlight-text="family member"
221
+ subtitle="Stay protected at all times."
222
+ >
223
+ ${panels.map(p => html`
224
+ <bd-tab-content-panel-family
225
+ title="${p.title}"
226
+ image-src="${p.imageSrc}"
227
+ image-alt="${p.imageAlt}"
228
+ >
229
+ <bd-h as="h3" slot="heading" style="color: var(--color-blue-600);">${p.heading}</bd-h>
230
+ <div slot="content">
231
+ <bd-p kind="small">${p.content}</bd-p>
232
+ </div>
233
+ </bd-tab-content-panel-family>
234
+ `)}
235
+ </bd-tabs-component-family>
236
+ `,
237
+ parameters: { docs: { description: { story: 'Fără buton CTA.' } } }
238
+ };
239
+
240
+ export const WithIcons = {
241
+ name : 'With Tab Icons',
242
+ render: () => html`
243
+ <bd-tabs-component-family
244
+ title="Customized online protection for each family member"
245
+ highlight-text="family member"
246
+ subtitle="Stay protected at all times."
247
+ >
248
+ ${panels.map((p, i) => html`
249
+ <bd-tab-content-panel-family
250
+ title="${p.title}"
251
+ icon="/assets/pic${i + 1}.png"
252
+ image-src="${p.imageSrc}"
253
+ image-alt="${p.imageAlt}"
254
+ cta-label="${p.ctaLabel}"
255
+ cta-href="${p.ctaHref}"
256
+ >
257
+ <bd-h as="h3" slot="heading" style="color: var(--color-blue-600);">${p.heading}</bd-h>
258
+ <div slot="content">
259
+ <bd-p kind="small">${p.content}</bd-p>
260
+ </div>
261
+ </bd-tab-content-panel-family>
262
+ `)}
263
+ </bd-tabs-component-family>
264
+ `,
265
+ parameters: { docs: { description: { story: 'Tab-uri cu iconiță lângă label.' } } }
266
+ };
267
+
268
+ export const MobileView = {
269
+ name : 'Mobile View (375px)',
270
+ render: () => html`
271
+ <bd-tabs-component-family
272
+ title="Customized online protection for each family member"
273
+ highlight-text="family member"
274
+ subtitle="Stay protected at all times."
275
+ >
276
+ ${renderPanels()}
277
+ </bd-tabs-component-family>
278
+ `,
279
+ parameters: {
280
+ viewport: { defaultViewport: 'mobile1' },
281
+ docs : { description: { story: 'Vedere mobilă la 375px.' } }
282
+ }
283
+ };
284
+
285
+ export const TabletView = {
286
+ name : 'Tablet View (768px)',
287
+ render: () => html`
288
+ <bd-tabs-component-family
289
+ title="Customized online protection for each family member"
290
+ highlight-text="family member"
291
+ subtitle="Stay protected at all times."
292
+ >
293
+ ${renderPanels()}
294
+ </bd-tabs-component-family>
295
+ `,
296
+ parameters: {
297
+ viewport: { defaultViewport: 'tablet' },
298
+ docs : { description: { story: 'Vedere tabletă la 768px.' } }
299
+ }
300
+ };
301
+
302
+ export const Playground = {
303
+ name: '🛝 Playground',
304
+ args: {
305
+ title : 'Customized online protection for each family member – Smart, Private & Safe',
306
+ subtitle : 'Your family is unique and so should be your online security.',
307
+ highlightText: 'family member'
308
+ },
309
+ render: (args) => html`
310
+ <bd-tabs-component-family
311
+ title="${args.title}"
312
+ highlight-text="${args.highlightText}"
313
+ subtitle="${args.subtitle}"
314
+ >
315
+ ${renderPanels()}
316
+ </bd-tabs-component-family>
317
+ `,
318
+ parameters: { docs: { description: { story: 'Playground interactiv.' } } }
319
+ };