@mccustomapps/helaui 0.1.10 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,7 @@
16
16
  - [TextField](#textfield)
17
17
  - [Calendar](#calendar)
18
18
  - [SunMandala](#sunmandala)
19
+ - [DestinationCard](#destinationcard)
19
20
  - [RainBackground](#rainbackground)
20
21
  - [Theming](#theming)
21
22
  - [i18n / Localisation](#i18n--localisation)
@@ -40,12 +41,13 @@ npm install @mccustomapps/helaui
40
41
  import '@mccustomapps/helaui/textfield.css';
41
42
  import '@mccustomapps/helaui/calendar.css';
42
43
  import '@mccustomapps/helaui/sun-mandala.css';
44
+ import '@mccustomapps/helaui/destination-card.css';
43
45
  import '@mccustomapps/helaui/rain-background.css';
44
46
  import '@mccustomapps/helaui/sinhala.css'; // Sinhala font face
45
47
  import '@mccustomapps/helaui/theme.css'; // CSS custom-property tokens
46
48
 
47
49
  // Import components
48
- import { TextField, Calendar, SunMandala, RainBackground } from '@mccustomapps/helaui';
50
+ import { TextField, Calendar, SunMandala, DestinationCard, RainBackground } from '@mccustomapps/helaui';
49
51
 
50
52
  // Mount components
51
53
  const field = new TextField({ variant: 'name' });
@@ -205,6 +207,42 @@ const mandala = new SunMandala({ src: '/my-mandala.png' });
205
207
 
206
208
  ---
207
209
 
210
+ ### DestinationCard
211
+
212
+ A decorative parchment **card shell** — cream background, teal/gold frame, side Sinhala script, and a peacock/lotus footer. The inner area is empty so you can place your own content.
213
+
214
+ ```js
215
+ import { DestinationCard } from '@mccustomapps/helaui';
216
+ import '@mccustomapps/helaui/destination-card.css';
217
+
218
+ const card = new DestinationCard({
219
+ height: '280px',
220
+ });
221
+
222
+ document.getElementById('container').appendChild(card.element);
223
+
224
+ // Optional: put your own content in the empty inner area
225
+ card.body.appendChild(document.createElement('div'));
226
+ ```
227
+
228
+ **Options — `DestinationCardOptions`**
229
+
230
+ | Option | Type | Default | Description |
231
+ |---|---|---|---|
232
+ | `content` | `HTMLElement \| string` | — | Inner content |
233
+ | `height` | `string` | `'280px'` | Minimum height of the inner area |
234
+ | `backdrop` | `boolean` | `false` | Blurred photo stage behind the card |
235
+ | `backdropImage` | `string` | — | Image used for the blurred stage |
236
+
237
+ **Methods**
238
+
239
+ ```js
240
+ card.setContent(node) // replace inner content
241
+ card.destroy()
242
+ ```
243
+
244
+ ---
245
+
208
246
  ### RainBackground
209
247
 
210
248
  An animated **tropical rain** background with falling food/leaf sprites. Built-in artwork is inlined in the JS bundle (no extra files to copy into `public/`).
@@ -216,7 +254,7 @@ import '@mccustomapps/helaui/rain-background.css';
216
254
  const rain = new RainBackground();
217
255
  ```
218
256
 
219
- **Next.js (App Router)** — use a Client Component. `RainBackground` already mounts itself on `document.body`; do not insert `rain.element` a second time.
257
+ **Next.js (App Router)** — use a Client Component. `RainBackground` already mounts itself on `document.body`; do **not** call `insertBefore` / `appendChild` on `rain.element` a second time.
220
258
 
221
259
  ```tsx
222
260
  'use client';
@@ -227,7 +265,7 @@ import '@mccustomapps/helaui/rain-background.css';
227
265
 
228
266
  export function HelaRain() {
229
267
  useEffect(() => {
230
- const rain = new RainBackground();
268
+ const rain = new RainBackground({ zIndex: 0 });
231
269
  return () => rain.destroy();
232
270
  }, []);
233
271
 
@@ -241,7 +279,7 @@ export function HelaRain() {
241
279
  |---|---|---|---|
242
280
  | `images` | `string[]` | built-in artwork | Image URLs |
243
281
  | `density` | `number` | `28` | Number of particles |
244
- | `minSize` / `maxSize` | `number` | `28` | Particle width in pixels |
282
+ | `minSize` / `maxSize` | `number` | `28` / `72` | Particle width in pixels |
245
283
  | `minDuration` / `maxDuration` | `number` | `8` / `18` | Fall duration in seconds |
246
284
  | `target` | `HTMLElement` | `document.body` | Mount container |
247
285
  | `zIndex` | `number` | `0` | Stacking order |
@@ -339,6 +377,7 @@ import '@mccustomapps/helaui/sinhala.css'; // Sinhala font face
339
377
  import '@mccustomapps/helaui/textfield.css'; // TextField styles
340
378
  import '@mccustomapps/helaui/calendar.css'; // Calendar styles
341
379
  import '@mccustomapps/helaui/sun-mandala.css'; // SunMandala styles
380
+ import '@mccustomapps/helaui/destination-card.css'; // DestinationCard styles
342
381
  import '@mccustomapps/helaui/rain-background.css'; // RainBackground styles
343
382
  ```
344
383
 
@@ -353,6 +392,7 @@ import type {
353
392
  TextFieldOptions,
354
393
  CalendarOptions,
355
394
  SunMandalaOptions,
395
+ DestinationCardOptions,
356
396
  RainBackgroundOptions,
357
397
  HelaTheme,
358
398
  HelaThemeKey,
@@ -0,0 +1,134 @@
1
+ /* HelaUI Destination Card — decorative parchment shell */
2
+
3
+ .helaui-destination-card-stage {
4
+ position: relative;
5
+ display: flex;
6
+ justify-content: center;
7
+ align-items: stretch;
8
+ padding: 28px 20px;
9
+ overflow: hidden;
10
+ border-radius: 32px;
11
+ isolation: isolate;
12
+ }
13
+
14
+ .helaui-destination-card-stage__blur {
15
+ position: absolute;
16
+ inset: -24px;
17
+ background-size: cover;
18
+ background-position: center;
19
+ filter: blur(22px) saturate(1.1);
20
+ transform: scale(1.08);
21
+ z-index: 0;
22
+ }
23
+
24
+ .helaui-destination-card-stage__blur::after {
25
+ content: '';
26
+ position: absolute;
27
+ inset: 0;
28
+ background: rgba(20, 28, 24, 0.28);
29
+ }
30
+
31
+ .helaui-destination-card {
32
+ --helaui-card-cream: #f9f3e6;
33
+ --helaui-card-ink: #2d1e17;
34
+ --helaui-card-teal: #1a4d4e;
35
+ --helaui-card-orange: var(--helaui-accent, #c86837);
36
+
37
+ position: relative;
38
+ z-index: 1;
39
+ display: flex;
40
+ flex-direction: column;
41
+ width: min(100%, 380px);
42
+ margin: 0 auto;
43
+ padding: 14px 16px 0;
44
+ box-sizing: border-box;
45
+ background:
46
+ radial-gradient(circle at 12% 18%, rgba(26, 77, 78, 0.06), transparent 28%),
47
+ radial-gradient(circle at 88% 72%, rgba(200, 104, 55, 0.08), transparent 32%),
48
+ var(--helaui-card-cream);
49
+ color: var(--helaui-card-ink);
50
+ border: 1.5px solid var(--helaui-card-teal);
51
+ border-radius: 30px;
52
+ box-shadow:
53
+ inset 0 0 0 5px var(--helaui-card-cream),
54
+ inset 0 0 0 6.5px #c4a574,
55
+ 0 18px 40px rgba(26, 32, 24, 0.28);
56
+ overflow: hidden;
57
+ font-family: Georgia, 'Times New Roman', serif;
58
+ }
59
+
60
+ .helaui-destination-card__watermark {
61
+ pointer-events: none;
62
+ position: absolute;
63
+ inset: 0;
64
+ opacity: 0.09;
65
+ background-image:
66
+ radial-gradient(circle at 50% 50%, transparent 42%, rgba(26, 77, 78, 0.55) 43%, transparent 46%),
67
+ repeating-radial-gradient(circle at 50% 50%, rgba(26, 77, 78, 0.35) 0 1px, transparent 1px 18px);
68
+ background-size: 220px 220px, 220px 220px;
69
+ background-position: 80% 30%, 10% 80%;
70
+ mix-blend-mode: multiply;
71
+ }
72
+
73
+ .helaui-destination-card__script {
74
+ pointer-events: none;
75
+ position: absolute;
76
+ top: 22%;
77
+ right: 8px;
78
+ writing-mode: vertical-rl;
79
+ font-size: 0.72rem;
80
+ letter-spacing: 0.28em;
81
+ color: #c4a574;
82
+ opacity: 0.55;
83
+ z-index: 2;
84
+ }
85
+
86
+ .helaui-destination-card__body {
87
+ position: relative;
88
+ z-index: 1;
89
+ flex: 1;
90
+ min-height: 280px;
91
+ }
92
+
93
+ .helaui-destination-card__footer {
94
+ position: relative;
95
+ height: 92px;
96
+ margin: 4px -16px 0;
97
+ background: linear-gradient(90deg, #c86837 0%, #b8432f 55%, #c86837 100%);
98
+ overflow: hidden;
99
+ }
100
+
101
+ .helaui-destination-card__footer::before {
102
+ content: '';
103
+ position: absolute;
104
+ inset: 0;
105
+ opacity: 0.22;
106
+ background-image:
107
+ radial-gradient(circle at 20% 40%, #f9f3e6 0 10%, transparent 11%),
108
+ radial-gradient(circle at 70% 70%, #f9f3e6 0 8%, transparent 9%),
109
+ radial-gradient(circle at 50% 20%, transparent 40%, rgba(249, 243, 230, 0.5) 41%, transparent 44%);
110
+ }
111
+
112
+ .helaui-destination-card__peacocks,
113
+ .helaui-destination-card__lotus {
114
+ position: absolute;
115
+ bottom: -4px;
116
+ z-index: 1;
117
+ }
118
+
119
+ .helaui-destination-card__peacocks {
120
+ left: 6px;
121
+ width: 58%;
122
+ }
123
+
124
+ .helaui-destination-card__peacocks svg,
125
+ .helaui-destination-card__lotus svg {
126
+ display: block;
127
+ width: 100%;
128
+ height: auto;
129
+ }
130
+
131
+ .helaui-destination-card__lotus {
132
+ right: 4px;
133
+ width: 34%;
134
+ }
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  BUILTIN_SUN_MANDALA_IMAGE: () => BUILTIN_SUN_MANDALA_IMAGE,
25
25
  Calendar: () => Calendar,
26
26
  DEFAULT_RAIN_IMAGES: () => DEFAULT_RAIN_IMAGES,
27
+ DestinationCard: () => DestinationCard,
27
28
  RainBackground: () => RainBackground,
28
29
  SINHALA_FONT_CLASS: () => SINHALA_FONT_CLASS,
29
30
  SINHALA_FONT_FAMILY: () => SINHALA_FONT_FAMILY,
@@ -40,6 +41,7 @@ __export(index_exports, {
40
41
  formatSinhalaNumeral: () => formatSinhalaNumeral,
41
42
  getCalendarTranslation: () => getCalendarTranslation,
42
43
  getDefaultRainImageUrls: () => getDefaultRainImageUrls,
44
+ getDestinationCardTranslation: () => getDestinationCardTranslation,
43
45
  getLocale: () => getLocale,
44
46
  getTextFieldTranslation: () => getTextFieldTranslation,
45
47
  resetTheme: () => resetTheme,
@@ -119,6 +121,14 @@ var defaultTranslations = {
119
121
  clear: "Clear",
120
122
  selectMonth: "Select Month",
121
123
  poyaDay: "Poya Day"
124
+ },
125
+ destinationCard: {
126
+ eyebrow: "Explore Sri Lanka",
127
+ reviewsLabel: "Reviews",
128
+ bookTrip: "Book Your Trip",
129
+ exploreNow: "Explore Now",
130
+ startFrom: "Start from",
131
+ perPerson: "/ person"
122
132
  }
123
133
  },
124
134
  si: {
@@ -176,6 +186,14 @@ var defaultTranslations = {
176
186
  clear: "\u0DB4\u0DD2\u0DBB\u0DD2\u0DC3\u0DD2\u0DAF\u0DD4 \u0D9A\u0DBB\u0DB1\u0DCA\u0DB1",
177
187
  selectMonth: "\u0DB8\u0DCF\u0DC3\u0DBA \u0DAD\u0DDD\u0DBB\u0DB1\u0DCA\u0DB1",
178
188
  poyaDay: "\u0DB4\u0DDD\u0DBA \u0DAF\u0DD2\u0DB1"
189
+ },
190
+ destinationCard: {
191
+ eyebrow: "\u0DC1\u0DCA\u200D\u0DBB\u0DD3 \u0DBD\u0D82\u0D9A\u0DCF\u0DC0 \u0DC3\u0DDC\u0DBA\u0DCF \u0DB6\u0DBD\u0DB1\u0DCA\u0DB1",
192
+ reviewsLabel: "\u0DC3\u0DB8\u0DCF\u0DBD\u0DDD\u0DA0\u0DB1",
193
+ bookTrip: "\u0D94\u0DB6\u0DDA \u0DC3\u0D82\u0DA0\u0DCF\u0DBB\u0DBA \u0DC0\u0DD9\u0DB1\u0DCA\u0D9A\u0DBB\u0DC0\u0DB1\u0DCA\u0DB1",
194
+ exploreNow: "\u0DAF\u0DD0\u0DB1\u0DCA \u0D9C\u0DC0\u0DDA\u0DC2\u0DAB\u0DBA \u0D9A\u0DBB\u0DB1\u0DCA\u0DB1",
195
+ startFrom: "\u0D86\u0DBB\u0DB8\u0DCA\u0DB7\u0DBA",
196
+ perPerson: "/ \u0DB4\u0DD4\u0DAF\u0DCA\u0D9C\u0DBD\u0DBA\u0D9A\u0DD4\u0DA7"
179
197
  }
180
198
  }
181
199
  };
@@ -265,6 +283,9 @@ function getTextFieldTranslation(variant, locale = currentLocale) {
265
283
  function getCalendarTranslation(locale = currentLocale) {
266
284
  return getMergedTranslations(locale).calendar ?? defaultTranslations[locale].calendar;
267
285
  }
286
+ function getDestinationCardTranslation(locale = currentLocale) {
287
+ return getMergedTranslations(locale).destinationCard ?? defaultTranslations[locale].destinationCard;
288
+ }
268
289
  function subscribeLocaleChange(listener) {
269
290
  localeListeners.add(listener);
270
291
  return () => localeListeners.delete(listener);
@@ -405,16 +426,43 @@ function pick(items) {
405
426
  return items[Math.floor(Math.random() * items.length)];
406
427
  }
407
428
  function toImageUrl(source) {
408
- return typeof source === "string" ? source : source.src;
429
+ if (typeof source === "string") return source;
430
+ if (source && typeof source === "object") {
431
+ const value = source;
432
+ if (typeof value.src === "string") return value.src;
433
+ if (typeof value.default === "string") return value.default;
434
+ if (value.default && typeof value.default === "object" && value.default !== null) {
435
+ const nested = value.default;
436
+ if (typeof nested.src === "string") return nested.src;
437
+ }
438
+ }
439
+ throw new Error("RainBackground image must be a URL string.");
440
+ }
441
+ function toDisplayUrl(source, cache) {
442
+ const existing = cache.get(source);
443
+ if (existing) return existing;
444
+ if (!source.startsWith("data:")) return source;
445
+ const comma = source.indexOf(",");
446
+ if (comma === -1) return source;
447
+ const header = source.slice(0, comma);
448
+ const data = source.slice(comma + 1);
449
+ const mime = header.match(/^data:([^;,]+)/i)?.[1] ?? "image/png";
450
+ const binary = atob(data);
451
+ const bytes = new Uint8Array(binary.length);
452
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
453
+ const url = URL.createObjectURL(new Blob([bytes], { type: mime }));
454
+ cache.set(source, url);
455
+ return url;
409
456
  }
410
457
  var RainBackground = class {
411
458
  element;
459
+ objectUrls = [];
412
460
  constructor(options = {}) {
413
461
  const {
414
462
  images = [...BUILTIN_RAIN_IMAGES],
415
463
  density = 28,
416
464
  minSize = 28,
417
- maxSize = 28,
465
+ maxSize = 72,
418
466
  minDuration = 8,
419
467
  maxDuration = 18,
420
468
  target = document.body,
@@ -427,8 +475,11 @@ var RainBackground = class {
427
475
  this.element.className = "helaui-rain-background";
428
476
  this.element.setAttribute("aria-hidden", "true");
429
477
  this.element.style.zIndex = String(zIndex);
478
+ const urlCache = /* @__PURE__ */ new Map();
479
+ const resolved = images.map((image) => toDisplayUrl(toImageUrl(image), urlCache));
480
+ this.objectUrls.push(...new Set(urlCache.values()));
430
481
  for (let index = 0; index < density; index += 1) {
431
- this.element.appendChild(this.createDrop(images, minSize, maxSize, minDuration, maxDuration));
482
+ this.element.appendChild(this.createDrop(resolved, minSize, maxSize, minDuration, maxDuration));
432
483
  }
433
484
  target.prepend(this.element);
434
485
  }
@@ -436,9 +487,16 @@ var RainBackground = class {
436
487
  const drop = document.createElement("div");
437
488
  const duration = randomBetween(minDuration, maxDuration);
438
489
  const size = randomBetween(minSize, maxSize);
439
- const src = toImageUrl(pick(images));
490
+ const src = pick(images);
491
+ const img = document.createElement("img");
492
+ img.className = "helaui-rain-background__image";
493
+ img.src = src;
494
+ img.alt = "";
495
+ img.draggable = false;
496
+ img.decoding = "async";
497
+ img.loading = "eager";
498
+ img.setAttribute("fetchpriority", "high");
440
499
  drop.className = "helaui-rain-background__drop";
441
- drop.style.backgroundImage = `url(${JSON.stringify(src)})`;
442
500
  drop.style.setProperty("--helaui-rain-x", `${randomBetween(0, 100)}%`);
443
501
  drop.style.setProperty("--helaui-rain-size", `${size}px`);
444
502
  drop.style.setProperty("--helaui-rain-duration", `${duration.toFixed(2)}s`);
@@ -446,11 +504,14 @@ var RainBackground = class {
446
504
  drop.style.setProperty("--helaui-rain-rotation", `${randomBetween(-35, 35).toFixed(1)}deg`);
447
505
  drop.style.setProperty("--helaui-rain-spin", `${randomBetween(90, 360).toFixed(0)}deg`);
448
506
  drop.style.setProperty("--helaui-rain-drift", `${randomBetween(-40, 40).toFixed(0)}px`);
507
+ drop.appendChild(img);
449
508
  return drop;
450
509
  }
451
510
  /** Removes the background from the DOM. */
452
511
  destroy() {
453
512
  this.element.remove();
513
+ for (const url of this.objectUrls) URL.revokeObjectURL(url);
514
+ this.objectUrls.length = 0;
454
515
  }
455
516
  };
456
517
  var DEFAULT_RAIN_IMAGES = ["fritter.png", "swirl.png", "kokis.png", "leaf.png"];
@@ -924,6 +985,106 @@ var SunMandala = class {
924
985
  }
925
986
  };
926
987
 
988
+ // src/components/DestinationCard.ts
989
+ var PEACOCK_SVG = `
990
+ <svg viewBox="0 0 220 110" aria-hidden="true">
991
+ <g fill="none" stroke-linecap="round">
992
+ <path d="M18 92c8-28 28-52 58-62" stroke="#1a6b6c" stroke-width="5"/>
993
+ <path d="M32 96c10-22 30-40 56-46" stroke="#2a8f7a" stroke-width="4"/>
994
+ <path d="M48 100c10-16 26-28 46-32" stroke="#c86837" stroke-width="3.5"/>
995
+ <ellipse cx="28" cy="38" rx="7" ry="10" fill="#1a4d4e" transform="rotate(-20 28 38)"/>
996
+ <circle cx="26" cy="34" r="1.6" fill="#f9f3e6"/>
997
+ <path d="M22 42c8 6 18 8 28 4" stroke="#1a4d4e" stroke-width="3"/>
998
+ <circle cx="62" cy="28" r="9" fill="#1a6b6c" opacity=".85"/>
999
+ <circle cx="62" cy="28" r="4" fill="#d4af37"/>
1000
+ <circle cx="88" cy="42" r="10" fill="#2a8f7a" opacity=".8"/>
1001
+ <circle cx="88" cy="42" r="4" fill="#c86837"/>
1002
+ <circle cx="74" cy="62" r="9" fill="#1a4d4e" opacity=".75"/>
1003
+ <circle cx="74" cy="62" r="3.5" fill="#d4af37"/>
1004
+ <path d="M108 96c8-24 26-44 54-52" stroke="#1a6b6c" stroke-width="5"/>
1005
+ <path d="M118 100c8-18 24-32 48-36" stroke="#c86837" stroke-width="3.5"/>
1006
+ <ellipse cx="116" cy="48" rx="6.5" ry="9" fill="#1a4d4e" transform="rotate(-18 116 48)"/>
1007
+ <circle cx="114" cy="44" r="1.4" fill="#f9f3e6"/>
1008
+ <circle cx="148" cy="36" r="8" fill="#1a6b6c" opacity=".85"/>
1009
+ <circle cx="148" cy="36" r="3.5" fill="#d4af37"/>
1010
+ <circle cx="168" cy="52" r="8" fill="#2a8f7a" opacity=".8"/>
1011
+ <circle cx="168" cy="52" r="3.5" fill="#c86837"/>
1012
+ </g>
1013
+ <ellipse cx="96" cy="98" rx="10" ry="6" fill="#e8c9d4"/>
1014
+ <path d="M90 96c4-10 12-12 18-2" fill="#d46a8a"/>
1015
+ <path d="M94 98c3-7 9-8 13-1" fill="#f4c6d4"/>
1016
+ </svg>
1017
+ `;
1018
+ var LOTUS_SVG = `
1019
+ <svg viewBox="0 0 120 110" aria-hidden="true">
1020
+ <path d="M60 96c-16-18-28-34-28-50 0-10 8-16 16-12 6 3 10 12 12 22 2-14 8-28 18-34 8-5 16 0 16 12 0 16-12 32-34 62z" fill="#e8a0b8"/>
1021
+ <path d="M60 96c-10-16-22-36-18-54 3-12 14-16 18-6 4-16 16-24 24-18 8 6 6 22-2 36-8 14-16 28-22 42z" fill="#d46a8a"/>
1022
+ <path d="M60 98c6-20 8-38 4-52-3-10-12-12-14-4-4-12-2-24 8-28 10-4 18 6 18 18 0 16-6 34-16 66z" fill="#f3c3d2"/>
1023
+ <circle cx="60" cy="42" r="8" fill="#d4af37"/>
1024
+ <circle cx="60" cy="42" r="3.5" fill="#f9f3e6"/>
1025
+ </svg>
1026
+ `;
1027
+ function el(tag, className, text) {
1028
+ const node = document.createElement(tag);
1029
+ node.className = className;
1030
+ if (text !== void 0) node.textContent = text;
1031
+ return node;
1032
+ }
1033
+ var DestinationCard = class {
1034
+ element;
1035
+ card;
1036
+ body;
1037
+ constructor(options = {}) {
1038
+ const { content, height = "280px", backdrop = false, backdropImage } = options;
1039
+ this.card = el("article", "helaui-destination-card");
1040
+ this.card.setAttribute("aria-label", "HelaUI card");
1041
+ const watermark = el("div", "helaui-destination-card__watermark");
1042
+ watermark.setAttribute("aria-hidden", "true");
1043
+ this.card.appendChild(watermark);
1044
+ const script = el("div", `helaui-destination-card__script ${SINHALA_FONT_CLASS}`, "\u0DC1\u0DCA\u200D\u0DBB\u0DD3 \u0DBD\u0D82\u0D9A\u0DCF\u0DC0");
1045
+ script.setAttribute("aria-hidden", "true");
1046
+ this.card.appendChild(script);
1047
+ this.body = el("div", "helaui-destination-card__body");
1048
+ this.body.style.minHeight = height;
1049
+ if (typeof content === "string") {
1050
+ this.body.innerHTML = content;
1051
+ } else if (content) {
1052
+ this.body.appendChild(content);
1053
+ }
1054
+ this.card.appendChild(this.body);
1055
+ const footer = el("div", "helaui-destination-card__footer");
1056
+ footer.setAttribute("aria-hidden", "true");
1057
+ const peacocks = el("div", "helaui-destination-card__peacocks");
1058
+ peacocks.innerHTML = PEACOCK_SVG.trim();
1059
+ const lotus = el("div", "helaui-destination-card__lotus");
1060
+ lotus.innerHTML = LOTUS_SVG.trim();
1061
+ footer.append(peacocks, lotus);
1062
+ this.card.appendChild(footer);
1063
+ if (backdrop) {
1064
+ const stage = el("div", "helaui-destination-card-stage");
1065
+ const blur = el("div", "helaui-destination-card-stage__blur");
1066
+ if (backdropImage) {
1067
+ blur.style.backgroundImage = `url("${backdropImage.replace(/"/g, "")}")`;
1068
+ }
1069
+ stage.append(blur, this.card);
1070
+ this.element = stage;
1071
+ } else {
1072
+ this.element = this.card;
1073
+ }
1074
+ }
1075
+ setContent(content) {
1076
+ this.body.replaceChildren();
1077
+ if (typeof content === "string") {
1078
+ this.body.innerHTML = content;
1079
+ } else {
1080
+ this.body.appendChild(content);
1081
+ }
1082
+ }
1083
+ destroy() {
1084
+ this.element.remove();
1085
+ }
1086
+ };
1087
+
927
1088
  // src/theme/defaultTheme.ts
928
1089
  var defaultTheme = {
929
1090
  primary: "#8D153A",
@@ -999,6 +1160,7 @@ function createThemeStyle(theme = {}) {
999
1160
  BUILTIN_SUN_MANDALA_IMAGE,
1000
1161
  Calendar,
1001
1162
  DEFAULT_RAIN_IMAGES,
1163
+ DestinationCard,
1002
1164
  RainBackground,
1003
1165
  SINHALA_FONT_CLASS,
1004
1166
  SINHALA_FONT_FAMILY,
@@ -1015,6 +1177,7 @@ function createThemeStyle(theme = {}) {
1015
1177
  formatSinhalaNumeral,
1016
1178
  getCalendarTranslation,
1017
1179
  getDefaultRainImageUrls,
1180
+ getDestinationCardTranslation,
1018
1181
  getLocale,
1019
1182
  getTextFieldTranslation,
1020
1183
  resetTheme,
package/dist/index.d.cts CHANGED
@@ -25,6 +25,14 @@ interface HelaTextFieldTranslations {
25
25
  email: TextFieldStrings;
26
26
  disabled: TextFieldStrings;
27
27
  }
28
+ interface DestinationCardStrings {
29
+ eyebrow: string;
30
+ reviewsLabel: string;
31
+ bookTrip: string;
32
+ exploreNow: string;
33
+ startFrom: string;
34
+ perPerson: string;
35
+ }
28
36
  interface CalendarStrings {
29
37
  monthNames: [string, string, string, string, string, string, string, string, string, string, string, string];
30
38
  sinhalaMonthNames: [string, string, string, string, string, string, string, string, string, string, string, string];
@@ -40,6 +48,7 @@ interface CalendarStrings {
40
48
  interface HelaTranslations {
41
49
  textField: HelaTextFieldTranslations;
42
50
  calendar: CalendarStrings;
51
+ destinationCard: DestinationCardStrings;
43
52
  }
44
53
  type TextFieldVariant = keyof HelaTextFieldTranslations;
45
54
  type DeepPartial<T> = {
@@ -84,6 +93,8 @@ declare function t(path: string, locale?: HelaLocale): string;
84
93
  declare function getTextFieldTranslation(variant: TextFieldVariant, locale?: HelaLocale): TextFieldStrings;
85
94
  /** Built-in Calendar strings for the active (or given) locale. */
86
95
  declare function getCalendarTranslation(locale?: HelaLocale): CalendarStrings;
96
+ /** Built-in DestinationCard strings for the active (or given) locale. */
97
+ declare function getDestinationCardTranslation(locale?: HelaLocale): DestinationCardStrings;
87
98
  /** Subscribe to locale or override changes. Returns an unsubscribe function. */
88
99
  declare function subscribeLocaleChange(listener: () => void): () => void;
89
100
 
@@ -162,6 +173,7 @@ interface RainBackgroundOptions {
162
173
  /** Full-screen animated background with falling image particles. */
163
174
  declare class RainBackground {
164
175
  readonly element: HTMLDivElement;
176
+ private readonly objectUrls;
165
177
  constructor(options?: RainBackgroundOptions);
166
178
  private createDrop;
167
179
  /** Removes the background from the DOM. */
@@ -306,6 +318,29 @@ declare class SunMandala {
306
318
  destroy(): void;
307
319
  }
308
320
 
321
+ interface DestinationCardOptions {
322
+ /** Optional node or HTML placed in the empty inner area. */
323
+ content?: HTMLElement | string;
324
+ /** Minimum height of the inner area. Defaults to `280px`. */
325
+ height?: string;
326
+ /** Blurred photo stage behind the card. Defaults to `false`. */
327
+ backdrop?: boolean;
328
+ /** Image used for the optional blurred stage. */
329
+ backdropImage?: string;
330
+ }
331
+ /**
332
+ * Decorative parchment card shell — frame, watermark, side script, and footer motifs.
333
+ * The inner area is empty so callers can place their own content.
334
+ */
335
+ declare class DestinationCard {
336
+ readonly element: HTMLElement;
337
+ readonly card: HTMLElement;
338
+ readonly body: HTMLDivElement;
339
+ constructor(options?: DestinationCardOptions);
340
+ setContent(content: HTMLElement | string): void;
341
+ destroy(): void;
342
+ }
343
+
309
344
  /** HelaUI brand palette — all values are customizable. */
310
345
  interface HelaTheme {
311
346
  primary: string;
@@ -338,4 +373,4 @@ declare const defaultTheme: HelaTheme;
338
373
  /** Maps theme keys to CSS custom property names. */
339
374
  declare const THEME_VAR_MAP: Record<HelaThemeKey, string>;
340
375
 
341
- export { type ApplyTranslationsOptions, BUILTIN_RAIN_IMAGES, BUILTIN_SUN_MANDALA_IMAGE, Calendar, type CalendarOptions, type CalendarStrings, DEFAULT_RAIN_IMAGES, type DeepPartial, type HelaLocale, type HelaTextFieldTranslations, type HelaTheme, type HelaThemeKey, type HelaTranslationOverrides, type HelaTranslations, type LocaleTranslationOverrides, RainBackground, type RainBackgroundOptions, SINHALA_FONT_CLASS, SINHALA_FONT_FAMILY, SINHALA_MONTH_NAMES, SunMandala, type SunMandalaOptions, THEME_VAR_MAP, TextField, type TextFieldOptions, type TextFieldStrings, type TextFieldVariant, applySinhalaFont, applyTheme, applyTranslations, createThemeStyle, defaultTheme, defaultTranslations, formatSinhalaNumeral, getCalendarTranslation, getDefaultRainImageUrls, getLocale, getTextFieldTranslation, resetTheme, resetTranslations, setLocale, sinhalaFontStyles, subscribeLocaleChange, t };
376
+ export { type ApplyTranslationsOptions, BUILTIN_RAIN_IMAGES, BUILTIN_SUN_MANDALA_IMAGE, Calendar, type CalendarOptions, type CalendarStrings, DEFAULT_RAIN_IMAGES, type DeepPartial, DestinationCard, type DestinationCardOptions, type DestinationCardStrings, type HelaLocale, type HelaTextFieldTranslations, type HelaTheme, type HelaThemeKey, type HelaTranslationOverrides, type HelaTranslations, type LocaleTranslationOverrides, RainBackground, type RainBackgroundOptions, SINHALA_FONT_CLASS, SINHALA_FONT_FAMILY, SINHALA_MONTH_NAMES, SunMandala, type SunMandalaOptions, THEME_VAR_MAP, TextField, type TextFieldOptions, type TextFieldStrings, type TextFieldVariant, applySinhalaFont, applyTheme, applyTranslations, createThemeStyle, defaultTheme, defaultTranslations, formatSinhalaNumeral, getCalendarTranslation, getDefaultRainImageUrls, getDestinationCardTranslation, getLocale, getTextFieldTranslation, resetTheme, resetTranslations, setLocale, sinhalaFontStyles, subscribeLocaleChange, t };
package/dist/index.d.ts CHANGED
@@ -25,6 +25,14 @@ interface HelaTextFieldTranslations {
25
25
  email: TextFieldStrings;
26
26
  disabled: TextFieldStrings;
27
27
  }
28
+ interface DestinationCardStrings {
29
+ eyebrow: string;
30
+ reviewsLabel: string;
31
+ bookTrip: string;
32
+ exploreNow: string;
33
+ startFrom: string;
34
+ perPerson: string;
35
+ }
28
36
  interface CalendarStrings {
29
37
  monthNames: [string, string, string, string, string, string, string, string, string, string, string, string];
30
38
  sinhalaMonthNames: [string, string, string, string, string, string, string, string, string, string, string, string];
@@ -40,6 +48,7 @@ interface CalendarStrings {
40
48
  interface HelaTranslations {
41
49
  textField: HelaTextFieldTranslations;
42
50
  calendar: CalendarStrings;
51
+ destinationCard: DestinationCardStrings;
43
52
  }
44
53
  type TextFieldVariant = keyof HelaTextFieldTranslations;
45
54
  type DeepPartial<T> = {
@@ -84,6 +93,8 @@ declare function t(path: string, locale?: HelaLocale): string;
84
93
  declare function getTextFieldTranslation(variant: TextFieldVariant, locale?: HelaLocale): TextFieldStrings;
85
94
  /** Built-in Calendar strings for the active (or given) locale. */
86
95
  declare function getCalendarTranslation(locale?: HelaLocale): CalendarStrings;
96
+ /** Built-in DestinationCard strings for the active (or given) locale. */
97
+ declare function getDestinationCardTranslation(locale?: HelaLocale): DestinationCardStrings;
87
98
  /** Subscribe to locale or override changes. Returns an unsubscribe function. */
88
99
  declare function subscribeLocaleChange(listener: () => void): () => void;
89
100
 
@@ -162,6 +173,7 @@ interface RainBackgroundOptions {
162
173
  /** Full-screen animated background with falling image particles. */
163
174
  declare class RainBackground {
164
175
  readonly element: HTMLDivElement;
176
+ private readonly objectUrls;
165
177
  constructor(options?: RainBackgroundOptions);
166
178
  private createDrop;
167
179
  /** Removes the background from the DOM. */
@@ -306,6 +318,29 @@ declare class SunMandala {
306
318
  destroy(): void;
307
319
  }
308
320
 
321
+ interface DestinationCardOptions {
322
+ /** Optional node or HTML placed in the empty inner area. */
323
+ content?: HTMLElement | string;
324
+ /** Minimum height of the inner area. Defaults to `280px`. */
325
+ height?: string;
326
+ /** Blurred photo stage behind the card. Defaults to `false`. */
327
+ backdrop?: boolean;
328
+ /** Image used for the optional blurred stage. */
329
+ backdropImage?: string;
330
+ }
331
+ /**
332
+ * Decorative parchment card shell — frame, watermark, side script, and footer motifs.
333
+ * The inner area is empty so callers can place their own content.
334
+ */
335
+ declare class DestinationCard {
336
+ readonly element: HTMLElement;
337
+ readonly card: HTMLElement;
338
+ readonly body: HTMLDivElement;
339
+ constructor(options?: DestinationCardOptions);
340
+ setContent(content: HTMLElement | string): void;
341
+ destroy(): void;
342
+ }
343
+
309
344
  /** HelaUI brand palette — all values are customizable. */
310
345
  interface HelaTheme {
311
346
  primary: string;
@@ -338,4 +373,4 @@ declare const defaultTheme: HelaTheme;
338
373
  /** Maps theme keys to CSS custom property names. */
339
374
  declare const THEME_VAR_MAP: Record<HelaThemeKey, string>;
340
375
 
341
- export { type ApplyTranslationsOptions, BUILTIN_RAIN_IMAGES, BUILTIN_SUN_MANDALA_IMAGE, Calendar, type CalendarOptions, type CalendarStrings, DEFAULT_RAIN_IMAGES, type DeepPartial, type HelaLocale, type HelaTextFieldTranslations, type HelaTheme, type HelaThemeKey, type HelaTranslationOverrides, type HelaTranslations, type LocaleTranslationOverrides, RainBackground, type RainBackgroundOptions, SINHALA_FONT_CLASS, SINHALA_FONT_FAMILY, SINHALA_MONTH_NAMES, SunMandala, type SunMandalaOptions, THEME_VAR_MAP, TextField, type TextFieldOptions, type TextFieldStrings, type TextFieldVariant, applySinhalaFont, applyTheme, applyTranslations, createThemeStyle, defaultTheme, defaultTranslations, formatSinhalaNumeral, getCalendarTranslation, getDefaultRainImageUrls, getLocale, getTextFieldTranslation, resetTheme, resetTranslations, setLocale, sinhalaFontStyles, subscribeLocaleChange, t };
376
+ export { type ApplyTranslationsOptions, BUILTIN_RAIN_IMAGES, BUILTIN_SUN_MANDALA_IMAGE, Calendar, type CalendarOptions, type CalendarStrings, DEFAULT_RAIN_IMAGES, type DeepPartial, DestinationCard, type DestinationCardOptions, type DestinationCardStrings, type HelaLocale, type HelaTextFieldTranslations, type HelaTheme, type HelaThemeKey, type HelaTranslationOverrides, type HelaTranslations, type LocaleTranslationOverrides, RainBackground, type RainBackgroundOptions, SINHALA_FONT_CLASS, SINHALA_FONT_FAMILY, SINHALA_MONTH_NAMES, SunMandala, type SunMandalaOptions, THEME_VAR_MAP, TextField, type TextFieldOptions, type TextFieldStrings, type TextFieldVariant, applySinhalaFont, applyTheme, applyTranslations, createThemeStyle, defaultTheme, defaultTranslations, formatSinhalaNumeral, getCalendarTranslation, getDefaultRainImageUrls, getDestinationCardTranslation, getLocale, getTextFieldTranslation, resetTheme, resetTranslations, setLocale, sinhalaFontStyles, subscribeLocaleChange, t };
package/dist/index.js CHANGED
@@ -66,6 +66,14 @@ var defaultTranslations = {
66
66
  clear: "Clear",
67
67
  selectMonth: "Select Month",
68
68
  poyaDay: "Poya Day"
69
+ },
70
+ destinationCard: {
71
+ eyebrow: "Explore Sri Lanka",
72
+ reviewsLabel: "Reviews",
73
+ bookTrip: "Book Your Trip",
74
+ exploreNow: "Explore Now",
75
+ startFrom: "Start from",
76
+ perPerson: "/ person"
69
77
  }
70
78
  },
71
79
  si: {
@@ -123,6 +131,14 @@ var defaultTranslations = {
123
131
  clear: "\u0DB4\u0DD2\u0DBB\u0DD2\u0DC3\u0DD2\u0DAF\u0DD4 \u0D9A\u0DBB\u0DB1\u0DCA\u0DB1",
124
132
  selectMonth: "\u0DB8\u0DCF\u0DC3\u0DBA \u0DAD\u0DDD\u0DBB\u0DB1\u0DCA\u0DB1",
125
133
  poyaDay: "\u0DB4\u0DDD\u0DBA \u0DAF\u0DD2\u0DB1"
134
+ },
135
+ destinationCard: {
136
+ eyebrow: "\u0DC1\u0DCA\u200D\u0DBB\u0DD3 \u0DBD\u0D82\u0D9A\u0DCF\u0DC0 \u0DC3\u0DDC\u0DBA\u0DCF \u0DB6\u0DBD\u0DB1\u0DCA\u0DB1",
137
+ reviewsLabel: "\u0DC3\u0DB8\u0DCF\u0DBD\u0DDD\u0DA0\u0DB1",
138
+ bookTrip: "\u0D94\u0DB6\u0DDA \u0DC3\u0D82\u0DA0\u0DCF\u0DBB\u0DBA \u0DC0\u0DD9\u0DB1\u0DCA\u0D9A\u0DBB\u0DC0\u0DB1\u0DCA\u0DB1",
139
+ exploreNow: "\u0DAF\u0DD0\u0DB1\u0DCA \u0D9C\u0DC0\u0DDA\u0DC2\u0DAB\u0DBA \u0D9A\u0DBB\u0DB1\u0DCA\u0DB1",
140
+ startFrom: "\u0D86\u0DBB\u0DB8\u0DCA\u0DB7\u0DBA",
141
+ perPerson: "/ \u0DB4\u0DD4\u0DAF\u0DCA\u0D9C\u0DBD\u0DBA\u0D9A\u0DD4\u0DA7"
126
142
  }
127
143
  }
128
144
  };
@@ -212,6 +228,9 @@ function getTextFieldTranslation(variant, locale = currentLocale) {
212
228
  function getCalendarTranslation(locale = currentLocale) {
213
229
  return getMergedTranslations(locale).calendar ?? defaultTranslations[locale].calendar;
214
230
  }
231
+ function getDestinationCardTranslation(locale = currentLocale) {
232
+ return getMergedTranslations(locale).destinationCard ?? defaultTranslations[locale].destinationCard;
233
+ }
215
234
  function subscribeLocaleChange(listener) {
216
235
  localeListeners.add(listener);
217
236
  return () => localeListeners.delete(listener);
@@ -352,16 +371,43 @@ function pick(items) {
352
371
  return items[Math.floor(Math.random() * items.length)];
353
372
  }
354
373
  function toImageUrl(source) {
355
- return typeof source === "string" ? source : source.src;
374
+ if (typeof source === "string") return source;
375
+ if (source && typeof source === "object") {
376
+ const value = source;
377
+ if (typeof value.src === "string") return value.src;
378
+ if (typeof value.default === "string") return value.default;
379
+ if (value.default && typeof value.default === "object" && value.default !== null) {
380
+ const nested = value.default;
381
+ if (typeof nested.src === "string") return nested.src;
382
+ }
383
+ }
384
+ throw new Error("RainBackground image must be a URL string.");
385
+ }
386
+ function toDisplayUrl(source, cache) {
387
+ const existing = cache.get(source);
388
+ if (existing) return existing;
389
+ if (!source.startsWith("data:")) return source;
390
+ const comma = source.indexOf(",");
391
+ if (comma === -1) return source;
392
+ const header = source.slice(0, comma);
393
+ const data = source.slice(comma + 1);
394
+ const mime = header.match(/^data:([^;,]+)/i)?.[1] ?? "image/png";
395
+ const binary = atob(data);
396
+ const bytes = new Uint8Array(binary.length);
397
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
398
+ const url = URL.createObjectURL(new Blob([bytes], { type: mime }));
399
+ cache.set(source, url);
400
+ return url;
356
401
  }
357
402
  var RainBackground = class {
358
403
  element;
404
+ objectUrls = [];
359
405
  constructor(options = {}) {
360
406
  const {
361
407
  images = [...BUILTIN_RAIN_IMAGES],
362
408
  density = 28,
363
409
  minSize = 28,
364
- maxSize = 28,
410
+ maxSize = 72,
365
411
  minDuration = 8,
366
412
  maxDuration = 18,
367
413
  target = document.body,
@@ -374,8 +420,11 @@ var RainBackground = class {
374
420
  this.element.className = "helaui-rain-background";
375
421
  this.element.setAttribute("aria-hidden", "true");
376
422
  this.element.style.zIndex = String(zIndex);
423
+ const urlCache = /* @__PURE__ */ new Map();
424
+ const resolved = images.map((image) => toDisplayUrl(toImageUrl(image), urlCache));
425
+ this.objectUrls.push(...new Set(urlCache.values()));
377
426
  for (let index = 0; index < density; index += 1) {
378
- this.element.appendChild(this.createDrop(images, minSize, maxSize, minDuration, maxDuration));
427
+ this.element.appendChild(this.createDrop(resolved, minSize, maxSize, minDuration, maxDuration));
379
428
  }
380
429
  target.prepend(this.element);
381
430
  }
@@ -383,9 +432,16 @@ var RainBackground = class {
383
432
  const drop = document.createElement("div");
384
433
  const duration = randomBetween(minDuration, maxDuration);
385
434
  const size = randomBetween(minSize, maxSize);
386
- const src = toImageUrl(pick(images));
435
+ const src = pick(images);
436
+ const img = document.createElement("img");
437
+ img.className = "helaui-rain-background__image";
438
+ img.src = src;
439
+ img.alt = "";
440
+ img.draggable = false;
441
+ img.decoding = "async";
442
+ img.loading = "eager";
443
+ img.setAttribute("fetchpriority", "high");
387
444
  drop.className = "helaui-rain-background__drop";
388
- drop.style.backgroundImage = `url(${JSON.stringify(src)})`;
389
445
  drop.style.setProperty("--helaui-rain-x", `${randomBetween(0, 100)}%`);
390
446
  drop.style.setProperty("--helaui-rain-size", `${size}px`);
391
447
  drop.style.setProperty("--helaui-rain-duration", `${duration.toFixed(2)}s`);
@@ -393,11 +449,14 @@ var RainBackground = class {
393
449
  drop.style.setProperty("--helaui-rain-rotation", `${randomBetween(-35, 35).toFixed(1)}deg`);
394
450
  drop.style.setProperty("--helaui-rain-spin", `${randomBetween(90, 360).toFixed(0)}deg`);
395
451
  drop.style.setProperty("--helaui-rain-drift", `${randomBetween(-40, 40).toFixed(0)}px`);
452
+ drop.appendChild(img);
396
453
  return drop;
397
454
  }
398
455
  /** Removes the background from the DOM. */
399
456
  destroy() {
400
457
  this.element.remove();
458
+ for (const url of this.objectUrls) URL.revokeObjectURL(url);
459
+ this.objectUrls.length = 0;
401
460
  }
402
461
  };
403
462
  var DEFAULT_RAIN_IMAGES = ["fritter.png", "swirl.png", "kokis.png", "leaf.png"];
@@ -871,6 +930,106 @@ var SunMandala = class {
871
930
  }
872
931
  };
873
932
 
933
+ // src/components/DestinationCard.ts
934
+ var PEACOCK_SVG = `
935
+ <svg viewBox="0 0 220 110" aria-hidden="true">
936
+ <g fill="none" stroke-linecap="round">
937
+ <path d="M18 92c8-28 28-52 58-62" stroke="#1a6b6c" stroke-width="5"/>
938
+ <path d="M32 96c10-22 30-40 56-46" stroke="#2a8f7a" stroke-width="4"/>
939
+ <path d="M48 100c10-16 26-28 46-32" stroke="#c86837" stroke-width="3.5"/>
940
+ <ellipse cx="28" cy="38" rx="7" ry="10" fill="#1a4d4e" transform="rotate(-20 28 38)"/>
941
+ <circle cx="26" cy="34" r="1.6" fill="#f9f3e6"/>
942
+ <path d="M22 42c8 6 18 8 28 4" stroke="#1a4d4e" stroke-width="3"/>
943
+ <circle cx="62" cy="28" r="9" fill="#1a6b6c" opacity=".85"/>
944
+ <circle cx="62" cy="28" r="4" fill="#d4af37"/>
945
+ <circle cx="88" cy="42" r="10" fill="#2a8f7a" opacity=".8"/>
946
+ <circle cx="88" cy="42" r="4" fill="#c86837"/>
947
+ <circle cx="74" cy="62" r="9" fill="#1a4d4e" opacity=".75"/>
948
+ <circle cx="74" cy="62" r="3.5" fill="#d4af37"/>
949
+ <path d="M108 96c8-24 26-44 54-52" stroke="#1a6b6c" stroke-width="5"/>
950
+ <path d="M118 100c8-18 24-32 48-36" stroke="#c86837" stroke-width="3.5"/>
951
+ <ellipse cx="116" cy="48" rx="6.5" ry="9" fill="#1a4d4e" transform="rotate(-18 116 48)"/>
952
+ <circle cx="114" cy="44" r="1.4" fill="#f9f3e6"/>
953
+ <circle cx="148" cy="36" r="8" fill="#1a6b6c" opacity=".85"/>
954
+ <circle cx="148" cy="36" r="3.5" fill="#d4af37"/>
955
+ <circle cx="168" cy="52" r="8" fill="#2a8f7a" opacity=".8"/>
956
+ <circle cx="168" cy="52" r="3.5" fill="#c86837"/>
957
+ </g>
958
+ <ellipse cx="96" cy="98" rx="10" ry="6" fill="#e8c9d4"/>
959
+ <path d="M90 96c4-10 12-12 18-2" fill="#d46a8a"/>
960
+ <path d="M94 98c3-7 9-8 13-1" fill="#f4c6d4"/>
961
+ </svg>
962
+ `;
963
+ var LOTUS_SVG = `
964
+ <svg viewBox="0 0 120 110" aria-hidden="true">
965
+ <path d="M60 96c-16-18-28-34-28-50 0-10 8-16 16-12 6 3 10 12 12 22 2-14 8-28 18-34 8-5 16 0 16 12 0 16-12 32-34 62z" fill="#e8a0b8"/>
966
+ <path d="M60 96c-10-16-22-36-18-54 3-12 14-16 18-6 4-16 16-24 24-18 8 6 6 22-2 36-8 14-16 28-22 42z" fill="#d46a8a"/>
967
+ <path d="M60 98c6-20 8-38 4-52-3-10-12-12-14-4-4-12-2-24 8-28 10-4 18 6 18 18 0 16-6 34-16 66z" fill="#f3c3d2"/>
968
+ <circle cx="60" cy="42" r="8" fill="#d4af37"/>
969
+ <circle cx="60" cy="42" r="3.5" fill="#f9f3e6"/>
970
+ </svg>
971
+ `;
972
+ function el(tag, className, text) {
973
+ const node = document.createElement(tag);
974
+ node.className = className;
975
+ if (text !== void 0) node.textContent = text;
976
+ return node;
977
+ }
978
+ var DestinationCard = class {
979
+ element;
980
+ card;
981
+ body;
982
+ constructor(options = {}) {
983
+ const { content, height = "280px", backdrop = false, backdropImage } = options;
984
+ this.card = el("article", "helaui-destination-card");
985
+ this.card.setAttribute("aria-label", "HelaUI card");
986
+ const watermark = el("div", "helaui-destination-card__watermark");
987
+ watermark.setAttribute("aria-hidden", "true");
988
+ this.card.appendChild(watermark);
989
+ const script = el("div", `helaui-destination-card__script ${SINHALA_FONT_CLASS}`, "\u0DC1\u0DCA\u200D\u0DBB\u0DD3 \u0DBD\u0D82\u0D9A\u0DCF\u0DC0");
990
+ script.setAttribute("aria-hidden", "true");
991
+ this.card.appendChild(script);
992
+ this.body = el("div", "helaui-destination-card__body");
993
+ this.body.style.minHeight = height;
994
+ if (typeof content === "string") {
995
+ this.body.innerHTML = content;
996
+ } else if (content) {
997
+ this.body.appendChild(content);
998
+ }
999
+ this.card.appendChild(this.body);
1000
+ const footer = el("div", "helaui-destination-card__footer");
1001
+ footer.setAttribute("aria-hidden", "true");
1002
+ const peacocks = el("div", "helaui-destination-card__peacocks");
1003
+ peacocks.innerHTML = PEACOCK_SVG.trim();
1004
+ const lotus = el("div", "helaui-destination-card__lotus");
1005
+ lotus.innerHTML = LOTUS_SVG.trim();
1006
+ footer.append(peacocks, lotus);
1007
+ this.card.appendChild(footer);
1008
+ if (backdrop) {
1009
+ const stage = el("div", "helaui-destination-card-stage");
1010
+ const blur = el("div", "helaui-destination-card-stage__blur");
1011
+ if (backdropImage) {
1012
+ blur.style.backgroundImage = `url("${backdropImage.replace(/"/g, "")}")`;
1013
+ }
1014
+ stage.append(blur, this.card);
1015
+ this.element = stage;
1016
+ } else {
1017
+ this.element = this.card;
1018
+ }
1019
+ }
1020
+ setContent(content) {
1021
+ this.body.replaceChildren();
1022
+ if (typeof content === "string") {
1023
+ this.body.innerHTML = content;
1024
+ } else {
1025
+ this.body.appendChild(content);
1026
+ }
1027
+ }
1028
+ destroy() {
1029
+ this.element.remove();
1030
+ }
1031
+ };
1032
+
874
1033
  // src/theme/defaultTheme.ts
875
1034
  var defaultTheme = {
876
1035
  primary: "#8D153A",
@@ -945,6 +1104,7 @@ export {
945
1104
  BUILTIN_SUN_MANDALA_IMAGE,
946
1105
  Calendar,
947
1106
  DEFAULT_RAIN_IMAGES,
1107
+ DestinationCard,
948
1108
  RainBackground,
949
1109
  SINHALA_FONT_CLASS,
950
1110
  SINHALA_FONT_FAMILY,
@@ -961,6 +1121,7 @@ export {
961
1121
  formatSinhalaNumeral,
962
1122
  getCalendarTranslation,
963
1123
  getDefaultRainImageUrls,
1124
+ getDestinationCardTranslation,
964
1125
  getLocale,
965
1126
  getTextFieldTranslation,
966
1127
  resetTheme,
@@ -17,9 +17,14 @@
17
17
  animation: helaui-rain-fall var(--helaui-rain-duration, 10s) linear infinite;
18
18
  animation-delay: var(--helaui-rain-delay, 0s);
19
19
  transform: rotate(var(--helaui-rain-rotation, 0deg));
20
- background-size: contain;
21
- background-repeat: no-repeat;
22
- background-position: center;
20
+ }
21
+
22
+ .helaui-rain-background__image {
23
+ display: block;
24
+ width: 100%;
25
+ height: 100%;
26
+ object-fit: contain;
27
+ pointer-events: none;
23
28
  }
24
29
 
25
30
  @keyframes helaui-rain-fall {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mccustomapps/helaui",
3
- "version": "0.1.10",
3
+ "version": "0.1.13",
4
4
  "description": "HelaUI components with Sinhala font support",
5
5
  "author": "mccustomapps",
6
6
  "license": "MIT",
@@ -26,6 +26,7 @@
26
26
  "./calendar.css": "./dist/calendar.css",
27
27
  "./rain-background.css": "./dist/rain-background.css",
28
28
  "./sun-mandala.css": "./dist/sun-mandala.css",
29
+ "./destination-card.css": "./dist/destination-card.css",
29
30
  "./theme.css": "./dist/theme.css",
30
31
  "./assets/rain/*": "./dist/assets/rain/*",
31
32
  "./assets/sun-mandala/*": "./dist/assets/sun-mandala/*"