@sankhyatronics/sankhya-cms 0.0.0-stage → 0.1.0
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/README.md +34 -3
- package/custom-elements.json +4008 -0
- package/dist/bento-grid/index.d.ts +27 -0
- package/dist/bento-grid/index.js +10 -0
- package/dist/bento-grid/index.js.map +1 -0
- package/dist/card/index.d.ts +20 -0
- package/dist/card/index.js +9 -0
- package/dist/card/index.js.map +1 -0
- package/dist/carousel/index.d.ts +44 -0
- package/dist/carousel/index.js +10 -0
- package/dist/carousel/index.js.map +1 -0
- package/dist/chunk-23IHTBAB.js +557 -0
- package/dist/chunk-23IHTBAB.js.map +1 -0
- package/dist/chunk-2VZFHTBC.js +203 -0
- package/dist/chunk-2VZFHTBC.js.map +1 -0
- package/dist/chunk-3ERKQNZT.js +155 -0
- package/dist/chunk-3ERKQNZT.js.map +1 -0
- package/dist/chunk-3JU3R22O.js +193 -0
- package/dist/chunk-3JU3R22O.js.map +1 -0
- package/dist/chunk-3TCP25JG.js +347 -0
- package/dist/chunk-3TCP25JG.js.map +1 -0
- package/dist/chunk-3VE6VFJ4.js +87 -0
- package/dist/chunk-3VE6VFJ4.js.map +1 -0
- package/dist/chunk-4HKSA5UT.js +312 -0
- package/dist/chunk-4HKSA5UT.js.map +1 -0
- package/dist/chunk-5REZSZCK.js +266 -0
- package/dist/chunk-5REZSZCK.js.map +1 -0
- package/dist/chunk-5UFRGZWG.js +268 -0
- package/dist/chunk-5UFRGZWG.js.map +1 -0
- package/dist/chunk-74MILER4.js +215 -0
- package/dist/chunk-74MILER4.js.map +1 -0
- package/dist/chunk-7PSYCTEN.js +118 -0
- package/dist/chunk-7PSYCTEN.js.map +1 -0
- package/dist/chunk-CDC4P77V.js +299 -0
- package/dist/chunk-CDC4P77V.js.map +1 -0
- package/dist/chunk-E52WO7R7.js +279 -0
- package/dist/chunk-E52WO7R7.js.map +1 -0
- package/dist/chunk-EUXUH3YW.js +15 -0
- package/dist/chunk-EUXUH3YW.js.map +1 -0
- package/dist/chunk-IMACP7HN.js +156 -0
- package/dist/chunk-IMACP7HN.js.map +1 -0
- package/dist/chunk-LHU23TF6.js +273 -0
- package/dist/chunk-LHU23TF6.js.map +1 -0
- package/dist/chunk-OEX5FDMR.js +250 -0
- package/dist/chunk-OEX5FDMR.js.map +1 -0
- package/dist/chunk-RHAOQ6K4.js +138 -0
- package/dist/chunk-RHAOQ6K4.js.map +1 -0
- package/dist/chunk-RMIJVTFS.js +397 -0
- package/dist/chunk-RMIJVTFS.js.map +1 -0
- package/dist/chunk-U7YKCCJG.js +228 -0
- package/dist/chunk-U7YKCCJG.js.map +1 -0
- package/dist/chunk-USNFT4DU.js +125 -0
- package/dist/chunk-USNFT4DU.js.map +1 -0
- package/dist/chunk-VYON56QO.js +90 -0
- package/dist/chunk-VYON56QO.js.map +1 -0
- package/dist/chunk-WCO7JAIK.js +195 -0
- package/dist/chunk-WCO7JAIK.js.map +1 -0
- package/dist/chunk-XHBC23TV.js +254 -0
- package/dist/chunk-XHBC23TV.js.map +1 -0
- package/dist/chunk-XXPBFHWR.js +70 -0
- package/dist/chunk-XXPBFHWR.js.map +1 -0
- package/dist/cms-theme.css +15 -0
- package/dist/content-block/index.d.ts +25 -0
- package/dist/content-block/index.js +11 -0
- package/dist/content-block/index.js.map +1 -0
- package/dist/cookie-consent/index.d.ts +33 -0
- package/dist/cookie-consent/index.js +9 -0
- package/dist/cookie-consent/index.js.map +1 -0
- package/dist/dropdown/index.d.ts +89 -0
- package/dist/dropdown/index.js +9 -0
- package/dist/dropdown/index.js.map +1 -0
- package/dist/feature-split/index.d.ts +38 -0
- package/dist/feature-split/index.js +11 -0
- package/dist/feature-split/index.js.map +1 -0
- package/dist/features-section/index.d.ts +50 -0
- package/dist/features-section/index.js +14 -0
- package/dist/features-section/index.js.map +1 -0
- package/dist/footer/index.d.ts +64 -0
- package/dist/footer/index.js +9 -0
- package/dist/footer/index.js.map +1 -0
- package/dist/header/index.d.ts +112 -0
- package/dist/header/index.js +10 -0
- package/dist/header/index.js.map +1 -0
- package/dist/hero/index.d.ts +32 -0
- package/dist/hero/index.js +10 -0
- package/dist/hero/index.js.map +1 -0
- package/dist/icon-button/index.d.ts +23 -0
- package/dist/icon-button/index.js +9 -0
- package/dist/icon-button/index.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/items-accordion/index.d.ts +31 -0
- package/dist/items-accordion/index.js +10 -0
- package/dist/items-accordion/index.js.map +1 -0
- package/dist/markdown/index.d.ts +33 -0
- package/dist/markdown/index.js +9 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/menu-grid/index.d.ts +21 -0
- package/dist/menu-grid/index.js +9 -0
- package/dist/menu-grid/index.js.map +1 -0
- package/dist/menu-grid-item/index.d.ts +53 -0
- package/dist/menu-grid-item/index.js +9 -0
- package/dist/menu-grid-item/index.js.map +1 -0
- package/dist/menu-item/index.d.ts +50 -0
- package/dist/menu-item/index.js +9 -0
- package/dist/menu-item/index.js.map +1 -0
- package/dist/promo-banner/index.d.ts +22 -0
- package/dist/promo-banner/index.js +10 -0
- package/dist/promo-banner/index.js.map +1 -0
- package/dist/schema/index.d.ts +72 -0
- package/dist/schema/index.js +75 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/select/index.d.ts +77 -0
- package/dist/select/index.js +10 -0
- package/dist/select/index.js.map +1 -0
- package/dist/shared/icons.d.ts +77 -0
- package/dist/shared/icons.js +64 -0
- package/dist/shared/icons.js.map +1 -0
- package/dist/shared/styles.d.ts +90 -0
- package/dist/shared/styles.js +48 -0
- package/dist/shared/styles.js.map +1 -0
- package/dist/stats/index.d.ts +17 -0
- package/dist/stats/index.js +9 -0
- package/dist/stats/index.js.map +1 -0
- package/dist/testimonials/index.d.ts +25 -0
- package/dist/testimonials/index.js +10 -0
- package/dist/testimonials/index.js.map +1 -0
- package/dist/theme.css +292 -0
- package/dist/themes.css +65 -0
- package/dist/theming/index.d.ts +28 -0
- package/dist/theming/index.js +53 -0
- package/dist/theming/index.js.map +1 -0
- package/package.json +161 -4
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buttonResetStyles,
|
|
3
|
+
controlBaseStyles,
|
|
4
|
+
fontFamilyStyles
|
|
5
|
+
} from "./chunk-RHAOQ6K4.js";
|
|
6
|
+
import {
|
|
7
|
+
__decorateClass
|
|
8
|
+
} from "./chunk-EUXUH3YW.js";
|
|
9
|
+
|
|
10
|
+
// src/dropdown/Dropdown.ts
|
|
11
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
12
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
13
|
+
var Dropdown = class extends LitElement {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.label = "";
|
|
17
|
+
this.href = "";
|
|
18
|
+
this.iconPosition = "left";
|
|
19
|
+
this.iconSize = "1.2rem";
|
|
20
|
+
this.iconColor = "currentColor";
|
|
21
|
+
this.align = "left";
|
|
22
|
+
this.target = "_self";
|
|
23
|
+
this.triggerClassName = "";
|
|
24
|
+
this.itemGroupClassName = "";
|
|
25
|
+
this.itemClassName = "";
|
|
26
|
+
this.disabled = false;
|
|
27
|
+
this.active = false;
|
|
28
|
+
this.inline = false;
|
|
29
|
+
this.open = false;
|
|
30
|
+
this.hasContent = false;
|
|
31
|
+
this.hasIcon = false;
|
|
32
|
+
this.closeOnOutsideMouseDown = (event) => {
|
|
33
|
+
if (this.open && !event.composedPath().includes(this)) this.setOpen(false);
|
|
34
|
+
};
|
|
35
|
+
this.closeOnEscape = (event) => {
|
|
36
|
+
if (!this.open || event.key !== "Escape") return;
|
|
37
|
+
const focusWasInside = event.composedPath().includes(this);
|
|
38
|
+
this.setOpen(false);
|
|
39
|
+
if (focusWasInside) this.trigger()?.focus();
|
|
40
|
+
};
|
|
41
|
+
/** Enter/Space/ArrowDown open the panel and focus its first entry; ArrowUp its last. */
|
|
42
|
+
this.onTitleKeyDown = (event) => {
|
|
43
|
+
if (!this.hasContent || this.disabled) return;
|
|
44
|
+
if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
event.stopPropagation();
|
|
47
|
+
this.setOpen(true);
|
|
48
|
+
this.focusItem((items) => items[0]);
|
|
49
|
+
} else if (event.key === "ArrowUp") {
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
event.stopPropagation();
|
|
52
|
+
this.setOpen(true);
|
|
53
|
+
this.focusItem((items) => items[items.length - 1]);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/** Arrow keys/Home/End move between the panel's entries; Tab closes the panel and moves on. */
|
|
57
|
+
this.onPanelKeyDown = (event) => {
|
|
58
|
+
if (event.key === "Tab") {
|
|
59
|
+
this.setOpen(false);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
63
|
+
const items = this.panelItems();
|
|
64
|
+
if (items.length === 0) return;
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
event.stopPropagation();
|
|
67
|
+
const path = event.composedPath();
|
|
68
|
+
const current = items.findIndex((item) => path.includes(item));
|
|
69
|
+
const last = items.length - 1;
|
|
70
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? last : event.key === "ArrowDown" ? current < 0 || current === last ? 0 : current + 1 : current <= 0 ? last : current - 1;
|
|
71
|
+
items[next].focus();
|
|
72
|
+
};
|
|
73
|
+
this.onTitleClick = (event) => {
|
|
74
|
+
event.stopPropagation();
|
|
75
|
+
if (!this.hasContent) return;
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
this.setOpen(!this.open);
|
|
78
|
+
};
|
|
79
|
+
/** A click anywhere inside the panel closes it, as in the source. */
|
|
80
|
+
this.onPanelClick = () => {
|
|
81
|
+
this.setOpen(false);
|
|
82
|
+
};
|
|
83
|
+
this.onContentSlotChange = (event) => {
|
|
84
|
+
this.hasContent = event.target.assignedNodes({ flatten: true }).some(
|
|
85
|
+
(node) => node.nodeType === Node.ELEMENT_NODE || !!node.textContent?.trim()
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
this.onIconSlotChange = (event) => {
|
|
89
|
+
this.hasIcon = event.target.assignedNodes({ flatten: true }).length > 0;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
connectedCallback() {
|
|
93
|
+
super.connectedCallback();
|
|
94
|
+
document.addEventListener("mousedown", this.closeOnOutsideMouseDown);
|
|
95
|
+
window.addEventListener("keydown", this.closeOnEscape);
|
|
96
|
+
}
|
|
97
|
+
disconnectedCallback() {
|
|
98
|
+
document.removeEventListener("mousedown", this.closeOnOutsideMouseDown);
|
|
99
|
+
window.removeEventListener("keydown", this.closeOnEscape);
|
|
100
|
+
if (Dropdown.openDropdown === this) Dropdown.openDropdown = null;
|
|
101
|
+
super.disconnectedCallback();
|
|
102
|
+
}
|
|
103
|
+
updated(changed) {
|
|
104
|
+
if (!changed.has("open")) return;
|
|
105
|
+
if (this.open) {
|
|
106
|
+
const previous = Dropdown.openDropdown;
|
|
107
|
+
Dropdown.openDropdown = this;
|
|
108
|
+
if (previous && previous !== this) previous.setOpen(false);
|
|
109
|
+
} else if (Dropdown.openDropdown === this) {
|
|
110
|
+
Dropdown.openDropdown = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
setOpen(open) {
|
|
114
|
+
if (this.open === open) return;
|
|
115
|
+
this.open = open;
|
|
116
|
+
this.dispatchEvent(new CustomEvent("dropdown-toggle", { detail: { open }, bubbles: true, composed: true }));
|
|
117
|
+
}
|
|
118
|
+
trigger() {
|
|
119
|
+
return this.renderRoot.querySelector(".menu-title");
|
|
120
|
+
}
|
|
121
|
+
/** Keyboard-reachable entries in the panel, in order: menu items (they delegate focus to their
|
|
122
|
+
* own link) plus any plain links/buttons/tabbable elements a caller slots in. */
|
|
123
|
+
panelItems() {
|
|
124
|
+
const slot = this.renderRoot.querySelector(".menu-dropdown slot");
|
|
125
|
+
if (!slot) return [];
|
|
126
|
+
const selector = 'st-menu-item, st-menu-grid-item, a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
127
|
+
const items = [];
|
|
128
|
+
for (const el of slot.assignedElements({ flatten: true })) {
|
|
129
|
+
if (el.matches(selector)) items.push(el);
|
|
130
|
+
items.push(...Array.from(el.querySelectorAll(selector)));
|
|
131
|
+
}
|
|
132
|
+
return items;
|
|
133
|
+
}
|
|
134
|
+
/** Moves focus into the panel once it has become visible (it is `visibility: hidden` until the
|
|
135
|
+
* `show` class lands, and hidden elements can't take focus). */
|
|
136
|
+
focusItem(pick) {
|
|
137
|
+
void this.updateComplete.then(() => pick(this.panelItems())?.focus());
|
|
138
|
+
}
|
|
139
|
+
render() {
|
|
140
|
+
const icon = html`<span
|
|
141
|
+
class="icon ${this.iconPosition === "left" ? "icon-left" : "icon-right"}"
|
|
142
|
+
?hidden=${!this.hasIcon}
|
|
143
|
+
style="width:${this.iconSize};height:${this.iconSize};color:${this.iconColor}"
|
|
144
|
+
><slot name="icon" @slotchange=${this.onIconSlotChange}></slot
|
|
145
|
+
></span>`;
|
|
146
|
+
const titleContent = html`
|
|
147
|
+
${this.iconPosition === "left" ? icon : nothing} ${this.label ? html`<span part="label">${this.label}</span>` : nothing}
|
|
148
|
+
${this.iconPosition === "right" ? icon : nothing}
|
|
149
|
+
${this.hasContent ? html`<span class="arrow" part="caret" aria-hidden="true">▼</span>` : nothing}
|
|
150
|
+
`;
|
|
151
|
+
const titleClass = `menu-title ${this.triggerClassName} ${this.open ? "active" : ""}`;
|
|
152
|
+
return html`
|
|
153
|
+
<div class="menu" part="menu">
|
|
154
|
+
${this.href ? html`
|
|
155
|
+
<a
|
|
156
|
+
part="trigger"
|
|
157
|
+
class=${titleClass}
|
|
158
|
+
href=${this.href}
|
|
159
|
+
target=${this.target}
|
|
160
|
+
aria-haspopup=${this.hasContent ? "true" : "false"}
|
|
161
|
+
aria-expanded=${this.open}
|
|
162
|
+
aria-disabled=${this.disabled ? "true" : nothing}
|
|
163
|
+
@click=${this.onTitleClick}
|
|
164
|
+
@keydown=${this.onTitleKeyDown}
|
|
165
|
+
>
|
|
166
|
+
${titleContent}
|
|
167
|
+
</a>
|
|
168
|
+
` : html`
|
|
169
|
+
<button
|
|
170
|
+
part="trigger"
|
|
171
|
+
class=${titleClass}
|
|
172
|
+
type="button"
|
|
173
|
+
aria-haspopup="true"
|
|
174
|
+
aria-expanded=${this.open}
|
|
175
|
+
?disabled=${this.disabled}
|
|
176
|
+
@click=${this.onTitleClick}
|
|
177
|
+
@keydown=${this.onTitleKeyDown}
|
|
178
|
+
>
|
|
179
|
+
${titleContent}
|
|
180
|
+
</button>
|
|
181
|
+
`}
|
|
182
|
+
<div
|
|
183
|
+
part="panel"
|
|
184
|
+
class="menu-dropdown align-${this.align} ${this.itemGroupClassName} ${this.open ? "show" : ""}"
|
|
185
|
+
role="menu"
|
|
186
|
+
@click=${this.onPanelClick}
|
|
187
|
+
@keydown=${this.onPanelKeyDown}
|
|
188
|
+
>
|
|
189
|
+
<slot @slotchange=${this.onContentSlotChange}></slot>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
`;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
/** The open dropdown, if any — the source's `DropdownContext.activeDropdown`. */
|
|
196
|
+
Dropdown.openDropdown = null;
|
|
197
|
+
Dropdown.styles = css`
|
|
198
|
+
${controlBaseStyles}
|
|
199
|
+
|
|
200
|
+
:host {
|
|
201
|
+
display: inline-block;
|
|
202
|
+
position: relative;
|
|
203
|
+
${fontFamilyStyles}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.menu-title {
|
|
207
|
+
${buttonResetStyles}
|
|
208
|
+
display: flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
justify-content: center;
|
|
211
|
+
box-sizing: border-box;
|
|
212
|
+
min-height: 40px;
|
|
213
|
+
padding: 12px;
|
|
214
|
+
gap: 8px;
|
|
215
|
+
text-align: center;
|
|
216
|
+
width: fit-content;
|
|
217
|
+
border-radius: var(--st-radius-sm);
|
|
218
|
+
color: var(--st-color-text);
|
|
219
|
+
font-size: 0.875rem;
|
|
220
|
+
text-decoration: none;
|
|
221
|
+
white-space: nowrap;
|
|
222
|
+
transition: background-color 0.3s ease;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.menu-title:hover,
|
|
226
|
+
.menu-title[aria-expanded='true'] {
|
|
227
|
+
background: var(--st-color-hover);
|
|
228
|
+
color: var(--st-color-text-strong);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
:host([active]) .menu-title {
|
|
232
|
+
background: var(--st-color-accent-subtle);
|
|
233
|
+
color: var(--st-color-accent-text);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
:host([disabled]) .menu-title {
|
|
237
|
+
color: var(--st-color-text-muted);
|
|
238
|
+
cursor: not-allowed;
|
|
239
|
+
pointer-events: none;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.icon {
|
|
243
|
+
display: inline-flex;
|
|
244
|
+
flex: none;
|
|
245
|
+
align-items: center;
|
|
246
|
+
justify-content: center;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.icon[hidden] {
|
|
250
|
+
display: none;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.arrow {
|
|
254
|
+
display: inline-block;
|
|
255
|
+
margin-left: 8px;
|
|
256
|
+
font-size: 0.75rem;
|
|
257
|
+
transition: transform 0.3s ease;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.menu-title[aria-expanded='true'] .arrow {
|
|
261
|
+
transform: rotate(180deg);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.menu-dropdown {
|
|
265
|
+
position: absolute;
|
|
266
|
+
top: 100%;
|
|
267
|
+
left: 0;
|
|
268
|
+
z-index: 100;
|
|
269
|
+
padding: 16px;
|
|
270
|
+
background: var(--st-color-surface);
|
|
271
|
+
color: var(--st-color-text);
|
|
272
|
+
border: 1px solid var(--st-color-border);
|
|
273
|
+
border-radius: var(--st-radius-sm);
|
|
274
|
+
box-shadow: 0 4px 16px var(--st-shadow-color-sm);
|
|
275
|
+
opacity: 0;
|
|
276
|
+
visibility: hidden;
|
|
277
|
+
transform: translateY(-10px);
|
|
278
|
+
/* visibility flips at once on open (so keyboard focus can move into the panel on the same
|
|
279
|
+
frame) and only after the 0.3s fade on close. */
|
|
280
|
+
transition:
|
|
281
|
+
opacity 0.3s ease,
|
|
282
|
+
transform 0.3s ease,
|
|
283
|
+
visibility 0s linear 0.3s;
|
|
284
|
+
pointer-events: none;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.menu-dropdown.align-right {
|
|
288
|
+
left: auto;
|
|
289
|
+
right: 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.menu-dropdown.show {
|
|
293
|
+
opacity: 1;
|
|
294
|
+
visibility: visible;
|
|
295
|
+
transform: translateY(0);
|
|
296
|
+
pointer-events: auto;
|
|
297
|
+
transition-delay: 0s;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Inline: the panel opens in the flow under its trigger and pushes what follows down, instead
|
|
301
|
+
of floating — the source's .touch-device layout. st-header turns this on for its menu while
|
|
302
|
+
the hamburger panel is showing. */
|
|
303
|
+
:host([inline]) {
|
|
304
|
+
display: block;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
:host([inline]) .menu-title {
|
|
308
|
+
width: 100%;
|
|
309
|
+
justify-content: flex-start;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
:host([inline]) .arrow {
|
|
313
|
+
margin-left: auto;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
:host([inline]) .menu-dropdown {
|
|
317
|
+
display: none;
|
|
318
|
+
position: relative;
|
|
319
|
+
top: auto;
|
|
320
|
+
left: auto;
|
|
321
|
+
right: auto;
|
|
322
|
+
width: 100%;
|
|
323
|
+
box-sizing: border-box;
|
|
324
|
+
box-shadow: none;
|
|
325
|
+
opacity: 1;
|
|
326
|
+
visibility: visible;
|
|
327
|
+
transform: none;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
:host([inline]) .menu-dropdown.show {
|
|
331
|
+
display: block;
|
|
332
|
+
pointer-events: auto;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@media (prefers-reduced-motion: reduce) {
|
|
336
|
+
.menu-dropdown,
|
|
337
|
+
.arrow {
|
|
338
|
+
transition: none;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
`;
|
|
342
|
+
__decorateClass([
|
|
343
|
+
property({ type: String })
|
|
344
|
+
], Dropdown.prototype, "label", 2);
|
|
345
|
+
__decorateClass([
|
|
346
|
+
property({ type: String })
|
|
347
|
+
], Dropdown.prototype, "href", 2);
|
|
348
|
+
__decorateClass([
|
|
349
|
+
property({ type: String, attribute: "icon-position" })
|
|
350
|
+
], Dropdown.prototype, "iconPosition", 2);
|
|
351
|
+
__decorateClass([
|
|
352
|
+
property({ type: String, attribute: "icon-size" })
|
|
353
|
+
], Dropdown.prototype, "iconSize", 2);
|
|
354
|
+
__decorateClass([
|
|
355
|
+
property({ type: String, attribute: "icon-color" })
|
|
356
|
+
], Dropdown.prototype, "iconColor", 2);
|
|
357
|
+
__decorateClass([
|
|
358
|
+
property({ type: String })
|
|
359
|
+
], Dropdown.prototype, "align", 2);
|
|
360
|
+
__decorateClass([
|
|
361
|
+
property({ type: String })
|
|
362
|
+
], Dropdown.prototype, "target", 2);
|
|
363
|
+
__decorateClass([
|
|
364
|
+
property({ type: String, attribute: "trigger-class-name" })
|
|
365
|
+
], Dropdown.prototype, "triggerClassName", 2);
|
|
366
|
+
__decorateClass([
|
|
367
|
+
property({ type: String, attribute: "item-group-class-name" })
|
|
368
|
+
], Dropdown.prototype, "itemGroupClassName", 2);
|
|
369
|
+
__decorateClass([
|
|
370
|
+
property({ type: String, attribute: "item-class-name" })
|
|
371
|
+
], Dropdown.prototype, "itemClassName", 2);
|
|
372
|
+
__decorateClass([
|
|
373
|
+
property({ type: Boolean, reflect: true })
|
|
374
|
+
], Dropdown.prototype, "disabled", 2);
|
|
375
|
+
__decorateClass([
|
|
376
|
+
property({ type: Boolean, reflect: true })
|
|
377
|
+
], Dropdown.prototype, "active", 2);
|
|
378
|
+
__decorateClass([
|
|
379
|
+
property({ type: Boolean, reflect: true })
|
|
380
|
+
], Dropdown.prototype, "inline", 2);
|
|
381
|
+
__decorateClass([
|
|
382
|
+
property({ type: Boolean, reflect: true })
|
|
383
|
+
], Dropdown.prototype, "open", 2);
|
|
384
|
+
__decorateClass([
|
|
385
|
+
state()
|
|
386
|
+
], Dropdown.prototype, "hasContent", 2);
|
|
387
|
+
__decorateClass([
|
|
388
|
+
state()
|
|
389
|
+
], Dropdown.prototype, "hasIcon", 2);
|
|
390
|
+
Dropdown = __decorateClass([
|
|
391
|
+
customElement("st-dropdown")
|
|
392
|
+
], Dropdown);
|
|
393
|
+
|
|
394
|
+
export {
|
|
395
|
+
Dropdown
|
|
396
|
+
};
|
|
397
|
+
//# sourceMappingURL=chunk-RMIJVTFS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dropdown/Dropdown.ts"],"sourcesContent":["import { LitElement, css, html, nothing } from 'lit';\r\nimport { customElement, property, state } from 'lit/decorators.js';\r\n\r\nimport { buttonResetStyles, controlBaseStyles, fontFamilyStyles } from '../shared/styles';\r\nimport type { DropdownToggleEventDetail } from './DropdownEvents';\r\n\r\n/**\r\n * A labeled trigger + slotted dropdown panel — nav menus (typically holding an `st-menu-grid`),\r\n * action menus, anything \"click a trigger, show content under it\". A 1:1 port of `SankhyaUI`'s\r\n * React `Dropdown` (`SankhyaCMS/SankhyaUI/src/components/Dropdown`): same markup\r\n * (`.menu` > `.menu-title` + `.menu-dropdown`), sizes, 0.3s slide/fade, and behavior —\r\n *\r\n * - only one dropdown is open at a time (the source's `DropdownProvider`, built in here, so no\r\n * provider element is needed);\r\n * - a mousedown outside, Escape, or a click inside the panel closes it;\r\n * - an `href` trigger toggles the panel when it has content, and is a plain link when it doesn't;\r\n * - `inline` opens the panel in the flow, the source's `.touch-device` layout.\r\n *\r\n * Colors and the panel's surface come from the suite theme (the source left those to the\r\n * consumer's `className`/`itemGroupClassName` utility classes). Two source rules are left out\r\n * because they break existing suite uses (`st-source-bar`'s site/library menus): the 100vw panel\r\n * at <=768px (overflows the screen from wherever the trigger sits; the header uses `inline`\r\n * instead) and `overflow: hidden` on slotted children (clips their focus rings). Suite additions\r\n * kept on top: `align`, `active`, `disabled`, `inline`, and the `dropdown-toggle` event.\r\n */\r\n@customElement('st-dropdown')\r\nexport class Dropdown extends LitElement {\r\n /** The open dropdown, if any — the source's `DropdownContext.activeDropdown`. */\r\n private static openDropdown: Dropdown | null = null;\r\n\r\n static styles = css`\r\n ${controlBaseStyles}\r\n\r\n :host {\r\n display: inline-block;\r\n position: relative;\r\n ${fontFamilyStyles}\r\n }\r\n\r\n .menu-title {\r\n ${buttonResetStyles}\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n box-sizing: border-box;\r\n min-height: 40px;\r\n padding: 12px;\r\n gap: 8px;\r\n text-align: center;\r\n width: fit-content;\r\n border-radius: var(--st-radius-sm);\r\n color: var(--st-color-text);\r\n font-size: 0.875rem;\r\n text-decoration: none;\r\n white-space: nowrap;\r\n transition: background-color 0.3s ease;\r\n }\r\n\r\n .menu-title:hover,\r\n .menu-title[aria-expanded='true'] {\r\n background: var(--st-color-hover);\r\n color: var(--st-color-text-strong);\r\n }\r\n\r\n :host([active]) .menu-title {\r\n background: var(--st-color-accent-subtle);\r\n color: var(--st-color-accent-text);\r\n }\r\n\r\n :host([disabled]) .menu-title {\r\n color: var(--st-color-text-muted);\r\n cursor: not-allowed;\r\n pointer-events: none;\r\n }\r\n\r\n .icon {\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n\r\n .icon[hidden] {\r\n display: none;\r\n }\r\n\r\n .arrow {\r\n display: inline-block;\r\n margin-left: 8px;\r\n font-size: 0.75rem;\r\n transition: transform 0.3s ease;\r\n }\r\n\r\n .menu-title[aria-expanded='true'] .arrow {\r\n transform: rotate(180deg);\r\n }\r\n\r\n .menu-dropdown {\r\n position: absolute;\r\n top: 100%;\r\n left: 0;\r\n z-index: 100;\r\n padding: 16px;\r\n background: var(--st-color-surface);\r\n color: var(--st-color-text);\r\n border: 1px solid var(--st-color-border);\r\n border-radius: var(--st-radius-sm);\r\n box-shadow: 0 4px 16px var(--st-shadow-color-sm);\r\n opacity: 0;\r\n visibility: hidden;\r\n transform: translateY(-10px);\r\n /* visibility flips at once on open (so keyboard focus can move into the panel on the same\r\n frame) and only after the 0.3s fade on close. */\r\n transition:\r\n opacity 0.3s ease,\r\n transform 0.3s ease,\r\n visibility 0s linear 0.3s;\r\n pointer-events: none;\r\n }\r\n\r\n .menu-dropdown.align-right {\r\n left: auto;\r\n right: 0;\r\n }\r\n\r\n .menu-dropdown.show {\r\n opacity: 1;\r\n visibility: visible;\r\n transform: translateY(0);\r\n pointer-events: auto;\r\n transition-delay: 0s;\r\n }\r\n\r\n /* Inline: the panel opens in the flow under its trigger and pushes what follows down, instead\r\n of floating — the source's .touch-device layout. st-header turns this on for its menu while\r\n the hamburger panel is showing. */\r\n :host([inline]) {\r\n display: block;\r\n }\r\n\r\n :host([inline]) .menu-title {\r\n width: 100%;\r\n justify-content: flex-start;\r\n }\r\n\r\n :host([inline]) .arrow {\r\n margin-left: auto;\r\n }\r\n\r\n :host([inline]) .menu-dropdown {\r\n display: none;\r\n position: relative;\r\n top: auto;\r\n left: auto;\r\n right: auto;\r\n width: 100%;\r\n box-sizing: border-box;\r\n box-shadow: none;\r\n opacity: 1;\r\n visibility: visible;\r\n transform: none;\r\n }\r\n\r\n :host([inline]) .menu-dropdown.show {\r\n display: block;\r\n pointer-events: auto;\r\n }\r\n\r\n @media (prefers-reduced-motion: reduce) {\r\n .menu-dropdown,\r\n .arrow {\r\n transition: none;\r\n }\r\n }\r\n `;\r\n\r\n /** Trigger label text (source: `title`, renamed because `title` is the native tooltip\r\n * attribute). Leave empty to show only `slot=\"icon\"`. */\r\n @property({ type: String })\r\n label = '';\r\n\r\n /** Renders the trigger as an `<a href>`. With panel content the click toggles the panel (as in\r\n * the source); with none it navigates. */\r\n @property({ type: String })\r\n href = '';\r\n\r\n @property({ type: String, attribute: 'icon-position' })\r\n iconPosition: 'left' | 'right' = 'left';\r\n\r\n /** Inline `width`/`height` of the `slot=\"icon\"` wrapper (source: `iconSize`, default `1.2rem`). */\r\n @property({ type: String, attribute: 'icon-size' })\r\n iconSize = '1.2rem';\r\n\r\n /** Inline `color` of the `slot=\"icon\"` wrapper (source: `iconColor`). */\r\n @property({ type: String, attribute: 'icon-color' })\r\n iconColor = 'currentColor';\r\n\r\n /** Which edge of the trigger the panel lines up with. */\r\n @property({ type: String })\r\n align: 'left' | 'right' = 'left';\r\n\r\n /** `target` of an `href` trigger (source default `_self`). */\r\n @property({ type: String })\r\n target = '_self';\r\n\r\n /** Extra class(es) on the trigger (source: `className`; a `class` on `st-dropdown` styles the host). */\r\n @property({ type: String, attribute: 'trigger-class-name' })\r\n triggerClassName = '';\r\n\r\n /** Extra class(es) on the panel (source: `itemGroupClassName`). */\r\n @property({ type: String, attribute: 'item-group-class-name' })\r\n itemGroupClassName = '';\r\n\r\n /** Unused, as in the source (`itemClassName` is never read there); kept for prop parity. */\r\n @property({ type: String, attribute: 'item-class-name' })\r\n itemClassName = '';\r\n\r\n @property({ type: Boolean, reflect: true })\r\n disabled = false;\r\n\r\n /** Highlights the trigger as the current section (e.g. the current page is one of its items). */\r\n @property({ type: Boolean, reflect: true })\r\n active = false;\r\n\r\n /** Opens the panel in the flow under the trigger instead of floating over the page (see the\r\n * styles). `st-header` sets this on its own menu's dropdowns while its hamburger panel shows. */\r\n @property({ type: Boolean, reflect: true })\r\n inline = false;\r\n\r\n /** Whether the panel is showing. Follows user toggles; may also be set from outside. */\r\n @property({ type: Boolean, reflect: true })\r\n open = false;\r\n\r\n /** Whether the default slot has content — the source's `children` check. */\r\n @state() private hasContent = false;\r\n\r\n /** Whether `slot=\"icon\"` has content. */\r\n @state() private hasIcon = false;\r\n\r\n connectedCallback(): void {\r\n super.connectedCallback();\r\n document.addEventListener('mousedown', this.closeOnOutsideMouseDown);\r\n window.addEventListener('keydown', this.closeOnEscape);\r\n }\r\n\r\n disconnectedCallback(): void {\r\n document.removeEventListener('mousedown', this.closeOnOutsideMouseDown);\r\n window.removeEventListener('keydown', this.closeOnEscape);\r\n if (Dropdown.openDropdown === this) Dropdown.openDropdown = null;\r\n super.disconnectedCallback();\r\n }\r\n\r\n protected updated(changed: Map<string, unknown>): void {\r\n if (!changed.has('open')) return;\r\n if (this.open) {\r\n const previous = Dropdown.openDropdown;\r\n Dropdown.openDropdown = this;\r\n if (previous && previous !== this) previous.setOpen(false);\r\n } else if (Dropdown.openDropdown === this) {\r\n Dropdown.openDropdown = null;\r\n }\r\n }\r\n\r\n private setOpen(open: boolean): void {\r\n if (this.open === open) return;\r\n this.open = open;\r\n this.dispatchEvent(new CustomEvent<DropdownToggleEventDetail>('dropdown-toggle', { detail: { open }, bubbles: true, composed: true }));\r\n }\r\n\r\n private closeOnOutsideMouseDown = (event: Event): void => {\r\n if (this.open && !event.composedPath().includes(this)) this.setOpen(false);\r\n };\r\n\r\n private closeOnEscape = (event: KeyboardEvent): void => {\r\n if (!this.open || event.key !== 'Escape') return;\r\n const focusWasInside = event.composedPath().includes(this);\r\n this.setOpen(false);\r\n if (focusWasInside) this.trigger()?.focus();\r\n };\r\n\r\n private trigger(): HTMLElement | null {\r\n return this.renderRoot.querySelector<HTMLElement>('.menu-title');\r\n }\r\n\r\n /** Keyboard-reachable entries in the panel, in order: menu items (they delegate focus to their\r\n * own link) plus any plain links/buttons/tabbable elements a caller slots in. */\r\n private panelItems(): HTMLElement[] {\r\n const slot = this.renderRoot.querySelector<HTMLSlotElement>('.menu-dropdown slot');\r\n if (!slot) return [];\r\n const selector =\r\n 'st-menu-item, st-menu-grid-item, a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\r\n const items: HTMLElement[] = [];\r\n for (const el of slot.assignedElements({ flatten: true })) {\r\n if (el.matches(selector)) items.push(el as HTMLElement);\r\n items.push(...Array.from(el.querySelectorAll<HTMLElement>(selector)));\r\n }\r\n return items;\r\n }\r\n\r\n /** Moves focus into the panel once it has become visible (it is `visibility: hidden` until the\r\n * `show` class lands, and hidden elements can't take focus). */\r\n private focusItem(pick: (items: HTMLElement[]) => HTMLElement | undefined): void {\r\n void this.updateComplete.then(() => pick(this.panelItems())?.focus());\r\n }\r\n\r\n /** Enter/Space/ArrowDown open the panel and focus its first entry; ArrowUp its last. */\r\n private onTitleKeyDown = (event: KeyboardEvent): void => {\r\n if (!this.hasContent || this.disabled) return;\r\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n this.setOpen(true);\r\n this.focusItem((items) => items[0]);\r\n } else if (event.key === 'ArrowUp') {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n this.setOpen(true);\r\n this.focusItem((items) => items[items.length - 1]);\r\n }\r\n };\r\n\r\n /** Arrow keys/Home/End move between the panel's entries; Tab closes the panel and moves on. */\r\n private onPanelKeyDown = (event: KeyboardEvent): void => {\r\n if (event.key === 'Tab') {\r\n this.setOpen(false);\r\n return;\r\n }\r\n if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) return;\r\n const items = this.panelItems();\r\n if (items.length === 0) return;\r\n event.preventDefault();\r\n event.stopPropagation();\r\n const path = event.composedPath();\r\n const current = items.findIndex((item) => path.includes(item));\r\n const last = items.length - 1;\r\n const next =\r\n event.key === 'Home'\r\n ? 0\r\n : event.key === 'End'\r\n ? last\r\n : event.key === 'ArrowDown'\r\n ? current < 0 || current === last ? 0 : current + 1\r\n : current <= 0 ? last : current - 1;\r\n items[next].focus();\r\n };\r\n\r\n private onTitleClick = (event: Event): void => {\r\n event.stopPropagation();\r\n if (!this.hasContent) return;\r\n event.preventDefault();\r\n this.setOpen(!this.open);\r\n };\r\n\r\n /** A click anywhere inside the panel closes it, as in the source. */\r\n private onPanelClick = (): void => {\r\n this.setOpen(false);\r\n };\r\n\r\n private onContentSlotChange = (event: Event): void => {\r\n this.hasContent = (event.target as HTMLSlotElement).assignedNodes({ flatten: true }).some(\r\n (node) => node.nodeType === Node.ELEMENT_NODE || !!node.textContent?.trim()\r\n );\r\n };\r\n\r\n private onIconSlotChange = (event: Event): void => {\r\n this.hasIcon = (event.target as HTMLSlotElement).assignedNodes({ flatten: true }).length > 0;\r\n };\r\n\r\n render() {\r\n const icon = html`<span\r\n class=\"icon ${this.iconPosition === 'left' ? 'icon-left' : 'icon-right'}\"\r\n ?hidden=${!this.hasIcon}\r\n style=\"width:${this.iconSize};height:${this.iconSize};color:${this.iconColor}\"\r\n ><slot name=\"icon\" @slotchange=${this.onIconSlotChange}></slot\r\n ></span>`;\r\n const titleContent = html`\r\n ${this.iconPosition === 'left' ? icon : nothing} ${this.label ? html`<span part=\"label\">${this.label}</span>` : nothing}\r\n ${this.iconPosition === 'right' ? icon : nothing}\r\n ${this.hasContent ? html`<span class=\"arrow\" part=\"caret\" aria-hidden=\"true\">▼</span>` : nothing}\r\n `;\r\n const titleClass = `menu-title ${this.triggerClassName} ${this.open ? 'active' : ''}`;\r\n\r\n return html`\r\n <div class=\"menu\" part=\"menu\">\r\n ${this.href\r\n ? html`\r\n <a\r\n part=\"trigger\"\r\n class=${titleClass}\r\n href=${this.href}\r\n target=${this.target}\r\n aria-haspopup=${this.hasContent ? 'true' : 'false'}\r\n aria-expanded=${this.open}\r\n aria-disabled=${this.disabled ? 'true' : nothing}\r\n @click=${this.onTitleClick}\r\n @keydown=${this.onTitleKeyDown}\r\n >\r\n ${titleContent}\r\n </a>\r\n `\r\n : html`\r\n <button\r\n part=\"trigger\"\r\n class=${titleClass}\r\n type=\"button\"\r\n aria-haspopup=\"true\"\r\n aria-expanded=${this.open}\r\n ?disabled=${this.disabled}\r\n @click=${this.onTitleClick}\r\n @keydown=${this.onTitleKeyDown}\r\n >\r\n ${titleContent}\r\n </button>\r\n `}\r\n <div\r\n part=\"panel\"\r\n class=\"menu-dropdown align-${this.align} ${this.itemGroupClassName} ${this.open ? 'show' : ''}\"\r\n role=\"menu\"\r\n @click=${this.onPanelClick}\r\n @keydown=${this.onPanelKeyDown}\r\n >\r\n <slot @slotchange=${this.onContentSlotChange}></slot>\r\n </div>\r\n </div>\r\n `;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ctaLinkStyles,
|
|
3
|
+
paddingScaleStyles
|
|
4
|
+
} from "./chunk-3VE6VFJ4.js";
|
|
5
|
+
import {
|
|
6
|
+
controlBaseStyles,
|
|
7
|
+
rootFontFamilyStyles
|
|
8
|
+
} from "./chunk-RHAOQ6K4.js";
|
|
9
|
+
import {
|
|
10
|
+
__decorateClass
|
|
11
|
+
} from "./chunk-EUXUH3YW.js";
|
|
12
|
+
|
|
13
|
+
// src/hero/Hero.ts
|
|
14
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
15
|
+
import { customElement, property } from "lit/decorators.js";
|
|
16
|
+
var Hero = class extends LitElement {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
this.title = "";
|
|
20
|
+
this.subtitle = "";
|
|
21
|
+
this.imageSrc = "";
|
|
22
|
+
this.actionLabel = "";
|
|
23
|
+
this.href = "";
|
|
24
|
+
this.textAlignment = "left";
|
|
25
|
+
this.padding = "none";
|
|
26
|
+
this.inverted = false;
|
|
27
|
+
this.focalPoint = "center";
|
|
28
|
+
this.onClick = (event) => {
|
|
29
|
+
const action = new CustomEvent("action", {
|
|
30
|
+
detail: { href: this.href },
|
|
31
|
+
bubbles: true,
|
|
32
|
+
composed: true,
|
|
33
|
+
cancelable: true
|
|
34
|
+
});
|
|
35
|
+
this.dispatchEvent(action);
|
|
36
|
+
if (action.defaultPrevented) event.preventDefault();
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
willUpdate() {
|
|
40
|
+
this.style.backgroundImage = this.imageSrc ? `url(${JSON.stringify(this.imageSrc)})` : "";
|
|
41
|
+
this.style.setProperty("--st-hero-position", Hero.positions[this.focalPoint] ?? Hero.positions.center);
|
|
42
|
+
}
|
|
43
|
+
render() {
|
|
44
|
+
return html`
|
|
45
|
+
<div class="overlay" part="overlay"></div>
|
|
46
|
+
<div class="content" part="content">
|
|
47
|
+
<h1 class="title" part="title">${this.title}</h1>
|
|
48
|
+
${this.subtitle ? html`<p class="subtitle" part="subtitle">${this.subtitle}</p>` : nothing}
|
|
49
|
+
${this.actionLabel ? html`<div class="actions" part="actions">
|
|
50
|
+
<a class="cta" part="action" href=${this.href || nothing} @click=${this.onClick}>${this.actionLabel}</a>
|
|
51
|
+
</div>` : nothing}
|
|
52
|
+
<slot></slot>
|
|
53
|
+
</div>
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
Hero.styles = css`
|
|
58
|
+
${controlBaseStyles}
|
|
59
|
+
${paddingScaleStyles}
|
|
60
|
+
|
|
61
|
+
@keyframes st-hero-zoom {
|
|
62
|
+
from { opacity: 0; transform: scale(1.1); }
|
|
63
|
+
to { opacity: 1; transform: scale(1); }
|
|
64
|
+
}
|
|
65
|
+
@keyframes st-hero-rise {
|
|
66
|
+
from { opacity: 0; transform: translateY(30px); }
|
|
67
|
+
to { opacity: 1; transform: none; }
|
|
68
|
+
}
|
|
69
|
+
@keyframes st-hero-fade {
|
|
70
|
+
from { opacity: 0; }
|
|
71
|
+
to { opacity: 1; }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
:host {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
position: relative;
|
|
79
|
+
overflow: hidden;
|
|
80
|
+
width: 100%;
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
background-color: var(--st-color-text-strong);
|
|
83
|
+
background-repeat: no-repeat;
|
|
84
|
+
background-size: cover;
|
|
85
|
+
background-position: var(--st-hero-position, center center);
|
|
86
|
+
color: var(--st-color-on-solid);
|
|
87
|
+
${rootFontFamilyStyles}
|
|
88
|
+
animation: st-hero-zoom 1.5s ease-out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:host([inverted]) {
|
|
92
|
+
background-color: var(--st-color-surface);
|
|
93
|
+
color: var(--st-color-text-strong);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.overlay {
|
|
97
|
+
position: absolute;
|
|
98
|
+
inset: 0;
|
|
99
|
+
z-index: 1;
|
|
100
|
+
background: linear-gradient(
|
|
101
|
+
to bottom,
|
|
102
|
+
color-mix(in oklch, var(--st-color-text-strong) 40%, transparent),
|
|
103
|
+
color-mix(in oklch, var(--st-color-text-strong) 20%, transparent),
|
|
104
|
+
color-mix(in oklch, var(--st-color-text-strong) 40%, transparent)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
:host([inverted]) .overlay {
|
|
109
|
+
display: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.content {
|
|
113
|
+
display: flex;
|
|
114
|
+
flex-direction: column;
|
|
115
|
+
gap: var(--st-space-20);
|
|
116
|
+
position: relative;
|
|
117
|
+
z-index: 2;
|
|
118
|
+
width: 80%;
|
|
119
|
+
margin: 0 auto;
|
|
120
|
+
text-align: center;
|
|
121
|
+
align-items: center;
|
|
122
|
+
/* Set here, not only on :host: text sits on the image/overlay, so a colour class the page puts
|
|
123
|
+
on the host (e.g. a theme text colour) must not turn it dark. */
|
|
124
|
+
color: var(--st-hero-text-color, var(--st-color-on-solid));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:host([inverted]) .content {
|
|
128
|
+
color: var(--st-hero-text-color, var(--st-color-text-strong));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
:host([text-alignment='left']) .content { text-align: left; align-items: flex-start; }
|
|
132
|
+
:host([text-alignment='right']) .content { text-align: right; align-items: flex-end; }
|
|
133
|
+
:host([text-alignment='justify']) .content { text-align: justify; align-items: stretch; }
|
|
134
|
+
|
|
135
|
+
.title {
|
|
136
|
+
font-size: var(--st-cms-text-display);
|
|
137
|
+
font-weight: var(--st-font-weight-bold);
|
|
138
|
+
line-height: 1.1;
|
|
139
|
+
color: inherit;
|
|
140
|
+
margin: 0;
|
|
141
|
+
animation: st-hero-rise 0.8s 0.3s ease-out backwards;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.subtitle {
|
|
145
|
+
font-size: var(--st-cms-text-xl);
|
|
146
|
+
line-height: 1.5;
|
|
147
|
+
color: inherit;
|
|
148
|
+
margin: 0;
|
|
149
|
+
animation: st-hero-fade 0.8s 0.5s ease-out backwards;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.actions {
|
|
153
|
+
display: flex;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
width: 100%;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.cta {
|
|
159
|
+
${ctaLinkStyles}
|
|
160
|
+
background: var(--st-button-primary-bg);
|
|
161
|
+
color: var(--st-button-primary-color);
|
|
162
|
+
font-weight: var(--st-button-font-weight);
|
|
163
|
+
font-size: var(--st-text-md);
|
|
164
|
+
animation: st-hero-rise 0.6s 0.7s ease-out backwards;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.cta:hover {
|
|
168
|
+
transform: scale(1.05);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.cta:active {
|
|
172
|
+
transform: scale(0.98);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@media (min-width: 768px) {
|
|
176
|
+
.content { padding: var(--st-space-32); }
|
|
177
|
+
}
|
|
178
|
+
@media (min-width: 1024px) {
|
|
179
|
+
.content { padding: var(--st-space-48); }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@media (prefers-reduced-motion: reduce) {
|
|
183
|
+
:host, .title, .subtitle, .cta { animation: none; }
|
|
184
|
+
.cta { transition: none; }
|
|
185
|
+
}
|
|
186
|
+
`;
|
|
187
|
+
Hero.positions = {
|
|
188
|
+
top: "center top",
|
|
189
|
+
left: "left center",
|
|
190
|
+
right: "right center",
|
|
191
|
+
bottom: "center bottom",
|
|
192
|
+
center: "center center"
|
|
193
|
+
};
|
|
194
|
+
__decorateClass([
|
|
195
|
+
property({ type: String })
|
|
196
|
+
], Hero.prototype, "title", 2);
|
|
197
|
+
__decorateClass([
|
|
198
|
+
property({ type: String })
|
|
199
|
+
], Hero.prototype, "subtitle", 2);
|
|
200
|
+
__decorateClass([
|
|
201
|
+
property({ type: String, attribute: "image-src" })
|
|
202
|
+
], Hero.prototype, "imageSrc", 2);
|
|
203
|
+
__decorateClass([
|
|
204
|
+
property({ type: String, attribute: "action-label" })
|
|
205
|
+
], Hero.prototype, "actionLabel", 2);
|
|
206
|
+
__decorateClass([
|
|
207
|
+
property({ type: String })
|
|
208
|
+
], Hero.prototype, "href", 2);
|
|
209
|
+
__decorateClass([
|
|
210
|
+
property({ type: String, reflect: true, attribute: "text-alignment" })
|
|
211
|
+
], Hero.prototype, "textAlignment", 2);
|
|
212
|
+
__decorateClass([
|
|
213
|
+
property({ type: String, reflect: true })
|
|
214
|
+
], Hero.prototype, "padding", 2);
|
|
215
|
+
__decorateClass([
|
|
216
|
+
property({ type: Boolean, reflect: true })
|
|
217
|
+
], Hero.prototype, "inverted", 2);
|
|
218
|
+
__decorateClass([
|
|
219
|
+
property({ type: String, attribute: "focal-point" })
|
|
220
|
+
], Hero.prototype, "focalPoint", 2);
|
|
221
|
+
Hero = __decorateClass([
|
|
222
|
+
customElement("st-hero")
|
|
223
|
+
], Hero);
|
|
224
|
+
|
|
225
|
+
export {
|
|
226
|
+
Hero
|
|
227
|
+
};
|
|
228
|
+
//# sourceMappingURL=chunk-U7YKCCJG.js.map
|