@opensite/ui 0.9.9 → 1.0.1

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.
@@ -864,6 +864,160 @@ var NavbarLogo = ({
864
864
  }
865
865
  );
866
866
  };
867
+ var svgCache = /* @__PURE__ */ new Map();
868
+ function DynamicIcon({
869
+ name,
870
+ size = 28,
871
+ color,
872
+ className,
873
+ alt
874
+ }) {
875
+ const [svgContent, setSvgContent] = React.useState(null);
876
+ const [isLoading, setIsLoading] = React.useState(true);
877
+ const [error, setError] = React.useState(null);
878
+ const { url, iconName } = React.useMemo(() => {
879
+ const separator = name.includes("/") ? "/" : ":";
880
+ const [prefix, iconName2] = name.split(separator);
881
+ const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}`;
882
+ return {
883
+ url: baseUrl,
884
+ iconName: iconName2
885
+ };
886
+ }, [name, size]);
887
+ React.useEffect(() => {
888
+ let isMounted = true;
889
+ const fetchSvg = async () => {
890
+ const cached = svgCache.get(url);
891
+ if (cached) {
892
+ if (isMounted) {
893
+ setSvgContent(cached);
894
+ setIsLoading(false);
895
+ }
896
+ return;
897
+ }
898
+ try {
899
+ setIsLoading(true);
900
+ setError(null);
901
+ const response = await fetch(url);
902
+ if (!response.ok) {
903
+ throw new Error(`Failed to fetch icon: ${response.status}`);
904
+ }
905
+ let svg = await response.text();
906
+ svg = processSvgForCurrentColor(svg);
907
+ svgCache.set(url, svg);
908
+ if (isMounted) {
909
+ setSvgContent(svg);
910
+ setIsLoading(false);
911
+ }
912
+ } catch (err) {
913
+ if (isMounted) {
914
+ setError(err instanceof Error ? err.message : "Failed to load icon");
915
+ setIsLoading(false);
916
+ }
917
+ }
918
+ };
919
+ fetchSvg();
920
+ return () => {
921
+ isMounted = false;
922
+ };
923
+ }, [url]);
924
+ if (isLoading) {
925
+ return /* @__PURE__ */ jsx(
926
+ "span",
927
+ {
928
+ className: cn("inline-block", className),
929
+ style: { width: size, height: size },
930
+ "aria-hidden": "true"
931
+ }
932
+ );
933
+ }
934
+ if (error || !svgContent) {
935
+ return /* @__PURE__ */ jsx(
936
+ "span",
937
+ {
938
+ className: cn("inline-block", className),
939
+ style: { width: size, height: size },
940
+ role: "img",
941
+ "aria-label": alt || iconName
942
+ }
943
+ );
944
+ }
945
+ return /* @__PURE__ */ jsx(
946
+ "span",
947
+ {
948
+ className: cn("inline-flex items-center justify-center", className),
949
+ style: {
950
+ width: size,
951
+ height: size,
952
+ color: color || "inherit"
953
+ },
954
+ role: "img",
955
+ "aria-label": alt || iconName,
956
+ dangerouslySetInnerHTML: { __html: svgContent }
957
+ }
958
+ );
959
+ }
960
+ function processSvgForCurrentColor(svg) {
961
+ let processed = svg;
962
+ processed = processed.replace(
963
+ /stroke=["'](#000000|#000|black)["']/gi,
964
+ 'stroke="currentColor"'
965
+ );
966
+ processed = processed.replace(
967
+ /fill=["'](#000000|#000|black)["']/gi,
968
+ 'fill="currentColor"'
969
+ );
970
+ return processed;
971
+ }
972
+ var platformIconMap = {
973
+ instagram: "cib/instagram",
974
+ linkedin: "cib/linkedin",
975
+ google: "cib/google",
976
+ facebook: "cib/facebook",
977
+ tiktok: "cib/tiktok",
978
+ youtube: "cib/youtube",
979
+ yelp: "cib/yelp",
980
+ spotify: "cib/spotify",
981
+ apple: "cib/apple",
982
+ x: "line-md/twitter-x"
983
+ };
984
+ var SocialLinkIcon = React.forwardRef(
985
+ ({
986
+ platformName,
987
+ label,
988
+ iconSize = 20,
989
+ iconColor,
990
+ iconClassName,
991
+ className,
992
+ ...pressableProps
993
+ }, ref) => {
994
+ const iconName = platformIconMap[platformName];
995
+ const accessibleLabel = label || platformName;
996
+ return /* @__PURE__ */ jsx(
997
+ Pressable,
998
+ {
999
+ ref,
1000
+ "aria-label": accessibleLabel,
1001
+ className: cn(
1002
+ "inline-flex items-center justify-center transition-colors",
1003
+ className
1004
+ ),
1005
+ ...pressableProps,
1006
+ children: /* @__PURE__ */ jsx(
1007
+ DynamicIcon,
1008
+ {
1009
+ name: iconName,
1010
+ size: iconSize,
1011
+ color: iconColor,
1012
+ className: iconClassName,
1013
+ alt: accessibleLabel
1014
+ }
1015
+ )
1016
+ }
1017
+ );
1018
+ }
1019
+ );
1020
+ SocialLinkIcon.displayName = "SocialLinkIcon";
867
1021
 
868
1022
  // lib/mediaPlaceholders.ts
869
1023
  var logoPlaceholders = {
@@ -933,36 +1087,35 @@ var NavbarFullscreenMenu = ({
933
1087
  const renderMenuItems = useMemo(() => {
934
1088
  if (menuSlot) return menuSlot;
935
1089
  if (!menuItems || menuItems.length === 0) return null;
936
- return menuItems.map((item, index) => /* @__PURE__ */ jsx(
1090
+ return /* @__PURE__ */ jsx("div", { className: "group/menu-container", children: menuItems.map((item, index) => /* @__PURE__ */ jsx(
937
1091
  "div",
938
1092
  {
939
- className: "mb-5 animate-in slide-in-from-bottom-4 fade-in",
1093
+ className: "group/menu-item mb-5 animate-in slide-in-from-bottom-4 fade-in",
940
1094
  style: {
941
1095
  animationDelay: `${0.2 + index * 0.1}s`,
942
1096
  animationFillMode: "both"
943
1097
  },
944
- children: /* @__PURE__ */ jsxs(Pressable, { href: item.href, className: "group relative inline-block", children: [
945
- /* @__PURE__ */ jsx("span", { className: "relative z-10 text-4xl font-black text-foreground uppercase transition-all duration-300 md:text-6xl group-hover:opacity-80 group-hover:blur-[6px]", children: item.label }),
946
- /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 h-1 w-0 bg-primary transition-all duration-300 group-hover:w-full" })
1098
+ children: /* @__PURE__ */ jsxs(Pressable, { href: item.href, className: "relative inline-block", children: [
1099
+ /* @__PURE__ */ jsx("span", { className: "relative z-10 text-4xl font-black text-foreground uppercase transition-all duration-300 md:text-6xl group-hover/menu-container:opacity-50 group-hover/menu-container:blur-[4px] group-hover/menu-item:!opacity-100 group-hover/menu-item:!blur-none", children: item.label }),
1100
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 h-1 w-0 bg-primary transition-all duration-300 group-hover/menu-item:w-full" })
947
1101
  ] })
948
1102
  },
949
1103
  item.label
950
- ));
1104
+ )) });
951
1105
  }, [menuSlot, menuItems]);
952
1106
  const renderSocialLinks = useMemo(() => {
953
1107
  if (socialLinksSlot) return socialLinksSlot;
954
1108
  if (!socialLinks || socialLinks.length === 0) return null;
955
- return socialLinks.map((link, index) => /* @__PURE__ */ jsx(
956
- Pressable,
1109
+ return socialLinks.map((link) => /* @__PURE__ */ jsx(
1110
+ SocialLinkIcon,
957
1111
  {
1112
+ platformName: link.platformName,
958
1113
  href: link.href,
959
- target: "_blank",
960
- rel: "noopener noreferrer",
961
- className: "group flex items-center gap-2 font-mono text-sm tracking-wider text-muted-foreground transition-colors hover:text-foreground hover:translate-x-1",
962
- style: { animationDelay: `${0.8 + index * 0.1}s` },
963
- children: /* @__PURE__ */ jsx("span", { children: link.label })
1114
+ label: link.label,
1115
+ iconSize: 24,
1116
+ className: "text-muted-foreground transition-all duration-300 hover:text-foreground hover:scale-110"
964
1117
  },
965
- link.label
1118
+ link.platformName
966
1119
  ));
967
1120
  }, [socialLinksSlot, socialLinks]);
968
1121
  const {
@@ -974,18 +1127,18 @@ var NavbarFullscreenMenu = ({
974
1127
  sectionContainerMaxWidth,
975
1128
  spacingOverride
976
1129
  } = getNavbarLayoutClasses(layoutVariant, { className, containerClassName });
977
- return /* @__PURE__ */ jsx(
978
- Section,
979
- {
980
- background,
981
- spacing: spacingOverride ?? spacing,
982
- className: sectionClasses,
983
- pattern,
984
- patternOpacity,
985
- containerClassName: sectionContainerClassName,
986
- containerMaxWidth: sectionContainerMaxWidth,
987
- children: /* @__PURE__ */ jsx("div", { className: containerWrapperClasses, children: /* @__PURE__ */ jsx("div", { className: navWrapperClasses, children: /* @__PURE__ */ jsxs("div", { className: innerContainerClasses, children: [
988
- /* @__PURE__ */ jsxs(
1130
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1131
+ /* @__PURE__ */ jsx(
1132
+ Section,
1133
+ {
1134
+ background,
1135
+ spacing: spacingOverride ?? spacing,
1136
+ className: sectionClasses,
1137
+ pattern,
1138
+ patternOpacity,
1139
+ containerClassName: sectionContainerClassName,
1140
+ containerMaxWidth: sectionContainerMaxWidth,
1141
+ children: /* @__PURE__ */ jsx("div", { className: containerWrapperClasses, children: /* @__PURE__ */ jsx("div", { className: navWrapperClasses, children: /* @__PURE__ */ jsx("div", { className: innerContainerClasses, children: /* @__PURE__ */ jsxs(
989
1142
  "nav",
990
1143
  {
991
1144
  className: cn(
@@ -1002,60 +1155,75 @@ var NavbarFullscreenMenu = ({
1002
1155
  optixFlowConfig
1003
1156
  }
1004
1157
  ) }),
1005
- /* @__PURE__ */ jsx("div", { className: "z-50", children: /* @__PURE__ */ jsxs(
1158
+ /* @__PURE__ */ jsx("div", { className: "z-50", children: /* @__PURE__ */ jsx(
1006
1159
  "button",
1007
1160
  {
1008
1161
  onClick: toggleMenu,
1009
- className: "text-lg tracking-wider text-foreground transition-colors hover:text-muted-foreground",
1010
- children: [
1011
- /* @__PURE__ */ jsx(
1012
- "span",
1013
- {
1014
- className: `inline-block transition-all duration-200 ${isOpen ? "opacity-0 -translate-y-2" : "opacity-100 translate-y-0"}`,
1015
- style: { display: isOpen ? "none" : "inline-block" },
1016
- children: isOpen ? "" : "\u2630"
1017
- }
1018
- ),
1019
- /* @__PURE__ */ jsx(
1020
- "span",
1021
- {
1022
- className: `inline-block transition-all duration-200 ${isOpen ? "opacity-100 translate-y-0" : "opacity-0 translate-y-2"}`,
1023
- style: { display: isOpen ? "inline-block" : "none" },
1024
- children: isOpen ? "\u2715" : ""
1025
- }
1026
- )
1027
- ]
1162
+ className: "text-2xl tracking-wider text-foreground transition-colors hover:text-muted-foreground",
1163
+ "aria-label": isOpen ? "Close menu" : "Open menu",
1164
+ children: "\u2630"
1028
1165
  }
1029
1166
  ) })
1030
1167
  ]
1031
1168
  }
1169
+ ) }) }) })
1170
+ }
1171
+ ),
1172
+ isOpen && /* @__PURE__ */ jsxs(
1173
+ "div",
1174
+ {
1175
+ className: cn(
1176
+ "fixed inset-0 z-50 flex flex-col bg-background animate-in fade-in duration-300",
1177
+ overlayClassName
1032
1178
  ),
1033
- isOpen && /* @__PURE__ */ jsx(
1034
- "div",
1035
- {
1036
- className: cn(
1037
- "fixed inset-0 z-40 overflow-hidden bg-background animate-in fade-in duration-300",
1038
- overlayClassName
1179
+ children: [
1180
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-6", children: [
1181
+ /* @__PURE__ */ jsx(
1182
+ NavbarLogo,
1183
+ {
1184
+ logo,
1185
+ logoSlot,
1186
+ logoClassName,
1187
+ optixFlowConfig
1188
+ }
1039
1189
  ),
1040
- children: /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col items-center justify-center px-6", children: [
1041
- /* @__PURE__ */ jsx("div", { className: cn("mb-16 text-center", menuItemsClassName), children: renderMenuItems }),
1042
- /* @__PURE__ */ jsx(
1043
- "div",
1044
- {
1045
- className: cn(
1046
- "flex flex-col gap-8 sm:flex-row sm:gap-12 animate-in slide-in-from-bottom-4 fade-in",
1047
- socialLinksClassName
1048
- ),
1049
- style: { animationDelay: "0.7s", animationFillMode: "both" },
1050
- children: renderSocialLinks
1051
- }
1052
- )
1053
- ] })
1054
- }
1055
- )
1056
- ] }) }) })
1057
- }
1058
- );
1190
+ /* @__PURE__ */ jsx(
1191
+ "button",
1192
+ {
1193
+ onClick: toggleMenu,
1194
+ className: "text-2xl text-foreground transition-colors hover:text-muted-foreground",
1195
+ "aria-label": "Close menu",
1196
+ children: "\u2715"
1197
+ }
1198
+ )
1199
+ ] }),
1200
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center overflow-y-auto px-6 py-8", children: [
1201
+ /* @__PURE__ */ jsx(
1202
+ "div",
1203
+ {
1204
+ className: cn(
1205
+ "mb-12 max-h-[60vh] overflow-y-auto text-center md:max-h-none",
1206
+ menuItemsClassName
1207
+ ),
1208
+ children: renderMenuItems
1209
+ }
1210
+ ),
1211
+ /* @__PURE__ */ jsx(
1212
+ "div",
1213
+ {
1214
+ className: cn(
1215
+ "flex flex-row flex-wrap items-center justify-center gap-6 animate-in slide-in-from-bottom-4 fade-in",
1216
+ socialLinksClassName
1217
+ ),
1218
+ style: { animationDelay: "0.7s", animationFillMode: "both" },
1219
+ children: renderSocialLinks
1220
+ }
1221
+ )
1222
+ ] })
1223
+ ]
1224
+ }
1225
+ )
1226
+ ] });
1059
1227
  };
1060
1228
 
1061
1229
  export { NavbarFullscreenMenu };