@janovix/blocks 1.0.0-rc.3 → 1.0.0-rc.4

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/dist/index.cjs CHANGED
@@ -10,6 +10,8 @@ var SliderPrimitive = require('@radix-ui/react-slider');
10
10
  var TogglePrimitive = require('@radix-ui/react-toggle');
11
11
  var DialogPrimitive = require('@radix-ui/react-dialog');
12
12
  var vaul = require('vaul');
13
+ var PopoverPrimitive = require('@radix-ui/react-popover');
14
+ var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
13
15
 
14
16
  function _interopNamespace(e) {
15
17
  if (e && e.__esModule) return e;
@@ -34,6 +36,8 @@ var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
34
36
  var SliderPrimitive__namespace = /*#__PURE__*/_interopNamespace(SliderPrimitive);
35
37
  var TogglePrimitive__namespace = /*#__PURE__*/_interopNamespace(TogglePrimitive);
36
38
  var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
39
+ var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
40
+ var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
37
41
 
38
42
  function composeEventHandlers(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
39
43
  return function handleEvent(event) {
@@ -10614,6 +10618,450 @@ function AvatarEditorDialog({
10614
10618
  ] }) })
10615
10619
  ] });
10616
10620
  }
10621
+ function Popover({
10622
+ ...props
10623
+ }) {
10624
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Root, { "data-slot": "popover", ...props });
10625
+ }
10626
+ function PopoverTrigger({
10627
+ ...props
10628
+ }) {
10629
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Trigger, { "data-slot": "popover-trigger", ...props });
10630
+ }
10631
+ function PopoverContent({
10632
+ className,
10633
+ align = "center",
10634
+ sideOffset = 4,
10635
+ ...props
10636
+ }) {
10637
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
10638
+ PopoverPrimitive__namespace.Content,
10639
+ {
10640
+ "data-slot": "popover-content",
10641
+ align,
10642
+ sideOffset,
10643
+ className: cn(
10644
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
10645
+ className
10646
+ ),
10647
+ ...props
10648
+ }
10649
+ ) });
10650
+ }
10651
+ function PopoverAnchor({
10652
+ ...props
10653
+ }) {
10654
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Anchor, { "data-slot": "popover-anchor", ...props });
10655
+ }
10656
+ function ScrollArea({
10657
+ className,
10658
+ children,
10659
+ ...props
10660
+ }) {
10661
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10662
+ ScrollAreaPrimitive__namespace.Root,
10663
+ {
10664
+ "data-slot": "scroll-area",
10665
+ className: cn("relative overflow-hidden", className),
10666
+ ...props,
10667
+ children: [
10668
+ /* @__PURE__ */ jsxRuntime.jsx(
10669
+ ScrollAreaPrimitive__namespace.Viewport,
10670
+ {
10671
+ "data-slot": "scroll-area-viewport",
10672
+ className: "h-full w-full rounded-[inherit]",
10673
+ children
10674
+ }
10675
+ ),
10676
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollBar, {}),
10677
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.Corner, {})
10678
+ ]
10679
+ }
10680
+ );
10681
+ }
10682
+ function ScrollBar({
10683
+ className,
10684
+ orientation = "vertical",
10685
+ ...props
10686
+ }) {
10687
+ return /* @__PURE__ */ jsxRuntime.jsx(
10688
+ ScrollAreaPrimitive__namespace.ScrollAreaScrollbar,
10689
+ {
10690
+ "data-slot": "scroll-bar",
10691
+ orientation,
10692
+ className: cn(
10693
+ "flex touch-none select-none transition-colors",
10694
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
10695
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
10696
+ className
10697
+ ),
10698
+ ...props,
10699
+ children: /* @__PURE__ */ jsxRuntime.jsx(
10700
+ ScrollAreaPrimitive__namespace.ScrollAreaThumb,
10701
+ {
10702
+ "data-slot": "scroll-thumb",
10703
+ className: "relative flex-1 rounded-full bg-border"
10704
+ }
10705
+ )
10706
+ }
10707
+ );
10708
+ }
10709
+ var sizeConfig = {
10710
+ sm: {
10711
+ button: "h-8 w-8",
10712
+ icon: "w-4 h-4",
10713
+ badge: "min-w-[16px] h-4 text-[10px] -top-1 -right-1",
10714
+ dot: "w-2.5 h-2.5 -top-0.5 -right-0.5"
10715
+ },
10716
+ md: {
10717
+ button: "h-9 w-9",
10718
+ icon: "w-5 h-5",
10719
+ badge: "min-w-[18px] h-[18px] text-[11px] -top-1 -right-1",
10720
+ dot: "w-3 h-3 -top-0.5 -right-0.5"
10721
+ },
10722
+ lg: {
10723
+ button: "h-10 w-10",
10724
+ icon: "w-6 h-6",
10725
+ badge: "min-w-[20px] h-5 text-xs -top-1.5 -right-1.5",
10726
+ dot: "w-3.5 h-3.5 -top-0.5 -right-0.5"
10727
+ }
10728
+ };
10729
+ var typeConfig = {
10730
+ info: {
10731
+ icon: lucideReact.Info,
10732
+ color: "text-blue-500",
10733
+ bg: "bg-blue-500/10"
10734
+ },
10735
+ success: {
10736
+ icon: lucideReact.CheckCircle,
10737
+ color: "text-green-500",
10738
+ bg: "bg-green-500/10"
10739
+ },
10740
+ warning: {
10741
+ icon: lucideReact.AlertTriangle,
10742
+ color: "text-amber-500",
10743
+ bg: "bg-amber-500/10"
10744
+ },
10745
+ error: {
10746
+ icon: lucideReact.XCircle,
10747
+ color: "text-red-500",
10748
+ bg: "bg-red-500/10"
10749
+ }
10750
+ };
10751
+ var dotColorConfig = {
10752
+ red: "bg-red-500",
10753
+ blue: "bg-blue-500",
10754
+ green: "bg-green-500",
10755
+ amber: "bg-amber-500",
10756
+ purple: "bg-purple-500",
10757
+ primary: "bg-primary"
10758
+ };
10759
+ var soundConfig = {
10760
+ chime: {
10761
+ frequencies: [880, 1100],
10762
+ durations: [0.1, 0.2],
10763
+ gain: 0.3
10764
+ },
10765
+ bell: {
10766
+ frequencies: [523, 659, 784],
10767
+ durations: [0.15, 0.15, 0.2],
10768
+ gain: 0.25
10769
+ },
10770
+ pop: {
10771
+ frequencies: [400, 600],
10772
+ durations: [0.05, 0.08],
10773
+ gain: 0.4
10774
+ },
10775
+ ding: {
10776
+ frequencies: [1200],
10777
+ durations: [0.15],
10778
+ gain: 0.2
10779
+ }
10780
+ };
10781
+ var pulseVariants = {
10782
+ ring: {
10783
+ animate: { scale: [1, 1.8], opacity: [0.6, 0] },
10784
+ transition: {
10785
+ duration: 1.2,
10786
+ repeat: Number.POSITIVE_INFINITY,
10787
+ ease: "easeOut"
10788
+ }
10789
+ },
10790
+ glow: {
10791
+ animate: { scale: [1, 1.3, 1], opacity: [0.8, 0.4, 0.8] },
10792
+ transition: {
10793
+ duration: 1.5,
10794
+ repeat: Number.POSITIVE_INFINITY,
10795
+ ease: "easeInOut"
10796
+ }
10797
+ },
10798
+ bounce: {
10799
+ animate: { scale: [1, 1.2, 1], y: [0, -2, 0] },
10800
+ transition: {
10801
+ duration: 0.6,
10802
+ repeat: Number.POSITIVE_INFINITY,
10803
+ ease: "easeInOut"
10804
+ }
10805
+ }
10806
+ };
10807
+ function formatTimeAgo(date) {
10808
+ const now = /* @__PURE__ */ new Date();
10809
+ const diffMs = now.getTime() - date.getTime();
10810
+ const diffSecs = Math.floor(diffMs / 1e3);
10811
+ const diffMins = Math.floor(diffSecs / 60);
10812
+ const diffHours = Math.floor(diffMins / 60);
10813
+ const diffDays = Math.floor(diffHours / 24);
10814
+ if (diffSecs < 60) return "just now";
10815
+ if (diffMins < 60) return `${diffMins}m ago`;
10816
+ if (diffHours < 24) return `${diffHours}h ago`;
10817
+ if (diffDays < 7) return `${diffDays}d ago`;
10818
+ return date.toLocaleDateString();
10819
+ }
10820
+ function playNotificationSound(soundType = "chime", soundUrl) {
10821
+ if (typeof window === "undefined" || soundType === "none") return;
10822
+ if (soundUrl) {
10823
+ const audio = new Audio(soundUrl);
10824
+ audio.volume = 0.5;
10825
+ audio.play().catch(() => {
10826
+ });
10827
+ return;
10828
+ }
10829
+ const config = soundConfig[soundType];
10830
+ if (!config) return;
10831
+ try {
10832
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
10833
+ let currentTime = audioContext.currentTime;
10834
+ config.frequencies.forEach((freq, i) => {
10835
+ const oscillator = audioContext.createOscillator();
10836
+ const gainNode = audioContext.createGain();
10837
+ oscillator.connect(gainNode);
10838
+ gainNode.connect(audioContext.destination);
10839
+ oscillator.frequency.setValueAtTime(freq, currentTime);
10840
+ gainNode.gain.setValueAtTime(config.gain, currentTime);
10841
+ gainNode.gain.exponentialRampToValueAtTime(
10842
+ 0.01,
10843
+ currentTime + config.durations[i]
10844
+ );
10845
+ oscillator.start(currentTime);
10846
+ oscillator.stop(currentTime + config.durations[i]);
10847
+ currentTime += config.durations[i] * 0.7;
10848
+ });
10849
+ } catch {
10850
+ }
10851
+ }
10852
+ function NotificationsWidget({
10853
+ notifications,
10854
+ onMarkAsRead,
10855
+ onMarkAllAsRead,
10856
+ onDismiss,
10857
+ onClearAll,
10858
+ onNotificationClick,
10859
+ size: size4 = "md",
10860
+ maxVisible = 5,
10861
+ playSound: _playSound = true,
10862
+ soundUrl,
10863
+ className,
10864
+ emptyMessage = "No notifications",
10865
+ title = "Notifications",
10866
+ dotColor = "red",
10867
+ showPulse = true,
10868
+ soundType = "chime",
10869
+ pulseStyle = "ring",
10870
+ soundCooldown = 2e3
10871
+ }) {
10872
+ const [isOpen, setIsOpen] = React32__namespace.useState(false);
10873
+ const [prevCount, setPrevCount] = React32__namespace.useState(0);
10874
+ const lastSoundPlayedRef = React32__namespace.useRef(0);
10875
+ const styles = sizeConfig[size4];
10876
+ const dotBgColor = dotColorConfig[dotColor];
10877
+ const unreadCount = notifications.filter((n) => !n.read).length;
10878
+ const visibleNotifications = notifications.slice(0, maxVisible);
10879
+ const hasMore = notifications.length > maxVisible;
10880
+ React32__namespace.useEffect(() => {
10881
+ if (soundType !== "none" && unreadCount > prevCount && prevCount > 0) {
10882
+ const now = Date.now();
10883
+ if (now - lastSoundPlayedRef.current >= soundCooldown) {
10884
+ playNotificationSound(soundType, soundUrl);
10885
+ lastSoundPlayedRef.current = now;
10886
+ }
10887
+ }
10888
+ setPrevCount(unreadCount);
10889
+ }, [unreadCount, prevCount, soundType, soundUrl, soundCooldown]);
10890
+ React32__namespace.useEffect(() => {
10891
+ if (isOpen && onMarkAsRead) {
10892
+ visibleNotifications.forEach((notification) => {
10893
+ if (!notification.read) {
10894
+ onMarkAsRead(notification.id);
10895
+ }
10896
+ });
10897
+ }
10898
+ }, [isOpen, onMarkAsRead, visibleNotifications]);
10899
+ const handleNotificationClick = (notification) => {
10900
+ if (notification.href) {
10901
+ onNotificationClick?.(notification);
10902
+ }
10903
+ };
10904
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
10905
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
10906
+ react.motion.button,
10907
+ {
10908
+ className: cn(
10909
+ "relative inline-flex items-center justify-center rounded-lg",
10910
+ "bg-muted/50 border border-border/50 hover:bg-muted/70 transition-colors",
10911
+ styles.button,
10912
+ className
10913
+ ),
10914
+ whileTap: { scale: 0.95 },
10915
+ "aria-label": `Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ""}`,
10916
+ children: [
10917
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bell, { className: cn(styles.icon, "text-muted-foreground") }),
10918
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
10919
+ react.motion.div,
10920
+ {
10921
+ initial: { scale: 0, opacity: 0 },
10922
+ animate: { scale: 1, opacity: 1 },
10923
+ exit: { scale: 0, opacity: 0 },
10924
+ className: cn(
10925
+ "absolute flex items-center justify-center rounded-full",
10926
+ dotBgColor,
10927
+ "text-white font-medium",
10928
+ unreadCount > 9 ? styles.badge : styles.dot,
10929
+ unreadCount > 9 && "px-1"
10930
+ ),
10931
+ children: unreadCount > 9 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: unreadCount > 99 ? "99+" : unreadCount }) : null
10932
+ }
10933
+ ) }),
10934
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: unreadCount > 0 && showPulse && pulseStyle !== "none" && /* @__PURE__ */ jsxRuntime.jsx(
10935
+ react.motion.div,
10936
+ {
10937
+ initial: { scale: 1, opacity: 0.5 },
10938
+ animate: pulseVariants[pulseStyle].animate,
10939
+ transition: pulseVariants[pulseStyle].transition,
10940
+ className: cn("absolute rounded-full", dotBgColor, styles.dot)
10941
+ }
10942
+ ) })
10943
+ ]
10944
+ }
10945
+ ) }),
10946
+ /* @__PURE__ */ jsxRuntime.jsxs(
10947
+ PopoverContent,
10948
+ {
10949
+ side: "bottom",
10950
+ align: "end",
10951
+ className: "w-80 p-0 overflow-hidden",
10952
+ children: [
10953
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border", children: [
10954
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
10955
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm", children: title }),
10956
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-2 py-0.5 text-xs font-medium rounded-full bg-primary/10 text-primary", children: [
10957
+ unreadCount,
10958
+ " new"
10959
+ ] })
10960
+ ] }),
10961
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: unreadCount > 0 && onMarkAllAsRead && /* @__PURE__ */ jsxRuntime.jsxs(
10962
+ Button,
10963
+ {
10964
+ variant: "ghost",
10965
+ size: "sm",
10966
+ className: "h-7 px-2 text-xs",
10967
+ onClick: () => onMarkAllAsRead(),
10968
+ children: [
10969
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCheck, { className: "w-3.5 h-3.5 mr-1" }),
10970
+ "Mark all read"
10971
+ ]
10972
+ }
10973
+ ) })
10974
+ ] }),
10975
+ notifications.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-8 px-4 text-center", children: [
10976
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-full bg-muted flex items-center justify-center mb-3", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bell, { className: "w-6 h-6 text-muted-foreground" }) }),
10977
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
10978
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10979
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "h-[320px]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border", children: visibleNotifications.map((notification) => {
10980
+ const config = typeConfig[notification.type || "info"];
10981
+ const Icon = config.icon;
10982
+ const hasLink = !!notification.href;
10983
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10984
+ react.motion.div,
10985
+ {
10986
+ initial: { opacity: 0, y: -10 },
10987
+ animate: { opacity: 1, y: 0 },
10988
+ className: cn(
10989
+ "relative flex gap-3 px-4 py-3 transition-colors group",
10990
+ hasLink && "cursor-pointer hover:bg-muted/50",
10991
+ !hasLink && "cursor-default",
10992
+ !notification.read && "bg-primary/5"
10993
+ ),
10994
+ onClick: () => handleNotificationClick(notification),
10995
+ children: [
10996
+ /* @__PURE__ */ jsxRuntime.jsx(
10997
+ "div",
10998
+ {
10999
+ className: cn(
11000
+ "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
11001
+ config.bg
11002
+ ),
11003
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: cn("w-4 h-4", config.color) })
11004
+ }
11005
+ ),
11006
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
11007
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
11008
+ "p",
11009
+ {
11010
+ className: cn(
11011
+ "text-sm line-clamp-1",
11012
+ !notification.read ? "font-semibold" : "font-medium",
11013
+ hasLink && "group-hover:underline"
11014
+ ),
11015
+ children: notification.title
11016
+ }
11017
+ ) }),
11018
+ notification.message && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground line-clamp-2 mt-0.5", children: notification.message }),
11019
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-muted-foreground/70 mt-1", children: formatTimeAgo(notification.timestamp) })
11020
+ ] }),
11021
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 flex items-start gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
11022
+ Button,
11023
+ {
11024
+ variant: "ghost",
11025
+ size: "icon",
11026
+ className: "h-6 w-6 text-muted-foreground hover:text-destructive",
11027
+ onClick: (e) => {
11028
+ e.stopPropagation();
11029
+ onDismiss(notification.id);
11030
+ },
11031
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3 h-3" })
11032
+ }
11033
+ ) })
11034
+ ]
11035
+ },
11036
+ notification.id
11037
+ );
11038
+ }) }) }),
11039
+ (hasMore || onClearAll) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2 border-t border-border bg-muted/30", children: [
11040
+ hasMore && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
11041
+ "+",
11042
+ notifications.length - maxVisible,
11043
+ " more"
11044
+ ] }),
11045
+ onClearAll && notifications.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
11046
+ Button,
11047
+ {
11048
+ variant: "ghost",
11049
+ size: "sm",
11050
+ className: "h-7 px-2 text-xs text-muted-foreground hover:text-destructive",
11051
+ onClick: () => onClearAll(),
11052
+ children: [
11053
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-3 h-3 mr-1" }),
11054
+ "Clear all"
11055
+ ]
11056
+ }
11057
+ )
11058
+ ] })
11059
+ ] })
11060
+ ]
11061
+ }
11062
+ )
11063
+ ] });
11064
+ }
10617
11065
 
10618
11066
  exports.AvatarEditor = AvatarEditor;
10619
11067
  exports.AvatarEditorDialog = AvatarEditorDialog;
@@ -10654,6 +11102,13 @@ exports.DropdownMenuSubContent = DropdownMenuSubContent2;
10654
11102
  exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger2;
10655
11103
  exports.DropdownMenuTrigger = DropdownMenuTrigger2;
10656
11104
  exports.LanguageSwitcher = LanguageSwitcher;
11105
+ exports.NotificationsWidget = NotificationsWidget;
11106
+ exports.Popover = Popover;
11107
+ exports.PopoverAnchor = PopoverAnchor;
11108
+ exports.PopoverContent = PopoverContent;
11109
+ exports.PopoverTrigger = PopoverTrigger;
11110
+ exports.ScrollArea = ScrollArea;
11111
+ exports.ScrollBar = ScrollBar;
10657
11112
  exports.Slider = Slider;
10658
11113
  exports.ThemeSwitcher = ThemeSwitcher;
10659
11114
  exports.Toggle = Toggle;