@repobit/dex-system-design 0.23.49 → 0.23.50
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 +7 -0
- package/package.json +4 -4
- package/src/components/card-features/card-features.css.js +108 -0
- package/src/components/card-features/card-features.js +145 -0
- package/src/components/card-features/card-features.stories.js +165 -0
- package/src/components/cards/card.css.js +9 -3
- package/src/components/cards/card.js +33 -10
- package/src/components/cards/card.stories.js +116 -64
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.50](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.49...@repobit/dex-system-design@0.23.50) (2026-06-17)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **DEX-1014:** new card features and card upgrades
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
## [0.23.49](https://github.com/bitdefender/dex-core/compare/@repobit/dex-system-design@0.23.48...@repobit/dex-system-design@0.23.49) (2026-06-16)
|
|
7
14
|
|
|
8
15
|
**Note:** Version bump only for package @repobit/dex-system-design
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@repobit/dex-system-design",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.50",
|
|
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",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"url": "https://github.com/bitdefender/dex-core/issues"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@repobit/dex-store": "1.3.
|
|
74
|
-
"@repobit/dex-store-elements": "1.4.
|
|
73
|
+
"@repobit/dex-store": "1.3.46",
|
|
74
|
+
"@repobit/dex-store-elements": "1.4.38",
|
|
75
75
|
"lit": "^3.3.2"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
"volta": {
|
|
89
89
|
"node": "24.14.0"
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "81584423e16ae1ca612de9c236fa98707297bc04"
|
|
92
92
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
--background-card-grey: var(--color-neutral-25);
|
|
7
|
+
--border-card-grey: var(--color-neutral-50);
|
|
8
|
+
padding-top: var(--spacing-32);
|
|
9
|
+
padding-bottom: var(--spacing-32);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.bd-card-features-s {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
position: relative;
|
|
17
|
+
width: 100%;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
padding-left: var(--spacing-14);
|
|
21
|
+
padding-right: var(--spacing-14);
|
|
22
|
+
max-width: 1400px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.bd-card-features-header-s {
|
|
27
|
+
margin-bottom: var(--spacing-48);
|
|
28
|
+
margin-top: var(--spacing-32);
|
|
29
|
+
text-align: center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.bd-card-features-title-s {
|
|
33
|
+
font-family: var(--typography-fontFamily-sans);
|
|
34
|
+
font-weight: var(--typography-fontWeight-semibold);
|
|
35
|
+
font-size: var(--typography-fontSize-2xl);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.bd-card-features-track-s {
|
|
39
|
+
width: 100%;
|
|
40
|
+
display: grid;
|
|
41
|
+
gap: var(--spacing-24);
|
|
42
|
+
grid-template-columns: repeat(3, 1fr);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.bd-card-features-box-s {
|
|
46
|
+
display: flex;
|
|
47
|
+
flex-direction: row;
|
|
48
|
+
align-items: flex-start;
|
|
49
|
+
gap: var(--spacing-16);
|
|
50
|
+
background: var(--background-card-grey);
|
|
51
|
+
border-radius: var(--spacing-20);
|
|
52
|
+
padding: var(--spacing-24);
|
|
53
|
+
font-family: var(--typography-fontFamily-sans);
|
|
54
|
+
box-sizing: border-box;
|
|
55
|
+
height: 100%;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.bd-card-features-icon-s {
|
|
59
|
+
flex-shrink: 0;
|
|
60
|
+
width: var(--spacing-32);
|
|
61
|
+
height: var(--spacing-32);
|
|
62
|
+
object-fit: contain;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.bd-card-features-content-s {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.bd-card-features-title-badge-wrapper-s {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: var(--spacing-8);
|
|
74
|
+
margin-bottom: var(--spacing-8);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.bd-card-features-item-title-s {
|
|
78
|
+
font-size: var(--typography-fontSize-md);
|
|
79
|
+
font-weight: var(--typography-fontWeight-semibold);
|
|
80
|
+
margin: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
::slotted(p) {
|
|
84
|
+
font-size: var(--typography-fontSize-sm);
|
|
85
|
+
color: var(--color-neutral-700);
|
|
86
|
+
line-height: var(--typography-lineHeight-normal);
|
|
87
|
+
margin: 0;
|
|
88
|
+
font-family: var(--typography-fontFamily-sans);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@media (max-width: 1024px) {
|
|
92
|
+
.bd-card-features-track-s {
|
|
93
|
+
grid-template-columns: repeat(2, 1fr);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@media (max-width: 600px) {
|
|
98
|
+
.bd-card-features-track-s {
|
|
99
|
+
grid-template-columns: 1fr;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.bd-card-features-box-s {
|
|
103
|
+
flex-direction: column;
|
|
104
|
+
align-items: flex-start;
|
|
105
|
+
height: auto;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { LitElement, html } from "lit";
|
|
2
|
+
import { tokens } from "../../tokens/tokens.js";
|
|
3
|
+
import "../badge/badge.js";
|
|
4
|
+
import "../heading/heading.js";
|
|
5
|
+
import cardFeaturesCSS from "./card-features.css.js";
|
|
6
|
+
|
|
7
|
+
class CardFeatures extends LitElement {
|
|
8
|
+
static properties = {
|
|
9
|
+
title: { type: String }
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.title = "";
|
|
15
|
+
this._resizeObserver = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
connectedCallback() {
|
|
19
|
+
super.connectedCallback();
|
|
20
|
+
this._resizeObserver = new ResizeObserver(() => this._equalizeHeights());
|
|
21
|
+
this._resizeObserver.observe(this);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
disconnectedCallback() {
|
|
25
|
+
super.disconnectedCallback();
|
|
26
|
+
this._resizeObserver?.disconnect();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
firstUpdated() {
|
|
30
|
+
this._equalizeHeights();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async _equalizeHeights() {
|
|
34
|
+
const items = [...this.querySelectorAll("bd-card-features-item")];
|
|
35
|
+
if (!items.length) return;
|
|
36
|
+
|
|
37
|
+
// Asteptam ca toate item-urile sa fie randate
|
|
38
|
+
await Promise.all(items.map((item) => item.updateComplete));
|
|
39
|
+
|
|
40
|
+
const boxes = items
|
|
41
|
+
.map((item) => item.shadowRoot?.querySelector(".bd-card-features-box-s"))
|
|
42
|
+
.filter(Boolean);
|
|
43
|
+
|
|
44
|
+
if (!boxes.length) return;
|
|
45
|
+
|
|
46
|
+
// Resetam inaltimea inainte de a masura, altfel maxHeight ramane "blocat"
|
|
47
|
+
boxes.forEach((box) => {
|
|
48
|
+
box.style.height = "auto";
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
requestAnimationFrame(() => {
|
|
52
|
+
const maxHeight = boxes.reduce(
|
|
53
|
+
(max, box) => Math.max(max, box.getBoundingClientRect().height),
|
|
54
|
+
0
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
boxes.forEach((box) => {
|
|
58
|
+
box.style.height = `${maxHeight}px`;
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
render() {
|
|
64
|
+
return html`
|
|
65
|
+
<section class="bd-card-features-s">
|
|
66
|
+
${this.title
|
|
67
|
+
? html`
|
|
68
|
+
<div class="bd-card-features-header-s">
|
|
69
|
+
<h1 class="bd-card-features-title-s">${this.title}</h1>
|
|
70
|
+
</div>
|
|
71
|
+
`
|
|
72
|
+
: null}
|
|
73
|
+
<div class="bd-card-features-track-s">
|
|
74
|
+
<slot @slotchange=${this._equalizeHeights}></slot>
|
|
75
|
+
</div>
|
|
76
|
+
</section>
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
class CardFeaturesItem extends LitElement {
|
|
82
|
+
static properties = {
|
|
83
|
+
title : { type: String },
|
|
84
|
+
icon : { type: String },
|
|
85
|
+
badge : { type: String },
|
|
86
|
+
badgeVariant: { type: String }
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
constructor() {
|
|
90
|
+
super();
|
|
91
|
+
this.title = "";
|
|
92
|
+
this.icon = "";
|
|
93
|
+
this.badge = "";
|
|
94
|
+
this.badgeVariant = "";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
_renderIcon() {
|
|
98
|
+
const iconEl = this.querySelector("[slot='icon']");
|
|
99
|
+
|
|
100
|
+
if (iconEl) {
|
|
101
|
+
return iconEl.cloneNode(true);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (this.icon) {
|
|
105
|
+
return html`<img
|
|
106
|
+
src="${this.icon}"
|
|
107
|
+
alt="icon"
|
|
108
|
+
class="bd-card-features-icon-s"
|
|
109
|
+
/>`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
render() {
|
|
116
|
+
return html`
|
|
117
|
+
<div class="bd-card-features-box-s">
|
|
118
|
+
${this._renderIcon()}
|
|
119
|
+
|
|
120
|
+
<div class="bd-card-features-content-s">
|
|
121
|
+
${this.title
|
|
122
|
+
? html`
|
|
123
|
+
<div class="bd-card-features-title-badge-wrapper-s">
|
|
124
|
+
<bd-h as="h5" fontWeight="700" class="bd-card-features-item-title-s">${this.title}</bd-h>
|
|
125
|
+
${this.badge
|
|
126
|
+
? html`<bd-badge
|
|
127
|
+
variant="${this.badgeVariant}"
|
|
128
|
+
size="sm"
|
|
129
|
+
>${this.badge}</bd-badge>`
|
|
130
|
+
: ""}
|
|
131
|
+
</div>
|
|
132
|
+
`
|
|
133
|
+
: ""}
|
|
134
|
+
<slot></slot>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
CardFeatures.styles = [tokens, cardFeaturesCSS];
|
|
142
|
+
CardFeaturesItem.styles = [tokens, cardFeaturesCSS];
|
|
143
|
+
|
|
144
|
+
customElements.define("bd-card-features-item", CardFeaturesItem);
|
|
145
|
+
customElements.define("bd-card-features-section", CardFeatures);
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import '../badge/badge.js';
|
|
3
|
+
import '../icons/analysis-icon.js';
|
|
4
|
+
import '../icons/arrow-up-icon.js';
|
|
5
|
+
import '../icons/close-icon.js';
|
|
6
|
+
import '../icons/family-icon.js';
|
|
7
|
+
import '../icons/globe-icon.js';
|
|
8
|
+
import '../icons/individual-icon.js';
|
|
9
|
+
import '../icons/machine-learning-icon.js';
|
|
10
|
+
import '../paragraph/paragraph.js';
|
|
11
|
+
import './card-features.js';
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
title : 'Components/CardFeatures',
|
|
15
|
+
tags : ['autodocs'],
|
|
16
|
+
parameters: {
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component: `
|
|
20
|
+
**CardFeatures** — variantă "wide" a componentei Card, cu iconiță în stânga, titlu + badge opțional, și descriere.
|
|
21
|
+
|
|
22
|
+
\`\`\`html
|
|
23
|
+
<bd-card-features-section title="Our AI-powered platform that detects and fights scams. In real time.">
|
|
24
|
+
<bd-card-features-item
|
|
25
|
+
title="Scam Copilot Chatbot"
|
|
26
|
+
badge="NEW"
|
|
27
|
+
badge-variant="bd-light-blue"
|
|
28
|
+
>
|
|
29
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
30
|
+
<bd-p>Specialized AI chatbot that helps you identify suspicious online interactions.</bd-p>
|
|
31
|
+
</bd-card-features-item>
|
|
32
|
+
</bd-card-features-section>
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
### Atribute \`bd-card-features-item\`
|
|
36
|
+
| Atribut | Tip | Descriere |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| \`title\` | String | Titlul cardului |
|
|
39
|
+
| \`badge\` | String | Text badge (ex: NEW, IMPROVED) |
|
|
40
|
+
| \`badge-variant\` | String | Variantă culoare badge (vezi \`bd-badge\`) |
|
|
41
|
+
`
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const iconMap = [
|
|
48
|
+
html`<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>`,
|
|
49
|
+
html`<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>`,
|
|
50
|
+
html`<bd-machine-learning-icon slot="icon" width="32" height="32" color="#006DFF"></bd-machine-learning-icon>`,
|
|
51
|
+
html`<bd-analysis-icon slot="icon" width="32" height="32" color="#006DFF"></bd-analysis-icon>`,
|
|
52
|
+
html`<bd-family-icon slot="icon" width="32" height="32" color="#006DFF"></bd-family-icon>`,
|
|
53
|
+
html`<bd-arrow-up-icon slot="icon" width="32" height="32" color="#006DFF"></bd-arrow-up-icon>`,
|
|
54
|
+
html`<bd-close-icon slot="icon" width="32" height="32" color="#006DFF"></bd-close-icon>`,
|
|
55
|
+
html`<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>`,
|
|
56
|
+
html`<bd-analysis-icon slot="icon" width="32" height="32" color="#006DFF"></bd-analysis-icon>`
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const items = [
|
|
60
|
+
{ title: 'Scam Copilot Chatbot', badge: 'NEW', variant: 'bd-light-blue', content: 'Specialized AI chatbot that helps you identify suspicious online interactions so you don\'t become a victim.' },
|
|
61
|
+
{ title: 'Scam Wave Alerts', badge: 'NEW', variant: 'bd-light-blue', content: 'Stay informed with real-time alerts about ongoing scam outbreaks detected in your area automatically.' },
|
|
62
|
+
{ title: 'Online Scam Protection', badge: 'NEW', variant: 'bd-light-blue', content: 'Focused on browsing activities, it specializes in detecting the newest scam patterns and tactics.' },
|
|
63
|
+
{ title: 'Remote Access Protection', badge: 'NEW', variant: 'bd-light-blue', content: 'Detects patterns of behavior typical of scammers seeking to access your data remotely.' },
|
|
64
|
+
{ title: 'SMS Protection', badge: 'IMPROVED', variant: 'bd-light-green', content: 'Detects scams and harmful links in SMS messages using advanced AI for fast, real-time protection.' },
|
|
65
|
+
{ title: 'Email Protection', badge: '', variant: '', content: 'Monitors web mailboxes from Gmail and Outlook 24/7 and marks every incoming email as safe or unsafe.' },
|
|
66
|
+
{ title: 'Scam Notification Protection', badge: '', variant: '', content: 'Scans and detects scam attempts directly in push notifications before you even tap or interact.' },
|
|
67
|
+
{ title: 'Chat Protection', badge: '', variant: '', content: 'Identifies scam messages in WhatsApp, Messenger, Telegram and Discord chats before they reach your attention.' },
|
|
68
|
+
{ title: 'Calendar Invites Protection', badge: '', variant: '', content: 'Finds and blocks scam calendar events before they can trick you with fake links or misleading invites.' }
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
export const Default = {
|
|
72
|
+
name : 'Default (9 items)',
|
|
73
|
+
render: () => html`
|
|
74
|
+
<bd-card-features-section title="Our AI-powered platform that detects and fights scams. In real time.">
|
|
75
|
+
${items.map((item, i) => html`
|
|
76
|
+
<bd-card-features-item title="${item.title}" badge="${item.badge}" badge-variant="${item.variant}">
|
|
77
|
+
${iconMap[i]}
|
|
78
|
+
<bd-p>${item.content}</bd-p>
|
|
79
|
+
</bd-card-features-item>
|
|
80
|
+
`)}
|
|
81
|
+
</bd-card-features-section>
|
|
82
|
+
`
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const NoSectionTitle = {
|
|
86
|
+
name : 'No Section Title',
|
|
87
|
+
render: () => html`
|
|
88
|
+
<bd-card-features-section>
|
|
89
|
+
${items.slice(0, 3).map((item, i) => html`
|
|
90
|
+
<bd-card-features-item title="${item.title}" badge="${item.badge}" badge-variant="${item.variant}">
|
|
91
|
+
${iconMap[i]}
|
|
92
|
+
<bd-p>${item.content}</bd-p>
|
|
93
|
+
</bd-card-features-item>
|
|
94
|
+
`)}
|
|
95
|
+
</bd-card-features-section>
|
|
96
|
+
`
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const NoBadge = {
|
|
100
|
+
name : 'Without Badge',
|
|
101
|
+
render: () => html`
|
|
102
|
+
<bd-card-features-section title="Core Features">
|
|
103
|
+
${items.slice(5, 8).map((item, i) => html`
|
|
104
|
+
<bd-card-features-item title="${item.title}">
|
|
105
|
+
${iconMap[i + 5]}
|
|
106
|
+
<bd-p>${item.content}</bd-p>
|
|
107
|
+
</bd-card-features-item>
|
|
108
|
+
`)}
|
|
109
|
+
</bd-card-features-section>
|
|
110
|
+
`
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const NoIcon = {
|
|
114
|
+
name : 'Without Icon',
|
|
115
|
+
render: () => html`
|
|
116
|
+
<bd-card-features-section title="Features">
|
|
117
|
+
${items.slice(0, 3).map(item => html`
|
|
118
|
+
<bd-card-features-item title="${item.title}" badge="${item.badge}" badge-variant="${item.variant}">
|
|
119
|
+
<bd-p>${item.content}</bd-p>
|
|
120
|
+
</bd-card-features-item>
|
|
121
|
+
`)}
|
|
122
|
+
</bd-card-features-section>
|
|
123
|
+
`
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const TwoColumns = {
|
|
127
|
+
name : 'Two Items',
|
|
128
|
+
render: () => html`
|
|
129
|
+
<bd-card-features-section title="Highlights">
|
|
130
|
+
${items.slice(0, 2).map((item, i) => html`
|
|
131
|
+
<bd-card-features-item title="${item.title}" badge="${item.badge}" badge-variant="${item.variant}">
|
|
132
|
+
${iconMap[i]}
|
|
133
|
+
<bd-p>${item.content}</bd-p>
|
|
134
|
+
</bd-card-features-item>
|
|
135
|
+
`)}
|
|
136
|
+
</bd-card-features-section>
|
|
137
|
+
`
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const SingleItem = {
|
|
141
|
+
name : 'Single Item (edge case)',
|
|
142
|
+
render: () => html`
|
|
143
|
+
<bd-card-features-section title="Feature">
|
|
144
|
+
<bd-card-features-item title="${items[0].title}" badge="${items[0].badge}" badge-variant="${items[0].variant}">
|
|
145
|
+
${iconMap[0]}
|
|
146
|
+
<bd-p>${items[0].content}</bd-p>
|
|
147
|
+
</bd-card-features-item>
|
|
148
|
+
</bd-card-features-section>
|
|
149
|
+
`
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const MobileView = {
|
|
153
|
+
name : 'Mobile View (375px)',
|
|
154
|
+
parameters: { viewport: { defaultViewport: 'mobile1' } },
|
|
155
|
+
render : () => html`
|
|
156
|
+
<bd-card-features-section title="Our AI-powered platform that detects and fights scams. In real time.">
|
|
157
|
+
${items.map((item, i) => html`
|
|
158
|
+
<bd-card-features-item title="${item.title}" badge="${item.badge}" badge-variant="${item.variant}">
|
|
159
|
+
${iconMap[i]}
|
|
160
|
+
<bd-p>${item.content}</bd-p>
|
|
161
|
+
</bd-card-features-item>
|
|
162
|
+
`)}
|
|
163
|
+
</bd-card-features-section>
|
|
164
|
+
`
|
|
165
|
+
};
|
|
@@ -8,7 +8,11 @@ export default css`
|
|
|
8
8
|
padding-top: var(--spacing-32);
|
|
9
9
|
padding-bottom: var(--spacing-32);
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
::slotted([slot="icon"]) {
|
|
12
|
+
width: 48px !important;
|
|
13
|
+
height: 48px !important;
|
|
14
|
+
flex-shrink: 0;
|
|
15
|
+
}
|
|
12
16
|
.bd-section-title-s {
|
|
13
17
|
font-family: var(--typography-fontFamily-sans);
|
|
14
18
|
font-weight: var(--typography-fontWeight-semibold);
|
|
@@ -68,7 +72,7 @@ export default css`
|
|
|
68
72
|
.bd-light-box-s {
|
|
69
73
|
flex: var(--spacing-0) var(--spacing-0) auto;
|
|
70
74
|
background: var(--background-card-grey);
|
|
71
|
-
border-radius:
|
|
75
|
+
border-radius: var(--spacing-20);
|
|
72
76
|
padding: var(--spacing-32);
|
|
73
77
|
display: flex;
|
|
74
78
|
flex-direction: column;
|
|
@@ -148,7 +152,9 @@ export default css`
|
|
|
148
152
|
flex-direction: column;
|
|
149
153
|
align-items: flex-start;
|
|
150
154
|
gap: var(--spacing-8);
|
|
151
|
-
padding-bottom: var(--spacing-
|
|
155
|
+
padding-bottom: var(--spacing-8);
|
|
156
|
+
left: -5px;
|
|
157
|
+
position: relative;
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
/* Header pentru carduri fără conținut */
|
|
@@ -19,7 +19,7 @@ class Card extends LitElement {
|
|
|
19
19
|
${this.title
|
|
20
20
|
? html`
|
|
21
21
|
<div class="bd-header-light-carousel-s">
|
|
22
|
-
<h1 class="bd-section-title-s">${this.title}</
|
|
22
|
+
<bd-h as="h1" class="bd-section-title-s">${this.title}</bd-h>
|
|
23
23
|
</div>
|
|
24
24
|
`
|
|
25
25
|
: null}
|
|
@@ -45,25 +45,48 @@ class CardItem extends LitElement {
|
|
|
45
45
|
this.align = "start";
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
_renderIcon() {
|
|
49
|
+
const iconEl = this.querySelector("[slot='icon']");
|
|
50
|
+
|
|
51
|
+
if (iconEl) {
|
|
52
|
+
return iconEl.cloneNode(true);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (this.icon) {
|
|
56
|
+
return html`<img
|
|
57
|
+
src="${this.icon}"
|
|
58
|
+
alt="icon"
|
|
59
|
+
class="bd-light-icon-s"
|
|
60
|
+
/>`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_hasContent() {
|
|
67
|
+
const slottedNodes = [...this.childNodes].filter((node) => {
|
|
68
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
69
|
+
return node.getAttribute("slot") !== "icon";
|
|
70
|
+
}
|
|
71
|
+
return node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== "";
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return slottedNodes.length > 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
48
77
|
render() {
|
|
49
78
|
const isCentered = this.align === "center";
|
|
50
|
-
const hasContent = this.
|
|
79
|
+
const hasContent = this._hasContent();
|
|
51
80
|
|
|
52
81
|
return html`
|
|
53
82
|
<div class="bd-light-box-s ${isCentered ? 'bd-light-box-center' : ''} ${!hasContent ? 'bd-light-box-no-content' : ''}">
|
|
54
83
|
<div class="bd-light-box-header-s ${isCentered ? 'bd-light-box-header-center' : ''}">
|
|
55
|
-
${this.
|
|
56
|
-
? html`<img
|
|
57
|
-
src="${this.icon}"
|
|
58
|
-
alt="icon"
|
|
59
|
-
class="bd-light-icon-s"
|
|
60
|
-
/>`
|
|
61
|
-
: ""}
|
|
84
|
+
${this._renderIcon()}
|
|
62
85
|
</div>
|
|
63
86
|
${this.title
|
|
64
87
|
? html`
|
|
65
88
|
<div class="bd-light-title-badge-wrapper-s ${!hasContent ? 'bd-light-title-no-content' : ''}">
|
|
66
|
-
<bd-h as="
|
|
89
|
+
<bd-h as="h5" fontWeight="700">${this.title}</bd-h>
|
|
67
90
|
</div>
|
|
68
91
|
`
|
|
69
92
|
: ""}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
|
+
import '../heading/heading.js';
|
|
3
|
+
import '../icons/analysis-icon.js';
|
|
4
|
+
import '../icons/arrow-down-icon.js';
|
|
5
|
+
import '../icons/arrow-up-icon.js';
|
|
6
|
+
import '../icons/close-icon.js';
|
|
7
|
+
import '../icons/family-icon.js';
|
|
8
|
+
import '../icons/globe-icon.js';
|
|
9
|
+
import '../icons/individual-icon.js';
|
|
10
|
+
import '../icons/machine-learning-icon.js';
|
|
11
|
+
import '../paragraph/paragraph.js';
|
|
2
12
|
import './card.js';
|
|
3
13
|
|
|
4
14
|
export default {
|
|
@@ -16,16 +26,17 @@ export default {
|
|
|
16
26
|
### Usage
|
|
17
27
|
\`\`\`html
|
|
18
28
|
<bd-card-section title="Why Bitdefender">
|
|
19
|
-
<bd-card-item title="AI Protection"
|
|
20
|
-
<
|
|
29
|
+
<bd-card-item title="AI Protection" align="center">
|
|
30
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
31
|
+
<bd-p>Powered by machine learning.</bd-p>
|
|
21
32
|
</bd-card-item>
|
|
22
33
|
</bd-card-section>
|
|
23
34
|
\`\`\`
|
|
24
35
|
|
|
25
36
|
### CardItem Behavior
|
|
26
37
|
- If \`align="center"\` — applies \`bd-light-box-center\` and \`bd-light-box-header-center\` classes
|
|
27
|
-
- If no slotted content — applies \`bd-light-box-no-content\` and \`bd-light-title-no-content\` classes
|
|
28
|
-
- Icon is
|
|
38
|
+
- If no slotted content (besides icon) — applies \`bd-light-box-no-content\` and \`bd-light-title-no-content\` classes
|
|
39
|
+
- Icon is rendered from a \`slot="icon"\` element if present, otherwise falls back to the \`icon\` prop as an image path
|
|
29
40
|
- Title uses \`<bd-h as="h4">\` with 10px vertical padding
|
|
30
41
|
`
|
|
31
42
|
}
|
|
@@ -42,11 +53,6 @@ export default {
|
|
|
42
53
|
description: 'Title of the card item, rendered using `<bd-h as="h4">`',
|
|
43
54
|
table : { type: { summary: 'string' }, defaultValue: { summary: '' }, category: 'CardItem' }
|
|
44
55
|
},
|
|
45
|
-
icon: {
|
|
46
|
-
control : 'text',
|
|
47
|
-
description: 'Path to the icon image shown at the top of the card',
|
|
48
|
-
table : { type: { summary: 'string' }, defaultValue: { summary: '' }, category: 'CardItem' }
|
|
49
|
-
},
|
|
50
56
|
align: {
|
|
51
57
|
control : { type: 'select' },
|
|
52
58
|
options : ['start', 'center'],
|
|
@@ -56,38 +62,53 @@ export default {
|
|
|
56
62
|
}
|
|
57
63
|
};
|
|
58
64
|
|
|
59
|
-
const sampleItems = [
|
|
60
|
-
{ title: 'AI Protection', icon: '/assets/brain.svg', content: 'Powered by machine learning and behavioral analysis.' },
|
|
61
|
-
{ title: 'Multi-layer Security', icon: '/assets/shield.svg', content: 'Multiple independent protection layers stop any attack.' },
|
|
62
|
-
{ title: 'VPN Included', icon: '/assets/vpn.svg', content: 'Unlimited encrypted VPN traffic for all your devices.' },
|
|
63
|
-
{ title: 'Parental Controls', icon: '/assets/parental.svg', content: 'Advanced controls to protect your children online.' }
|
|
64
|
-
];
|
|
65
|
-
|
|
66
65
|
// ─── Stories ───────────────────────────────────────────────────────────────
|
|
67
66
|
|
|
68
67
|
export const Default = {
|
|
69
68
|
name : 'Default (4 items)',
|
|
70
69
|
render: () => html`
|
|
71
70
|
<bd-card-section title="Why Bitdefender">
|
|
72
|
-
|
|
73
|
-
<bd-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
<bd-card-item title="AI Protection">
|
|
72
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
73
|
+
<bd-p>Powered by machine learning and behavioral analysis.</bd-p>
|
|
74
|
+
</bd-card-item>
|
|
75
|
+
<bd-card-item title="Multi-layer Security">
|
|
76
|
+
<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
|
|
77
|
+
<bd-p>Multiple independent protection layers stop any attack.</bd-p>
|
|
78
|
+
</bd-card-item>
|
|
79
|
+
<bd-card-item title="VPN Included">
|
|
80
|
+
<bd-machine-learning-icon slot="icon" width="32" height="32" color="#006DFF"></bd-machine-learning-icon>
|
|
81
|
+
<bd-p>Unlimited encrypted VPN traffic for all your devices.</bd-p>
|
|
82
|
+
</bd-card-item>
|
|
83
|
+
<bd-card-item title="Parental Controls">
|
|
84
|
+
<bd-analysis-icon slot="icon" width="32" height="32" color="#006DFF"></bd-analysis-icon>
|
|
85
|
+
<bd-p>Advanced controls to protect your children online.</bd-p>
|
|
86
|
+
</bd-card-item>
|
|
77
87
|
</bd-card-section>
|
|
78
88
|
`,
|
|
79
|
-
parameters: { docs: { description: { story: 'Four card items with icons, titles, and slotted content inside a section with a title.' } } }
|
|
89
|
+
parameters: { docs: { description: { story: 'Four card items with icons, titles, and slotted `bd-p` content inside a section with a title.' } } }
|
|
80
90
|
};
|
|
81
91
|
|
|
82
92
|
export const NoSectionTitle = {
|
|
83
93
|
name : 'No Section Title',
|
|
84
94
|
render: () => html`
|
|
85
95
|
<bd-card-section>
|
|
86
|
-
|
|
87
|
-
<bd-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
<bd-card-item title="AI Protection">
|
|
97
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
98
|
+
<bd-p>Powered by machine learning and behavioral analysis.</bd-p>
|
|
99
|
+
</bd-card-item>
|
|
100
|
+
<bd-card-item title="Multi-layer Security">
|
|
101
|
+
<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
|
|
102
|
+
<bd-p>Multiple independent protection layers stop any attack.</bd-p>
|
|
103
|
+
</bd-card-item>
|
|
104
|
+
<bd-card-item title="VPN Included">
|
|
105
|
+
<bd-machine-learning-icon slot="icon" width="32" height="32" color="#006DFF"></bd-machine-learning-icon>
|
|
106
|
+
<bd-p>Unlimited encrypted VPN traffic for all your devices.</bd-p>
|
|
107
|
+
</bd-card-item>
|
|
108
|
+
<bd-card-item title="Parental Controls">
|
|
109
|
+
<bd-analysis-icon slot="icon" width="32" height="32" color="#006DFF"></bd-analysis-icon>
|
|
110
|
+
<bd-p>Advanced controls to protect your children online.</bd-p>
|
|
111
|
+
</bd-card-item>
|
|
91
112
|
</bd-card-section>
|
|
92
113
|
`,
|
|
93
114
|
parameters: { docs: { description: { story: 'Section without `title` prop — the header div is conditionally omitted.' } } }
|
|
@@ -97,11 +118,22 @@ export const CenteredAlignment = {
|
|
|
97
118
|
name : 'Centered Alignment',
|
|
98
119
|
render: () => html`
|
|
99
120
|
<bd-card-section title="Features">
|
|
100
|
-
|
|
101
|
-
<bd-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
<bd-card-item title="AI Protection" align="center">
|
|
122
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
123
|
+
<bd-p>Powered by machine learning and behavioral analysis.</bd-p>
|
|
124
|
+
</bd-card-item>
|
|
125
|
+
<bd-card-item title="Multi-layer Security" align="center">
|
|
126
|
+
<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
|
|
127
|
+
<bd-p>Multiple independent protection layers stop any attack.</bd-p>
|
|
128
|
+
</bd-card-item>
|
|
129
|
+
<bd-card-item title="VPN Included" align="center">
|
|
130
|
+
<bd-machine-learning-icon slot="icon" width="32" height="32" color="#006DFF"></bd-machine-learning-icon>
|
|
131
|
+
<bd-p>Unlimited encrypted VPN traffic for all your devices.</bd-p>
|
|
132
|
+
</bd-card-item>
|
|
133
|
+
<bd-card-item title="Parental Controls" align="center">
|
|
134
|
+
<bd-analysis-icon slot="icon" width="32" height="32" color="#006DFF"></bd-analysis-icon>
|
|
135
|
+
<bd-p>Advanced controls to protect your children online.</bd-p>
|
|
136
|
+
</bd-card-item>
|
|
105
137
|
</bd-card-section>
|
|
106
138
|
`,
|
|
107
139
|
parameters: { docs: { description: { story: '`align="center"` applied to all items — centers icon, title, and content.' } } }
|
|
@@ -111,11 +143,13 @@ export const MixedAlignment = {
|
|
|
111
143
|
name : 'Mixed Alignment',
|
|
112
144
|
render: () => html`
|
|
113
145
|
<bd-card-section title="Mixed Layout">
|
|
114
|
-
<bd-card-item title="Left Aligned"
|
|
115
|
-
<
|
|
146
|
+
<bd-card-item title="Left Aligned" align="start">
|
|
147
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
148
|
+
<bd-p>Default left-aligned content.</bd-p>
|
|
116
149
|
</bd-card-item>
|
|
117
|
-
<bd-card-item title="Center Aligned"
|
|
118
|
-
<
|
|
150
|
+
<bd-card-item title="Center Aligned" align="center">
|
|
151
|
+
<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
|
|
152
|
+
<bd-p>Centered content.</bd-p>
|
|
119
153
|
</bd-card-item>
|
|
120
154
|
</bd-card-section>
|
|
121
155
|
`,
|
|
@@ -126,26 +160,42 @@ export const NoIcon = {
|
|
|
126
160
|
name : 'No Icons',
|
|
127
161
|
render: () => html`
|
|
128
162
|
<bd-card-section title="Features">
|
|
129
|
-
|
|
130
|
-
<bd-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
163
|
+
<bd-card-item title="AI Protection">
|
|
164
|
+
<bd-p>Powered by machine learning and behavioral analysis.</bd-p>
|
|
165
|
+
</bd-card-item>
|
|
166
|
+
<bd-card-item title="Multi-layer Security">
|
|
167
|
+
<bd-p>Multiple independent protection layers stop any attack.</bd-p>
|
|
168
|
+
</bd-card-item>
|
|
169
|
+
<bd-card-item title="VPN Included">
|
|
170
|
+
<bd-p>Unlimited encrypted VPN traffic for all your devices.</bd-p>
|
|
171
|
+
</bd-card-item>
|
|
172
|
+
<bd-card-item title="Parental Controls">
|
|
173
|
+
<bd-p>Advanced controls to protect your children online.</bd-p>
|
|
174
|
+
</bd-card-item>
|
|
134
175
|
</bd-card-section>
|
|
135
176
|
`,
|
|
136
|
-
parameters: { docs: { description: { story: 'Items without `icon`
|
|
177
|
+
parameters: { docs: { description: { story: 'Items without a `slot="icon"` element — the icon wrapper renders empty.' } } }
|
|
137
178
|
};
|
|
138
179
|
|
|
139
180
|
export const NoSlottedContent = {
|
|
140
181
|
name : 'No Slotted Content',
|
|
141
182
|
render: () => html`
|
|
142
183
|
<bd-card-section title="Features">
|
|
143
|
-
|
|
144
|
-
<bd-
|
|
145
|
-
|
|
184
|
+
<bd-card-item title="AI Protection">
|
|
185
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
186
|
+
</bd-card-item>
|
|
187
|
+
<bd-card-item title="Multi-layer Security">
|
|
188
|
+
<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
|
|
189
|
+
</bd-card-item>
|
|
190
|
+
<bd-card-item title="VPN Included">
|
|
191
|
+
<bd-machine-learning-icon slot="icon" width="32" height="32" color="#006DFF"></bd-machine-learning-icon>
|
|
192
|
+
</bd-card-item>
|
|
193
|
+
<bd-card-item title="Parental Controls">
|
|
194
|
+
<bd-analysis-icon slot="icon" width="32" height="32" color="#006DFF"></bd-analysis-icon>
|
|
195
|
+
</bd-card-item>
|
|
146
196
|
</bd-card-section>
|
|
147
197
|
`,
|
|
148
|
-
parameters: { docs: { description: { story: 'Items with
|
|
198
|
+
parameters: { docs: { description: { story: 'Items with only an icon and title — applies `bd-light-box-no-content` and `bd-light-title-no-content` CSS classes.' } } }
|
|
149
199
|
};
|
|
150
200
|
|
|
151
201
|
export const TitleOnly = {
|
|
@@ -164,28 +214,29 @@ export const RichContent = {
|
|
|
164
214
|
name : 'Rich Slotted Content',
|
|
165
215
|
render: () => html`
|
|
166
216
|
<bd-card-section title="Plan Details">
|
|
167
|
-
<bd-card-item title="What's included"
|
|
168
|
-
<
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
<p>Windows, macOS, Android, iOS</p>
|
|
217
|
+
<bd-card-item title="What's included">
|
|
218
|
+
<bd-family-icon slot="icon" width="32" height="32" color="#006DFF"></bd-family-icon>
|
|
219
|
+
<bd-p>Antivirus & Anti-malware</bd-p>
|
|
220
|
+
<bd-p>VPN (200+ servers)</bd-p>
|
|
221
|
+
<bd-p>Password Manager</bd-p>
|
|
222
|
+
</bd-card-item>
|
|
223
|
+
<bd-card-item title="Supported Platforms">
|
|
224
|
+
<bd-arrow-up-icon slot="icon" width="32" height="32" color="#006DFF"></bd-arrow-up-icon>
|
|
225
|
+
<bd-p>Windows, macOS, Android, iOS</bd-p>
|
|
176
226
|
<a href="#">View all platforms</a>
|
|
177
227
|
</bd-card-item>
|
|
178
228
|
</bd-card-section>
|
|
179
229
|
`,
|
|
180
|
-
parameters: { docs: { description: { story: 'Rich slotted content
|
|
230
|
+
parameters: { docs: { description: { story: 'Rich slotted content using multiple `bd-p` elements and a link.' } } }
|
|
181
231
|
};
|
|
182
232
|
|
|
183
233
|
export const SingleItem = {
|
|
184
234
|
name : 'Single Item (edge case)',
|
|
185
235
|
render: () => html`
|
|
186
236
|
<bd-card-section title="Feature">
|
|
187
|
-
<bd-card-item title="AI Protection"
|
|
188
|
-
<
|
|
237
|
+
<bd-card-item title="AI Protection">
|
|
238
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
239
|
+
<bd-p>Machine learning-powered protection.</bd-p>
|
|
189
240
|
</bd-card-item>
|
|
190
241
|
</bd-card-section>
|
|
191
242
|
`,
|
|
@@ -197,18 +248,19 @@ export const Playground = {
|
|
|
197
248
|
args: {
|
|
198
249
|
sectionTitle: 'Why Bitdefender',
|
|
199
250
|
itemTitle : 'AI Protection',
|
|
200
|
-
icon : '/assets/brain.svg',
|
|
201
251
|
align : 'start'
|
|
202
252
|
},
|
|
203
253
|
render: (args) => html`
|
|
204
254
|
<bd-card-section title="${args.sectionTitle}">
|
|
205
|
-
<bd-card-item title="${args.itemTitle}"
|
|
206
|
-
<
|
|
255
|
+
<bd-card-item title="${args.itemTitle}" align="${args.align}">
|
|
256
|
+
<bd-individual-icon slot="icon" width="32" height="32" color="#006DFF"></bd-individual-icon>
|
|
257
|
+
<bd-p>Powered by machine learning and behavioral analysis.</bd-p>
|
|
207
258
|
</bd-card-item>
|
|
208
|
-
<bd-card-item title="VPN Included"
|
|
209
|
-
<
|
|
259
|
+
<bd-card-item title="VPN Included" align="${args.align}">
|
|
260
|
+
<bd-globe-icon slot="icon" width="32" height="32" color="#006DFF"></bd-globe-icon>
|
|
261
|
+
<bd-p>Unlimited encrypted VPN traffic.</bd-p>
|
|
210
262
|
</bd-card-item>
|
|
211
263
|
</bd-card-section>
|
|
212
264
|
`,
|
|
213
|
-
parameters: { docs: { description: { story: 'Interactive playground. Adjust section title, item title,
|
|
265
|
+
parameters: { docs: { description: { story: 'Interactive playground. Adjust section title, item title, and alignment via Controls.' } } }
|
|
214
266
|
};
|