@rimelight/ui 0.0.2 → 0.0.4

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 (105) hide show
  1. package/package.json +6 -3
  2. package/src/components/carousel/carousel-script.ts +188 -188
  3. package/src/components/carousel/index.ts +1 -1
  4. package/src/components/input-otp/InputOtpTypes.ts +6 -6
  5. package/src/components/radio-group/RadioGroupTypes.ts +6 -6
  6. package/src/components/select/SelectTypes.ts +13 -13
  7. package/src/components/switch/SwitchTypes.ts +6 -6
  8. package/src/components/toast/index.ts +1 -1
  9. package/src/components/toast/toast-manager.ts +234 -234
  10. package/src/components/toggle/ToggleTypes.ts +14 -14
  11. package/src/config/cookies.ts +5 -5
  12. package/src/config/index.ts +3 -3
  13. package/src/config/links.ts +4 -4
  14. package/src/config/site.config.ts +27 -45
  15. package/src/domain/cms/index.ts +1 -1
  16. package/src/domain/cms/types.ts +20 -20
  17. package/src/domain/i18n/constants.ts +21 -21
  18. package/src/domain/i18n/cookie-storage.ts +13 -13
  19. package/src/domain/i18n/country-to-locale-map.ts +67 -67
  20. package/src/domain/i18n/fetcher.ts +278 -268
  21. package/src/domain/i18n/flags.ts +16 -16
  22. package/src/domain/i18n/format.ts +90 -90
  23. package/src/domain/i18n/index.ts +10 -10
  24. package/src/domain/i18n/middleware/i18n.ts +161 -160
  25. package/src/domain/i18n/resolve-locale.ts +55 -55
  26. package/src/domain/i18n/schema.ts +29 -29
  27. package/src/domain/index.ts +4 -4
  28. package/src/domain/seo/constants.ts +6 -20
  29. package/src/domain/seo/index.ts +3 -3
  30. package/src/domain/seo/services/index.ts +3 -3
  31. package/src/domain/seo/services/llms.ts +56 -53
  32. package/src/domain/seo/services/robots.ts +12 -12
  33. package/src/domain/seo/services/sitemap.ts +56 -56
  34. package/src/domain/seo/utils.ts +36 -37
  35. package/src/domain/theme/constants.ts +6 -6
  36. package/src/domain/theme/index.ts +2 -2
  37. package/src/domain/theme/schema.ts +10 -10
  38. package/src/env.d.ts +49 -49
  39. package/src/integrations/index.ts +1 -0
  40. package/src/integrations/sri.ts +92 -0
  41. package/src/lib/component-preview.ts +73 -73
  42. package/src/lib/index.ts +6 -6
  43. package/src/lib/remark/remark-modified-time.ts +13 -13
  44. package/src/lib/showcase-preview-elements.ts +408 -408
  45. package/src/lib/showcase-renderer.ts +218 -218
  46. package/src/lib/showcase-utils.ts +189 -189
  47. package/src/lib/turnstile.ts +35 -35
  48. package/src/lib/utils/starwind/positioning.ts +318 -318
  49. package/src/locales/de/blog.json +9 -9
  50. package/src/locales/de/common.json +19 -19
  51. package/src/locales/de/errors.json +6 -6
  52. package/src/locales/de/home.json +20 -20
  53. package/src/locales/de/messages.json +8 -8
  54. package/src/locales/de/nav.json +13 -13
  55. package/src/locales/de/projects.json +9 -9
  56. package/src/locales/de/services.json +43 -43
  57. package/src/locales/en/blog.json +9 -9
  58. package/src/locales/en/chat.json +29 -29
  59. package/src/locales/en/common.json +19 -19
  60. package/src/locales/en/cookie_notice.json +11 -11
  61. package/src/locales/en/editor.json +24 -24
  62. package/src/locales/en/errors.json +6 -6
  63. package/src/locales/en/home.json +20 -20
  64. package/src/locales/en/messages.json +8 -8
  65. package/src/locales/en/nav.json +13 -13
  66. package/src/locales/en/projects.json +9 -9
  67. package/src/locales/en/services.json +43 -43
  68. package/src/locales/es/blog.json +9 -9
  69. package/src/locales/es/common.json +19 -19
  70. package/src/locales/es/errors.json +6 -6
  71. package/src/locales/es/home.json +20 -20
  72. package/src/locales/es/messages.json +8 -8
  73. package/src/locales/es/nav.json +13 -13
  74. package/src/locales/es/projects.json +9 -9
  75. package/src/locales/es/services.json +43 -43
  76. package/src/locales/ja/blog.json +9 -9
  77. package/src/locales/ja/common.json +19 -19
  78. package/src/locales/ja/errors.json +6 -6
  79. package/src/locales/ja/home.json +20 -20
  80. package/src/locales/ja/messages.json +8 -8
  81. package/src/locales/ja/nav.json +13 -13
  82. package/src/locales/ja/projects.json +9 -9
  83. package/src/locales/ja/services.json +43 -43
  84. package/src/locales/pt/blog.json +9 -9
  85. package/src/locales/pt/chat.json +29 -29
  86. package/src/locales/pt/common.json +19 -19
  87. package/src/locales/pt/cookie_notice.json +11 -11
  88. package/src/locales/pt/editor.json +24 -24
  89. package/src/locales/pt/errors.json +6 -6
  90. package/src/locales/pt/home.json +20 -20
  91. package/src/locales/pt/messages.json +8 -8
  92. package/src/locales/pt/nav.json +13 -13
  93. package/src/locales/pt/projects.json +9 -9
  94. package/src/locales/pt/services.json +43 -43
  95. package/src/middleware/index.ts +2 -0
  96. package/src/middleware/security.ts +15 -0
  97. package/src/middleware/sri.ts +78 -0
  98. package/src/utils/index.ts +2 -2
  99. package/src/utils/server/cookies.ts +98 -98
  100. package/src/utils/server/index.ts +1 -1
  101. package/src/utils/shared/deep-merge.ts +36 -36
  102. package/src/utils/shared/embed.ts +105 -105
  103. package/src/utils/shared/index.ts +3 -3
  104. package/src/utils/shared/parse-boolean.ts +14 -14
  105. package/worker-configuration.d.ts +0 -14791
@@ -1,408 +1,408 @@
1
- /**
2
- * Showcase Preview Custom Elements
3
- *
4
- * This module defines all the custom elements used in the component showcase. It must be imported
5
- * client-side before the showcase renderers run.
6
- */
7
-
8
- // Register all custom elements
9
- export function registerShowcasePreviewElements() {
10
- if (typeof HTMLElement === "undefined" || typeof customElements === "undefined") return
11
-
12
- // Hero Preview Custom Element
13
- class HeroPreviewElement extends HTMLElement {
14
- innerEl: HTMLElement | null = null
15
- contentWrapper: HTMLElement | null = null
16
- titleEl: HTMLElement | null = null
17
- descEl: HTMLElement | null = null
18
-
19
- static get observedAttributes() {
20
- return ["data-orientation", "data-alignment", "data-inverted"]
21
- }
22
-
23
- connectedCallback() {
24
- this.innerEl = this.querySelector<HTMLElement>(".hero-preview-inner")
25
- this.contentWrapper = this.querySelector<HTMLElement>(".hero-content-wrapper")
26
- this.titleEl = this.querySelector<HTMLElement>("[data-hero-title]")
27
- this.descEl = this.querySelector<HTMLElement>("[data-hero-description]")
28
- if (!this.innerEl || !this.contentWrapper) return
29
- this.updateClasses()
30
- }
31
-
32
- attributeChangedCallback(_name: string, _oldValue: string, newValue: string) {
33
- if (_oldValue !== newValue) {
34
- this.updateClasses()
35
- }
36
- }
37
-
38
- updateClasses() {
39
- if (!this.innerEl || !this.contentWrapper) return
40
-
41
- const orientation = this.dataset.orientation || "vertical"
42
- const alignment = this.dataset.alignment || "center"
43
- const inverted = this.dataset.inverted === "true"
44
-
45
- const effectiveAlignment = orientation === "horizontal" ? "left" : alignment
46
-
47
- // Reset inner container classes
48
- this.innerEl.className = "hero-preview-inner w-full"
49
-
50
- // Apply orientation classes
51
- if (orientation === "horizontal") {
52
- this.innerEl.classList.add(
53
- "flex",
54
- "flex-col",
55
- "gap-8",
56
- "items-center",
57
- "lg:grid",
58
- "lg:grid-cols-2",
59
- "lg:items-center",
60
- "lg:gap-16"
61
- )
62
- } else {
63
- this.innerEl.classList.add("flex", "flex-col", "gap-8", "items-center")
64
- if (!inverted) {
65
- this.innerEl.classList.add("flex-col-reverse")
66
- }
67
- }
68
-
69
- // Apply alignment classes
70
- this.innerEl.classList.remove(
71
- "text-start",
72
- "text-center",
73
- "text-end",
74
- "items-start",
75
- "items-end"
76
- )
77
- if (effectiveAlignment === "left") {
78
- this.innerEl.classList.add("text-start", "items-start")
79
- } else if (effectiveAlignment === "right") {
80
- this.innerEl.classList.add("text-end", "items-end")
81
- } else {
82
- this.innerEl.classList.add("text-center", "items-center")
83
- }
84
-
85
- // Update content wrapper
86
- this.contentWrapper.classList.remove(
87
- "text-center",
88
- "items-center",
89
- "text-start",
90
- "items-start",
91
- "text-end",
92
- "items-end"
93
- )
94
- if (effectiveAlignment === "left") {
95
- this.contentWrapper.classList.add("text-start", "items-start")
96
- } else if (effectiveAlignment === "right") {
97
- this.contentWrapper.classList.add("text-end", "items-end")
98
- } else {
99
- this.contentWrapper.classList.add("text-center", "items-center")
100
- }
101
- }
102
- }
103
-
104
- // Section Preview Custom Element
105
- class SectionPreviewElement extends HTMLElement {
106
- innerEl: HTMLElement | null = null
107
- contentEl: HTMLElement | null = null
108
- titleEl: HTMLElement | null = null
109
- descEl: HTMLElement | null = null
110
-
111
- static get observedAttributes() {
112
- return ["data-orientation", "data-alignment", "data-inverted"]
113
- }
114
-
115
- connectedCallback() {
116
- this.innerEl = this.querySelector<HTMLElement>(".section-preview-inner")
117
- this.contentEl = this.querySelector<HTMLElement>(".section-content")
118
- this.titleEl = this.querySelector<HTMLElement>("[data-section-title]")
119
- this.descEl = this.querySelector<HTMLElement>("[data-section-description]")
120
- if (!this.innerEl || !this.contentEl) return
121
- this.updateClasses()
122
- }
123
-
124
- attributeChangedCallback(_name: string, _oldValue: string, newValue: string) {
125
- if (_oldValue !== newValue) {
126
- this.updateClasses()
127
- }
128
- }
129
-
130
- updateClasses() {
131
- if (!this.innerEl || !this.contentEl) return
132
-
133
- const orientation = this.dataset.orientation || "vertical"
134
- const alignment = this.dataset.alignment || "center"
135
- const inverted = this.dataset.inverted === "true"
136
-
137
- const effectiveAlignment = orientation === "horizontal" ? "left" : alignment
138
-
139
- // Reset inner container classes
140
- this.innerEl.className = "section-preview-inner w-full"
141
- this.innerEl.classList.add("container", "py-24")
142
-
143
- // Apply orientation classes
144
- if (orientation === "horizontal") {
145
- this.innerEl.classList.add("lg:grid", "lg:grid-cols-2", "lg:items-center", "lg:gap-16")
146
- } else {
147
- this.innerEl.classList.add("flex", "flex-col", "gap-12")
148
- if (inverted) {
149
- this.innerEl.classList.add("flex-col-reverse")
150
- }
151
- }
152
-
153
- // Apply alignment classes
154
- this.innerEl.classList.remove(
155
- "text-start",
156
- "text-center",
157
- "text-end",
158
- "items-start",
159
- "items-end"
160
- )
161
- if (effectiveAlignment === "left") {
162
- this.innerEl.classList.add("text-start", "items-start")
163
- } else if (effectiveAlignment === "right") {
164
- this.innerEl.classList.add("text-end", "items-end")
165
- } else {
166
- this.innerEl.classList.add("text-center", "items-center")
167
- }
168
-
169
- // Update content element
170
- this.contentEl.classList.remove(
171
- "text-center",
172
- "items-center",
173
- "text-start",
174
- "items-start",
175
- "text-end",
176
- "items-end"
177
- )
178
- if (effectiveAlignment === "left") {
179
- this.contentEl.classList.add("text-start", "items-start")
180
- } else if (effectiveAlignment === "right") {
181
- this.contentEl.classList.add("text-end", "items-end")
182
- } else {
183
- this.contentEl.classList.add("text-center", "items-center")
184
- }
185
- }
186
- }
187
-
188
- // Button Preview Custom Element
189
- class ButtonPreviewElement extends HTMLElement {
190
- button: HTMLElement | null = null
191
-
192
- static get observedAttributes() {
193
- return ["data-variant", "data-size", "data-children"]
194
- }
195
-
196
- connectedCallback() {
197
- this.button = this.querySelector<HTMLElement>("[data-preview-button]")
198
- if (!this.button) return
199
- this.updateButton()
200
- }
201
-
202
- attributeChangedCallback(_name: string, oldValue: string, newValue: string) {
203
- if (oldValue !== newValue && this.button) {
204
- this.updateButton()
205
- }
206
- }
207
-
208
- updateButton() {
209
- if (!this.button) return
210
-
211
- const variant = this.dataset.variant || "default"
212
- const size = this.dataset.size || "md"
213
- const children = this.dataset.children || "Button"
214
-
215
- // Remove all variant classes
216
- this.button.classList.remove(
217
- "bg-input",
218
- "hover:bg-input/90",
219
- "text-foreground",
220
- "bg-primary",
221
- "text-primary-foreground",
222
- "hover:bg-primary/90",
223
- "bg-secondary",
224
- "text-secondary-foreground",
225
- "hover:bg-secondary/90",
226
- "border",
227
- "border-input",
228
- "bg-background",
229
- "hover:bg-accent",
230
- "hover:text-accent-foreground",
231
- "bg-info",
232
- "text-info-foreground",
233
- "hover:bg-info/90",
234
- "bg-success",
235
- "text-success-foreground",
236
- "hover:bg-success/90",
237
- "bg-warning",
238
- "text-warning-foreground",
239
- "hover:bg-warning/90",
240
- "bg-error",
241
- "text-error-foreground",
242
- "hover:bg-error/90"
243
- )
244
-
245
- // Add new variant classes
246
- const variantClasses: Record<string, string[]> = {
247
- default: ["bg-input", "hover:bg-input/90", "text-foreground"],
248
- primary: ["bg-primary", "text-primary-foreground", "hover:bg-primary/90"],
249
- secondary: ["bg-secondary", "text-secondary-foreground", "hover:bg-secondary/90"],
250
- outline: [
251
- "border",
252
- "border-input",
253
- "bg-background",
254
- "hover:bg-accent",
255
- "hover:text-accent-foreground"
256
- ],
257
- ghost: ["hover:bg-accent", "hover:text-accent-foreground"],
258
- info: ["bg-info", "text-info-foreground", "hover:bg-info/90"],
259
- success: ["bg-success", "text-success-foreground", "hover:bg-success/90"],
260
- warning: ["bg-warning", "text-warning-foreground", "hover:bg-warning/90"],
261
- error: ["bg-error", "text-error-foreground", "hover:bg-error/90"]
262
- }
263
-
264
- variantClasses[variant]?.forEach((cls: string) => this.button?.classList.add(cls))
265
-
266
- // Remove all size classes
267
- this.button.classList.remove(
268
- "h-9",
269
- "px-3",
270
- "text-sm",
271
- "h-11",
272
- "px-4",
273
- "text-base",
274
- "h-12",
275
- "px-6",
276
- "text-lg"
277
- )
278
-
279
- // Add new size classes
280
- const sizeClasses: Record<string, string[]> = {
281
- sm: ["h-9", "px-3", "text-sm"],
282
- md: ["h-11", "px-4", "text-base"],
283
- lg: ["h-12", "px-6", "text-lg"]
284
- }
285
-
286
- sizeClasses[size]?.forEach((cls: string) => this.button?.classList.add(cls))
287
-
288
- // Update text
289
- this.button.textContent = children
290
- }
291
- }
292
-
293
- // Testimonials Preview Custom Element
294
- class TestimonialsPreviewElement extends HTMLElement {
295
- card: HTMLElement | null = null
296
- quoteEl: HTMLElement | null = null
297
- personEl: HTMLElement | null = null
298
- metaEl: HTMLElement | null = null
299
-
300
- static get observedAttributes() {
301
- return ["data-alignment"]
302
- }
303
-
304
- connectedCallback() {
305
- this.card = this.querySelector<HTMLElement>(".testimonial-card")
306
- this.quoteEl = this.querySelector<HTMLElement>("[data-testimonials-quote]")
307
- this.personEl = this.querySelector<HTMLElement>(".testimonial-person")
308
- this.metaEl = this.querySelector<HTMLElement>(".testimonial-meta")
309
- if (!this.card || !this.quoteEl || !this.personEl || !this.metaEl) return
310
- this.updateClasses()
311
- }
312
-
313
- attributeChangedCallback(_name: string, oldValue: string, newValue: string) {
314
- if (oldValue !== newValue) {
315
- this.updateClasses()
316
- }
317
- }
318
-
319
- updateClasses() {
320
- if (!this.card || !this.quoteEl || !this.personEl || !this.metaEl) return
321
-
322
- const alignment = this.dataset.alignment || "center"
323
-
324
- // Reset all alignment classes
325
- this.quoteEl.classList.remove("text-left", "text-center", "text-right")
326
- this.personEl.classList.remove(
327
- "flex-row",
328
- "flex-row-reverse",
329
- "items-center",
330
- "justify-start",
331
- "justify-end",
332
- "flex-col",
333
- "text-center"
334
- )
335
- this.metaEl.classList.remove(
336
- "text-left",
337
- "text-right",
338
- "items-start",
339
- "items-end",
340
- "items-center"
341
- )
342
-
343
- if (alignment === "left") {
344
- this.quoteEl.classList.add("text-left")
345
- this.personEl.classList.add("flex-row", "items-center", "justify-start")
346
- this.metaEl.classList.add("text-left", "items-start")
347
- } else if (alignment === "right") {
348
- this.quoteEl.classList.add("text-right")
349
- this.personEl.classList.add("flex-row-reverse", "items-center", "justify-end")
350
- this.metaEl.classList.add("text-right", "items-end")
351
- } else {
352
- // center
353
- this.quoteEl.classList.add("text-center")
354
- this.personEl.classList.add("flex-col", "items-center", "justify-center", "text-center")
355
- this.metaEl.classList.add("items-center")
356
- }
357
- }
358
- }
359
-
360
- // Language Switcher Preview Custom Element
361
- class LanguageSwitcherPreviewElement extends HTMLElement {
362
- static get observedAttributes() {
363
- return ["data-variant"]
364
- }
365
-
366
- connectedCallback() {
367
- this.updateVariant()
368
- }
369
-
370
- attributeChangedCallback(_name: string, oldValue: string, newValue: string) {
371
- if (oldValue !== newValue) {
372
- this.updateVariant()
373
- }
374
- }
375
-
376
- updateVariant() {
377
- // Dispatch event for the actual component to handle
378
- this.dispatchEvent(
379
- new CustomEvent("language-switcher:variant-change", {
380
- detail: { variant: this.dataset.variant },
381
- bubbles: true
382
- })
383
- )
384
- }
385
- }
386
-
387
- // Register all custom elements
388
- if (!customElements.get("hero-preview")) {
389
- customElements.define("hero-preview", HeroPreviewElement)
390
- }
391
- if (!customElements.get("section-preview")) {
392
- customElements.define("section-preview", SectionPreviewElement)
393
- }
394
- if (!customElements.get("button-preview")) {
395
- customElements.define("button-preview", ButtonPreviewElement)
396
- }
397
- if (!customElements.get("testimonials-preview")) {
398
- customElements.define("testimonials-preview", TestimonialsPreviewElement)
399
- }
400
- if (!customElements.get("language-switcher-preview")) {
401
- customElements.define("language-switcher-preview", LanguageSwitcherPreviewElement)
402
- }
403
- }
404
-
405
- // Auto-register when module is imported
406
- if (typeof window !== "undefined") {
407
- registerShowcasePreviewElements()
408
- }
1
+ /**
2
+ * Showcase Preview Custom Elements
3
+ *
4
+ * This module defines all the custom elements used in the component showcase. It must be imported
5
+ * client-side before the showcase renderers run.
6
+ */
7
+
8
+ // Register all custom elements
9
+ export function registerShowcasePreviewElements() {
10
+ if (typeof HTMLElement === "undefined" || typeof customElements === "undefined") return
11
+
12
+ // Hero Preview Custom Element
13
+ class HeroPreviewElement extends HTMLElement {
14
+ innerEl: HTMLElement | null = null
15
+ contentWrapper: HTMLElement | null = null
16
+ titleEl: HTMLElement | null = null
17
+ descEl: HTMLElement | null = null
18
+
19
+ static get observedAttributes() {
20
+ return ["data-orientation", "data-alignment", "data-inverted"]
21
+ }
22
+
23
+ connectedCallback() {
24
+ this.innerEl = this.querySelector<HTMLElement>(".hero-preview-inner")
25
+ this.contentWrapper = this.querySelector<HTMLElement>(".hero-content-wrapper")
26
+ this.titleEl = this.querySelector<HTMLElement>("[data-hero-title]")
27
+ this.descEl = this.querySelector<HTMLElement>("[data-hero-description]")
28
+ if (!this.innerEl || !this.contentWrapper) return
29
+ this.updateClasses()
30
+ }
31
+
32
+ attributeChangedCallback(_name: string, _oldValue: string, newValue: string) {
33
+ if (_oldValue !== newValue) {
34
+ this.updateClasses()
35
+ }
36
+ }
37
+
38
+ updateClasses() {
39
+ if (!this.innerEl || !this.contentWrapper) return
40
+
41
+ const orientation = this.dataset.orientation || "vertical"
42
+ const alignment = this.dataset.alignment || "center"
43
+ const inverted = this.dataset.inverted === "true"
44
+
45
+ const effectiveAlignment = orientation === "horizontal" ? "left" : alignment
46
+
47
+ // Reset inner container classes
48
+ this.innerEl.className = "hero-preview-inner w-full"
49
+
50
+ // Apply orientation classes
51
+ if (orientation === "horizontal") {
52
+ this.innerEl.classList.add(
53
+ "flex",
54
+ "flex-col",
55
+ "gap-8",
56
+ "items-center",
57
+ "lg:grid",
58
+ "lg:grid-cols-2",
59
+ "lg:items-center",
60
+ "lg:gap-16"
61
+ )
62
+ } else {
63
+ this.innerEl.classList.add("flex", "flex-col", "gap-8", "items-center")
64
+ if (!inverted) {
65
+ this.innerEl.classList.add("flex-col-reverse")
66
+ }
67
+ }
68
+
69
+ // Apply alignment classes
70
+ this.innerEl.classList.remove(
71
+ "text-start",
72
+ "text-center",
73
+ "text-end",
74
+ "items-start",
75
+ "items-end"
76
+ )
77
+ if (effectiveAlignment === "left") {
78
+ this.innerEl.classList.add("text-start", "items-start")
79
+ } else if (effectiveAlignment === "right") {
80
+ this.innerEl.classList.add("text-end", "items-end")
81
+ } else {
82
+ this.innerEl.classList.add("text-center", "items-center")
83
+ }
84
+
85
+ // Update content wrapper
86
+ this.contentWrapper.classList.remove(
87
+ "text-center",
88
+ "items-center",
89
+ "text-start",
90
+ "items-start",
91
+ "text-end",
92
+ "items-end"
93
+ )
94
+ if (effectiveAlignment === "left") {
95
+ this.contentWrapper.classList.add("text-start", "items-start")
96
+ } else if (effectiveAlignment === "right") {
97
+ this.contentWrapper.classList.add("text-end", "items-end")
98
+ } else {
99
+ this.contentWrapper.classList.add("text-center", "items-center")
100
+ }
101
+ }
102
+ }
103
+
104
+ // Section Preview Custom Element
105
+ class SectionPreviewElement extends HTMLElement {
106
+ innerEl: HTMLElement | null = null
107
+ contentEl: HTMLElement | null = null
108
+ titleEl: HTMLElement | null = null
109
+ descEl: HTMLElement | null = null
110
+
111
+ static get observedAttributes() {
112
+ return ["data-orientation", "data-alignment", "data-inverted"]
113
+ }
114
+
115
+ connectedCallback() {
116
+ this.innerEl = this.querySelector<HTMLElement>(".section-preview-inner")
117
+ this.contentEl = this.querySelector<HTMLElement>(".section-content")
118
+ this.titleEl = this.querySelector<HTMLElement>("[data-section-title]")
119
+ this.descEl = this.querySelector<HTMLElement>("[data-section-description]")
120
+ if (!this.innerEl || !this.contentEl) return
121
+ this.updateClasses()
122
+ }
123
+
124
+ attributeChangedCallback(_name: string, _oldValue: string, newValue: string) {
125
+ if (_oldValue !== newValue) {
126
+ this.updateClasses()
127
+ }
128
+ }
129
+
130
+ updateClasses() {
131
+ if (!this.innerEl || !this.contentEl) return
132
+
133
+ const orientation = this.dataset.orientation || "vertical"
134
+ const alignment = this.dataset.alignment || "center"
135
+ const inverted = this.dataset.inverted === "true"
136
+
137
+ const effectiveAlignment = orientation === "horizontal" ? "left" : alignment
138
+
139
+ // Reset inner container classes
140
+ this.innerEl.className = "section-preview-inner w-full"
141
+ this.innerEl.classList.add("container", "py-24")
142
+
143
+ // Apply orientation classes
144
+ if (orientation === "horizontal") {
145
+ this.innerEl.classList.add("lg:grid", "lg:grid-cols-2", "lg:items-center", "lg:gap-16")
146
+ } else {
147
+ this.innerEl.classList.add("flex", "flex-col", "gap-12")
148
+ if (inverted) {
149
+ this.innerEl.classList.add("flex-col-reverse")
150
+ }
151
+ }
152
+
153
+ // Apply alignment classes
154
+ this.innerEl.classList.remove(
155
+ "text-start",
156
+ "text-center",
157
+ "text-end",
158
+ "items-start",
159
+ "items-end"
160
+ )
161
+ if (effectiveAlignment === "left") {
162
+ this.innerEl.classList.add("text-start", "items-start")
163
+ } else if (effectiveAlignment === "right") {
164
+ this.innerEl.classList.add("text-end", "items-end")
165
+ } else {
166
+ this.innerEl.classList.add("text-center", "items-center")
167
+ }
168
+
169
+ // Update content element
170
+ this.contentEl.classList.remove(
171
+ "text-center",
172
+ "items-center",
173
+ "text-start",
174
+ "items-start",
175
+ "text-end",
176
+ "items-end"
177
+ )
178
+ if (effectiveAlignment === "left") {
179
+ this.contentEl.classList.add("text-start", "items-start")
180
+ } else if (effectiveAlignment === "right") {
181
+ this.contentEl.classList.add("text-end", "items-end")
182
+ } else {
183
+ this.contentEl.classList.add("text-center", "items-center")
184
+ }
185
+ }
186
+ }
187
+
188
+ // Button Preview Custom Element
189
+ class ButtonPreviewElement extends HTMLElement {
190
+ button: HTMLElement | null = null
191
+
192
+ static get observedAttributes() {
193
+ return ["data-variant", "data-size", "data-children"]
194
+ }
195
+
196
+ connectedCallback() {
197
+ this.button = this.querySelector<HTMLElement>("[data-preview-button]")
198
+ if (!this.button) return
199
+ this.updateButton()
200
+ }
201
+
202
+ attributeChangedCallback(_name: string, oldValue: string, newValue: string) {
203
+ if (oldValue !== newValue && this.button) {
204
+ this.updateButton()
205
+ }
206
+ }
207
+
208
+ updateButton() {
209
+ if (!this.button) return
210
+
211
+ const variant = this.dataset.variant || "default"
212
+ const size = this.dataset.size || "md"
213
+ const children = this.dataset.children || "Button"
214
+
215
+ // Remove all variant classes
216
+ this.button.classList.remove(
217
+ "bg-input",
218
+ "hover:bg-input/90",
219
+ "text-foreground",
220
+ "bg-primary",
221
+ "text-primary-foreground",
222
+ "hover:bg-primary/90",
223
+ "bg-secondary",
224
+ "text-secondary-foreground",
225
+ "hover:bg-secondary/90",
226
+ "border",
227
+ "border-input",
228
+ "bg-background",
229
+ "hover:bg-accent",
230
+ "hover:text-accent-foreground",
231
+ "bg-info",
232
+ "text-info-foreground",
233
+ "hover:bg-info/90",
234
+ "bg-success",
235
+ "text-success-foreground",
236
+ "hover:bg-success/90",
237
+ "bg-warning",
238
+ "text-warning-foreground",
239
+ "hover:bg-warning/90",
240
+ "bg-error",
241
+ "text-error-foreground",
242
+ "hover:bg-error/90"
243
+ )
244
+
245
+ // Add new variant classes
246
+ const variantClasses: Record<string, string[]> = {
247
+ default: ["bg-input", "hover:bg-input/90", "text-foreground"],
248
+ primary: ["bg-primary", "text-primary-foreground", "hover:bg-primary/90"],
249
+ secondary: ["bg-secondary", "text-secondary-foreground", "hover:bg-secondary/90"],
250
+ outline: [
251
+ "border",
252
+ "border-input",
253
+ "bg-background",
254
+ "hover:bg-accent",
255
+ "hover:text-accent-foreground"
256
+ ],
257
+ ghost: ["hover:bg-accent", "hover:text-accent-foreground"],
258
+ info: ["bg-info", "text-info-foreground", "hover:bg-info/90"],
259
+ success: ["bg-success", "text-success-foreground", "hover:bg-success/90"],
260
+ warning: ["bg-warning", "text-warning-foreground", "hover:bg-warning/90"],
261
+ error: ["bg-error", "text-error-foreground", "hover:bg-error/90"]
262
+ }
263
+
264
+ variantClasses[variant]?.forEach((cls: string) => this.button?.classList.add(cls))
265
+
266
+ // Remove all size classes
267
+ this.button.classList.remove(
268
+ "h-9",
269
+ "px-3",
270
+ "text-sm",
271
+ "h-11",
272
+ "px-4",
273
+ "text-base",
274
+ "h-12",
275
+ "px-6",
276
+ "text-lg"
277
+ )
278
+
279
+ // Add new size classes
280
+ const sizeClasses: Record<string, string[]> = {
281
+ sm: ["h-9", "px-3", "text-sm"],
282
+ md: ["h-11", "px-4", "text-base"],
283
+ lg: ["h-12", "px-6", "text-lg"]
284
+ }
285
+
286
+ sizeClasses[size]?.forEach((cls: string) => this.button?.classList.add(cls))
287
+
288
+ // Update text
289
+ this.button.textContent = children
290
+ }
291
+ }
292
+
293
+ // Testimonials Preview Custom Element
294
+ class TestimonialsPreviewElement extends HTMLElement {
295
+ card: HTMLElement | null = null
296
+ quoteEl: HTMLElement | null = null
297
+ personEl: HTMLElement | null = null
298
+ metaEl: HTMLElement | null = null
299
+
300
+ static get observedAttributes() {
301
+ return ["data-alignment"]
302
+ }
303
+
304
+ connectedCallback() {
305
+ this.card = this.querySelector<HTMLElement>(".testimonial-card")
306
+ this.quoteEl = this.querySelector<HTMLElement>("[data-testimonials-quote]")
307
+ this.personEl = this.querySelector<HTMLElement>(".testimonial-person")
308
+ this.metaEl = this.querySelector<HTMLElement>(".testimonial-meta")
309
+ if (!this.card || !this.quoteEl || !this.personEl || !this.metaEl) return
310
+ this.updateClasses()
311
+ }
312
+
313
+ attributeChangedCallback(_name: string, oldValue: string, newValue: string) {
314
+ if (oldValue !== newValue) {
315
+ this.updateClasses()
316
+ }
317
+ }
318
+
319
+ updateClasses() {
320
+ if (!this.card || !this.quoteEl || !this.personEl || !this.metaEl) return
321
+
322
+ const alignment = this.dataset.alignment || "center"
323
+
324
+ // Reset all alignment classes
325
+ this.quoteEl.classList.remove("text-left", "text-center", "text-right")
326
+ this.personEl.classList.remove(
327
+ "flex-row",
328
+ "flex-row-reverse",
329
+ "items-center",
330
+ "justify-start",
331
+ "justify-end",
332
+ "flex-col",
333
+ "text-center"
334
+ )
335
+ this.metaEl.classList.remove(
336
+ "text-left",
337
+ "text-right",
338
+ "items-start",
339
+ "items-end",
340
+ "items-center"
341
+ )
342
+
343
+ if (alignment === "left") {
344
+ this.quoteEl.classList.add("text-left")
345
+ this.personEl.classList.add("flex-row", "items-center", "justify-start")
346
+ this.metaEl.classList.add("text-left", "items-start")
347
+ } else if (alignment === "right") {
348
+ this.quoteEl.classList.add("text-right")
349
+ this.personEl.classList.add("flex-row-reverse", "items-center", "justify-end")
350
+ this.metaEl.classList.add("text-right", "items-end")
351
+ } else {
352
+ // center
353
+ this.quoteEl.classList.add("text-center")
354
+ this.personEl.classList.add("flex-col", "items-center", "justify-center", "text-center")
355
+ this.metaEl.classList.add("items-center")
356
+ }
357
+ }
358
+ }
359
+
360
+ // Language Switcher Preview Custom Element
361
+ class LanguageSwitcherPreviewElement extends HTMLElement {
362
+ static get observedAttributes() {
363
+ return ["data-variant"]
364
+ }
365
+
366
+ connectedCallback() {
367
+ this.updateVariant()
368
+ }
369
+
370
+ attributeChangedCallback(_name: string, oldValue: string, newValue: string) {
371
+ if (oldValue !== newValue) {
372
+ this.updateVariant()
373
+ }
374
+ }
375
+
376
+ updateVariant() {
377
+ // Dispatch event for the actual component to handle
378
+ this.dispatchEvent(
379
+ new CustomEvent("language-switcher:variant-change", {
380
+ detail: { variant: this.dataset.variant },
381
+ bubbles: true
382
+ })
383
+ )
384
+ }
385
+ }
386
+
387
+ // Register all custom elements
388
+ if (!customElements.get("hero-preview")) {
389
+ customElements.define("hero-preview", HeroPreviewElement)
390
+ }
391
+ if (!customElements.get("section-preview")) {
392
+ customElements.define("section-preview", SectionPreviewElement)
393
+ }
394
+ if (!customElements.get("button-preview")) {
395
+ customElements.define("button-preview", ButtonPreviewElement)
396
+ }
397
+ if (!customElements.get("testimonials-preview")) {
398
+ customElements.define("testimonials-preview", TestimonialsPreviewElement)
399
+ }
400
+ if (!customElements.get("language-switcher-preview")) {
401
+ customElements.define("language-switcher-preview", LanguageSwitcherPreviewElement)
402
+ }
403
+ }
404
+
405
+ // Auto-register when module is imported
406
+ if (typeof window !== "undefined") {
407
+ registerShowcasePreviewElements()
408
+ }