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

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) {
@@ -9652,9 +9656,11 @@ function AvatarEditor({
9652
9656
  const [dragStart, setDragStart] = React32.useState({ x: 0, y: 0 });
9653
9657
  const [showGrid, setShowGrid] = React32.useState(initialShowGrid);
9654
9658
  React32.useEffect(() => {
9655
- if (imageSource && !imageSource.startsWith("data:")) {
9659
+ if (imageSource) {
9656
9660
  const img = new Image();
9657
- img.crossOrigin = "anonymous";
9661
+ if (!imageSource.startsWith("data:")) {
9662
+ img.crossOrigin = "anonymous";
9663
+ }
9658
9664
  img.onload = () => {
9659
9665
  setImage(img);
9660
9666
  setImageLoaded(true);
@@ -9842,6 +9848,7 @@ function AvatarEditor({
9842
9848
  const handleTouchMove = React32.useCallback(
9843
9849
  (e) => {
9844
9850
  if (!isDragging) return;
9851
+ e.preventDefault();
9845
9852
  const touch = e.touches[0];
9846
9853
  setPosition({
9847
9854
  x: touch.clientX - dragStart.x,
@@ -10424,14 +10431,13 @@ function DrawerDescription({
10424
10431
  function useMediaQuery(query) {
10425
10432
  const [matches, setMatches] = React32.useState(false);
10426
10433
  React32.useEffect(() => {
10434
+ if (typeof window === "undefined") return;
10427
10435
  const media = window.matchMedia(query);
10428
- if (media.matches !== matches) {
10429
- setMatches(media.matches);
10430
- }
10436
+ setMatches(media.matches);
10431
10437
  const listener = () => setMatches(media.matches);
10432
10438
  media.addEventListener("change", listener);
10433
10439
  return () => media.removeEventListener("change", listener);
10434
- }, [matches, query]);
10440
+ }, [query]);
10435
10441
  return matches;
10436
10442
  }
10437
10443
  function AvatarEditorDialog({
@@ -10454,7 +10460,15 @@ function AvatarEditorDialog({
10454
10460
  const [editedValue, setEditedValue] = React32.useState(value ?? null);
10455
10461
  const [isSaving, setIsSaving] = React32.useState(false);
10456
10462
  const [feedback, setFeedback] = React32.useState(null);
10463
+ const [mobileEditorSize, setMobileEditorSize] = React32.useState(editorSize);
10457
10464
  const isMobile = useMediaQuery("(max-width: 640px)");
10465
+ React32.useEffect(() => {
10466
+ if (typeof window !== "undefined" && isMobile) {
10467
+ setMobileEditorSize(Math.min(editorSize + 40, window.innerWidth - 48));
10468
+ } else {
10469
+ setMobileEditorSize(editorSize);
10470
+ }
10471
+ }, [isMobile, editorSize]);
10458
10472
  const handleOpenChange = React32.useCallback(
10459
10473
  (open) => {
10460
10474
  if (open) {
@@ -10529,7 +10543,6 @@ function AvatarEditorDialog({
10529
10543
  ]
10530
10544
  }
10531
10545
  );
10532
- const mobileEditorSize = isMobile ? Math.min(editorSize + 40, window.innerWidth - 48) : editorSize;
10533
10546
  const EditorContent = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
10534
10547
  /* @__PURE__ */ jsxRuntime.jsx(
10535
10548
  AvatarEditor,
@@ -10614,6 +10627,453 @@ function AvatarEditorDialog({
10614
10627
  ] }) })
10615
10628
  ] });
10616
10629
  }
10630
+ function Popover({
10631
+ ...props
10632
+ }) {
10633
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Root, { "data-slot": "popover", ...props });
10634
+ }
10635
+ function PopoverTrigger({
10636
+ ...props
10637
+ }) {
10638
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Trigger, { "data-slot": "popover-trigger", ...props });
10639
+ }
10640
+ function PopoverContent({
10641
+ className,
10642
+ align = "center",
10643
+ sideOffset = 4,
10644
+ ...props
10645
+ }) {
10646
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
10647
+ PopoverPrimitive__namespace.Content,
10648
+ {
10649
+ "data-slot": "popover-content",
10650
+ align,
10651
+ sideOffset,
10652
+ className: cn(
10653
+ "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",
10654
+ className
10655
+ ),
10656
+ ...props
10657
+ }
10658
+ ) });
10659
+ }
10660
+ function PopoverAnchor({
10661
+ ...props
10662
+ }) {
10663
+ return /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Anchor, { "data-slot": "popover-anchor", ...props });
10664
+ }
10665
+ function ScrollArea({
10666
+ className,
10667
+ children,
10668
+ ...props
10669
+ }) {
10670
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10671
+ ScrollAreaPrimitive__namespace.Root,
10672
+ {
10673
+ "data-slot": "scroll-area",
10674
+ className: cn("relative overflow-hidden", className),
10675
+ ...props,
10676
+ children: [
10677
+ /* @__PURE__ */ jsxRuntime.jsx(
10678
+ ScrollAreaPrimitive__namespace.Viewport,
10679
+ {
10680
+ "data-slot": "scroll-area-viewport",
10681
+ className: "h-full w-full rounded-[inherit]",
10682
+ children
10683
+ }
10684
+ ),
10685
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollBar, {}),
10686
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollAreaPrimitive__namespace.Corner, {})
10687
+ ]
10688
+ }
10689
+ );
10690
+ }
10691
+ function ScrollBar({
10692
+ className,
10693
+ orientation = "vertical",
10694
+ ...props
10695
+ }) {
10696
+ return /* @__PURE__ */ jsxRuntime.jsx(
10697
+ ScrollAreaPrimitive__namespace.ScrollAreaScrollbar,
10698
+ {
10699
+ "data-slot": "scroll-bar",
10700
+ orientation,
10701
+ className: cn(
10702
+ "flex touch-none select-none transition-colors",
10703
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
10704
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
10705
+ className
10706
+ ),
10707
+ ...props,
10708
+ children: /* @__PURE__ */ jsxRuntime.jsx(
10709
+ ScrollAreaPrimitive__namespace.ScrollAreaThumb,
10710
+ {
10711
+ "data-slot": "scroll-thumb",
10712
+ className: "relative flex-1 rounded-full bg-border"
10713
+ }
10714
+ )
10715
+ }
10716
+ );
10717
+ }
10718
+ var sizeConfig = {
10719
+ sm: {
10720
+ button: "h-8 w-8",
10721
+ icon: "w-4 h-4",
10722
+ badge: "min-w-[16px] h-4 text-[10px] -top-1 -right-1",
10723
+ dot: "w-2.5 h-2.5 -top-0.5 -right-0.5"
10724
+ },
10725
+ md: {
10726
+ button: "h-9 w-9",
10727
+ icon: "w-5 h-5",
10728
+ badge: "min-w-[18px] h-[18px] text-[11px] -top-1 -right-1",
10729
+ dot: "w-3 h-3 -top-0.5 -right-0.5"
10730
+ },
10731
+ lg: {
10732
+ button: "h-10 w-10",
10733
+ icon: "w-6 h-6",
10734
+ badge: "min-w-[20px] h-5 text-xs -top-1.5 -right-1.5",
10735
+ dot: "w-3.5 h-3.5 -top-0.5 -right-0.5"
10736
+ }
10737
+ };
10738
+ var typeConfig = {
10739
+ info: {
10740
+ icon: lucideReact.Info,
10741
+ color: "text-blue-500",
10742
+ bg: "bg-blue-500/10"
10743
+ },
10744
+ success: {
10745
+ icon: lucideReact.CheckCircle,
10746
+ color: "text-green-500",
10747
+ bg: "bg-green-500/10"
10748
+ },
10749
+ warning: {
10750
+ icon: lucideReact.AlertTriangle,
10751
+ color: "text-amber-500",
10752
+ bg: "bg-amber-500/10"
10753
+ },
10754
+ error: {
10755
+ icon: lucideReact.XCircle,
10756
+ color: "text-red-500",
10757
+ bg: "bg-red-500/10"
10758
+ }
10759
+ };
10760
+ var dotColorConfig = {
10761
+ red: "bg-red-500",
10762
+ blue: "bg-blue-500",
10763
+ green: "bg-green-500",
10764
+ amber: "bg-amber-500",
10765
+ purple: "bg-purple-500",
10766
+ primary: "bg-primary"
10767
+ };
10768
+ var soundConfig = {
10769
+ chime: {
10770
+ frequencies: [880, 1100],
10771
+ durations: [0.1, 0.2],
10772
+ gain: 0.3
10773
+ },
10774
+ bell: {
10775
+ frequencies: [523, 659, 784],
10776
+ durations: [0.15, 0.15, 0.2],
10777
+ gain: 0.25
10778
+ },
10779
+ pop: {
10780
+ frequencies: [400, 600],
10781
+ durations: [0.05, 0.08],
10782
+ gain: 0.4
10783
+ },
10784
+ ding: {
10785
+ frequencies: [1200],
10786
+ durations: [0.15],
10787
+ gain: 0.2
10788
+ }
10789
+ };
10790
+ var pulseVariants = {
10791
+ ring: {
10792
+ animate: { scale: [1, 1.8], opacity: [0.6, 0] },
10793
+ transition: {
10794
+ duration: 1.2,
10795
+ repeat: Number.POSITIVE_INFINITY,
10796
+ ease: "easeOut"
10797
+ }
10798
+ },
10799
+ glow: {
10800
+ animate: { scale: [1, 1.3, 1], opacity: [0.8, 0.4, 0.8] },
10801
+ transition: {
10802
+ duration: 1.5,
10803
+ repeat: Number.POSITIVE_INFINITY,
10804
+ ease: "easeInOut"
10805
+ }
10806
+ },
10807
+ bounce: {
10808
+ animate: { scale: [1, 1.2, 1], y: [0, -2, 0] },
10809
+ transition: {
10810
+ duration: 0.6,
10811
+ repeat: Number.POSITIVE_INFINITY,
10812
+ ease: "easeInOut"
10813
+ }
10814
+ }
10815
+ };
10816
+ function formatTimeAgo(date) {
10817
+ const now = /* @__PURE__ */ new Date();
10818
+ const diffMs = now.getTime() - date.getTime();
10819
+ const diffSecs = Math.floor(diffMs / 1e3);
10820
+ const diffMins = Math.floor(diffSecs / 60);
10821
+ const diffHours = Math.floor(diffMins / 60);
10822
+ const diffDays = Math.floor(diffHours / 24);
10823
+ if (diffSecs < 60) return "just now";
10824
+ if (diffMins < 60) return `${diffMins}m ago`;
10825
+ if (diffHours < 24) return `${diffHours}h ago`;
10826
+ if (diffDays < 7) return `${diffDays}d ago`;
10827
+ return date.toLocaleDateString();
10828
+ }
10829
+ function playNotificationSound(soundType = "chime", soundUrl) {
10830
+ if (typeof window === "undefined" || soundType === "none") return;
10831
+ if (soundUrl) {
10832
+ const audio = new Audio(soundUrl);
10833
+ audio.volume = 0.5;
10834
+ audio.play().catch(() => {
10835
+ });
10836
+ return;
10837
+ }
10838
+ const config = soundConfig[soundType];
10839
+ if (!config) return;
10840
+ try {
10841
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
10842
+ let currentTime = audioContext.currentTime;
10843
+ config.frequencies.forEach((freq, i) => {
10844
+ const oscillator = audioContext.createOscillator();
10845
+ const gainNode = audioContext.createGain();
10846
+ oscillator.connect(gainNode);
10847
+ gainNode.connect(audioContext.destination);
10848
+ oscillator.frequency.setValueAtTime(freq, currentTime);
10849
+ gainNode.gain.setValueAtTime(config.gain, currentTime);
10850
+ gainNode.gain.exponentialRampToValueAtTime(
10851
+ 0.01,
10852
+ currentTime + config.durations[i]
10853
+ );
10854
+ oscillator.start(currentTime);
10855
+ oscillator.stop(currentTime + config.durations[i]);
10856
+ currentTime += config.durations[i] * 0.7;
10857
+ });
10858
+ } catch {
10859
+ }
10860
+ }
10861
+ function NotificationsWidget({
10862
+ notifications,
10863
+ onMarkAsRead,
10864
+ onMarkAllAsRead,
10865
+ onDismiss,
10866
+ onClearAll,
10867
+ onNotificationClick,
10868
+ size: size4 = "md",
10869
+ maxVisible = 5,
10870
+ playSound = true,
10871
+ soundUrl,
10872
+ className,
10873
+ emptyMessage = "No notifications",
10874
+ title = "Notifications",
10875
+ dotColor = "red",
10876
+ showPulse = true,
10877
+ soundType = "chime",
10878
+ pulseStyle = "ring",
10879
+ soundCooldown = 2e3
10880
+ }) {
10881
+ const [isOpen, setIsOpen] = React32__namespace.useState(false);
10882
+ const [prevCount, setPrevCount] = React32__namespace.useState(0);
10883
+ const lastSoundPlayedRef = React32__namespace.useRef(0);
10884
+ const styles = sizeConfig[size4];
10885
+ const dotBgColor = dotColorConfig[dotColor];
10886
+ const unreadCount = notifications.filter((n) => !n.read).length;
10887
+ const visibleNotifications = React32__namespace.useMemo(
10888
+ () => notifications.slice(0, maxVisible),
10889
+ [notifications, maxVisible]
10890
+ );
10891
+ const hasMore = notifications.length > maxVisible;
10892
+ React32__namespace.useEffect(() => {
10893
+ if (playSound && soundType !== "none" && unreadCount > prevCount && prevCount > 0) {
10894
+ const now = Date.now();
10895
+ if (now - lastSoundPlayedRef.current >= soundCooldown) {
10896
+ playNotificationSound(soundType, soundUrl);
10897
+ lastSoundPlayedRef.current = now;
10898
+ }
10899
+ }
10900
+ setPrevCount(unreadCount);
10901
+ }, [unreadCount, prevCount, soundType, soundUrl, soundCooldown, playSound]);
10902
+ React32__namespace.useEffect(() => {
10903
+ if (isOpen && onMarkAsRead) {
10904
+ visibleNotifications.forEach((notification) => {
10905
+ if (!notification.read) {
10906
+ onMarkAsRead(notification.id);
10907
+ }
10908
+ });
10909
+ }
10910
+ }, [isOpen, onMarkAsRead, visibleNotifications]);
10911
+ const handleNotificationClick = (notification) => {
10912
+ if (notification.href) {
10913
+ onNotificationClick?.(notification);
10914
+ }
10915
+ };
10916
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
10917
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
10918
+ react.motion.button,
10919
+ {
10920
+ className: cn(
10921
+ "relative inline-flex items-center justify-center rounded-lg",
10922
+ "bg-muted/50 border border-border/50 hover:bg-muted/70 transition-colors",
10923
+ styles.button,
10924
+ className
10925
+ ),
10926
+ whileTap: { scale: 0.95 },
10927
+ "aria-label": `Notifications${unreadCount > 0 ? ` (${unreadCount} unread)` : ""}`,
10928
+ children: [
10929
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bell, { className: cn(styles.icon, "text-muted-foreground") }),
10930
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
10931
+ react.motion.div,
10932
+ {
10933
+ initial: { scale: 0, opacity: 0 },
10934
+ animate: { scale: 1, opacity: 1 },
10935
+ exit: { scale: 0, opacity: 0 },
10936
+ className: cn(
10937
+ "absolute flex items-center justify-center rounded-full",
10938
+ dotBgColor,
10939
+ "text-white font-medium",
10940
+ unreadCount > 9 ? styles.badge : styles.dot,
10941
+ unreadCount > 9 && "px-1"
10942
+ ),
10943
+ children: unreadCount > 9 ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: unreadCount > 99 ? "99+" : unreadCount }) : null
10944
+ }
10945
+ ) }),
10946
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: unreadCount > 0 && showPulse && pulseStyle !== "none" && /* @__PURE__ */ jsxRuntime.jsx(
10947
+ react.motion.div,
10948
+ {
10949
+ initial: { scale: 1, opacity: 0.5 },
10950
+ animate: pulseVariants[pulseStyle].animate,
10951
+ transition: pulseVariants[pulseStyle].transition,
10952
+ className: cn("absolute rounded-full", dotBgColor, styles.dot)
10953
+ }
10954
+ ) })
10955
+ ]
10956
+ }
10957
+ ) }),
10958
+ /* @__PURE__ */ jsxRuntime.jsxs(
10959
+ PopoverContent,
10960
+ {
10961
+ side: "bottom",
10962
+ align: "end",
10963
+ className: "w-80 p-0 overflow-hidden",
10964
+ children: [
10965
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border", children: [
10966
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
10967
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm", children: title }),
10968
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-2 py-0.5 text-xs font-medium rounded-full bg-primary/10 text-primary", children: [
10969
+ unreadCount,
10970
+ " new"
10971
+ ] })
10972
+ ] }),
10973
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: unreadCount > 0 && onMarkAllAsRead && /* @__PURE__ */ jsxRuntime.jsxs(
10974
+ Button,
10975
+ {
10976
+ variant: "ghost",
10977
+ size: "sm",
10978
+ className: "h-7 px-2 text-xs",
10979
+ onClick: () => onMarkAllAsRead(),
10980
+ children: [
10981
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCheck, { className: "w-3.5 h-3.5 mr-1" }),
10982
+ "Mark all read"
10983
+ ]
10984
+ }
10985
+ ) })
10986
+ ] }),
10987
+ notifications.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-8 px-4 text-center", children: [
10988
+ /* @__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" }) }),
10989
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: emptyMessage })
10990
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10991
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "h-[320px]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border", children: visibleNotifications.map((notification) => {
10992
+ const config = typeConfig[notification.type || "info"];
10993
+ const Icon = config.icon;
10994
+ const hasLink = !!notification.href;
10995
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10996
+ react.motion.div,
10997
+ {
10998
+ initial: { opacity: 0, y: -10 },
10999
+ animate: { opacity: 1, y: 0 },
11000
+ className: cn(
11001
+ "relative flex gap-3 px-4 py-3 transition-colors group",
11002
+ hasLink && "cursor-pointer hover:bg-muted/50",
11003
+ !hasLink && "cursor-default",
11004
+ !notification.read && "bg-primary/5"
11005
+ ),
11006
+ onClick: () => handleNotificationClick(notification),
11007
+ children: [
11008
+ /* @__PURE__ */ jsxRuntime.jsx(
11009
+ "div",
11010
+ {
11011
+ className: cn(
11012
+ "flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center",
11013
+ config.bg
11014
+ ),
11015
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: cn("w-4 h-4", config.color) })
11016
+ }
11017
+ ),
11018
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
11019
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
11020
+ "p",
11021
+ {
11022
+ className: cn(
11023
+ "text-sm line-clamp-1",
11024
+ !notification.read ? "font-semibold" : "font-medium",
11025
+ hasLink && "group-hover:underline"
11026
+ ),
11027
+ children: notification.title
11028
+ }
11029
+ ) }),
11030
+ notification.message && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground line-clamp-2 mt-0.5", children: notification.message }),
11031
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-muted-foreground/70 mt-1", children: formatTimeAgo(notification.timestamp) })
11032
+ ] }),
11033
+ /* @__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(
11034
+ Button,
11035
+ {
11036
+ variant: "ghost",
11037
+ size: "icon",
11038
+ className: "h-6 w-6 text-muted-foreground hover:text-destructive",
11039
+ onClick: (e) => {
11040
+ e.stopPropagation();
11041
+ onDismiss(notification.id);
11042
+ },
11043
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3 h-3" })
11044
+ }
11045
+ ) })
11046
+ ]
11047
+ },
11048
+ notification.id
11049
+ );
11050
+ }) }) }),
11051
+ (hasMore || onClearAll) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2 border-t border-border bg-muted/30", children: [
11052
+ hasMore && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
11053
+ "+",
11054
+ notifications.length - maxVisible,
11055
+ " more"
11056
+ ] }),
11057
+ onClearAll && notifications.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
11058
+ Button,
11059
+ {
11060
+ variant: "ghost",
11061
+ size: "sm",
11062
+ className: "h-7 px-2 text-xs text-muted-foreground hover:text-destructive",
11063
+ onClick: () => onClearAll(),
11064
+ children: [
11065
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "w-3 h-3 mr-1" }),
11066
+ "Clear all"
11067
+ ]
11068
+ }
11069
+ )
11070
+ ] })
11071
+ ] })
11072
+ ]
11073
+ }
11074
+ )
11075
+ ] });
11076
+ }
10617
11077
 
10618
11078
  exports.AvatarEditor = AvatarEditor;
10619
11079
  exports.AvatarEditorDialog = AvatarEditorDialog;
@@ -10654,6 +11114,13 @@ exports.DropdownMenuSubContent = DropdownMenuSubContent2;
10654
11114
  exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger2;
10655
11115
  exports.DropdownMenuTrigger = DropdownMenuTrigger2;
10656
11116
  exports.LanguageSwitcher = LanguageSwitcher;
11117
+ exports.NotificationsWidget = NotificationsWidget;
11118
+ exports.Popover = Popover;
11119
+ exports.PopoverAnchor = PopoverAnchor;
11120
+ exports.PopoverContent = PopoverContent;
11121
+ exports.PopoverTrigger = PopoverTrigger;
11122
+ exports.ScrollArea = ScrollArea;
11123
+ exports.ScrollBar = ScrollBar;
10657
11124
  exports.Slider = Slider;
10658
11125
  exports.ThemeSwitcher = ThemeSwitcher;
10659
11126
  exports.Toggle = Toggle;