pelatform-ui 1.3.0 → 1.4.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,4 +1,45 @@
1
1
  "use client";
2
+ import {
3
+ HoverBackground
4
+ } from "./chunk-4Z3DBWB6.js";
5
+ import {
6
+ useMetaColor
7
+ } from "./chunk-V3ET2B55.js";
8
+ import {
9
+ Alert,
10
+ AlertDialog,
11
+ AlertDialogAction,
12
+ AlertDialogCancel,
13
+ AlertDialogContent,
14
+ AlertDialogDescription,
15
+ AlertDialogFooter,
16
+ AlertDialogHeader,
17
+ AlertDialogTitle,
18
+ AlertTitle,
19
+ Avatar,
20
+ AvatarFallback,
21
+ AvatarImage,
22
+ Badge,
23
+ Button,
24
+ Collapsible,
25
+ CollapsibleContent,
26
+ CollapsibleTrigger,
27
+ Drawer,
28
+ DrawerContent,
29
+ DrawerDescription,
30
+ DrawerTitle,
31
+ DrawerTrigger,
32
+ DropdownMenu,
33
+ DropdownMenuContent,
34
+ DropdownMenuItem,
35
+ DropdownMenuRadioGroup,
36
+ DropdownMenuRadioItem,
37
+ DropdownMenuSub,
38
+ DropdownMenuSubContent,
39
+ DropdownMenuSubTrigger,
40
+ DropdownMenuTrigger,
41
+ TooltipProvider
42
+ } from "./chunk-Z4CHIWTI.js";
2
43
 
3
44
  // src/components/icons.tsx
4
45
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -641,7 +682,895 @@ function Logo({ className }) {
641
682
  }
642
683
  );
643
684
  }
685
+
686
+ // src/components/layout/main-nav.tsx
687
+ import { ArrowUpRightIcon, ChevronDownIcon } from "lucide-react";
688
+ import { cn as cn2 } from "@pelatform/utils";
689
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
690
+ function MainNav({ pathname, items, className }) {
691
+ return /* @__PURE__ */ jsx3("div", { className: cn2("me-4 hidden items-center justify-center md:flex", className), children: /* @__PURE__ */ jsx3("nav", { className: "flex items-center gap-4 font-medium text-sm xl:gap-6", children: items.map((item) => /* @__PURE__ */ jsx3(NavItemRenderer, { item, pathname, level: 1 }, item.title)) }) });
692
+ }
693
+ function NavItemRenderer({ item, pathname, level }) {
694
+ const hasChildren = item.children && item.children.length > 0;
695
+ const isActive = hasChildren ? item.children?.some(
696
+ (child) => child.href === pathname || child.href !== "/" && child.href && pathname?.startsWith(child.href) || (child.children?.some(
697
+ (grandchild) => grandchild.href === pathname || grandchild.href !== "/" && grandchild.href && pathname?.startsWith(grandchild.href)
698
+ ) ?? false)
699
+ ) ?? false : item.href === pathname || item.href !== "/" && item.href && pathname?.startsWith(item.href);
700
+ if (hasChildren && level <= 3) {
701
+ return /* @__PURE__ */ jsxs2(DropdownMenu, { children: [
702
+ /* @__PURE__ */ jsx3(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
703
+ "button",
704
+ {
705
+ className: cn2(
706
+ "inline-flex cursor-pointer items-center gap-1 transition-colors hover:text-foreground/80 focus-visible:outline-0",
707
+ isActive ? "text-foreground" : "text-foreground/60"
708
+ ),
709
+ children: [
710
+ item.icon && /* @__PURE__ */ jsx3(item.icon, {}),
711
+ item.title,
712
+ /* @__PURE__ */ jsx3(ChevronDownIcon, { className: "size-3.5" })
713
+ ]
714
+ }
715
+ ) }),
716
+ /* @__PURE__ */ jsx3(
717
+ DropdownMenuContent,
718
+ {
719
+ className: "min-w-[150px] overflow-visible",
720
+ side: "bottom",
721
+ align: "start",
722
+ sideOffset: 15,
723
+ alignOffset: -10,
724
+ children: item.children?.map((child) => /* @__PURE__ */ jsx3(
725
+ ChildNavItemRenderer,
726
+ {
727
+ item: child,
728
+ pathname,
729
+ level: level + 1
730
+ },
731
+ child.title
732
+ ))
733
+ }
734
+ )
735
+ ] });
736
+ }
737
+ return /* @__PURE__ */ jsxs2(
738
+ "a",
739
+ {
740
+ href: item.href ?? "#",
741
+ ...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
742
+ className: cn2(
743
+ "relative inline-flex items-center gap-1 transition-colors hover:text-foreground/80",
744
+ isActive ? "text-foreground" : "text-foreground/60"
745
+ ),
746
+ children: [
747
+ item.icon && /* @__PURE__ */ jsx3(item.icon, {}),
748
+ item.title,
749
+ item.external && /* @__PURE__ */ jsx3(ArrowUpRightIcon, { className: "size-3.5 opacity-60" })
750
+ ]
751
+ }
752
+ );
753
+ }
754
+ function ChildNavItemRenderer({ item, pathname, level }) {
755
+ const hasChildren = item.children && item.children.length > 0;
756
+ const isChildActive = item.href === pathname || item.href !== "/" && item.href && pathname?.startsWith(item.href) || hasChildren && (item.children?.some(
757
+ (grandchild) => grandchild.href === pathname || grandchild.href !== "/" && grandchild.href && pathname?.startsWith(grandchild.href)
758
+ ) ?? false);
759
+ if (hasChildren && level <= 3) {
760
+ return /* @__PURE__ */ jsxs2(DropdownMenuSub, { children: [
761
+ /* @__PURE__ */ jsxs2(
762
+ DropdownMenuSubTrigger,
763
+ {
764
+ className: cn2(
765
+ "flex w-full items-center justify-between text-muted-foreground hover:text-foreground",
766
+ isChildActive && "font-medium text-foreground"
767
+ ),
768
+ children: [
769
+ item.icon && /* @__PURE__ */ jsx3(item.icon, {}),
770
+ item.title
771
+ ]
772
+ }
773
+ ),
774
+ /* @__PURE__ */ jsx3(DropdownMenuSubContent, { className: "min-w-[150px]", children: item.children?.map((child) => /* @__PURE__ */ jsx3(
775
+ ChildNavItemRenderer,
776
+ {
777
+ item: child,
778
+ pathname,
779
+ level: level + 1
780
+ },
781
+ child.title
782
+ )) })
783
+ ] });
784
+ }
785
+ return /* @__PURE__ */ jsx3(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs2(
786
+ "a",
787
+ {
788
+ href: item.href || "",
789
+ ...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
790
+ className: cn2(
791
+ "block w-full cursor-pointer transition-colors",
792
+ isChildActive ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
793
+ ),
794
+ children: [
795
+ item.icon && /* @__PURE__ */ jsx3(item.icon, {}),
796
+ item.title,
797
+ item.external && /* @__PURE__ */ jsx3(ArrowUpRightIcon, { className: "size-3.5 opacity-60" })
798
+ ]
799
+ }
800
+ ) });
801
+ }
802
+
803
+ // src/components/layout/mobile-nav.tsx
804
+ import { useCallback, useState } from "react";
805
+ import { ArrowUpRightIcon as ArrowUpRightIcon2, ChevronDownIcon as ChevronDownIcon2, MenuIcon } from "lucide-react";
806
+ import { cn as cn3 } from "@pelatform/utils";
807
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
808
+ function MobileNav({ children, className }) {
809
+ const { setMetaColor, metaColor } = useMetaColor();
810
+ const [open, setOpen] = useState(false);
811
+ const onOpenChange = useCallback(
812
+ (open2) => {
813
+ setOpen(open2);
814
+ setMetaColor(open2 ? "#09090b" : metaColor);
815
+ },
816
+ [setMetaColor, metaColor]
817
+ );
818
+ return /* @__PURE__ */ jsx4("div", { className: cn3("flex items-center gap-2.5 md:hidden", className), children: /* @__PURE__ */ jsxs3(Drawer, { open, onOpenChange, children: [
819
+ /* @__PURE__ */ jsx4(DrawerTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(Button, { variant: "ghost", className: "group/toggle size-8 px-0 text-foreground", children: [
820
+ /* @__PURE__ */ jsx4(MenuIcon, {}),
821
+ /* @__PURE__ */ jsx4("span", { className: "sr-only", children: "Toggle Menu" })
822
+ ] }) }),
823
+ /* @__PURE__ */ jsxs3(DrawerContent, { className: "max-h-[60svh] p-0", children: [
824
+ /* @__PURE__ */ jsx4(DrawerTitle, { className: "sr-only", children: "Mobile menu" }),
825
+ /* @__PURE__ */ jsx4("div", { className: "overflow-auto p-6", children }),
826
+ /* @__PURE__ */ jsx4(DrawerDescription, { className: "sr-only", children: "Mobile menu" })
827
+ ] })
828
+ ] }) });
829
+ }
830
+ function MobileNavItemRenderer({
831
+ item,
832
+ pathname,
833
+ level,
834
+ onOpenChange
835
+ }) {
836
+ const [isOpen, setIsOpen] = useState(false);
837
+ const hasChildren = item.children && item.children.length > 0;
838
+ const isActive = hasChildren ? item.children?.some(
839
+ (child) => child.href === pathname || child.href !== "/" && child.href && pathname?.startsWith(child.href) || (child.children?.some(
840
+ (grandchild) => grandchild.href === pathname || grandchild.href !== "/" && grandchild.href && pathname?.startsWith(grandchild.href)
841
+ ) ?? false)
842
+ ) ?? false : item.href === pathname || item.href !== "/" && item.href && pathname?.startsWith(item.href);
843
+ if (hasChildren && level <= 3) {
844
+ console.log(item);
845
+ return /* @__PURE__ */ jsxs3(Collapsible, { open: isOpen, onOpenChange: setIsOpen, children: [
846
+ /* @__PURE__ */ jsxs3(
847
+ CollapsibleTrigger,
848
+ {
849
+ className: cn3(
850
+ "flex w-full cursor-pointer items-center gap-1 text-sm transition-colors",
851
+ isOpen ? "text-foreground" : "text-foreground/60"
852
+ ),
853
+ children: [
854
+ item.icon && /* @__PURE__ */ jsx4(item.icon, {}),
855
+ item.title,
856
+ /* @__PURE__ */ jsx4(
857
+ ChevronDownIcon2,
858
+ {
859
+ className: cn3(
860
+ "ms-auto size-3.5 opacity-60 transition-transform",
861
+ isOpen && "rotate-180"
862
+ )
863
+ }
864
+ )
865
+ ]
866
+ }
867
+ ),
868
+ /* @__PURE__ */ jsx4(CollapsibleContent, { children: /* @__PURE__ */ jsx4("div", { className: cn3("flex flex-col space-y-2.5 pt-3", `ps-5`), children: item.children.map((child) => /* @__PURE__ */ jsx4(
869
+ MobileNavItemRenderer,
870
+ {
871
+ item: child,
872
+ pathname,
873
+ level: level + 1,
874
+ onOpenChange
875
+ },
876
+ child.title
877
+ )) }) })
878
+ ] });
879
+ }
880
+ return /* @__PURE__ */ jsxs3(
881
+ "a",
882
+ {
883
+ href: item.href || "#",
884
+ ...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
885
+ onClick: () => {
886
+ if (!item.external && item.href) {
887
+ window.location.href = item.href.toString();
888
+ }
889
+ onOpenChange?.(false);
890
+ },
891
+ className: cn3(
892
+ "inline-flex items-center gap-1 text-sm transition-colors",
893
+ isActive ? "text-foreground" : "text-foreground/60"
894
+ ),
895
+ children: [
896
+ item.icon && /* @__PURE__ */ jsx4(item.icon, {}),
897
+ item.title,
898
+ item.external && /* @__PURE__ */ jsx4(ArrowUpRightIcon2, { className: "ms-1 size-3.5 opacity-60" })
899
+ ]
900
+ }
901
+ );
902
+ }
903
+
904
+ // src/components/layout/site-footer.tsx
905
+ import { cn as cn4 } from "@pelatform/utils";
906
+ import { jsx as jsx5 } from "react/jsx-runtime";
907
+ function SiteFooter({ children, className }) {
908
+ return /* @__PURE__ */ jsx5("footer", { className: cn4("border-border border-t py-5 md:py-0", className), children: /* @__PURE__ */ jsx5("div", { className: "container flex flex-col items-center justify-between gap-4 py-4 md:h-16 md:flex-row", children }) });
909
+ }
910
+
911
+ // src/components/layout/site-header.tsx
912
+ import { cn as cn5 } from "@pelatform/utils";
913
+ import { jsx as jsx6 } from "react/jsx-runtime";
914
+ function SiteHeader({ children, className }) {
915
+ return /* @__PURE__ */ jsx6(
916
+ "header",
917
+ {
918
+ className: cn5(
919
+ "sticky top-0 z-50 w-full border-border border-b bg-background/95 backdrop-blur-sm supports-backdrop-filter:bg-background/60",
920
+ className
921
+ ),
922
+ children: /* @__PURE__ */ jsx6("div", { className: "container flex h-16 items-center justify-between gap-4", children })
923
+ }
924
+ );
925
+ }
926
+
927
+ // src/components/providers/query-provider.tsx
928
+ import { useState as useState2 } from "react";
929
+ import { QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query";
930
+ import { CircleAlertIcon } from "lucide-react";
931
+ import { toast } from "sonner";
932
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
933
+ var QueryProvider = ({
934
+ client: clientProps,
935
+ children
936
+ }) => {
937
+ const [queryClient] = useState2(
938
+ () => new QueryClient({
939
+ /** Global query defaults for all queries */
940
+ defaultOptions: {
941
+ queries: {
942
+ /** Time before data is considered stale (5 minutes) */
943
+ staleTime: 5 * 60 * 1e3,
944
+ /** Time before inactive queries are garbage collected (10 minutes) */
945
+ gcTime: 10 * 60 * 1e3,
946
+ /** Number of retry attempts on failure */
947
+ retry: (failureCount, error) => {
948
+ if (error?.status && error.status >= 400 && error.status < 500) {
949
+ return false;
950
+ }
951
+ return failureCount < 3;
952
+ },
953
+ /** Delay between retries (exponential backoff) */
954
+ retryDelay: (attemptIndex) => Math.min(1e3 * 2 ** attemptIndex, 3e4),
955
+ /** Refetch on window focus for fresh data */
956
+ refetchOnWindowFocus: true,
957
+ /** Refetch when network reconnects */
958
+ refetchOnReconnect: true
959
+ },
960
+ mutations: {
961
+ /** Number of retry attempts for mutations */
962
+ retry: 1,
963
+ /** Delay between mutation retries */
964
+ retryDelay: 1e3
965
+ }
966
+ },
967
+ /** Global query cache with error handling */
968
+ queryCache: new QueryCache({
969
+ onError: (error, query) => {
970
+ const message = error?.response?.data?.message || error?.message || "Something went wrong. Please try again.";
971
+ if (process.env.NODE_ENV === "development") {
972
+ console.error("Query Error:", {
973
+ error,
974
+ queryKey: query.queryKey,
975
+ message,
976
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
977
+ });
978
+ }
979
+ toast.custom(
980
+ () => /* @__PURE__ */ jsxs4(Alert, { variant: "default", children: [
981
+ /* @__PURE__ */ jsx7(CircleAlertIcon, {}),
982
+ /* @__PURE__ */ jsx7(AlertTitle, { children: message })
983
+ ] }),
984
+ {
985
+ position: "top-center",
986
+ duration: 5e3,
987
+ // Show for 5 seconds
988
+ id: `query-error-${query.queryHash}`
989
+ // Prevent duplicate toasts
990
+ }
991
+ );
992
+ }
993
+ })
994
+ })
995
+ );
996
+ return /* @__PURE__ */ jsx7(QueryClientProvider, { client: clientProps ?? queryClient, children });
997
+ };
998
+
999
+ // src/components/providers/theme-provider.tsx
1000
+ import { ThemeProvider as NextThemesProvider } from "next-themes";
1001
+ import { DEFAULT_THEME_MODE, THEME_MODES } from "@pelatform/utils";
1002
+ import { jsx as jsx8 } from "react/jsx-runtime";
1003
+ function ThemeProvider({ children, ...props }) {
1004
+ return /* @__PURE__ */ jsx8(
1005
+ NextThemesProvider,
1006
+ {
1007
+ attribute: "class",
1008
+ defaultTheme: DEFAULT_THEME_MODE,
1009
+ enableSystem: true,
1010
+ disableTransitionOnChange: true,
1011
+ enableColorScheme: true,
1012
+ storageKey: "theme",
1013
+ themes: [THEME_MODES.LIGHT, THEME_MODES.DARK, THEME_MODES.SYSTEM],
1014
+ ...props,
1015
+ children: /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 0, children })
1016
+ }
1017
+ );
1018
+ }
1019
+
1020
+ // src/components/ui/alert.tsx
1021
+ import {
1022
+ CheckIcon,
1023
+ CircleAlertIcon as CircleAlertIcon2,
1024
+ CircleCheckIcon,
1025
+ CircleXIcon,
1026
+ InfoIcon,
1027
+ TriangleAlertIcon
1028
+ } from "lucide-react";
1029
+ import { toast as toast2 } from "sonner";
1030
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1031
+ var iconMap = {
1032
+ primary: /* @__PURE__ */ jsx9(CircleAlertIcon2, { className: "text-primary" }),
1033
+ success: /* @__PURE__ */ jsx9(CircleCheckIcon, { className: "text-success" }),
1034
+ info: /* @__PURE__ */ jsx9(InfoIcon, { className: "text-info" }),
1035
+ warning: /* @__PURE__ */ jsx9(TriangleAlertIcon, { className: "text-warning" }),
1036
+ destructive: /* @__PURE__ */ jsx9(CircleXIcon, { className: "text-destructive" })
1037
+ };
1038
+ function AlertToast({
1039
+ message = "This is a toast",
1040
+ icon = "success",
1041
+ variant = "default"
1042
+ }) {
1043
+ toast2.custom(
1044
+ () => /* @__PURE__ */ jsxs5(Alert, { variant, children: [
1045
+ iconMap[icon ?? "success"],
1046
+ /* @__PURE__ */ jsx9(AlertTitle, { children: message })
1047
+ ] }),
1048
+ {
1049
+ // Auto-dismiss after 4 seconds
1050
+ duration: 4e3,
1051
+ // Generate unique ID to prevent duplicate toasts
1052
+ id: `alert-toast-${Date.now()}`
1053
+ }
1054
+ );
1055
+ }
1056
+ function AlertNotification({ message, variant = "info" }) {
1057
+ if (!message) return null;
1058
+ const getIcon = () => {
1059
+ switch (variant) {
1060
+ case "destructive":
1061
+ return /* @__PURE__ */ jsx9(CircleAlertIcon2, {});
1062
+ case "success":
1063
+ return /* @__PURE__ */ jsx9(CheckIcon, {});
1064
+ case "info":
1065
+ return /* @__PURE__ */ jsx9(InfoIcon, {});
1066
+ case "warning":
1067
+ return /* @__PURE__ */ jsx9(TriangleAlertIcon, {});
1068
+ default:
1069
+ return /* @__PURE__ */ jsx9(InfoIcon, {});
1070
+ }
1071
+ };
1072
+ return /* @__PURE__ */ jsxs5(Alert, { variant, children: [
1073
+ getIcon(),
1074
+ /* @__PURE__ */ jsx9(AlertTitle, { children: message })
1075
+ ] });
1076
+ }
1077
+ var AlertComingsoon = ({
1078
+ message = "This feature is coming soon.",
1079
+ variant = "default"
1080
+ } = {}) => {
1081
+ toast2.custom(
1082
+ () => /* @__PURE__ */ jsxs5(Alert, { variant, children: [
1083
+ /* @__PURE__ */ jsx9(CircleAlertIcon2, {}),
1084
+ /* @__PURE__ */ jsx9(AlertTitle, { children: message })
1085
+ ] }),
1086
+ {
1087
+ // Toast configuration for better UX
1088
+ position: "top-center",
1089
+ duration: 3e3,
1090
+ // Show for 3 seconds
1091
+ id: `coming-soon-${Date.now()}`
1092
+ // Prevent duplicate toasts
1093
+ }
1094
+ );
1095
+ };
1096
+
1097
+ // src/components/ui/comingsoon.tsx
1098
+ import { cn as cn6 } from "@pelatform/utils";
1099
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1100
+ function ComingSoon({ className, title, description }) {
1101
+ return /* @__PURE__ */ jsx10("div", { className: cn6("h-[calc(100vh-54px)] w-full overflow-hidden", className), children: /* @__PURE__ */ jsx10(
1102
+ HoverBackground,
1103
+ {
1104
+ colors: {
1105
+ background: "bg-gradient-to-br from-black via-gray-900 to-zinc-900",
1106
+ objects: [
1107
+ "bg-emerald-500/30",
1108
+ "bg-teal-500/30",
1109
+ "bg-green-500/30",
1110
+ "bg-lime-500/30",
1111
+ "bg-cyan-500/30",
1112
+ "bg-blue-500/30"
1113
+ ],
1114
+ glow: "shadow-emerald-400/70"
1115
+ },
1116
+ objectCount: 8,
1117
+ children: /* @__PURE__ */ jsxs6("div", { className: "flex h-full flex-col items-center justify-center space-y-4 text-center", children: [
1118
+ title && /* @__PURE__ */ jsx10("h2", { className: "font-bold text-4xl text-white/90", children: title }),
1119
+ description && /* @__PURE__ */ jsx10("p", { className: "max-w-md text-emerald-100/80 text-lg", children: description })
1120
+ ] })
1121
+ }
1122
+ ) });
1123
+ }
1124
+
1125
+ // src/components/ui/dialog.tsx
1126
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
1127
+ var ConfirmDismissDialog = ({
1128
+ open,
1129
+ onOpenChange,
1130
+ onConfirm,
1131
+ onCancel,
1132
+ title = "Discard changes?",
1133
+ description = "You have unsaved changes. Are you sure you want to close this dialog?",
1134
+ confirmText = "Discard changes",
1135
+ cancelText = "Cancel",
1136
+ maxWidth = "md:max-w-[375px]"
1137
+ }) => {
1138
+ const handleCancel = () => {
1139
+ if (onCancel) {
1140
+ onCancel();
1141
+ } else {
1142
+ onOpenChange(false);
1143
+ }
1144
+ };
1145
+ return /* @__PURE__ */ jsx11(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs7(AlertDialogContent, { className: maxWidth, children: [
1146
+ /* @__PURE__ */ jsxs7(AlertDialogHeader, { children: [
1147
+ /* @__PURE__ */ jsx11(AlertDialogTitle, { children: title }),
1148
+ /* @__PURE__ */ jsx11(AlertDialogDescription, { children: description })
1149
+ ] }),
1150
+ /* @__PURE__ */ jsxs7(AlertDialogFooter, { children: [
1151
+ /* @__PURE__ */ jsx11(AlertDialogCancel, { onClick: handleCancel, children: cancelText }),
1152
+ /* @__PURE__ */ jsx11(AlertDialogAction, { onClick: onConfirm, children: confirmText })
1153
+ ] })
1154
+ ] }) });
1155
+ };
1156
+
1157
+ // src/components/ui/language-switcher.tsx
1158
+ import { useTransition } from "react";
1159
+ import { GlobeIcon, LanguagesIcon } from "lucide-react";
1160
+ import { cn as cn7 } from "@pelatform/utils";
1161
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1162
+ function getFlagUrl(flag) {
1163
+ const flagCode = flag.toLowerCase();
1164
+ return `"https://assets.pelatform.com"/media/flags/${flagCode}.svg`;
1165
+ }
1166
+ function LanguageSwitcher({
1167
+ className,
1168
+ type = "dropdown",
1169
+ variant = "ghost",
1170
+ size = "default",
1171
+ showNames = true,
1172
+ showFlags = true,
1173
+ label = "Language",
1174
+ i18nEnabled = true,
1175
+ currentLocale,
1176
+ locales,
1177
+ onLocaleChange,
1178
+ customFlagUrl = false,
1179
+ badgeVariant = "info-outline"
1180
+ }) {
1181
+ const [isPending, startTransition] = useTransition();
1182
+ const languages = locales ?? [];
1183
+ const currentLanguage = languages.find((l) => l.code === currentLocale);
1184
+ function handleLanguageChange(newLocale) {
1185
+ if (newLocale === currentLocale) return;
1186
+ startTransition(() => {
1187
+ const maybePromise = onLocaleChange?.(newLocale);
1188
+ return maybePromise;
1189
+ });
1190
+ }
1191
+ if (!i18nEnabled && (languages?.length ?? 0) <= 1) {
1192
+ return null;
1193
+ }
1194
+ if (type === "dropdown") {
1195
+ return /* @__PURE__ */ jsxs8(DropdownMenu, { children: [
1196
+ /* @__PURE__ */ jsx12(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs8(
1197
+ Button,
1198
+ {
1199
+ variant,
1200
+ size,
1201
+ className: cn7(
1202
+ "group/toggle size-8 px-0 text-foreground ring-0! focus:outline-none! focus:ring-0! focus-visible:outline-none! focus-visible:ring-0! focus-visible:ring-offset-0!",
1203
+ isPending && "cursor-not-allowed opacity-50",
1204
+ className
1205
+ ),
1206
+ disabled: isPending,
1207
+ children: [
1208
+ /* @__PURE__ */ jsx12(LanguagesIcon, { className: "size-4" }),
1209
+ /* @__PURE__ */ jsx12("span", { className: "sr-only", children: isPending ? "Changing language..." : "Language dropdown" })
1210
+ ]
1211
+ }
1212
+ ) }),
1213
+ /* @__PURE__ */ jsx12(
1214
+ DropdownMenuContent,
1215
+ {
1216
+ align: "end",
1217
+ className: "min-w-[150px]",
1218
+ onCloseAutoFocus: (e) => e.preventDefault(),
1219
+ children: languages.map((lang) => /* @__PURE__ */ jsxs8(
1220
+ DropdownMenuItem,
1221
+ {
1222
+ onClick: () => handleLanguageChange(lang.code),
1223
+ className: cn7("gap-2", currentLocale === lang.code && "bg-accent"),
1224
+ children: [
1225
+ showFlags && lang.flag ? /* @__PURE__ */ jsx12(
1226
+ "img",
1227
+ {
1228
+ src: customFlagUrl ? lang.flag : getFlagUrl(lang.flag),
1229
+ alt: `${lang.name} flag`,
1230
+ className: "size-4 rounded-full object-cover",
1231
+ width: 24,
1232
+ height: 24
1233
+ }
1234
+ ) : showFlags ? /* @__PURE__ */ jsx12(GlobeIcon, { className: "size-4" }) : null,
1235
+ showNames && /* @__PURE__ */ jsx12("span", { className: "text-sm", children: lang.name }),
1236
+ currentLocale === lang.code && /* @__PURE__ */ jsx12("span", { className: "ms-auto text-muted-foreground text-xs", children: "\u2713" })
1237
+ ]
1238
+ },
1239
+ lang.code
1240
+ ))
1241
+ }
1242
+ )
1243
+ ] });
1244
+ }
1245
+ return /* @__PURE__ */ jsxs8(DropdownMenuSub, { children: [
1246
+ /* @__PURE__ */ jsxs8(
1247
+ DropdownMenuSubTrigger,
1248
+ {
1249
+ className: cn7(
1250
+ "flex items-center gap-2 data-[state=open]:**:data-[slot=badge]:border-input **:data-[slot=dropdown-menu-sub-trigger-indicator]:hidden hover:**:data-[slot=badge]:border-input",
1251
+ className
1252
+ ),
1253
+ children: [
1254
+ /* @__PURE__ */ jsx12(GlobeIcon, { className: "size-4" }),
1255
+ /* @__PURE__ */ jsxs8("span", { className: "relative flex grow items-center justify-between gap-2", children: [
1256
+ label,
1257
+ currentLanguage && /* @__PURE__ */ jsxs8(Badge, { variant: badgeVariant, className: "absolute end-0 top-1/2 -translate-y-1/2", children: [
1258
+ currentLanguage.name,
1259
+ showFlags && currentLanguage.flag && /* @__PURE__ */ jsx12(
1260
+ "img",
1261
+ {
1262
+ src: customFlagUrl ? currentLanguage.flag : getFlagUrl(currentLanguage.flag),
1263
+ alt: currentLanguage.name,
1264
+ className: "ms-1 size-3.5 rounded-full",
1265
+ width: 24,
1266
+ height: 24
1267
+ }
1268
+ )
1269
+ ] })
1270
+ ] })
1271
+ ]
1272
+ }
1273
+ ),
1274
+ /* @__PURE__ */ jsx12(DropdownMenuSubContent, { className: "w-48", children: /* @__PURE__ */ jsx12(
1275
+ DropdownMenuRadioGroup,
1276
+ {
1277
+ value: currentLocale,
1278
+ onValueChange: (value) => handleLanguageChange(value),
1279
+ children: languages.map((item) => /* @__PURE__ */ jsxs8(
1280
+ DropdownMenuRadioItem,
1281
+ {
1282
+ value: item.code,
1283
+ className: "flex items-center gap-2",
1284
+ children: [
1285
+ showFlags && item.flag ? /* @__PURE__ */ jsx12(
1286
+ "img",
1287
+ {
1288
+ src: customFlagUrl ? item.flag : getFlagUrl(item.flag),
1289
+ alt: `${item.name} flag`,
1290
+ className: "size-4 rounded-full object-cover",
1291
+ width: 24,
1292
+ height: 24
1293
+ }
1294
+ ) : showFlags ? /* @__PURE__ */ jsx12(GlobeIcon, { className: "size-4" }) : null,
1295
+ /* @__PURE__ */ jsx12("span", { children: item.name })
1296
+ ]
1297
+ },
1298
+ item.code
1299
+ ))
1300
+ }
1301
+ ) })
1302
+ ] });
1303
+ }
1304
+
1305
+ // src/components/ui/mode-switcher.tsx
1306
+ import * as React from "react";
1307
+ import { MonitorIcon, MoonIcon, SunIcon } from "lucide-react";
1308
+ import { useTheme } from "next-themes";
1309
+ import { cn as cn8, THEME_MODES as THEME_MODES2 } from "@pelatform/utils";
1310
+ import { Fragment, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1311
+ function ModeSwitcher({
1312
+ className,
1313
+ variant = "ghost",
1314
+ size = "default",
1315
+ cycleOrder = [THEME_MODES2.SYSTEM, THEME_MODES2.LIGHT, THEME_MODES2.DARK],
1316
+ type = "toogle",
1317
+ label = {
1318
+ system: "System",
1319
+ dark: "Dark",
1320
+ light: "Light"
1321
+ }
1322
+ }) {
1323
+ const { setTheme, theme } = useTheme();
1324
+ const toggleTheme = React.useCallback(() => {
1325
+ const currentIndex = cycleOrder.indexOf(theme);
1326
+ const nextIndex = (currentIndex + 1) % cycleOrder.length;
1327
+ const nextTheme = cycleOrder[nextIndex];
1328
+ setTheme(nextTheme);
1329
+ }, [theme, setTheme, cycleOrder]);
1330
+ const getCurrentIcon = (withLabel = false) => {
1331
+ if (theme === THEME_MODES2.SYSTEM) {
1332
+ return /* @__PURE__ */ jsxs9(Fragment, { children: [
1333
+ /* @__PURE__ */ jsx13(MonitorIcon, {}),
1334
+ " ",
1335
+ withLabel && /* @__PURE__ */ jsx13("span", { children: label.system })
1336
+ ] });
1337
+ }
1338
+ if (theme === THEME_MODES2.DARK) {
1339
+ return /* @__PURE__ */ jsxs9(Fragment, { children: [
1340
+ /* @__PURE__ */ jsx13(MoonIcon, {}),
1341
+ " ",
1342
+ withLabel && /* @__PURE__ */ jsx13("span", { children: label.dark })
1343
+ ] });
1344
+ }
1345
+ return /* @__PURE__ */ jsxs9(Fragment, { children: [
1346
+ /* @__PURE__ */ jsx13(SunIcon, {}),
1347
+ " ",
1348
+ withLabel && /* @__PURE__ */ jsx13("span", { children: label.light })
1349
+ ] });
1350
+ };
1351
+ const isActive = (val) => theme === val;
1352
+ if (type === "toogle") {
1353
+ return /* @__PURE__ */ jsxs9(
1354
+ Button,
1355
+ {
1356
+ size,
1357
+ variant,
1358
+ className: cn8("group/toggle size-8 px-0 text-foreground", className),
1359
+ onClick: toggleTheme,
1360
+ "aria-label": "Switch theme",
1361
+ children: [
1362
+ getCurrentIcon(),
1363
+ /* @__PURE__ */ jsx13("span", { className: "sr-only", children: "Toggle theme" })
1364
+ ]
1365
+ }
1366
+ );
1367
+ }
1368
+ if (type === "dropdown") {
1369
+ return /* @__PURE__ */ jsxs9(DropdownMenu, { children: [
1370
+ /* @__PURE__ */ jsx13(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs9(
1371
+ Button,
1372
+ {
1373
+ size,
1374
+ variant,
1375
+ className: cn8(
1376
+ "group/toggle size-8 px-0 text-foreground ring-0! focus:outline-none! focus:ring-0! focus-visible:outline-none! focus-visible:ring-0! focus-visible:ring-offset-0!",
1377
+ className
1378
+ ),
1379
+ children: [
1380
+ getCurrentIcon(),
1381
+ /* @__PURE__ */ jsx13("span", { className: "sr-only", children: "Toggle theme" })
1382
+ ]
1383
+ }
1384
+ ) }),
1385
+ /* @__PURE__ */ jsxs9(DropdownMenuContent, { align: "end", onCloseAutoFocus: (e) => e.preventDefault(), children: [
1386
+ /* @__PURE__ */ jsxs9(
1387
+ DropdownMenuItem,
1388
+ {
1389
+ className: isActive("light") ? "bg-accent" : "",
1390
+ onClick: () => setTheme("light"),
1391
+ children: [
1392
+ /* @__PURE__ */ jsx13(SunIcon, {}),
1393
+ label.light
1394
+ ]
1395
+ }
1396
+ ),
1397
+ /* @__PURE__ */ jsxs9(
1398
+ DropdownMenuItem,
1399
+ {
1400
+ className: isActive("dark") ? "bg-accent" : "",
1401
+ onClick: () => setTheme("dark"),
1402
+ children: [
1403
+ /* @__PURE__ */ jsx13(MoonIcon, {}),
1404
+ label.dark
1405
+ ]
1406
+ }
1407
+ ),
1408
+ /* @__PURE__ */ jsxs9(
1409
+ DropdownMenuItem,
1410
+ {
1411
+ className: isActive("system") ? "bg-accent" : "",
1412
+ onClick: () => setTheme("system"),
1413
+ children: [
1414
+ /* @__PURE__ */ jsx13(MonitorIcon, {}),
1415
+ label.system
1416
+ ]
1417
+ }
1418
+ )
1419
+ ] })
1420
+ ] });
1421
+ }
1422
+ return /* @__PURE__ */ jsx13(
1423
+ DropdownMenuItem,
1424
+ {
1425
+ className,
1426
+ onSelect: (e) => {
1427
+ e.preventDefault();
1428
+ toggleTheme();
1429
+ },
1430
+ children: getCurrentIcon(true)
1431
+ }
1432
+ );
1433
+ }
1434
+
1435
+ // src/components/ui/screen-loader.tsx
1436
+ import { LoaderIcon } from "lucide-react";
1437
+ import { cn as cn9 } from "@pelatform/utils";
1438
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
1439
+ function ScreenLoader({
1440
+ loadingText = "Loading...",
1441
+ className = "",
1442
+ spinnerClassName,
1443
+ textClassName = "text-muted-foreground font-medium text-sm",
1444
+ contentLoader = false
1445
+ } = {}) {
1446
+ if (contentLoader) {
1447
+ return /* @__PURE__ */ jsx14("div", { className: cn9("flex w-full grow items-center justify-center", className), children: /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2.5", children: [
1448
+ /* @__PURE__ */ jsx14(LoaderIcon, { className: cn9("size-6 animate-spin", spinnerClassName) }),
1449
+ /* @__PURE__ */ jsx14("span", { className: textClassName, children: loadingText })
1450
+ ] }) });
1451
+ }
1452
+ return /* @__PURE__ */ jsxs10(
1453
+ "div",
1454
+ {
1455
+ className: cn9(
1456
+ "fixed inset-0 z-500 flex flex-col items-center justify-center gap-2 bg-background transition-opacity duration-700 ease-in-out",
1457
+ className
1458
+ ),
1459
+ role: "status",
1460
+ "aria-live": "polite",
1461
+ "aria-label": loadingText,
1462
+ children: [
1463
+ /* @__PURE__ */ jsx14(LoaderIcon, { className: cn9("size-6 animate-spin", spinnerClassName) }),
1464
+ /* @__PURE__ */ jsx14("div", { className: textClassName, children: loadingText })
1465
+ ]
1466
+ }
1467
+ );
1468
+ }
1469
+
1470
+ // src/components/ui/toolbar.tsx
1471
+ import { cn as cn10 } from "@pelatform/utils";
1472
+ import { jsx as jsx15 } from "react/jsx-runtime";
1473
+ var Toolbar = ({ children }) => {
1474
+ return /* @__PURE__ */ jsx15("div", { className: "flex grow items-center justify-between gap-2.5 pb-5", children });
1475
+ };
1476
+ var ToolbarHeading = ({
1477
+ children,
1478
+ className
1479
+ }) => {
1480
+ return /* @__PURE__ */ jsx15("div", { className: cn10("flex flex-col flex-wrap gap-px", className), children });
1481
+ };
1482
+ var ToolbarTitle = ({
1483
+ children,
1484
+ className
1485
+ }) => {
1486
+ return /* @__PURE__ */ jsx15("h1", { className: cn10("font-semibold text-foreground text-lg", className), children });
1487
+ };
1488
+ var ToolbarActions = ({ children }) => {
1489
+ return /* @__PURE__ */ jsx15("div", { className: "flex flex-wrap items-center gap-1.5 lg:gap-3.5", children });
1490
+ };
1491
+
1492
+ // src/components/ui/user-avatar.tsx
1493
+ import { cva } from "class-variance-authority";
1494
+ import { cn as cn11 } from "@pelatform/utils";
1495
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1496
+ var avatarStatusVariants = cva(
1497
+ "flex size-2 items-center rounded-full border-2 border-background",
1498
+ {
1499
+ variants: {
1500
+ variant: {
1501
+ online: "bg-green-600",
1502
+ offline: "bg-zinc-400 dark:bg-zinc-500",
1503
+ busy: "bg-yellow-600",
1504
+ away: "bg-blue-600"
1505
+ }
1506
+ },
1507
+ defaultVariants: {
1508
+ variant: "online"
1509
+ }
1510
+ }
1511
+ );
1512
+ function UserAvatar({ className, indicator = false, src, alt }) {
1513
+ const name = alt ?? "User";
1514
+ const initial = getInitials(name);
1515
+ return /* @__PURE__ */ jsxs11(Avatar, { className, children: [
1516
+ /* @__PURE__ */ jsx16(AvatarImage, { src: src ?? void 0, alt: name }),
1517
+ /* @__PURE__ */ jsx16(AvatarFallback, { children: initial }),
1518
+ indicator && /* @__PURE__ */ jsx16(AvatarIndicator, { className: "-end-2 -top-2", children: /* @__PURE__ */ jsx16(AvatarStatus, { variant: "online", className: "size-2.5" }) })
1519
+ ] });
1520
+ }
1521
+ var getInitials = (name, count) => {
1522
+ if (!name || typeof name !== "string") {
1523
+ return "";
1524
+ }
1525
+ const initials = name.split(" ").filter(Boolean).map((part) => part[0].toUpperCase());
1526
+ return count && count > 0 ? initials.slice(0, count).join("") : initials.join("");
1527
+ };
1528
+ function AvatarIndicator({ className, ...props }) {
1529
+ return /* @__PURE__ */ jsx16(
1530
+ "div",
1531
+ {
1532
+ "data-slot": "avatar-indicator",
1533
+ className: cn11("absolute flex size-6 items-center justify-center", className),
1534
+ ...props
1535
+ }
1536
+ );
1537
+ }
1538
+ function AvatarStatus({
1539
+ className,
1540
+ variant,
1541
+ ...props
1542
+ }) {
1543
+ return /* @__PURE__ */ jsx16(
1544
+ "div",
1545
+ {
1546
+ "data-slot": "avatar-status",
1547
+ className: cn11(avatarStatusVariants({ variant }), className),
1548
+ ...props
1549
+ }
1550
+ );
1551
+ }
644
1552
  export {
1553
+ AlertComingsoon,
1554
+ AlertNotification,
1555
+ AlertToast,
1556
+ ComingSoon,
1557
+ ConfirmDismissDialog,
645
1558
  Icons,
646
- Logo
1559
+ LanguageSwitcher,
1560
+ Logo,
1561
+ MainNav,
1562
+ MobileNav,
1563
+ MobileNavItemRenderer,
1564
+ ModeSwitcher,
1565
+ QueryProvider,
1566
+ ScreenLoader,
1567
+ SiteFooter,
1568
+ SiteHeader,
1569
+ ThemeProvider,
1570
+ Toolbar,
1571
+ ToolbarActions,
1572
+ ToolbarHeading,
1573
+ ToolbarTitle,
1574
+ UserAvatar,
1575
+ getInitials
647
1576
  };