@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.
Files changed (134) hide show
  1. package/README.md +34 -3
  2. package/custom-elements.json +4008 -0
  3. package/dist/bento-grid/index.d.ts +27 -0
  4. package/dist/bento-grid/index.js +10 -0
  5. package/dist/bento-grid/index.js.map +1 -0
  6. package/dist/card/index.d.ts +20 -0
  7. package/dist/card/index.js +9 -0
  8. package/dist/card/index.js.map +1 -0
  9. package/dist/carousel/index.d.ts +44 -0
  10. package/dist/carousel/index.js +10 -0
  11. package/dist/carousel/index.js.map +1 -0
  12. package/dist/chunk-23IHTBAB.js +557 -0
  13. package/dist/chunk-23IHTBAB.js.map +1 -0
  14. package/dist/chunk-2VZFHTBC.js +203 -0
  15. package/dist/chunk-2VZFHTBC.js.map +1 -0
  16. package/dist/chunk-3ERKQNZT.js +155 -0
  17. package/dist/chunk-3ERKQNZT.js.map +1 -0
  18. package/dist/chunk-3JU3R22O.js +193 -0
  19. package/dist/chunk-3JU3R22O.js.map +1 -0
  20. package/dist/chunk-3TCP25JG.js +347 -0
  21. package/dist/chunk-3TCP25JG.js.map +1 -0
  22. package/dist/chunk-3VE6VFJ4.js +87 -0
  23. package/dist/chunk-3VE6VFJ4.js.map +1 -0
  24. package/dist/chunk-4HKSA5UT.js +312 -0
  25. package/dist/chunk-4HKSA5UT.js.map +1 -0
  26. package/dist/chunk-5REZSZCK.js +266 -0
  27. package/dist/chunk-5REZSZCK.js.map +1 -0
  28. package/dist/chunk-5UFRGZWG.js +268 -0
  29. package/dist/chunk-5UFRGZWG.js.map +1 -0
  30. package/dist/chunk-74MILER4.js +215 -0
  31. package/dist/chunk-74MILER4.js.map +1 -0
  32. package/dist/chunk-7PSYCTEN.js +118 -0
  33. package/dist/chunk-7PSYCTEN.js.map +1 -0
  34. package/dist/chunk-CDC4P77V.js +299 -0
  35. package/dist/chunk-CDC4P77V.js.map +1 -0
  36. package/dist/chunk-E52WO7R7.js +279 -0
  37. package/dist/chunk-E52WO7R7.js.map +1 -0
  38. package/dist/chunk-EUXUH3YW.js +15 -0
  39. package/dist/chunk-EUXUH3YW.js.map +1 -0
  40. package/dist/chunk-IMACP7HN.js +156 -0
  41. package/dist/chunk-IMACP7HN.js.map +1 -0
  42. package/dist/chunk-LHU23TF6.js +273 -0
  43. package/dist/chunk-LHU23TF6.js.map +1 -0
  44. package/dist/chunk-OEX5FDMR.js +250 -0
  45. package/dist/chunk-OEX5FDMR.js.map +1 -0
  46. package/dist/chunk-RHAOQ6K4.js +138 -0
  47. package/dist/chunk-RHAOQ6K4.js.map +1 -0
  48. package/dist/chunk-RMIJVTFS.js +397 -0
  49. package/dist/chunk-RMIJVTFS.js.map +1 -0
  50. package/dist/chunk-U7YKCCJG.js +228 -0
  51. package/dist/chunk-U7YKCCJG.js.map +1 -0
  52. package/dist/chunk-USNFT4DU.js +125 -0
  53. package/dist/chunk-USNFT4DU.js.map +1 -0
  54. package/dist/chunk-VYON56QO.js +90 -0
  55. package/dist/chunk-VYON56QO.js.map +1 -0
  56. package/dist/chunk-WCO7JAIK.js +195 -0
  57. package/dist/chunk-WCO7JAIK.js.map +1 -0
  58. package/dist/chunk-XHBC23TV.js +254 -0
  59. package/dist/chunk-XHBC23TV.js.map +1 -0
  60. package/dist/chunk-XXPBFHWR.js +70 -0
  61. package/dist/chunk-XXPBFHWR.js.map +1 -0
  62. package/dist/cms-theme.css +15 -0
  63. package/dist/content-block/index.d.ts +25 -0
  64. package/dist/content-block/index.js +11 -0
  65. package/dist/content-block/index.js.map +1 -0
  66. package/dist/cookie-consent/index.d.ts +33 -0
  67. package/dist/cookie-consent/index.js +9 -0
  68. package/dist/cookie-consent/index.js.map +1 -0
  69. package/dist/dropdown/index.d.ts +89 -0
  70. package/dist/dropdown/index.js +9 -0
  71. package/dist/dropdown/index.js.map +1 -0
  72. package/dist/feature-split/index.d.ts +38 -0
  73. package/dist/feature-split/index.js +11 -0
  74. package/dist/feature-split/index.js.map +1 -0
  75. package/dist/features-section/index.d.ts +50 -0
  76. package/dist/features-section/index.js +14 -0
  77. package/dist/features-section/index.js.map +1 -0
  78. package/dist/footer/index.d.ts +64 -0
  79. package/dist/footer/index.js +9 -0
  80. package/dist/footer/index.js.map +1 -0
  81. package/dist/header/index.d.ts +112 -0
  82. package/dist/header/index.js +10 -0
  83. package/dist/header/index.js.map +1 -0
  84. package/dist/hero/index.d.ts +32 -0
  85. package/dist/hero/index.js +10 -0
  86. package/dist/hero/index.js.map +1 -0
  87. package/dist/icon-button/index.d.ts +23 -0
  88. package/dist/icon-button/index.js +9 -0
  89. package/dist/icon-button/index.js.map +1 -0
  90. package/dist/index.d.ts +22 -0
  91. package/dist/index.js +93 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/items-accordion/index.d.ts +31 -0
  94. package/dist/items-accordion/index.js +10 -0
  95. package/dist/items-accordion/index.js.map +1 -0
  96. package/dist/markdown/index.d.ts +33 -0
  97. package/dist/markdown/index.js +9 -0
  98. package/dist/markdown/index.js.map +1 -0
  99. package/dist/menu-grid/index.d.ts +21 -0
  100. package/dist/menu-grid/index.js +9 -0
  101. package/dist/menu-grid/index.js.map +1 -0
  102. package/dist/menu-grid-item/index.d.ts +53 -0
  103. package/dist/menu-grid-item/index.js +9 -0
  104. package/dist/menu-grid-item/index.js.map +1 -0
  105. package/dist/menu-item/index.d.ts +50 -0
  106. package/dist/menu-item/index.js +9 -0
  107. package/dist/menu-item/index.js.map +1 -0
  108. package/dist/promo-banner/index.d.ts +22 -0
  109. package/dist/promo-banner/index.js +10 -0
  110. package/dist/promo-banner/index.js.map +1 -0
  111. package/dist/schema/index.d.ts +72 -0
  112. package/dist/schema/index.js +75 -0
  113. package/dist/schema/index.js.map +1 -0
  114. package/dist/select/index.d.ts +77 -0
  115. package/dist/select/index.js +10 -0
  116. package/dist/select/index.js.map +1 -0
  117. package/dist/shared/icons.d.ts +77 -0
  118. package/dist/shared/icons.js +64 -0
  119. package/dist/shared/icons.js.map +1 -0
  120. package/dist/shared/styles.d.ts +90 -0
  121. package/dist/shared/styles.js +48 -0
  122. package/dist/shared/styles.js.map +1 -0
  123. package/dist/stats/index.d.ts +17 -0
  124. package/dist/stats/index.js +9 -0
  125. package/dist/stats/index.js.map +1 -0
  126. package/dist/testimonials/index.d.ts +25 -0
  127. package/dist/testimonials/index.js +10 -0
  128. package/dist/testimonials/index.js.map +1 -0
  129. package/dist/theme.css +292 -0
  130. package/dist/themes.css +65 -0
  131. package/dist/theming/index.d.ts +28 -0
  132. package/dist/theming/index.js +53 -0
  133. package/dist/theming/index.js.map +1 -0
  134. 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