analytica-frontend-lib 1.1.53 → 1.1.54

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.
@@ -663,6 +663,52 @@ var IconButton_default = IconButton;
663
663
  // src/components/Modal/Modal.tsx
664
664
  var import_react4 = require("react");
665
665
  var import_phosphor_react2 = require("phosphor-react");
666
+
667
+ // src/components/Modal/utils/videoUtils.ts
668
+ var isYouTubeUrl = (url) => {
669
+ const youtubeRegex = /^(https?:\/\/)?((www|m|music)\.)?(youtube\.com|youtu\.be|youtube-nocookie\.com)\/.+/i;
670
+ return youtubeRegex.test(url);
671
+ };
672
+ var isValidYouTubeHost = (host) => {
673
+ if (host === "youtu.be") return "youtu.be";
674
+ const isValidYouTubeCom = host === "youtube.com" || host.endsWith(".youtube.com") && /^(www|m|music)\.youtube\.com$/.test(host);
675
+ if (isValidYouTubeCom) return "youtube";
676
+ const isValidNoCookie = host === "youtube-nocookie.com" || host.endsWith(".youtube-nocookie.com") && /^(www|m|music)\.youtube-nocookie\.com$/.test(host);
677
+ if (isValidNoCookie) return "nocookie";
678
+ return null;
679
+ };
680
+ var extractYoutuBeId = (pathname) => {
681
+ const firstSeg = pathname.split("/").filter(Boolean)[0];
682
+ return firstSeg || null;
683
+ };
684
+ var extractYouTubeId = (pathname, searchParams) => {
685
+ const parts = pathname.split("/").filter(Boolean);
686
+ const [first, second] = parts;
687
+ if (first === "embed" && second) return second;
688
+ if (first === "shorts" && second) return second;
689
+ if (first === "live" && second) return second;
690
+ const v = searchParams.get("v");
691
+ if (v) return v;
692
+ return null;
693
+ };
694
+ var getYouTubeVideoId = (url) => {
695
+ try {
696
+ const u = new URL(url);
697
+ const hostType = isValidYouTubeHost(u.hostname.toLowerCase());
698
+ if (!hostType) return null;
699
+ if (hostType === "youtu.be") {
700
+ return extractYoutuBeId(u.pathname);
701
+ }
702
+ return extractYouTubeId(u.pathname, u.searchParams);
703
+ } catch {
704
+ return null;
705
+ }
706
+ };
707
+ var getYouTubeEmbedUrl = (videoId) => {
708
+ return `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=0&rel=0&modestbranding=1`;
709
+ };
710
+
711
+ // src/components/Modal/Modal.tsx
666
712
  var import_jsx_runtime5 = require("react/jsx-runtime");
667
713
  var SIZE_CLASSES2 = {
668
714
  xs: "max-w-[360px]",
@@ -678,11 +724,17 @@ var Modal = ({
678
724
  children,
679
725
  size = "md",
680
726
  className = "",
681
- closeOnBackdropClick = true,
682
727
  closeOnEscape = true,
683
728
  footer,
684
- hideCloseButton = false
729
+ hideCloseButton = false,
730
+ variant = "default",
731
+ description,
732
+ image,
733
+ imageAlt,
734
+ actionLink,
735
+ actionLabel
685
736
  }) => {
737
+ const titleId = (0, import_react4.useId)();
686
738
  (0, import_react4.useEffect)(() => {
687
739
  if (!isOpen || !closeOnEscape) return;
688
740
  const handleEscape = (event) => {
@@ -704,16 +756,6 @@ var Modal = ({
704
756
  document.body.style.overflow = originalOverflow;
705
757
  };
706
758
  }, [isOpen]);
707
- const handleBackdropClick = (event) => {
708
- if (closeOnBackdropClick && event.target === event.currentTarget) {
709
- onClose();
710
- }
711
- };
712
- const handleBackdropKeyDown = (event) => {
713
- if (closeOnBackdropClick && (event.key === "Enter" || event.key === " ")) {
714
- onClose();
715
- }
716
- };
717
759
  if (!isOpen) return null;
718
760
  const sizeClasses = SIZE_CLASSES2[size];
719
761
  const baseClasses = "bg-secondary-50 rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4";
@@ -724,18 +766,105 @@ var Modal = ({
724
766
  dialogResetClasses,
725
767
  className
726
768
  );
727
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
728
- "div",
769
+ const normalizeUrl = (href) => /^https?:\/\//i.test(href) ? href : `https://${href}`;
770
+ const handleActionClick = () => {
771
+ if (actionLink) {
772
+ window.open(normalizeUrl(actionLink), "_blank", "noopener,noreferrer");
773
+ }
774
+ };
775
+ if (variant === "activity") {
776
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
777
+ "dialog",
778
+ {
779
+ className: modalClasses,
780
+ "aria-labelledby": titleId,
781
+ "aria-modal": "true",
782
+ open: true,
783
+ children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex justify-end p-6 pb-0", children: !hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
785
+ "button",
786
+ {
787
+ onClick: onClose,
788
+ className: "p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2",
789
+ "aria-label": "Fechar modal",
790
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_phosphor_react2.X, { size: 18 })
791
+ }
792
+ ) }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col items-center px-6 pb-6 gap-5", children: [
794
+ image && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
795
+ "img",
796
+ {
797
+ src: image,
798
+ alt: imageAlt ?? "",
799
+ className: "w-[122px] h-[122px] object-contain"
800
+ }
801
+ ) }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
803
+ "h2",
804
+ {
805
+ id: titleId,
806
+ className: "text-lg font-semibold text-text-950 text-center",
807
+ children: title
808
+ }
809
+ ),
810
+ description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-normal text-text-400 text-center max-w-md leading-[21px]", children: description }),
811
+ actionLink && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "w-full", children: [
812
+ (() => {
813
+ const normalized = normalizeUrl(actionLink);
814
+ const isYT = isYouTubeUrl(normalized);
815
+ if (!isYT) return null;
816
+ const id = getYouTubeVideoId(normalized);
817
+ if (!id) {
818
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
819
+ Button_default,
820
+ {
821
+ variant: "solid",
822
+ action: "primary",
823
+ size: "large",
824
+ className: "w-full",
825
+ onClick: handleActionClick,
826
+ children: actionLabel || "Iniciar Atividade"
827
+ }
828
+ );
829
+ }
830
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
831
+ "iframe",
832
+ {
833
+ src: getYouTubeEmbedUrl(id),
834
+ className: "w-full aspect-video rounded-lg",
835
+ allowFullScreen: true,
836
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
837
+ title: "V\xEDdeo YouTube"
838
+ }
839
+ );
840
+ })(),
841
+ !isYouTubeUrl(normalizeUrl(actionLink)) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
842
+ Button_default,
843
+ {
844
+ variant: "solid",
845
+ action: "primary",
846
+ size: "large",
847
+ className: "w-full",
848
+ onClick: handleActionClick,
849
+ children: actionLabel || "Iniciar Atividade"
850
+ }
851
+ )
852
+ ] })
853
+ ] })
854
+ ]
855
+ }
856
+ ) });
857
+ }
858
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
859
+ "dialog",
729
860
  {
730
- className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs",
731
- onClick: handleBackdropClick,
732
- onKeyDown: handleBackdropKeyDown,
733
- role: "button",
734
- tabIndex: closeOnBackdropClick ? 0 : -1,
735
- "aria-label": "Fechar modal clicando no fundo",
736
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("dialog", { className: modalClasses, "aria-labelledby": "modal-title", open: true, children: [
861
+ className: modalClasses,
862
+ "aria-labelledby": titleId,
863
+ "aria-modal": "true",
864
+ open: true,
865
+ children: [
737
866
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between px-6 py-6", children: [
738
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: "modal-title", className: "text-lg font-semibold text-text-950", children: title }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: titleId, className: "text-lg font-semibold text-text-950", children: title }),
739
868
  !hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
740
869
  "button",
741
870
  {
@@ -746,11 +875,11 @@ var Modal = ({
746
875
  }
747
876
  )
748
877
  ] }),
749
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "px-6 pb-6", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-text-500 font-normal text-sm leading-6", children }) }),
878
+ children && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "px-6 pb-6", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-text-500 font-normal text-sm leading-6", children }) }),
750
879
  footer && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex justify-end gap-3 px-6 pb-6", children: footer })
751
- ] })
880
+ ]
752
881
  }
753
- );
882
+ ) });
754
883
  };
755
884
  var Modal_default = Modal;
756
885
 
@@ -1239,7 +1368,6 @@ var NotificationCenter = ({
1239
1368
  title: "Notifica\xE7\xF5es",
1240
1369
  size: "md",
1241
1370
  hideCloseButton: false,
1242
- closeOnBackdropClick: true,
1243
1371
  closeOnEscape: true,
1244
1372
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col h-full max-h-[80vh]", children: [
1245
1373
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "px-0 pb-3 border-b border-border-200", children: [