@sankhyatronics/sankhya-cms 0.1.1 → 0.1.2
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/custom-elements.json +1843 -952
- package/dist/carousel/index.d.ts +38 -2
- package/dist/carousel/index.js +1 -1
- package/dist/chunk-47RPYNFG.js +527 -0
- package/dist/chunk-47RPYNFG.js.map +1 -0
- package/dist/chunk-5DNVCMOO.js +509 -0
- package/dist/chunk-5DNVCMOO.js.map +1 -0
- package/dist/{chunk-RMIJVTFS.js → chunk-FWIG6GEZ.js} +8 -4
- package/dist/chunk-FWIG6GEZ.js.map +1 -0
- package/dist/{chunk-U7YKCCJG.js → chunk-K4ZICRZL.js} +134 -14
- package/dist/chunk-K4ZICRZL.js.map +1 -0
- package/dist/chunk-N63TVWK3.js +203 -0
- package/dist/chunk-N63TVWK3.js.map +1 -0
- package/dist/{chunk-23IHTBAB.js → chunk-PKTRYUGP.js} +108 -2
- package/dist/chunk-PKTRYUGP.js.map +1 -0
- package/dist/{chunk-LHU23TF6.js → chunk-TH5NZXA7.js} +5 -4
- package/dist/chunk-TH5NZXA7.js.map +1 -0
- package/dist/dropdown/index.js +1 -1
- package/dist/header/index.d.ts +18 -0
- package/dist/header/index.js +1 -1
- package/dist/hero/index.d.ts +29 -4
- package/dist/hero/index.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +23 -19
- package/dist/menu-item/index.js +1 -1
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/section/index.d.ts +33 -0
- package/dist/section/index.js +10 -0
- package/dist/section/index.js.map +1 -0
- package/dist/testimonials/index.d.ts +62 -6
- package/dist/testimonials/index.js +2 -1
- package/package.json +6 -2
- package/dist/chunk-23IHTBAB.js.map +0 -1
- package/dist/chunk-LHU23TF6.js.map +0 -1
- package/dist/chunk-RMIJVTFS.js.map +0 -1
- package/dist/chunk-U7YKCCJG.js.map +0 -1
- package/dist/chunk-WCO7JAIK.js +0 -195
- package/dist/chunk-WCO7JAIK.js.map +0 -1
- package/dist/chunk-XHBC23TV.js +0 -254
- package/dist/chunk-XHBC23TV.js.map +0 -1
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chevronIcon
|
|
3
|
+
} from "./chunk-3TCP25JG.js";
|
|
4
|
+
import {
|
|
5
|
+
invertedSectionTextStyles,
|
|
6
|
+
reducedMotionStyles,
|
|
7
|
+
sectionHeaderStyles,
|
|
8
|
+
sectionSubtitleStyles,
|
|
9
|
+
sectionTitleStyles
|
|
10
|
+
} from "./chunk-3VE6VFJ4.js";
|
|
11
|
+
import {
|
|
12
|
+
buttonResetStyles,
|
|
13
|
+
cardShellStyles,
|
|
14
|
+
controlBaseStyles,
|
|
15
|
+
rootFontFamilyStyles
|
|
16
|
+
} from "./chunk-RHAOQ6K4.js";
|
|
17
|
+
import {
|
|
18
|
+
__decorateClass
|
|
19
|
+
} from "./chunk-EUXUH3YW.js";
|
|
20
|
+
|
|
21
|
+
// src/testimonials/Testimonials.ts
|
|
22
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
23
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
24
|
+
import "iconify-icon";
|
|
25
|
+
var Testimonials = class extends LitElement {
|
|
26
|
+
constructor() {
|
|
27
|
+
super(...arguments);
|
|
28
|
+
this.title = "";
|
|
29
|
+
this.subtitle = "";
|
|
30
|
+
this.items = [];
|
|
31
|
+
this.inverted = false;
|
|
32
|
+
this.layout = "grid";
|
|
33
|
+
this.linkLabel = "See full story";
|
|
34
|
+
this.actionLabel = "";
|
|
35
|
+
this.actionHref = "";
|
|
36
|
+
this.atStart = true;
|
|
37
|
+
this.atEnd = false;
|
|
38
|
+
this.syncArrows = () => {
|
|
39
|
+
const track = this.track;
|
|
40
|
+
if (!track) return;
|
|
41
|
+
this.atStart = track.scrollLeft <= 1;
|
|
42
|
+
this.atEnd = track.scrollLeft + track.clientWidth >= track.scrollWidth - 1;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
get track() {
|
|
46
|
+
return this.renderRoot.querySelector(".track");
|
|
47
|
+
}
|
|
48
|
+
updated(changed) {
|
|
49
|
+
if (changed.has("layout") || changed.has("items")) {
|
|
50
|
+
this.resizeObserver?.disconnect();
|
|
51
|
+
const track = this.track;
|
|
52
|
+
if (track) {
|
|
53
|
+
this.resizeObserver = new ResizeObserver(this.syncArrows);
|
|
54
|
+
this.resizeObserver.observe(track);
|
|
55
|
+
}
|
|
56
|
+
this.syncArrows();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
disconnectedCallback() {
|
|
60
|
+
this.resizeObserver?.disconnect();
|
|
61
|
+
super.disconnectedCallback();
|
|
62
|
+
}
|
|
63
|
+
scrollByCard(direction) {
|
|
64
|
+
const track = this.track;
|
|
65
|
+
const card = track?.querySelector(".card");
|
|
66
|
+
if (!track || !card) return;
|
|
67
|
+
const gap = parseFloat(getComputedStyle(track).columnGap) || 0;
|
|
68
|
+
track.scrollBy({ left: direction * (card.offsetWidth + gap) });
|
|
69
|
+
}
|
|
70
|
+
onLinkClick(event, href) {
|
|
71
|
+
const link = new CustomEvent("testimonials-link", {
|
|
72
|
+
detail: { href },
|
|
73
|
+
bubbles: true,
|
|
74
|
+
composed: true,
|
|
75
|
+
cancelable: true
|
|
76
|
+
});
|
|
77
|
+
this.dispatchEvent(link);
|
|
78
|
+
if (link.defaultPrevented) event.preventDefault();
|
|
79
|
+
}
|
|
80
|
+
renderLink(label, href, className, part) {
|
|
81
|
+
return html`<a class="link ${className}" part=${part} href=${href} @click=${(event) => this.onLinkClick(event, href)}>
|
|
82
|
+
${label} ${chevronIcon("right", 14)}
|
|
83
|
+
</a>`;
|
|
84
|
+
}
|
|
85
|
+
renderItem(item) {
|
|
86
|
+
const story = !!(item.headline || item.logoSrc);
|
|
87
|
+
const rating = item.rating ?? (item.quote && !story ? 5 : 0);
|
|
88
|
+
const metrics = item.metrics ?? [];
|
|
89
|
+
return html`
|
|
90
|
+
<article class="card" part="card">
|
|
91
|
+
${item.mediaSrc ? html`<div class="media" part="media"><img src=${item.mediaSrc} alt="" loading="lazy" /></div>` : nothing}
|
|
92
|
+
<div class="body" part="body">
|
|
93
|
+
${item.logoSrc ? html`<img class="logo" part="logo" src=${item.logoSrc} alt=${item.company || item.name || ""} />` : nothing}
|
|
94
|
+
${item.headline ? html`<h3 class="headline" part="headline">${item.headline}</h3>` : nothing}
|
|
95
|
+
${rating > 0 ? html`<div class="stars" part="stars" role="img" aria-label="${rating} / 5">
|
|
96
|
+
${Array.from({ length: rating }, () => html`<iconify-icon icon="mdi:star"></iconify-icon>`)}
|
|
97
|
+
</div>` : nothing}
|
|
98
|
+
${item.quote ? html`<blockquote class="quote" part="quote">"${item.quote}"</blockquote>` : nothing}
|
|
99
|
+
${metrics.length ? html`<dl class="metrics" part="metrics">
|
|
100
|
+
${metrics.map((metric) => html`<div class="metric"><dt>${metric.label}</dt><dd>${metric.value}</dd></div>`)}
|
|
101
|
+
</dl>` : nothing}
|
|
102
|
+
${item.name ? html`<div class="author" part="author">
|
|
103
|
+
${item.imageSrc ? html`<img class="avatar" part="avatar" src=${item.imageSrc} alt=${item.name} />` : html`<div class="avatar avatar-placeholder" part="avatar">${item.name.charAt(0)}</div>`}
|
|
104
|
+
<span class="author-info">
|
|
105
|
+
<span class="name" part="name">${item.name}</span>
|
|
106
|
+
${item.role || item.company ? html`<span class="role" part="role">${[item.role, item.company].filter(Boolean).join(" at ")}</span>` : nothing}
|
|
107
|
+
</span>
|
|
108
|
+
</div>` : nothing}
|
|
109
|
+
${item.href ? this.renderLink(item.linkLabel || this.linkLabel, item.href, "", "link") : nothing}
|
|
110
|
+
</div>
|
|
111
|
+
</article>
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
render() {
|
|
115
|
+
const carousel = this.layout === "carousel";
|
|
116
|
+
const sectionLink = this.actionLabel && this.actionHref;
|
|
117
|
+
return html`
|
|
118
|
+
<div class="container" part="container">
|
|
119
|
+
<div class="header" part="header">
|
|
120
|
+
<div>
|
|
121
|
+
<h2 class="title" part="title">${this.title}</h2>
|
|
122
|
+
${this.subtitle ? html`<p class="subtitle" part="subtitle">${this.subtitle}</p>` : nothing}
|
|
123
|
+
</div>
|
|
124
|
+
${carousel ? html`<div class="controls" part="controls">
|
|
125
|
+
${sectionLink ? this.renderLink(this.actionLabel, this.actionHref, "section-link", "action") : nothing}
|
|
126
|
+
<button class="arrow" part="arrow prev" type="button" aria-label="Previous" ?disabled=${this.atStart} @click=${() => this.scrollByCard(-1)}>
|
|
127
|
+
${chevronIcon("left", 16)}
|
|
128
|
+
</button>
|
|
129
|
+
<button class="arrow" part="arrow next" type="button" aria-label="Next" ?disabled=${this.atEnd} @click=${() => this.scrollByCard(1)}>
|
|
130
|
+
${chevronIcon("right", 16)}
|
|
131
|
+
</button>
|
|
132
|
+
</div>` : nothing}
|
|
133
|
+
</div>
|
|
134
|
+
${carousel ? html`<div class="track" part="track" role="region" aria-label=${this.title || "Stories"} tabindex="0" @scroll=${this.syncArrows}>
|
|
135
|
+
${this.items.map((item) => this.renderItem(item))}
|
|
136
|
+
</div>` : html`<div class="grid" part="grid">${this.items.map((item) => this.renderItem(item))}</div>`}
|
|
137
|
+
${sectionLink ? html`<div class="section-footer">${this.renderLink(this.actionLabel, this.actionHref, carousel ? "footer-link" : "", "action")}</div>` : nothing}
|
|
138
|
+
</div>
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
Testimonials.styles = css`
|
|
143
|
+
${controlBaseStyles}
|
|
144
|
+
|
|
145
|
+
:host {
|
|
146
|
+
display: block;
|
|
147
|
+
box-sizing: border-box;
|
|
148
|
+
padding: var(--st-space-48) var(--st-space-24);
|
|
149
|
+
background-color: var(--st-color-subtle);
|
|
150
|
+
${rootFontFamilyStyles}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
:host([inverted]) {
|
|
154
|
+
background-color: var(--st-color-text-strong);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.container {
|
|
158
|
+
max-width: var(--st-cms-container-max);
|
|
159
|
+
margin: 0 auto;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.header {
|
|
163
|
+
${sectionHeaderStyles}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.title {
|
|
167
|
+
${sectionTitleStyles}
|
|
168
|
+
margin-bottom: var(--st-space-16);
|
|
169
|
+
letter-spacing: -0.02em;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.subtitle {
|
|
173
|
+
${sectionSubtitleStyles}
|
|
174
|
+
margin: 0 auto;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
${invertedSectionTextStyles}
|
|
178
|
+
|
|
179
|
+
.grid {
|
|
180
|
+
display: grid;
|
|
181
|
+
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
|
182
|
+
gap: var(--st-space-32);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.card {
|
|
186
|
+
${cardShellStyles}
|
|
187
|
+
display: flex;
|
|
188
|
+
flex-direction: column;
|
|
189
|
+
margin: 0;
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
box-shadow: 0 1px 3px var(--st-shadow-color-sm);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.media {
|
|
195
|
+
aspect-ratio: 16 / 9;
|
|
196
|
+
background: var(--st-color-divider);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.media img {
|
|
200
|
+
display: block;
|
|
201
|
+
width: 100%;
|
|
202
|
+
height: 100%;
|
|
203
|
+
object-fit: cover;
|
|
204
|
+
transition: transform 0.4s ease;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.card:hover .media img {
|
|
208
|
+
transform: scale(1.03);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.body {
|
|
212
|
+
flex: 1;
|
|
213
|
+
display: flex;
|
|
214
|
+
flex-direction: column;
|
|
215
|
+
gap: var(--st-space-24);
|
|
216
|
+
padding: var(--st-space-24);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.logo {
|
|
220
|
+
display: block;
|
|
221
|
+
height: 32px;
|
|
222
|
+
width: auto;
|
|
223
|
+
max-width: 60%;
|
|
224
|
+
object-fit: contain;
|
|
225
|
+
object-position: left center;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.headline {
|
|
229
|
+
margin: 0;
|
|
230
|
+
font-size: var(--st-text-xl);
|
|
231
|
+
font-weight: var(--st-font-weight-semibold);
|
|
232
|
+
line-height: 1.35;
|
|
233
|
+
color: var(--st-color-text-strong);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.stars {
|
|
237
|
+
display: flex;
|
|
238
|
+
gap: var(--st-space-4);
|
|
239
|
+
color: var(--st-favorite-accent);
|
|
240
|
+
font-size: var(--st-size-20);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.quote {
|
|
244
|
+
font-size: var(--st-text-md);
|
|
245
|
+
line-height: 1.6;
|
|
246
|
+
color: var(--st-color-text);
|
|
247
|
+
font-style: italic;
|
|
248
|
+
margin: 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.metrics {
|
|
252
|
+
display: flex;
|
|
253
|
+
flex-wrap: wrap;
|
|
254
|
+
gap: var(--st-space-16) var(--st-space-32);
|
|
255
|
+
margin: 0;
|
|
256
|
+
padding-top: var(--st-space-16);
|
|
257
|
+
border-top: 1px solid var(--st-color-border);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.metric {
|
|
261
|
+
display: flex;
|
|
262
|
+
flex-direction: column-reverse;
|
|
263
|
+
min-width: 6rem;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.metric dt {
|
|
267
|
+
font-size: var(--st-text-sm);
|
|
268
|
+
color: var(--st-color-text-muted);
|
|
269
|
+
line-height: 1.4;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.metric dd {
|
|
273
|
+
margin: 0;
|
|
274
|
+
font-size: var(--st-cms-text-4xl);
|
|
275
|
+
font-weight: var(--st-font-weight-bold);
|
|
276
|
+
letter-spacing: -0.02em;
|
|
277
|
+
line-height: 1.1;
|
|
278
|
+
color: var(--st-color-text-strong);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.author {
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
gap: var(--st-space-12);
|
|
285
|
+
margin-top: auto;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.avatar {
|
|
289
|
+
width: var(--st-space-48);
|
|
290
|
+
height: var(--st-space-48);
|
|
291
|
+
border-radius: var(--st-radius-circle);
|
|
292
|
+
object-fit: cover;
|
|
293
|
+
background-color: var(--st-color-divider);
|
|
294
|
+
flex-shrink: 0;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.avatar-placeholder {
|
|
298
|
+
display: flex;
|
|
299
|
+
align-items: center;
|
|
300
|
+
justify-content: center;
|
|
301
|
+
color: var(--st-color-text-muted);
|
|
302
|
+
font-weight: var(--st-font-weight-semibold);
|
|
303
|
+
font-size: var(--st-text-xl);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.author-info {
|
|
307
|
+
display: flex;
|
|
308
|
+
flex-direction: column;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.name {
|
|
312
|
+
font-weight: var(--st-font-weight-semibold);
|
|
313
|
+
color: var(--st-color-text-strong);
|
|
314
|
+
font-size: var(--st-text-xl);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.role {
|
|
318
|
+
font-size: var(--st-text-sm);
|
|
319
|
+
color: var(--st-color-text-faint);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.link {
|
|
323
|
+
display: inline-flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
gap: var(--st-space-8);
|
|
326
|
+
margin-top: auto;
|
|
327
|
+
font-weight: var(--st-font-weight-semibold);
|
|
328
|
+
color: var(--st-color-text-strong);
|
|
329
|
+
text-decoration: none;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.author + .link {
|
|
333
|
+
margin-top: 0;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.link:hover {
|
|
337
|
+
text-decoration: underline;
|
|
338
|
+
text-underline-offset: 3px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.link svg {
|
|
342
|
+
transition: transform 0.2s ease;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.link:hover svg {
|
|
346
|
+
transform: translateX(3px);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* ---- layout="carousel" ---- */
|
|
350
|
+
|
|
351
|
+
:host([layout='carousel']) .header {
|
|
352
|
+
display: flex;
|
|
353
|
+
align-items: flex-end;
|
|
354
|
+
justify-content: space-between;
|
|
355
|
+
gap: var(--st-space-24);
|
|
356
|
+
text-align: left;
|
|
357
|
+
margin-bottom: var(--st-space-32);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
:host([layout='carousel']) .subtitle {
|
|
361
|
+
margin: 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.controls {
|
|
365
|
+
display: flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
gap: var(--st-space-12);
|
|
368
|
+
flex: none;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.section-link {
|
|
372
|
+
margin-right: var(--st-space-12);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
:host([inverted]) .section-link {
|
|
376
|
+
color: var(--st-color-on-solid);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.arrow {
|
|
380
|
+
${buttonResetStyles}
|
|
381
|
+
display: inline-flex;
|
|
382
|
+
align-items: center;
|
|
383
|
+
justify-content: center;
|
|
384
|
+
width: var(--st-size-40);
|
|
385
|
+
height: var(--st-size-40);
|
|
386
|
+
border-radius: var(--st-radius-circle);
|
|
387
|
+
border: 1px solid var(--st-color-border-strong);
|
|
388
|
+
color: var(--st-color-text-strong);
|
|
389
|
+
background: var(--st-color-surface);
|
|
390
|
+
transition:
|
|
391
|
+
background 0.2s ease,
|
|
392
|
+
opacity 0.2s ease;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.arrow:hover:not(:disabled) {
|
|
396
|
+
background: var(--st-color-hover);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.arrow:disabled {
|
|
400
|
+
opacity: 0.4;
|
|
401
|
+
cursor: default;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.track {
|
|
405
|
+
display: flex;
|
|
406
|
+
gap: var(--st-space-24);
|
|
407
|
+
overflow-x: auto;
|
|
408
|
+
scroll-snap-type: x mandatory;
|
|
409
|
+
scroll-behavior: smooth;
|
|
410
|
+
scrollbar-width: none;
|
|
411
|
+
/* Room for the cards' shadow and focus rings. */
|
|
412
|
+
padding: var(--st-space-4) var(--st-space-4) var(--st-space-12);
|
|
413
|
+
margin: calc(-1 * var(--st-space-4));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.track::-webkit-scrollbar {
|
|
417
|
+
display: none;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.track > .card {
|
|
421
|
+
flex: 0 0 min(380px, 85%);
|
|
422
|
+
scroll-snap-align: start;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/* The section link sits under the grid; in the carousel it is next to the arrows instead, and
|
|
426
|
+
only drops below the row on a narrow screen. */
|
|
427
|
+
.section-footer {
|
|
428
|
+
margin-top: var(--st-space-32);
|
|
429
|
+
text-align: center;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
:host([inverted]) .section-footer .link {
|
|
433
|
+
color: var(--st-color-on-solid);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
:host([layout='carousel']) .section-footer {
|
|
437
|
+
margin-top: 0;
|
|
438
|
+
text-align: left;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.footer-link {
|
|
442
|
+
display: none;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
@media (max-width: 768px) {
|
|
446
|
+
:host {
|
|
447
|
+
padding: var(--st-space-40) var(--st-space-20);
|
|
448
|
+
}
|
|
449
|
+
.grid {
|
|
450
|
+
grid-template-columns: 1fr;
|
|
451
|
+
}
|
|
452
|
+
:host([layout='carousel']) .header {
|
|
453
|
+
flex-direction: column;
|
|
454
|
+
align-items: flex-start;
|
|
455
|
+
}
|
|
456
|
+
.section-link {
|
|
457
|
+
display: none;
|
|
458
|
+
}
|
|
459
|
+
.footer-link {
|
|
460
|
+
display: inline-flex;
|
|
461
|
+
margin-top: var(--st-space-24);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
${reducedMotionStyles}
|
|
466
|
+
@media (prefers-reduced-motion: reduce) {
|
|
467
|
+
.track {
|
|
468
|
+
scroll-behavior: auto;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
`;
|
|
472
|
+
__decorateClass([
|
|
473
|
+
property({ type: String })
|
|
474
|
+
], Testimonials.prototype, "title", 2);
|
|
475
|
+
__decorateClass([
|
|
476
|
+
property({ type: String })
|
|
477
|
+
], Testimonials.prototype, "subtitle", 2);
|
|
478
|
+
__decorateClass([
|
|
479
|
+
property({ attribute: false })
|
|
480
|
+
], Testimonials.prototype, "items", 2);
|
|
481
|
+
__decorateClass([
|
|
482
|
+
property({ type: Boolean, reflect: true })
|
|
483
|
+
], Testimonials.prototype, "inverted", 2);
|
|
484
|
+
__decorateClass([
|
|
485
|
+
property({ type: String, reflect: true })
|
|
486
|
+
], Testimonials.prototype, "layout", 2);
|
|
487
|
+
__decorateClass([
|
|
488
|
+
property({ type: String, attribute: "link-label" })
|
|
489
|
+
], Testimonials.prototype, "linkLabel", 2);
|
|
490
|
+
__decorateClass([
|
|
491
|
+
property({ type: String, attribute: "action-label" })
|
|
492
|
+
], Testimonials.prototype, "actionLabel", 2);
|
|
493
|
+
__decorateClass([
|
|
494
|
+
property({ type: String, attribute: "action-href" })
|
|
495
|
+
], Testimonials.prototype, "actionHref", 2);
|
|
496
|
+
__decorateClass([
|
|
497
|
+
state()
|
|
498
|
+
], Testimonials.prototype, "atStart", 2);
|
|
499
|
+
__decorateClass([
|
|
500
|
+
state()
|
|
501
|
+
], Testimonials.prototype, "atEnd", 2);
|
|
502
|
+
Testimonials = __decorateClass([
|
|
503
|
+
customElement("st-testimonials")
|
|
504
|
+
], Testimonials);
|
|
505
|
+
|
|
506
|
+
export {
|
|
507
|
+
Testimonials
|
|
508
|
+
};
|
|
509
|
+
//# sourceMappingURL=chunk-5DNVCMOO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testimonials/Testimonials.ts"],"sourcesContent":["import { LitElement, css, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport 'iconify-icon';\n\nimport { chevronIcon } from '../shared/icons';\nimport { buttonResetStyles, cardShellStyles, controlBaseStyles, rootFontFamilyStyles } from '../shared/styles';\nimport { sectionSubtitleStyles, sectionTitleStyles, invertedSectionTextStyles, sectionHeaderStyles, reducedMotionStyles } from '../shared/section-styles';\nimport type { TestimonialsLinkEventDetail } from './TestimonialsEvents';\n\nexport interface TestimonialMetric {\n /** The figure, e.g. `100M` or `85%`. */\n value: string;\n label: string;\n}\n\nexport interface TestimonialItem {\n id?: string;\n name?: string;\n role?: string;\n company?: string;\n /** Author photo. */\n imageSrc?: string;\n quote?: string;\n /** Number of stars. Default: 5 for a plain quote card; no stars on a story card (one with a `headline` or `logoSrc`). `0` hides them. */\n rating?: number;\n /** Customer logo, shown at the top of the card body. */\n logoSrc?: string;\n /** Wide image across the top of the card. */\n mediaSrc?: string;\n /** Story headline, e.g. \"Acme gives 100 million users a seamless sign-in\". */\n headline?: string;\n /** Key results, shown as large figures. */\n metrics?: TestimonialMetric[];\n /** Link to the full story. */\n href?: string;\n /** Text of the story link; defaults to the section's `link-label`. */\n linkLabel?: string;\n}\n\n/**\n * Section heading plus customer quotes or stories. Port of React `Testimonials`.\n *\n * Each card shows whatever its item has: a cover image, the customer logo, a story headline, a\n * quote with star rating, result metrics, the author and a \"See full story\" link. `layout=\"grid\"`\n * (default) lays the cards out in a grid; `layout=\"carousel\"` puts them on one horizontally\n * scrolling, snapping row with previous/next buttons next to the heading — the customer-story\n * carousel of enterprise marketing pages. `action-label`/`action-href` add a section-level link\n * (e.g. \"Explore customer stories\").\n *\n * @fires testimonials-link - Cancelable, before following a story or section link. Detail `{ href }`.\n */\n@customElement('st-testimonials')\nexport class Testimonials extends LitElement {\n static styles = css`\n ${controlBaseStyles}\n\n :host {\n display: block;\n box-sizing: border-box;\n padding: var(--st-space-48) var(--st-space-24);\n background-color: var(--st-color-subtle);\n ${rootFontFamilyStyles}\n }\n\n :host([inverted]) {\n background-color: var(--st-color-text-strong);\n }\n\n .container {\n max-width: var(--st-cms-container-max);\n margin: 0 auto;\n }\n\n .header {\n ${sectionHeaderStyles}\n }\n\n .title {\n ${sectionTitleStyles}\n margin-bottom: var(--st-space-16);\n letter-spacing: -0.02em;\n }\n\n .subtitle {\n ${sectionSubtitleStyles}\n margin: 0 auto;\n }\n\n ${invertedSectionTextStyles}\n\n .grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));\n gap: var(--st-space-32);\n }\n\n .card {\n ${cardShellStyles}\n display: flex;\n flex-direction: column;\n margin: 0;\n overflow: hidden;\n box-shadow: 0 1px 3px var(--st-shadow-color-sm);\n }\n\n .media {\n aspect-ratio: 16 / 9;\n background: var(--st-color-divider);\n }\n\n .media img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n transition: transform 0.4s ease;\n }\n\n .card:hover .media img {\n transform: scale(1.03);\n }\n\n .body {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: var(--st-space-24);\n padding: var(--st-space-24);\n }\n\n .logo {\n display: block;\n height: 32px;\n width: auto;\n max-width: 60%;\n object-fit: contain;\n object-position: left center;\n }\n\n .headline {\n margin: 0;\n font-size: var(--st-text-xl);\n font-weight: var(--st-font-weight-semibold);\n line-height: 1.35;\n color: var(--st-color-text-strong);\n }\n\n .stars {\n display: flex;\n gap: var(--st-space-4);\n color: var(--st-favorite-accent);\n font-size: var(--st-size-20);\n }\n\n .quote {\n font-size: var(--st-text-md);\n line-height: 1.6;\n color: var(--st-color-text);\n font-style: italic;\n margin: 0;\n }\n\n .metrics {\n display: flex;\n flex-wrap: wrap;\n gap: var(--st-space-16) var(--st-space-32);\n margin: 0;\n padding-top: var(--st-space-16);\n border-top: 1px solid var(--st-color-border);\n }\n\n .metric {\n display: flex;\n flex-direction: column-reverse;\n min-width: 6rem;\n }\n\n .metric dt {\n font-size: var(--st-text-sm);\n color: var(--st-color-text-muted);\n line-height: 1.4;\n }\n\n .metric dd {\n margin: 0;\n font-size: var(--st-cms-text-4xl);\n font-weight: var(--st-font-weight-bold);\n letter-spacing: -0.02em;\n line-height: 1.1;\n color: var(--st-color-text-strong);\n }\n\n .author {\n display: flex;\n align-items: center;\n gap: var(--st-space-12);\n margin-top: auto;\n }\n\n .avatar {\n width: var(--st-space-48);\n height: var(--st-space-48);\n border-radius: var(--st-radius-circle);\n object-fit: cover;\n background-color: var(--st-color-divider);\n flex-shrink: 0;\n }\n\n .avatar-placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--st-color-text-muted);\n font-weight: var(--st-font-weight-semibold);\n font-size: var(--st-text-xl);\n }\n\n .author-info {\n display: flex;\n flex-direction: column;\n }\n\n .name {\n font-weight: var(--st-font-weight-semibold);\n color: var(--st-color-text-strong);\n font-size: var(--st-text-xl);\n }\n\n .role {\n font-size: var(--st-text-sm);\n color: var(--st-color-text-faint);\n }\n\n .link {\n display: inline-flex;\n align-items: center;\n gap: var(--st-space-8);\n margin-top: auto;\n font-weight: var(--st-font-weight-semibold);\n color: var(--st-color-text-strong);\n text-decoration: none;\n }\n\n .author + .link {\n margin-top: 0;\n }\n\n .link:hover {\n text-decoration: underline;\n text-underline-offset: 3px;\n }\n\n .link svg {\n transition: transform 0.2s ease;\n }\n\n .link:hover svg {\n transform: translateX(3px);\n }\n\n /* ---- layout=\"carousel\" ---- */\n\n :host([layout='carousel']) .header {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--st-space-24);\n text-align: left;\n margin-bottom: var(--st-space-32);\n }\n\n :host([layout='carousel']) .subtitle {\n margin: 0;\n }\n\n .controls {\n display: flex;\n align-items: center;\n gap: var(--st-space-12);\n flex: none;\n }\n\n .section-link {\n margin-right: var(--st-space-12);\n }\n\n :host([inverted]) .section-link {\n color: var(--st-color-on-solid);\n }\n\n .arrow {\n ${buttonResetStyles}\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--st-size-40);\n height: var(--st-size-40);\n border-radius: var(--st-radius-circle);\n border: 1px solid var(--st-color-border-strong);\n color: var(--st-color-text-strong);\n background: var(--st-color-surface);\n transition:\n background 0.2s ease,\n opacity 0.2s ease;\n }\n\n .arrow:hover:not(:disabled) {\n background: var(--st-color-hover);\n }\n\n .arrow:disabled {\n opacity: 0.4;\n cursor: default;\n }\n\n .track {\n display: flex;\n gap: var(--st-space-24);\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-behavior: smooth;\n scrollbar-width: none;\n /* Room for the cards' shadow and focus rings. */\n padding: var(--st-space-4) var(--st-space-4) var(--st-space-12);\n margin: calc(-1 * var(--st-space-4));\n }\n\n .track::-webkit-scrollbar {\n display: none;\n }\n\n .track > .card {\n flex: 0 0 min(380px, 85%);\n scroll-snap-align: start;\n }\n\n /* The section link sits under the grid; in the carousel it is next to the arrows instead, and\n only drops below the row on a narrow screen. */\n .section-footer {\n margin-top: var(--st-space-32);\n text-align: center;\n }\n\n :host([inverted]) .section-footer .link {\n color: var(--st-color-on-solid);\n }\n\n :host([layout='carousel']) .section-footer {\n margin-top: 0;\n text-align: left;\n }\n\n .footer-link {\n display: none;\n }\n\n @media (max-width: 768px) {\n :host {\n padding: var(--st-space-40) var(--st-space-20);\n }\n .grid {\n grid-template-columns: 1fr;\n }\n :host([layout='carousel']) .header {\n flex-direction: column;\n align-items: flex-start;\n }\n .section-link {\n display: none;\n }\n .footer-link {\n display: inline-flex;\n margin-top: var(--st-space-24);\n }\n }\n\n ${reducedMotionStyles}\n @media (prefers-reduced-motion: reduce) {\n .track {\n scroll-behavior: auto;\n }\n }\n `;\n\n @property({ type: String })\n title = '';\n\n @property({ type: String })\n subtitle = '';\n\n @property({ attribute: false })\n items: TestimonialItem[] = [];\n\n @property({ type: Boolean, reflect: true })\n inverted = false;\n\n /** `grid` (default) or `carousel`: one scrolling row with previous/next buttons. */\n @property({ type: String, reflect: true })\n layout: 'grid' | 'carousel' = 'grid';\n\n /** Default text of each card's story link. */\n @property({ type: String, attribute: 'link-label' })\n linkLabel = 'See full story';\n\n /** Section-level link text, e.g. \"Explore customer stories\". */\n @property({ type: String, attribute: 'action-label' })\n actionLabel = '';\n\n @property({ type: String, attribute: 'action-href' })\n actionHref = '';\n\n @state() private atStart = true;\n @state() private atEnd = false;\n\n private get track(): HTMLElement | null {\n return this.renderRoot.querySelector('.track');\n }\n\n private resizeObserver?: ResizeObserver;\n\n protected updated(changed: Map<string, unknown>): void {\n if (changed.has('layout') || changed.has('items')) {\n this.resizeObserver?.disconnect();\n const track = this.track;\n if (track) {\n this.resizeObserver = new ResizeObserver(this.syncArrows);\n this.resizeObserver.observe(track);\n }\n this.syncArrows();\n }\n }\n\n disconnectedCallback(): void {\n this.resizeObserver?.disconnect();\n super.disconnectedCallback();\n }\n\n private syncArrows = (): void => {\n const track = this.track;\n if (!track) return;\n this.atStart = track.scrollLeft <= 1;\n this.atEnd = track.scrollLeft + track.clientWidth >= track.scrollWidth - 1;\n };\n\n private scrollByCard(direction: 1 | -1): void {\n const track = this.track;\n const card = track?.querySelector<HTMLElement>('.card');\n if (!track || !card) return;\n const gap = parseFloat(getComputedStyle(track).columnGap) || 0;\n track.scrollBy({ left: direction * (card.offsetWidth + gap) });\n }\n\n private onLinkClick(event: MouseEvent, href: string): void {\n const link = new CustomEvent<TestimonialsLinkEventDetail>('testimonials-link', {\n detail: { href },\n bubbles: true,\n composed: true,\n cancelable: true\n });\n this.dispatchEvent(link);\n if (link.defaultPrevented) event.preventDefault();\n }\n\n private renderLink(label: string, href: string, className: string, part: string) {\n return html`<a class=\"link ${className}\" part=${part} href=${href} @click=${(event: MouseEvent) => this.onLinkClick(event, href)}>\n ${label} ${chevronIcon('right', 14)}\n </a>`;\n }\n\n private renderItem(item: TestimonialItem) {\n const story = !!(item.headline || item.logoSrc);\n const rating = item.rating ?? (item.quote && !story ? 5 : 0);\n const metrics = item.metrics ?? [];\n return html`\n <article class=\"card\" part=\"card\">\n ${item.mediaSrc ? html`<div class=\"media\" part=\"media\"><img src=${item.mediaSrc} alt=\"\" loading=\"lazy\" /></div>` : nothing}\n <div class=\"body\" part=\"body\">\n ${item.logoSrc ? html`<img class=\"logo\" part=\"logo\" src=${item.logoSrc} alt=${item.company || item.name || ''} />` : nothing}\n ${item.headline ? html`<h3 class=\"headline\" part=\"headline\">${item.headline}</h3>` : nothing}\n ${rating > 0\n ? html`<div class=\"stars\" part=\"stars\" role=\"img\" aria-label=\"${rating} / 5\">\n ${Array.from({ length: rating }, () => html`<iconify-icon icon=\"mdi:star\"></iconify-icon>`)}\n </div>`\n : nothing}\n ${item.quote ? html`<blockquote class=\"quote\" part=\"quote\">\"${item.quote}\"</blockquote>` : nothing}\n ${metrics.length\n ? html`<dl class=\"metrics\" part=\"metrics\">\n ${metrics.map(metric => html`<div class=\"metric\"><dt>${metric.label}</dt><dd>${metric.value}</dd></div>`)}\n </dl>`\n : nothing}\n ${item.name\n ? html`<div class=\"author\" part=\"author\">\n ${item.imageSrc\n ? html`<img class=\"avatar\" part=\"avatar\" src=${item.imageSrc} alt=${item.name} />`\n : html`<div class=\"avatar avatar-placeholder\" part=\"avatar\">${item.name.charAt(0)}</div>`}\n <span class=\"author-info\">\n <span class=\"name\" part=\"name\">${item.name}</span>\n ${item.role || item.company\n ? html`<span class=\"role\" part=\"role\">${[item.role, item.company].filter(Boolean).join(' at ')}</span>`\n : nothing}\n </span>\n </div>`\n : nothing}\n ${item.href ? this.renderLink(item.linkLabel || this.linkLabel, item.href, '', 'link') : nothing}\n </div>\n </article>\n `;\n }\n\n render() {\n const carousel = this.layout === 'carousel';\n const sectionLink = this.actionLabel && this.actionHref;\n return html`\n <div class=\"container\" part=\"container\">\n <div class=\"header\" part=\"header\">\n <div>\n <h2 class=\"title\" part=\"title\">${this.title}</h2>\n ${this.subtitle ? html`<p class=\"subtitle\" part=\"subtitle\">${this.subtitle}</p>` : nothing}\n </div>\n ${carousel\n ? html`<div class=\"controls\" part=\"controls\">\n ${sectionLink ? this.renderLink(this.actionLabel, this.actionHref, 'section-link', 'action') : nothing}\n <button class=\"arrow\" part=\"arrow prev\" type=\"button\" aria-label=\"Previous\" ?disabled=${this.atStart} @click=${() => this.scrollByCard(-1)}>\n ${chevronIcon('left', 16)}\n </button>\n <button class=\"arrow\" part=\"arrow next\" type=\"button\" aria-label=\"Next\" ?disabled=${this.atEnd} @click=${() => this.scrollByCard(1)}>\n ${chevronIcon('right', 16)}\n </button>\n </div>`\n : nothing}\n </div>\n ${carousel\n ? html`<div class=\"track\" part=\"track\" role=\"region\" aria-label=${this.title || 'Stories'} tabindex=\"0\" @scroll=${this.syncArrows}>\n ${this.items.map(item => this.renderItem(item))}\n </div>`\n : html`<div class=\"grid\" part=\"grid\">${this.items.map(item => this.renderItem(item))}</div>`}\n ${sectionLink\n ? html`<div class=\"section-footer\">${this.renderLink(this.actionLabel, this.actionHref, carousel ? 'footer-link' : '', 'action')}</div>`\n : nothing}\n </div>\n `;\n }\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAkMf,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAqFnB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASvB;AAAA,EADC,SAAS,EAAE,MAAM,OAAO,CAAC;AAAA,GA5Uf,aA6UX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,OAAO,CAAC;AAAA,GA/Uf,aAgVX;AAGA;AAAA,EADC,SAAS,EAAE,WAAW,MAAM,CAAC;AAAA,GAlVnB,aAmVX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,SAAS,SAAS,KAAK,CAAC;AAAA,GArV/B,aAsVX;AAIA;AAAA,EADC,SAAS,EAAE,MAAM,QAAQ,SAAS,KAAK,CAAC;AAAA,GAzV9B,aA0VX;AAIA;AAAA,EADC,SAAS,EAAE,MAAM,QAAQ,WAAW,aAAa,CAAC;AAAA,GA7VxC,aA8VX;AAIA;AAAA,EADC,SAAS,EAAE,MAAM,QAAQ,WAAW,eAAe,CAAC;AAAA,GAjW1C,aAkWX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,QAAQ,WAAW,cAAc,CAAC;AAAA,GApWzC,aAqWX;AAEiB;AAAA,EAAhB,MAAM;AAAA,GAvWI,aAuWM;AACA;AAAA,EAAhB,MAAM;AAAA,GAxWI,aAwWM;AAxWN,eAAN;AAAA,EADN,cAAc,iBAAiB;AAAA,GACnB;","names":[]}
|
|
@@ -215,7 +215,8 @@ Dropdown.styles = css`
|
|
|
215
215
|
text-align: center;
|
|
216
216
|
width: fit-content;
|
|
217
217
|
border-radius: var(--st-radius-sm);
|
|
218
|
-
|
|
218
|
+
/* --st-nav-*: set by a host bar (st-header's transparent state) to recolour its nav items. */
|
|
219
|
+
color: var(--st-nav-color, var(--st-color-text));
|
|
219
220
|
font-size: 0.875rem;
|
|
220
221
|
text-decoration: none;
|
|
221
222
|
white-space: nowrap;
|
|
@@ -224,8 +225,8 @@ Dropdown.styles = css`
|
|
|
224
225
|
|
|
225
226
|
.menu-title:hover,
|
|
226
227
|
.menu-title[aria-expanded='true'] {
|
|
227
|
-
background: var(--st-color-hover);
|
|
228
|
-
color: var(--st-color-text-strong);
|
|
228
|
+
background: var(--st-nav-hover-bg, var(--st-color-hover));
|
|
229
|
+
color: var(--st-nav-color, var(--st-color-text-strong));
|
|
229
230
|
}
|
|
230
231
|
|
|
231
232
|
:host([active]) .menu-title {
|
|
@@ -269,6 +270,9 @@ Dropdown.styles = css`
|
|
|
269
270
|
padding: 16px;
|
|
270
271
|
background: var(--st-color-surface);
|
|
271
272
|
color: var(--st-color-text);
|
|
273
|
+
/* The panel has its own surface: items in it never take the bar's nav colours. */
|
|
274
|
+
--st-nav-color: initial;
|
|
275
|
+
--st-nav-hover-bg: initial;
|
|
272
276
|
border: 1px solid var(--st-color-border);
|
|
273
277
|
border-radius: var(--st-radius-sm);
|
|
274
278
|
box-shadow: 0 4px 16px var(--st-shadow-color-sm);
|
|
@@ -394,4 +398,4 @@ Dropdown = __decorateClass([
|
|
|
394
398
|
export {
|
|
395
399
|
Dropdown
|
|
396
400
|
};
|
|
397
|
-
//# sourceMappingURL=chunk-
|
|
401
|
+
//# sourceMappingURL=chunk-FWIG6GEZ.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 /* --st-nav-*: set by a host bar (st-header's transparent state) to recolour its nav items. */\r\n color: var(--st-nav-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-nav-hover-bg, var(--st-color-hover));\r\n color: var(--st-nav-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 /* The panel has its own surface: items in it never take the bar's nav colours. */\r\n --st-nav-color: initial;\r\n --st-nav-hover-bg: initial;\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":[]}
|