kupos-ui-components-lib 9.11.10 → 10.0.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.
@@ -1,6 +1,15 @@
1
1
  import React from "react";
2
2
  import LottiePlayer from "../../assets/LottiePlayer";
3
3
  import commonService from "../../utils/CommonService";
4
+ if (typeof document !== "undefined") {
5
+ if (!document.getElementById("feature-spin-style")) {
6
+ const style = document.createElement("style");
7
+ style.id = "feature-spin-style";
8
+ style.textContent =
9
+ "@keyframes feature-spin { to { transform: rotate(360deg); } }";
10
+ document.head.appendChild(style);
11
+ }
12
+ }
4
13
  const HARDCODED_OPERATORS = [
5
14
  {
6
15
  logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
@@ -309,7 +318,7 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
309
318
  React.createElement("span", { style: { color: "#FF5C60" } }, "viendo"),
310
319
  " |",
311
320
  " ",
312
- React.createElement("span", { className: "bold-text", ref: (node) => commonService.startComprandoCount(node, 4, 16), style: { fontVariantNumeric: "tabular-nums" } }),
321
+ React.createElement("span", { className: "bold-text", ref: (node) => commonService.startComprandoDealCount(node, 4, 16), style: { fontVariantNumeric: "tabular-nums" } }),
313
322
  " ",
314
323
  "han comprado"))),
315
324
  React.createElement("div", { className: "flex flex-col justify-center gap-[12px] py-[2px] pl-[22px] pr-[10px] relative mb-[16px]" },
@@ -353,16 +362,42 @@ const FeatureServiceUi = ({ serviceItem, showTopLabel, isSoldOut, getAnimationIc
353
362
  "m\u00E1x. ",
354
363
  maxSeatsPerBooking,
355
364
  " pasajes"))),
356
- React.createElement("button", { type: "button", onClick: () => onBookButtonPress === null || onBookButtonPress === void 0 ? void 0 : onBookButtonPress(ticketQuantity, serviceItem), className: "flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-white bold-text text-[13px] mt-[4px] justify-center border-none cursor-pointer text-center", style: {
365
+ React.createElement("button", { type: "button", ref: (btn) => {
366
+ if (!btn)
367
+ return;
368
+ btn.onclick = () => {
369
+ if (btn.disabled)
370
+ return;
371
+ btn.disabled = true;
372
+ btn.style.cursor = "not-allowed";
373
+ btn.style.opacity = "0.85";
374
+ const normal = btn.querySelector('[data-btn-state="normal"]');
375
+ const loading = btn.querySelector('[data-btn-state="loading"]');
376
+ if (normal)
377
+ normal.style.display = "none";
378
+ if (loading)
379
+ loading.style.display = "flex";
380
+ onBookButtonPress === null || onBookButtonPress === void 0 ? void 0 : onBookButtonPress(ticketQuantity, serviceItem);
381
+ };
382
+ }, className: "flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-white bold-text text-[13px] mt-[4px] justify-center border-none cursor-pointer text-center", style: {
357
383
  backgroundColor: "#FF5C60",
358
384
  whiteSpace: "nowrap",
385
+ minWidth: "120px",
359
386
  } },
360
- React.createElement(LottiePlayer
361
- // animationData={serviceItem.icons.flexibleAnim}
362
- , {
363
- // animationData={serviceItem.icons.flexibleAnim}
364
- animationData: getAnimationIcon("thunderAnimation"), width: "16px", height: "16px" }),
365
- React.createElement("span", { className: "whitespace-nowrap" }, "\u00A1Lo quiero!"))),
387
+ React.createElement("span", { "data-btn-state": "normal", style: { display: "flex", alignItems: "center", gap: "6px" } },
388
+ React.createElement(LottiePlayer, { animationData: getAnimationIcon("thunderAnimation"), width: "16px", height: "16px" }),
389
+ React.createElement("span", { className: "whitespace-nowrap" }, "\u00A1Lo quiero!")),
390
+ React.createElement("span", { "data-btn-state": "loading", style: { display: "none", alignItems: "center", gap: "6px" } },
391
+ React.createElement("span", { style: {
392
+ display: "inline-block",
393
+ width: "16px",
394
+ height: "16px",
395
+ border: "2px solid rgba(255,255,255,0.4)",
396
+ borderTopColor: "#fff",
397
+ borderRadius: "50%",
398
+ animation: "feature-spin 0.7s linear infinite",
399
+ flexShrink: 0,
400
+ } })))),
366
401
  React.createElement("div", { className: `absolute bottom-[11px] right-[18px] cursor-pointer transition-transform duration-300 ease-in-out ${isItemExpanded ? "rotate-180" : ""}`, onClick: onToggleExpand },
367
402
  React.createElement("img", { src: (_f = serviceItem.icons) === null || _f === void 0 ? void 0 : _f.downArrow, alt: "down arrow", style: {
368
403
  width: "14px",
@@ -29,6 +29,7 @@ declare const commonService: {
29
29
  startCountdown: (node: HTMLSpanElement | null, countdownSeconds?: number) => void;
30
30
  startDealCountdown: (node: HTMLSpanElement | null, countdownSeconds: number) => void;
31
31
  startComprandoCount: (node: HTMLSpanElement | null, min?: number, max?: number) => void;
32
+ startComprandoDealCount: (node: HTMLSpanElement | null, min?: number, max?: number) => void;
32
33
  timeToMinutes: (time: string) => number;
33
34
  minutesToTime: (minutes: number) => string;
34
35
  generateTimeSlots: (from: string, to: string, windowHours: number) => Array<{
@@ -411,6 +411,41 @@ const commonService = {
411
411
  node.dataset.comprandoId = String(id);
412
412
  node.dataset.comprandoConfig = configKey;
413
413
  },
414
+ startComprandoDealCount: (node, min = 4, max = 16) => {
415
+ if (!node)
416
+ return;
417
+ const configKey = `${min}-${max}`;
418
+ if (node.dataset.comprandoDealId &&
419
+ node.dataset.comprandoDealConfig === configKey) {
420
+ return;
421
+ }
422
+ const prevId = node.dataset.comprandoDealId;
423
+ if (prevId)
424
+ clearInterval(Number(prevId));
425
+ const initialValue = Math.floor(Math.random() * (max - min + 1)) + min;
426
+ node.textContent = String(initialValue);
427
+ const id = setInterval(() => {
428
+ const current = Number(node.textContent) || initialValue;
429
+ const changePercent = 0.05; // 5% change
430
+ const baseChange = Math.ceil(current * changePercent);
431
+ const rand = Math.random();
432
+ let change = baseChange;
433
+ if (baseChange <= 1) {
434
+ change = rand < 0.7 ? 1 : rand < 0.9 ? 2 : 3;
435
+ }
436
+ else {
437
+ const variation = rand < 0.4 ? 0 : rand < 0.7 ? 1 : rand < 0.9 ? 2 : 3;
438
+ change = baseChange + variation;
439
+ }
440
+ // Only increase, never decrease
441
+ let next = current + change;
442
+ // Clamp within max
443
+ next = Math.min(max, next);
444
+ node.textContent = String(next);
445
+ }, 5000); // Update every 5 seconds
446
+ node.dataset.comprandoDealId = String(id);
447
+ node.dataset.comprandoDealConfig = configKey;
448
+ },
414
449
  timeToMinutes: (time) => {
415
450
  const [h, m] = time.split(":").map(Number);
416
451
  return h * 60 + (m || 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "9.11.10",
3
+ "version": "10.0.0",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -2,6 +2,16 @@ import React from "react";
2
2
  import LottiePlayer from "../../assets/LottiePlayer";
3
3
  import commonService from "../../utils/CommonService";
4
4
 
5
+ if (typeof document !== "undefined") {
6
+ if (!document.getElementById("feature-spin-style")) {
7
+ const style = document.createElement("style");
8
+ style.id = "feature-spin-style";
9
+ style.textContent =
10
+ "@keyframes feature-spin { to { transform: rotate(360deg); } }";
11
+ document.head.appendChild(style);
12
+ }
13
+ }
14
+
5
15
  const HARDCODED_OPERATORS = [
6
16
  {
7
17
  logo: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Turbus_logo.svg/320px-Turbus_logo.svg.png",
@@ -523,7 +533,7 @@ const FeatureServiceUi = ({
523
533
  <span
524
534
  className="bold-text"
525
535
  ref={(node) =>
526
- commonService.startComprandoCount(node, 4, 16)
536
+ commonService.startComprandoDealCount(node, 4, 16)
527
537
  }
528
538
  style={{ fontVariantNumeric: "tabular-nums" }}
529
539
  />{" "}
@@ -628,20 +638,59 @@ const FeatureServiceUi = ({
628
638
 
629
639
  <button
630
640
  type="button"
631
- onClick={() => onBookButtonPress?.(ticketQuantity, serviceItem)}
632
- className="flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-white bold-text text-[13px] mt-[4px] justify-center border-none cursor-pointer text-center"
641
+ ref={(btn) => {
642
+ if (!btn) return;
643
+ btn.onclick = () => {
644
+ if (btn.disabled) return;
645
+ btn.disabled = true;
646
+ btn.style.cursor = "not-allowed";
647
+ btn.style.opacity = "0.85";
648
+ const normal = btn.querySelector(
649
+ '[data-btn-state="normal"]',
650
+ ) as HTMLElement | null;
651
+ const loading = btn.querySelector(
652
+ '[data-btn-state="loading"]',
653
+ ) as HTMLElement | null;
654
+ if (normal) normal.style.display = "none";
655
+ if (loading) loading.style.display = "flex";
656
+ onBookButtonPress?.(ticketQuantity, serviceItem);
657
+ };
658
+ }}
659
+ className="flex items-center gap-[6px] px-[20px] py-[10px] rounded-[16px] text-white bold-text text-[13px] mt-[4px] justify-center border-none cursor-pointer text-center"
633
660
  style={{
634
661
  backgroundColor: "#FF5C60",
635
662
  whiteSpace: "nowrap",
663
+ minWidth: "120px",
636
664
  }}
637
665
  >
638
- <LottiePlayer
639
- // animationData={serviceItem.icons.flexibleAnim}
640
- animationData={getAnimationIcon("thunderAnimation")}
641
- width="16px"
642
- height="16px"
643
- />
644
- <span className="whitespace-nowrap">¡Lo quiero!</span>
666
+ <span
667
+ data-btn-state="normal"
668
+ style={{ display: "flex", alignItems: "center", gap: "6px" }}
669
+ >
670
+ <LottiePlayer
671
+ animationData={getAnimationIcon("thunderAnimation")}
672
+ width="16px"
673
+ height="16px"
674
+ />
675
+ <span className="whitespace-nowrap">¡Lo quiero!</span>
676
+ </span>
677
+ <span
678
+ data-btn-state="loading"
679
+ style={{ display: "none", alignItems: "center", gap: "6px" }}
680
+ >
681
+ <span
682
+ style={{
683
+ display: "inline-block",
684
+ width: "16px",
685
+ height: "16px",
686
+ border: "2px solid rgba(255,255,255,0.4)",
687
+ borderTopColor: "#fff",
688
+ borderRadius: "50%",
689
+ animation: "feature-spin 0.7s linear infinite",
690
+ flexShrink: 0,
691
+ }}
692
+ />
693
+ </span>
645
694
  </button>
646
695
  </div>
647
696
 
@@ -482,6 +482,54 @@ const commonService = {
482
482
  node.dataset.comprandoConfig = configKey;
483
483
  },
484
484
 
485
+ startComprandoDealCount: (
486
+ node: HTMLSpanElement | null,
487
+ min: number = 4,
488
+ max: number = 16,
489
+ ) => {
490
+ if (!node) return;
491
+
492
+ const configKey = `${min}-${max}`;
493
+ if (
494
+ node.dataset.comprandoDealId &&
495
+ node.dataset.comprandoDealConfig === configKey
496
+ ) {
497
+ return;
498
+ }
499
+
500
+ const prevId = node.dataset.comprandoDealId;
501
+ if (prevId) clearInterval(Number(prevId));
502
+
503
+ const initialValue = Math.floor(Math.random() * (max - min + 1)) + min;
504
+ node.textContent = String(initialValue);
505
+
506
+ const id = setInterval(() => {
507
+ const current = Number(node.textContent) || initialValue;
508
+ const changePercent = 0.05; // 5% change
509
+ const baseChange = Math.ceil(current * changePercent);
510
+
511
+ const rand = Math.random();
512
+ let change = baseChange;
513
+ if (baseChange <= 1) {
514
+ change = rand < 0.7 ? 1 : rand < 0.9 ? 2 : 3;
515
+ } else {
516
+ const variation = rand < 0.4 ? 0 : rand < 0.7 ? 1 : rand < 0.9 ? 2 : 3;
517
+ change = baseChange + variation;
518
+ }
519
+
520
+ // Only increase, never decrease
521
+ let next = current + change;
522
+
523
+ // Clamp within max
524
+ next = Math.min(max, next);
525
+
526
+ node.textContent = String(next);
527
+ }, 5000); // Update every 5 seconds
528
+
529
+ node.dataset.comprandoDealId = String(id);
530
+ node.dataset.comprandoDealConfig = configKey;
531
+ },
532
+
485
533
  timeToMinutes: (time: string): number => {
486
534
  const [h, m] = time.split(":").map(Number);
487
535
  return h * 60 + (m || 0);