@repobit/dex-system-design 0.24.18 → 0.24.19
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/ai-guardian-demo.stories.js +454 -33
- package/src/components/bento-grid/bento-grid.css.js +12 -1
- package/src/components/bento-grid/bento-grid.js +59 -1
- package/src/components/bento-grid/bento-grid.stories.js +45 -0
- package/src/components/comparison-panel/comparison-panel.css.js +52 -1
- package/src/components/comparison-panel/comparison-panel.js +6 -1
- package/src/components/compatibility-table/compatibility-table.css.js +29 -13
- package/src/components/compatibility-table/compatibility-table.js +13 -7
- package/src/components/cta-banner/cta-banner.css.js +11 -0
- package/src/components/cta-banner/cta-banner.js +7 -0
- package/src/components/footer/footer-lp.css.js +10 -21
- package/src/components/footer/footer-lp.js +3 -18
- package/src/components/footer/footer-nav-menu.css.js +0 -2
- package/src/components/footer/footer.css.js +18 -35
- package/src/components/footer/footer.js +5 -1
- package/src/components/header/header-code.css.js +208 -10
- package/src/components/header/header-code.js +148 -5
- package/src/components/header/header-code.stories.js +83 -27
- package/src/components/page-switcher/page-switcher.js +1 -1
- package/src/components/paragraph/paragraph.js +6 -0
- package/src/components/quick-answer/quick-answer.css.js +2 -0
- package/src/components/quick-answer/quick-answer.js +1 -1
- package/src/components/terminal/terminal-message.css.js +153 -13
- package/src/components/terminal/terminal-message.js +37 -11
|
@@ -29,6 +29,29 @@ function resolveBreakpoint(width, maxColumns, minCardWidth) {
|
|
|
29
29
|
return { bp, columns, gap };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// Same breakpoint logic as resolveBreakpoint, but allows more than 2
|
|
33
|
+
// columns (up to maxColumns) since the "equal" variant is a plain grid
|
|
34
|
+
// with no wide/narrow pairing to worry about.
|
|
35
|
+
function resolveEqualColumns(width, maxColumns, minCardWidth) {
|
|
36
|
+
const bp =
|
|
37
|
+
width >= BREAKPOINT_WIDTHS.xl
|
|
38
|
+
? "xl" :
|
|
39
|
+
width >= BREAKPOINT_WIDTHS.lg
|
|
40
|
+
? "lg" :
|
|
41
|
+
width >= BREAKPOINT_WIDTHS.md ? "md" : "sm";
|
|
42
|
+
|
|
43
|
+
let columns = bp === "sm" ? 1 : bp === "md" ? Math.min(2, maxColumns) : Math.max(1, maxColumns);
|
|
44
|
+
const gap = DEFAULT_GAP[bp];
|
|
45
|
+
|
|
46
|
+
while (columns > 1) {
|
|
47
|
+
const cardWidth = (width - gap * (columns - 1)) / columns;
|
|
48
|
+
if (cardWidth >= minCardWidth) break;
|
|
49
|
+
columns -= 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { bp, columns, gap };
|
|
53
|
+
}
|
|
54
|
+
|
|
32
55
|
class BentoGrid extends LitElement {
|
|
33
56
|
static properties = {
|
|
34
57
|
eyebrow : { type: String },
|
|
@@ -37,7 +60,20 @@ class BentoGrid extends LitElement {
|
|
|
37
60
|
maxColumns : { type: Number, attribute: "max-columns" },
|
|
38
61
|
minCardWidth: { type: Number, attribute: "min-card-width" },
|
|
39
62
|
// Fixed wide/narrow ratio applied to every pair, e.g. 0.7 = 70/30.
|
|
63
|
+
// Only used when variant="weighted" (the default).
|
|
40
64
|
wideShare : { type: Number, attribute: "wide-share" },
|
|
65
|
+
// "weighted" (default) — pairs of cards alternate wide/narrow, per
|
|
66
|
+
// _applyWeights below. "equal" — every card gets the same width, laid
|
|
67
|
+
// out in a plain N-column grid (see equalColumns) with no pairing.
|
|
68
|
+
variant : { type: String, reflect: true },
|
|
69
|
+
// Column count for variant="equal" at the widest breakpoint (xl/lg);
|
|
70
|
+
// narrows to min(2, this) at md, and 1 at sm, same responsive pattern
|
|
71
|
+
// as the weighted variant. Ignored when variant="weighted".
|
|
72
|
+
equalColumns: { type: Number, attribute: "equal-columns" },
|
|
73
|
+
// "none" (default, transparent) | "tint" (light blue wash behind the
|
|
74
|
+
// whole grid, including header text) — a section-level background, not
|
|
75
|
+
// per-card (bd-bento-item already has its own `tone="tint"` for that).
|
|
76
|
+
background : { type: String, reflect: true },
|
|
41
77
|
_columns : { state: true },
|
|
42
78
|
_gap : { state: true }
|
|
43
79
|
};
|
|
@@ -52,6 +88,9 @@ class BentoGrid extends LitElement {
|
|
|
52
88
|
this.maxColumns = 2;
|
|
53
89
|
this.minCardWidth = 260;
|
|
54
90
|
this.wideShare = 0.7;
|
|
91
|
+
this.variant = "weighted";
|
|
92
|
+
this.equalColumns = 3;
|
|
93
|
+
this.background = "none";
|
|
55
94
|
this._columns = 2;
|
|
56
95
|
this._gap = 24;
|
|
57
96
|
this._resizeObserver = null;
|
|
@@ -124,13 +163,32 @@ class BentoGrid extends LitElement {
|
|
|
124
163
|
}
|
|
125
164
|
}
|
|
126
165
|
|
|
166
|
+
// Every item gets the same span (TOTAL_UNITS / columns) — no pairing, no
|
|
167
|
+
// "featured" full-width exception. Rounds if TOTAL_UNITS doesn't divide
|
|
168
|
+
// evenly by columns; for common column counts (1, 2, 3, 4, 6, 8, 12, 24)
|
|
169
|
+
// against TOTAL_UNITS=24 this is always exact.
|
|
170
|
+
_applyEqualSpans(columns) {
|
|
171
|
+
const span = Math.round(TOTAL_UNITS / Math.max(1, columns));
|
|
172
|
+
this._items.forEach((item) => item.style.setProperty("--bd-bento-span", String(span)));
|
|
173
|
+
}
|
|
174
|
+
|
|
127
175
|
_relayout() {
|
|
128
176
|
const width = this.clientWidth || 960;
|
|
177
|
+
this.style.setProperty("--bd-bento-total-units", String(TOTAL_UNITS));
|
|
178
|
+
|
|
179
|
+
if (this.variant === "equal") {
|
|
180
|
+
const { columns, gap } = resolveEqualColumns(width, this.equalColumns, this.minCardWidth);
|
|
181
|
+
this._columns = columns;
|
|
182
|
+
this._gap = gap;
|
|
183
|
+
this.style.setProperty("--bd-bento-gap", `${gap}px`);
|
|
184
|
+
this._applyEqualSpans(columns);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
129
188
|
const { columns, gap } = resolveBreakpoint(width, this.maxColumns, this.minCardWidth);
|
|
130
189
|
this._columns = columns;
|
|
131
190
|
this._gap = gap;
|
|
132
191
|
this.style.setProperty("--bd-bento-gap", `${gap}px`);
|
|
133
|
-
this.style.setProperty("--bd-bento-total-units", String(TOTAL_UNITS));
|
|
134
192
|
|
|
135
193
|
if (columns <= 1) {
|
|
136
194
|
this._items.forEach((item) => item.style.setProperty("--bd-bento-span", String(TOTAL_UNITS)));
|
|
@@ -437,4 +437,49 @@ export const TwentySevenItems = {
|
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
|
+
};
|
|
441
|
+
export const EqualColumns = {
|
|
442
|
+
name : "Equal Columns — Protection",
|
|
443
|
+
render: () => html`
|
|
444
|
+
<bd-bento-grid
|
|
445
|
+
variant="equal"
|
|
446
|
+
equal-columns="3"
|
|
447
|
+
eyebrow="Protection"
|
|
448
|
+
title="What does VPN for AI Agents protect against?"
|
|
449
|
+
description="Each protection is structural: every request the agent routes through Bitdefender's MCP tools gets it automatically, with no setting to remember."
|
|
450
|
+
>
|
|
451
|
+
<bd-bento-item>
|
|
452
|
+
<span slot="icon" style="color: var(--color-blue-500, #006DFF); font-weight: 700; font-family: var(--typography-fontFamily-mono, monospace); font-size: 14px; letter-spacing: 0.05em;">01</span>
|
|
453
|
+
<bd-h as="h5" font-weight="700" style="margin: 0 0 8px;">IP masking</bd-h>
|
|
454
|
+
<bd-p color="var(--color-neutral-600)">When your agent reaches a site, it sees the VPN server's exit IP. Not your home address or IP-based location.</bd-p>
|
|
455
|
+
</bd-bento-item>
|
|
456
|
+
<bd-bento-item>
|
|
457
|
+
<span slot="icon" style="color: var(--color-blue-500, #006DFF); font-weight: 700; font-family: var(--typography-fontFamily-mono, monospace); font-size: 14px; letter-spacing: 0.05em;">02</span>
|
|
458
|
+
<bd-h as="h5" font-weight="700" style="margin: 0 0 8px;">Per-prompt encryption</bd-h>
|
|
459
|
+
<bd-p color="var(--color-neutral-600)">Every request opens its own encrypted tunnel from your device to Bitdefender, so whoever runs the Wi-Fi you're on can't read what the agent sends.</bd-p>
|
|
460
|
+
</bd-bento-item>
|
|
461
|
+
<bd-bento-item>
|
|
462
|
+
<span slot="icon" style="color: var(--color-blue-500, #006DFF); font-weight: 700; font-family: var(--typography-fontFamily-mono, monospace); font-size: 14px; letter-spacing: 0.05em;">03</span>
|
|
463
|
+
<bd-h as="h5" font-weight="700" style="margin: 0 0 8px;">Session isolation</bd-h>
|
|
464
|
+
<bd-p color="var(--color-neutral-600)">Each tunnel closes the moment the prompt ends. If a site sees two of your agent's requests, it has no network-level way to tell they came from the same prompt.</bd-p>
|
|
465
|
+
</bd-bento-item>
|
|
466
|
+
<bd-bento-item>
|
|
467
|
+
<span slot="icon" style="color: var(--color-blue-500, #006DFF); font-weight: 700; font-family: var(--typography-fontFamily-mono, monospace); font-size: 14px; letter-spacing: 0.05em;">04</span>
|
|
468
|
+
<bd-h as="h5" font-weight="700" style="margin: 0 0 8px;">Clean-IP browsing</bd-h>
|
|
469
|
+
<bd-p color="var(--color-neutral-600)">Every request exits on the VPN server's IP rather than the same home address every time, useful for QA and monitoring work that needs a neutral, non-residential vantage point.</bd-p>
|
|
470
|
+
</bd-bento-item>
|
|
471
|
+
<bd-bento-item>
|
|
472
|
+
<span slot="icon" style="color: var(--color-blue-500, #006DFF); font-weight: 700; font-family: var(--typography-fontFamily-mono, monospace); font-size: 14px; letter-spacing: 0.05em;">05</span>
|
|
473
|
+
<bd-h as="h5" font-weight="700" style="margin: 0 0 8px;">Regional routing</bd-h>
|
|
474
|
+
<bd-p color="var(--color-neutral-600)">Need to see a page the way a visitor from a specific country would? Route that one request through a Bitdefender server there, just for that prompt.</bd-p>
|
|
475
|
+
</bd-bento-item>
|
|
476
|
+
</bd-bento-grid>
|
|
477
|
+
`,
|
|
478
|
+
parameters: {
|
|
479
|
+
docs: {
|
|
480
|
+
description: {
|
|
481
|
+
story: "`variant=\"equal\"` gives every card the same width in a plain N-column grid (`equal-columns`), with no wide/narrow pairing — unlike the default `\"weighted\"` variant."
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
440
485
|
};
|
|
@@ -4,7 +4,50 @@ export default css`
|
|
|
4
4
|
:host {
|
|
5
5
|
display: block;
|
|
6
6
|
font-family: var(--typography-fontFamily-sans);
|
|
7
|
-
|
|
7
|
+
background: var(--color-blue-50);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* === DARK VARIANT === */
|
|
11
|
+
:host([variant="dark"]) {
|
|
12
|
+
background: linear-gradient(160deg, var(--color-neutral-800) 0%, #0b1626 100%);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:host([variant="dark"]) .bd-comparisonpanel-title {
|
|
16
|
+
color: var(--color-neutral-0, #fff);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
:host([variant="dark"]) .bd-comparisonpanel-description {
|
|
20
|
+
color: rgba(255, 255, 255, 0.55);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
:host([variant="dark"]) .bd-comparisonpanel-eyebrow {
|
|
24
|
+
color: var(--color-blue-400, #60a5fa);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:host([variant="dark"]) .bd-comparisonpanel-column {
|
|
28
|
+
background: rgba(255, 255, 255, 0.03);
|
|
29
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:host([variant="dark"]) .bd-comparisonpanel-column-title {
|
|
33
|
+
color: rgba(255, 255, 255, 0.5);
|
|
34
|
+
font-family: var(--typography-fontFamily-mono, monospace);
|
|
35
|
+
text-transform: uppercase;
|
|
36
|
+
letter-spacing: 0.08em;
|
|
37
|
+
font-weight: var(--typography-fontWeight-bold, 700);
|
|
38
|
+
font-size: var(--typography-fontSize-xs, 12px);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:host([variant="dark"]) .bd-comparisonpanel-text {
|
|
42
|
+
color: rgba(255, 255, 255, 0.85);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
:host([variant="dark"]) .bd-comparisonpanel-icon--positive {
|
|
46
|
+
background: var(--color-green-600, #16a34a);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
:host([variant="dark"]) .bd-comparisonpanel-icon--negative {
|
|
50
|
+
background: var(--color-red-500, #ef4444);
|
|
8
51
|
}
|
|
9
52
|
|
|
10
53
|
.bd-comparisonpanel-wrapper {
|
|
@@ -14,6 +57,14 @@ export default css`
|
|
|
14
57
|
box-sizing: border-box;
|
|
15
58
|
}
|
|
16
59
|
|
|
60
|
+
/* Small eyebrow label above the title, e.g. "Clarity" — optional, only
|
|
61
|
+
rendered when the eyebrow prop/slot is used. */
|
|
62
|
+
.bd-comparisonpanel-eyebrow {
|
|
63
|
+
margin: 0 0 var(--spacing-8);
|
|
64
|
+
color: var(--color-blue-500);
|
|
65
|
+
font-weight: var(--typography-fontWeight-bold, 700);
|
|
66
|
+
}
|
|
67
|
+
|
|
17
68
|
.bd-comparisonpanel-title {
|
|
18
69
|
margin: 0 0 var(--spacing-16);
|
|
19
70
|
color: var(--color-neutral-900);
|
|
@@ -11,7 +11,11 @@ class ComparisonPanel extends LitElement {
|
|
|
11
11
|
positiveTitle: { type: String, attribute: "positive-title" },
|
|
12
12
|
negativeTitle: { type: String, attribute: "negative-title" },
|
|
13
13
|
positiveItems: { type: Array }, // [String]
|
|
14
|
-
negativeItems: { type: Array }
|
|
14
|
+
negativeItems: { type: Array }, // [String]
|
|
15
|
+
// "light" (default) — white section, bordered white cards, sans-serif
|
|
16
|
+
// column titles. "dark" — near-black gradient section, dark bordered
|
|
17
|
+
// cards, mono uppercase column titles (e.g. "VPN FOR AI AGENTS IS").
|
|
18
|
+
variant : { type: String, reflect: true }
|
|
15
19
|
};
|
|
16
20
|
|
|
17
21
|
static styles = [tokens, comparisonPanelCSS];
|
|
@@ -24,6 +28,7 @@ class ComparisonPanel extends LitElement {
|
|
|
24
28
|
this.negativeTitle = "";
|
|
25
29
|
this.positiveItems = [];
|
|
26
30
|
this.negativeItems = [];
|
|
31
|
+
this.variant = "light";
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
_renderColumn(kind, colTitle, items, slotName) {
|
|
@@ -9,7 +9,27 @@ export default css`
|
|
|
9
9
|
:host([background="blue"]) {
|
|
10
10
|
background: var(--color-blue-50);
|
|
11
11
|
}
|
|
12
|
+
/* variant="light" + background="bordered": renders its own card border
|
|
13
|
+
and hover lift, so callers don't need a manual wrapper <div> just to
|
|
14
|
+
get the bordered-card look (e.g. side-by-side requirement cards). */
|
|
15
|
+
:host([variant="light"][background="bordered"]) {
|
|
16
|
+
display: block;
|
|
17
|
+
border: 1px solid var(--color-neutral-100, #e5e7eb);
|
|
18
|
+
border-radius: var(--radius-xl, 12px);
|
|
19
|
+
padding: var(--spacing-24, 24px);
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
:host([variant="light"][background="bordered"]:hover) {
|
|
25
|
+
transform: translateY(-3px);
|
|
26
|
+
box-shadow: 0 14px 40px #00000012;
|
|
27
|
+
border-color: var(--color-neutral-200);
|
|
28
|
+
}
|
|
12
29
|
|
|
30
|
+
:host([variant="light"][background="bordered"]) .bd-compattable-wrapper {
|
|
31
|
+
padding: 0;
|
|
32
|
+
}
|
|
13
33
|
.bd-compattable-wrapper {
|
|
14
34
|
max-width: var(--layout-ensemble-max-width, 1200px);
|
|
15
35
|
margin: 0 auto;
|
|
@@ -31,7 +51,6 @@ export default css`
|
|
|
31
51
|
max-width: 72ch;
|
|
32
52
|
}
|
|
33
53
|
|
|
34
|
-
/* === TABLE CONTAINER (default / outcome / risk) === */
|
|
35
54
|
.bd-compattable-table-container {
|
|
36
55
|
display: table;
|
|
37
56
|
width: 100%;
|
|
@@ -42,7 +61,6 @@ export default css`
|
|
|
42
61
|
box-sizing: border-box;
|
|
43
62
|
}
|
|
44
63
|
|
|
45
|
-
/* === HEADER ROW === */
|
|
46
64
|
.bd-compattable-header {
|
|
47
65
|
display: grid;
|
|
48
66
|
grid-template-columns: 1.4fr 1.2fr 2.4fr;
|
|
@@ -74,7 +92,6 @@ export default css`
|
|
|
74
92
|
color: var(--color-neutral-600);
|
|
75
93
|
}
|
|
76
94
|
|
|
77
|
-
/* === BODY === */
|
|
78
95
|
.bd-compattable-body {
|
|
79
96
|
display: flex;
|
|
80
97
|
flex-direction: column;
|
|
@@ -141,15 +158,15 @@ export default css`
|
|
|
141
158
|
.bd-compattable-cell--riskcoverage {
|
|
142
159
|
color: var(--color-neutral-900);
|
|
143
160
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
161
|
+
.bd-compattable-lightgrid {
|
|
162
|
+
display: grid;
|
|
163
|
+
grid-template-columns: repeat(var(--bd-compattable-light-columns, 2), 1fr);
|
|
164
|
+
column-gap: var(--spacing-48);
|
|
165
|
+
row-gap: 0;
|
|
166
|
+
}
|
|
167
|
+
.bd-compattable-lightrow:last-of-type {
|
|
168
|
+
border-bottom: none;
|
|
169
|
+
}
|
|
153
170
|
.bd-compattable-lightrow {
|
|
154
171
|
display: flex;
|
|
155
172
|
align-items: baseline;
|
|
@@ -171,7 +188,6 @@ export default css`
|
|
|
171
188
|
text-align: right;
|
|
172
189
|
}
|
|
173
190
|
|
|
174
|
-
/* === RESPONSIVE === */
|
|
175
191
|
@media (max-width: 767px) {
|
|
176
192
|
.bd-compattable-header {
|
|
177
193
|
display: none;
|
|
@@ -6,12 +6,13 @@ import compatibilityTableCSS from "./compatibility-table.css.js";
|
|
|
6
6
|
|
|
7
7
|
class CompatibilityTable extends LitElement {
|
|
8
8
|
static properties = {
|
|
9
|
-
title
|
|
10
|
-
description: { type: String },
|
|
11
|
-
variant
|
|
12
|
-
background
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
title : { type: String },
|
|
10
|
+
description : { type: String },
|
|
11
|
+
variant : { type: String, reflect: true },
|
|
12
|
+
background : { type: String, reflect: true },
|
|
13
|
+
lightColumns: { type: Number, attribute: "light-columns" },
|
|
14
|
+
columns : { type: Array },
|
|
15
|
+
rows : { type: Array }
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
static styles = [tokens, compatibilityTableCSS];
|
|
@@ -22,6 +23,7 @@ class CompatibilityTable extends LitElement {
|
|
|
22
23
|
this.description = "";
|
|
23
24
|
this.variant = "default";
|
|
24
25
|
this.background = "default";
|
|
26
|
+
this.lightColumns = 2;
|
|
25
27
|
this.columns = null;
|
|
26
28
|
this.rows = [];
|
|
27
29
|
}
|
|
@@ -95,7 +97,11 @@ class CompatibilityTable extends LitElement {
|
|
|
95
97
|
|
|
96
98
|
${this.variant === "light"
|
|
97
99
|
? html`
|
|
98
|
-
<div
|
|
100
|
+
<div
|
|
101
|
+
class="bd-compattable-lightgrid"
|
|
102
|
+
role="table"
|
|
103
|
+
style="--bd-compattable-light-columns: ${this.lightColumns};"
|
|
104
|
+
>
|
|
99
105
|
${this.rows.map((row) => this._renderRow(row))}
|
|
100
106
|
<slot name="rows"></slot>
|
|
101
107
|
</div>
|
|
@@ -16,7 +16,18 @@ export default css`
|
|
|
16
16
|
text-align: center;
|
|
17
17
|
gap: var(--spacing-16);
|
|
18
18
|
}
|
|
19
|
+
.bd-ctabanner-footer {
|
|
20
|
+
margin-top: var(--spacing-24, 24px);
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: var(--spacing-8, 8px);
|
|
25
|
+
text-align: center;
|
|
26
|
+
}
|
|
19
27
|
|
|
28
|
+
.bd-ctabanner-footer:not(:has(*)) {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
20
31
|
.bd-ctabanner-title {
|
|
21
32
|
display: block;
|
|
22
33
|
margin: 0;
|
|
@@ -55,6 +55,13 @@ class CtaBanner extends LitElement {
|
|
|
55
55
|
</bd-p>
|
|
56
56
|
`
|
|
57
57
|
: nothing}
|
|
58
|
+
|
|
59
|
+
<!-- Freeform slot for extra content below the caption — contact
|
|
60
|
+
info, legal disclaimers, links, etc. Empty by default,
|
|
61
|
+
collapses to nothing when unused. -->
|
|
62
|
+
<div class="bd-ctabanner-footer">
|
|
63
|
+
<slot name="footer"></slot>
|
|
64
|
+
</div>
|
|
58
65
|
</div>
|
|
59
66
|
`;
|
|
60
67
|
}
|
|
@@ -10,11 +10,6 @@ export default css`
|
|
|
10
10
|
line-height: var(--typography-lineHeight-normal);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
.footer-secondary-nav {
|
|
14
|
-
padding-top: var(--spacing-12);
|
|
15
|
-
padding-bottom: var(--spacing-12);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
13
|
bd-divider-vertical {
|
|
19
14
|
display: inline-block;
|
|
20
15
|
width: var(--form-input-default-borderWidth);
|
|
@@ -47,7 +42,15 @@ padding-bottom: var(--spacing-12);
|
|
|
47
42
|
padding: var(--spacing-24) 0;
|
|
48
43
|
}
|
|
49
44
|
|
|
45
|
+
/* Wraps the "secondary-nav" slot (Legal / Privacy Policy / etc). This
|
|
46
|
+
rule previously only had padding — no display:flex/gap — so the
|
|
47
|
+
slotted links ran together with no spacing between them, same root
|
|
48
|
+
cause as the bug that was fixed in bd-footer's own footer.css.js. */
|
|
50
49
|
.footer-secondary-nav {
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
flex-wrap: wrap;
|
|
53
|
+
gap: var(--spacing-16);
|
|
51
54
|
padding: var(--spacing-24) 0;
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -80,7 +83,6 @@ padding-bottom: var(--spacing-12);
|
|
|
80
83
|
background: var(--color-blue-500);
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
/* Stiluri pentru link-uri */
|
|
84
86
|
a {
|
|
85
87
|
color: var(--color-neutral-0);
|
|
86
88
|
text-decoration: none;
|
|
@@ -90,7 +92,6 @@ padding-bottom: var(--spacing-12);
|
|
|
90
92
|
text-decoration: underline;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
/* Stiluri de accesibilitate */
|
|
94
95
|
a:focus {
|
|
95
96
|
outline: none;
|
|
96
97
|
border: 2px solid var(--color-blue-500);
|
|
@@ -98,18 +99,6 @@ padding-bottom: var(--spacing-12);
|
|
|
98
99
|
border-radius: 4px;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
/* Stiluri pentru navigație */
|
|
102
|
-
.footer-nav-main {
|
|
103
|
-
display: flex;
|
|
104
|
-
align-items: center;
|
|
105
|
-
justify-content: flex-start;
|
|
106
|
-
gap: var(--spacing-16);
|
|
107
|
-
flex-wrap: wrap;
|
|
108
|
-
font-size: var(--typography-body-regular-fontSize);
|
|
109
|
-
font-family: var(--typography-fontFamily-sans, 'IBM Plex Sans');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* Responsive styles */
|
|
113
102
|
@media (max-width: 768px) {
|
|
114
103
|
.footer-top {
|
|
115
104
|
flex-direction: column;
|
|
@@ -128,13 +117,13 @@ padding-bottom: var(--spacing-12);
|
|
|
128
117
|
margin-bottom: var(--spacing-16);
|
|
129
118
|
}
|
|
130
119
|
|
|
131
|
-
.footer-nav
|
|
120
|
+
.footer-secondary-nav {
|
|
132
121
|
flex-direction: column;
|
|
133
122
|
align-items: flex-start;
|
|
134
123
|
gap: var(--spacing-8);
|
|
135
124
|
}
|
|
136
125
|
|
|
137
|
-
.footer-nav
|
|
126
|
+
.footer-secondary-nav bd-divider-vertical {
|
|
138
127
|
display: none !important;
|
|
139
128
|
}
|
|
140
129
|
}
|
|
@@ -22,7 +22,7 @@ export class BdFooterLP extends LitElement {
|
|
|
22
22
|
|
|
23
23
|
connectedCallback() {
|
|
24
24
|
super.connectedCallback();
|
|
25
|
-
this._initLocaleFromUrl();
|
|
25
|
+
this._initLocaleFromUrl();
|
|
26
26
|
window.addEventListener('resize', this._handleResize.bind(this));
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -33,12 +33,12 @@ export class BdFooterLP extends LitElement {
|
|
|
33
33
|
|
|
34
34
|
firstUpdated() {
|
|
35
35
|
super.firstUpdated();
|
|
36
|
-
this._toggleImpressumVisibility();
|
|
36
|
+
this._toggleImpressumVisibility();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
updated(changedProps) {
|
|
40
40
|
if (changedProps.has('currentLocale')) {
|
|
41
|
-
this._toggleImpressumVisibility();
|
|
41
|
+
this._toggleImpressumVisibility();
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -49,27 +49,22 @@ export class BdFooterLP extends LitElement {
|
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// Creează o versiune normalized a localeMap cu cheile lowercase
|
|
53
52
|
const normalizedLocaleMap = {};
|
|
54
53
|
Object.entries(localeMap).forEach(([key, value]) => {
|
|
55
54
|
normalizedLocaleMap[key.toLowerCase()] = value;
|
|
56
55
|
});
|
|
57
56
|
|
|
58
|
-
// Încearcă mai întâi potrivire exactă
|
|
59
57
|
let country = normalizedLocaleMap[urlLocale];
|
|
60
58
|
|
|
61
|
-
// Dacă nu găsești potrivire exactă, încearcă să găsești cea mai bună potrivire
|
|
62
59
|
if (!country && urlLocale) {
|
|
63
60
|
const localeParts = urlLocale.split('-');
|
|
64
61
|
const language = localeParts[0];
|
|
65
62
|
const region = localeParts[1];
|
|
66
63
|
|
|
67
|
-
// Caută o potrivire care să înceapă cu același limbaj
|
|
68
64
|
const possibleMatches = Object.entries(normalizedLocaleMap)
|
|
69
65
|
.filter(([key]) => key.startsWith(language));
|
|
70
66
|
|
|
71
67
|
if (possibleMatches.length > 0) {
|
|
72
|
-
// Preferă potrivirea cu aceeași regiune dacă există
|
|
73
68
|
const exactRegionMatch = possibleMatches.find(([key]) => {
|
|
74
69
|
const keyParts = key.split('-');
|
|
75
70
|
const keyRegion = keyParts[1];
|
|
@@ -81,13 +76,11 @@ export class BdFooterLP extends LitElement {
|
|
|
81
76
|
|
|
82
77
|
if (exactRegionMatch) {
|
|
83
78
|
country = exactRegionMatch[1];
|
|
84
|
-
// Recuperează cheia originală din localeMap
|
|
85
79
|
const originalKey = Object.keys(localeMap).find(k =>
|
|
86
80
|
k.toLowerCase() === exactRegionMatch[0]
|
|
87
81
|
);
|
|
88
82
|
this.currentLocale = originalKey || exactRegionMatch[0];
|
|
89
83
|
} else {
|
|
90
|
-
// Altfel, ia prima potrivire
|
|
91
84
|
country = possibleMatches[0][1];
|
|
92
85
|
const originalKey = Object.keys(localeMap).find(k =>
|
|
93
86
|
k.toLowerCase() === possibleMatches[0][0]
|
|
@@ -114,25 +107,20 @@ export class BdFooterLP extends LitElement {
|
|
|
114
107
|
const footerNav = assignedElements[0];
|
|
115
108
|
const impressumLink = footerNav.querySelector('.impressum-link');
|
|
116
109
|
|
|
117
|
-
// Găsește separatorul care precede link-ul Impressum
|
|
118
110
|
let impressumDivider = null;
|
|
119
111
|
if (impressumLink) {
|
|
120
112
|
impressumDivider = impressumLink.previousElementSibling;
|
|
121
113
|
|
|
122
|
-
// Verifică dacă elementul anterior este un divider și este imediat înainte
|
|
123
114
|
if (impressumDivider && impressumDivider.tagName === 'BD-DIVIDER-VERTICAL' &&
|
|
124
115
|
impressumDivider.nextElementSibling === impressumLink) {
|
|
125
|
-
// Este separatorul corect
|
|
126
116
|
} else {
|
|
127
117
|
impressumDivider = null;
|
|
128
118
|
}
|
|
129
119
|
}
|
|
130
120
|
|
|
131
121
|
if (impressumLink) {
|
|
132
|
-
// Ascunde/afișează link-ul Impressum
|
|
133
122
|
impressumLink.style.display = showImpressum ? '' : 'none';
|
|
134
123
|
|
|
135
|
-
// Ascunde/afișează și separatorul dinaintea lui
|
|
136
124
|
if (impressumDivider && impressumDivider.tagName === 'BD-DIVIDER-VERTICAL') {
|
|
137
125
|
impressumDivider.style.display = showImpressum ? '' : 'none';
|
|
138
126
|
}
|
|
@@ -214,14 +202,12 @@ export class BdFooterLP extends LitElement {
|
|
|
214
202
|
`;
|
|
215
203
|
}
|
|
216
204
|
|
|
217
|
-
// Metodă specifică pentru secondary-nav care verifică și elementele custom
|
|
218
205
|
_hasSecondaryNavContent() {
|
|
219
206
|
const slot = this.shadowRoot?.querySelector('slot[name="secondary-nav"]');
|
|
220
207
|
if (!slot) return false;
|
|
221
208
|
|
|
222
209
|
const assignedNodes = slot.assignedNodes({ flatten: true });
|
|
223
210
|
|
|
224
|
-
// Verifică dacă există noduri care nu sunt doar spații albe
|
|
225
211
|
const hasContent = assignedNodes.some(node => {
|
|
226
212
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
227
213
|
return true;
|
|
@@ -235,7 +221,6 @@ export class BdFooterLP extends LitElement {
|
|
|
235
221
|
return hasContent;
|
|
236
222
|
}
|
|
237
223
|
|
|
238
|
-
// Metodă helper pentru a verifica dacă un slot are conținut
|
|
239
224
|
_hasSlotContent(slotName) {
|
|
240
225
|
const slot = this.shadowRoot?.querySelector(`slot[name="${slotName}"]`);
|
|
241
226
|
if (!slot) return false;
|
|
@@ -16,7 +16,6 @@ export default css`
|
|
|
16
16
|
font-weight: var(--typography-heading-h3-fontWeight);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/* Corect: stilizare pentru elementele slottate */
|
|
20
19
|
::slotted(a) {
|
|
21
20
|
color: white !important;
|
|
22
21
|
text-decoration: none;
|
|
@@ -33,5 +32,4 @@ export default css`
|
|
|
33
32
|
display: none !important;
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
|
-
|
|
37
35
|
`;
|