elseware-ui 2.31.1 → 2.31.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.js CHANGED
@@ -3132,7 +3132,7 @@ var ShapeSwitch = () => {
3132
3132
  {
3133
3133
  value: currentShape,
3134
3134
  onChange: handleChange,
3135
- className: "\r\n px-3 py-2 rounded-md border border-gray-300\r\n bg-white dark:bg-gray-800\r\n text-sm shadow-sm\r\n focus:outline-none\r\n ",
3135
+ className: "\n px-3 py-2 rounded-md border border-gray-300\n bg-white dark:bg-gray-800\n text-sm shadow-sm\n focus:outline-none\n ",
3136
3136
  children: Object.keys(Shape).map((shape) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: shape, children: shape }, shape))
3137
3137
  }
3138
3138
  ) });
@@ -16126,7 +16126,7 @@ function MarkdownTOC({ title = "Table of Contents" }) {
16126
16126
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
16127
16127
  "h2",
16128
16128
  {
16129
- className: "\r\n text-sm\r\n font-semibold\r\n uppercase\r\n tracking-wide\r\n text-gray-900\r\n dark:text-gray-100\r\n ",
16129
+ className: "\n text-sm\n font-semibold\n uppercase\n tracking-wide\n text-gray-900\n dark:text-gray-100\n ",
16130
16130
  children: title
16131
16131
  }
16132
16132
  ) }),
@@ -17455,7 +17455,7 @@ var GlowWrapper = ({
17455
17455
  /* @__PURE__ */ jsxRuntime.jsx(
17456
17456
  "span",
17457
17457
  {
17458
- className: "\r\n pointer-events-none absolute inset-0 opacity-0 \r\n group-hover:opacity-100 transition-opacity duration-300\r\n ",
17458
+ className: "\n pointer-events-none absolute inset-0 opacity-0 \n group-hover:opacity-100 transition-opacity duration-300\n ",
17459
17459
  style: { background: "var(--glow-bg)" }
17460
17460
  }
17461
17461
  ),
@@ -17487,49 +17487,60 @@ function ShowMore({ text, limit }) {
17487
17487
  }
17488
17488
  var ShowMore_default = ShowMore;
17489
17489
  function CommentComposer({
17490
+ avatar,
17490
17491
  placeholder = "Add a comment...",
17491
17492
  submitLabel = "Comment",
17493
+ disabled,
17494
+ loading,
17492
17495
  onSubmit
17493
17496
  }) {
17494
17497
  const [value, setValue] = React4.useState("");
17495
17498
  const [focused, setFocused] = React4.useState(false);
17496
17499
  async function handleSubmit() {
17497
- if (!value.trim()) return;
17498
- await onSubmit?.(value);
17500
+ const content = value.trim();
17501
+ if (!content) {
17502
+ return;
17503
+ }
17504
+ await onSubmit?.(content);
17505
+ setValue("");
17506
+ setFocused(false);
17507
+ }
17508
+ function handleCancel() {
17499
17509
  setValue("");
17500
17510
  setFocused(false);
17501
17511
  }
17502
17512
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
17503
- /* @__PURE__ */ jsxRuntime.jsx(Avatar, { size: "md" }),
17513
+ /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: avatar, size: "md" }),
17504
17514
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-3", children: [
17505
17515
  /* @__PURE__ */ jsxRuntime.jsx(
17506
17516
  "textarea",
17507
17517
  {
17508
17518
  value,
17519
+ disabled: disabled || loading,
17509
17520
  placeholder,
17510
17521
  onFocus: () => setFocused(true),
17511
17522
  onChange: (e) => setValue(e.target.value),
17512
- className: "w-full min-h-[48px] bg-transparent border-b border-gray-300 dark:border-neutral-700 outline-none resize-none pb-2 text-sm"
17523
+ className: "\n w-full\n min-h-[56px]\n resize-none\n bg-transparent\n border-b\n border-gray-300\n dark:border-neutral-700\n outline-none\n pb-2\n text-sm\n "
17513
17524
  }
17514
17525
  ),
17515
17526
  (focused || value.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
17516
17527
  /* @__PURE__ */ jsxRuntime.jsx(
17517
17528
  Button_default,
17518
17529
  {
17530
+ text: "Cancel",
17519
17531
  variant: "secondary",
17520
17532
  appearance: "ghost",
17521
- text: "Cancel",
17522
- onClick: () => {
17523
- setValue("");
17524
- setFocused(false);
17525
- }
17533
+ disabled: loading,
17534
+ onClick: handleCancel
17526
17535
  }
17527
17536
  ),
17528
17537
  /* @__PURE__ */ jsxRuntime.jsx(
17529
17538
  Button_default,
17530
17539
  {
17531
- variant: "primary",
17532
17540
  text: submitLabel,
17541
+ variant: "primary",
17542
+ loading,
17543
+ disabled: !value.trim(),
17533
17544
  onClick: handleSubmit
17534
17545
  }
17535
17546
  )
@@ -17538,6 +17549,78 @@ function CommentComposer({
17538
17549
  ] });
17539
17550
  }
17540
17551
  var CommentComposer_default = CommentComposer;
17552
+ function CommentActions({
17553
+ likes = 0,
17554
+ dislikes = 0,
17555
+ liked = false,
17556
+ disliked = false,
17557
+ canReply = true,
17558
+ onLike,
17559
+ onDislike,
17560
+ onReply
17561
+ }) {
17562
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17563
+ "div",
17564
+ {
17565
+ className: "\n flex\n items-center\n gap-5\n text-sm\n text-gray-500\n dark:text-gray-400\n ",
17566
+ children: [
17567
+ /* @__PURE__ */ jsxRuntime.jsxs(
17568
+ "button",
17569
+ {
17570
+ onClick: onLike,
17571
+ className: `
17572
+ inline-flex
17573
+ items-center
17574
+ gap-1
17575
+ transition-colors
17576
+ ${liked ? "text-blue-500" : "hover:text-gray-900 dark:hover:text-gray-100"}
17577
+ `,
17578
+ children: [
17579
+ liked ? /* @__PURE__ */ jsxRuntime.jsx(bi.BiSolidLike, { size: 18 }) : /* @__PURE__ */ jsxRuntime.jsx(bi.BiLike, { size: 18 }),
17580
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: likes })
17581
+ ]
17582
+ }
17583
+ ),
17584
+ /* @__PURE__ */ jsxRuntime.jsxs(
17585
+ "button",
17586
+ {
17587
+ onClick: onDislike,
17588
+ className: `
17589
+ inline-flex
17590
+ items-center
17591
+ gap-1
17592
+ transition-colors
17593
+ ${disliked ? "text-red-500" : "hover:text-gray-900 dark:hover:text-gray-100"}
17594
+ `,
17595
+ children: [
17596
+ disliked ? /* @__PURE__ */ jsxRuntime.jsx(bi.BiSolidDislike, { size: 18 }) : /* @__PURE__ */ jsxRuntime.jsx(bi.BiDislike, { size: 18 }),
17597
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: dislikes })
17598
+ ]
17599
+ }
17600
+ ),
17601
+ canReply && /* @__PURE__ */ jsxRuntime.jsx(
17602
+ "button",
17603
+ {
17604
+ onClick: onReply,
17605
+ className: "\n font-medium\n transition-colors\n hover:text-gray-900\n dark:hover:text-gray-100\n ",
17606
+ children: "Reply"
17607
+ }
17608
+ )
17609
+ ]
17610
+ }
17611
+ );
17612
+ }
17613
+ var CommentActions_default = CommentActions;
17614
+ function CommentContent({ content }) {
17615
+ return /* @__PURE__ */ jsxRuntime.jsx(
17616
+ "div",
17617
+ {
17618
+ className: "\n text-sm\n leading-relaxed\n whitespace-pre-wrap\n break-words\n text-gray-700\n dark:text-gray-300\n ",
17619
+ children: content
17620
+ }
17621
+ );
17622
+ }
17623
+ var CommentContent_default = CommentContent;
17541
17624
 
17542
17625
  // src/compositions/comment-thread/utils/formatCommentDate.ts
17543
17626
  function formatCommentDate(date) {
@@ -17545,11 +17628,26 @@ function formatCommentDate(date) {
17545
17628
  const target = new Date(date);
17546
17629
  const seconds = Math.floor((now.getTime() - target.getTime()) / 1e3);
17547
17630
  const intervals = [
17548
- { label: "year", seconds: 31536e3 },
17549
- { label: "month", seconds: 2592e3 },
17550
- { label: "day", seconds: 86400 },
17551
- { label: "hour", seconds: 3600 },
17552
- { label: "minute", seconds: 60 }
17631
+ {
17632
+ label: "year",
17633
+ seconds: 31536e3
17634
+ },
17635
+ {
17636
+ label: "month",
17637
+ seconds: 2592e3
17638
+ },
17639
+ {
17640
+ label: "day",
17641
+ seconds: 86400
17642
+ },
17643
+ {
17644
+ label: "hour",
17645
+ seconds: 3600
17646
+ },
17647
+ {
17648
+ label: "minute",
17649
+ seconds: 60
17650
+ }
17553
17651
  ];
17554
17652
  for (const interval of intervals) {
17555
17653
  const count = Math.floor(seconds / interval.seconds);
@@ -17562,67 +17660,27 @@ function formatCommentDate(date) {
17562
17660
  function CommentHeader({
17563
17661
  avatar,
17564
17662
  username,
17663
+ firstName,
17664
+ lastName,
17565
17665
  createdAt,
17566
17666
  edited
17567
17667
  }) {
17668
+ const displayName = firstName || lastName ? `${firstName ?? ""} ${lastName ?? ""}`.trim() : username;
17568
17669
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
17569
17670
  /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: avatar, size: "sm" }),
17570
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm flex-wrap", children: [
17571
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900 dark:text-gray-100", children: [
17671
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap min-w-0", children: [
17672
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-sm text-gray-900 dark:text-gray-100", children: displayName }),
17673
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
17572
17674
  "@",
17573
17675
  username
17574
17676
  ] }),
17575
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500 dark:text-gray-400", children: formatCommentDate(createdAt) }),
17576
- edited && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-400", children: "edited" })
17677
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "\xB7" }),
17678
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: formatCommentDate(createdAt) }),
17679
+ edited && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: "(edited)" })
17577
17680
  ] })
17578
17681
  ] });
17579
17682
  }
17580
17683
  var CommentHeader_default = CommentHeader;
17581
- function CommentContent({ content }) {
17582
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm leading-relaxed whitespace-pre-wrap text-gray-700 dark:text-gray-300", children: content });
17583
- }
17584
- var CommentContent_default = CommentContent;
17585
- function CommentActions({
17586
- likes = 0,
17587
- dislikes = 0,
17588
- onLike,
17589
- onDislike,
17590
- onReply
17591
- }) {
17592
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-5 text-sm text-gray-500 dark:text-gray-400", children: [
17593
- /* @__PURE__ */ jsxRuntime.jsxs(
17594
- "button",
17595
- {
17596
- onClick: onLike,
17597
- className: "inline-flex items-center gap-1 hover:text-gray-900 dark:hover:text-gray-100 transition-colors",
17598
- children: [
17599
- /* @__PURE__ */ jsxRuntime.jsx(bi.BiLike, { size: 18 }),
17600
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: likes })
17601
- ]
17602
- }
17603
- ),
17604
- /* @__PURE__ */ jsxRuntime.jsxs(
17605
- "button",
17606
- {
17607
- onClick: onDislike,
17608
- className: "inline-flex items-center gap-1 hover:text-gray-900 dark:hover:text-gray-100 transition-colors",
17609
- children: [
17610
- /* @__PURE__ */ jsxRuntime.jsx(bi.BiDislike, { size: 18 }),
17611
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: dislikes })
17612
- ]
17613
- }
17614
- ),
17615
- /* @__PURE__ */ jsxRuntime.jsx(
17616
- "button",
17617
- {
17618
- onClick: onReply,
17619
- className: "font-medium hover:text-gray-900 dark:hover:text-gray-100 transition-colors",
17620
- children: "Reply"
17621
- }
17622
- )
17623
- ] });
17624
- }
17625
- var CommentActions_default = CommentActions;
17626
17684
  function CommentMenu({
17627
17685
  canEdit,
17628
17686
  canDelete,
@@ -17642,28 +17700,34 @@ function CommentMenu({
17642
17700
  document.addEventListener("mousedown", handleOutsideClick);
17643
17701
  return () => document.removeEventListener("mousedown", handleOutsideClick);
17644
17702
  }, []);
17703
+ const hasActions = canEdit || canDelete || canReport;
17704
+ if (!hasActions) {
17705
+ return null;
17706
+ }
17645
17707
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: "relative", children: [
17646
17708
  /* @__PURE__ */ jsxRuntime.jsx(
17647
17709
  "button",
17648
17710
  {
17711
+ type: "button",
17649
17712
  onClick: () => setOpen((prev) => !prev),
17650
- className: "text-gray-500 hover:text-gray-900 dark:hover:text-gray-100 transition-colors",
17713
+ className: "\n text-gray-500\n hover:text-gray-900\n dark:hover:text-gray-100\n transition-colors\n ",
17651
17714
  children: /* @__PURE__ */ jsxRuntime.jsx(bi.BiDotsVerticalRounded, { size: 18 })
17652
17715
  }
17653
17716
  ),
17654
17717
  open && /* @__PURE__ */ jsxRuntime.jsxs(
17655
17718
  "div",
17656
17719
  {
17657
- className: "\r\n absolute\r\n right-0\r\n top-full\r\n mt-2\r\n min-w-[160px]\r\n overflow-hidden\r\n rounded-xl\r\n border\r\n border-gray-200\r\n dark:border-neutral-800\r\n bg-white\r\n dark:bg-neutral-900\r\n shadow-xl\r\n z-50\r\n ",
17720
+ className: "\n absolute\n right-0\n top-full\n mt-2\n min-w-[180px]\n overflow-hidden\n rounded-xl\n border\n border-gray-200\n dark:border-neutral-800\n bg-white\n dark:bg-neutral-900\n shadow-xl\n z-50\n ",
17658
17721
  children: [
17659
17722
  canEdit && /* @__PURE__ */ jsxRuntime.jsxs(
17660
17723
  "button",
17661
17724
  {
17725
+ type: "button",
17662
17726
  onClick: () => {
17663
17727
  setOpen(false);
17664
17728
  onEdit?.();
17665
17729
  },
17666
- className: "w-full flex items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800",
17730
+ className: "\n w-full\n flex\n items-center\n gap-2\n px-4\n py-3\n text-sm\n hover:bg-gray-100\n dark:hover:bg-neutral-800\n ",
17667
17731
  children: [
17668
17732
  /* @__PURE__ */ jsxRuntime.jsx(bi.BiEdit, {}),
17669
17733
  "Edit"
@@ -17673,11 +17737,12 @@ function CommentMenu({
17673
17737
  canDelete && /* @__PURE__ */ jsxRuntime.jsxs(
17674
17738
  "button",
17675
17739
  {
17740
+ type: "button",
17676
17741
  onClick: () => {
17677
17742
  setOpen(false);
17678
17743
  onDelete?.();
17679
17744
  },
17680
- className: "w-full flex items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800",
17745
+ className: "\n w-full\n flex\n items-center\n gap-2\n px-4\n py-3\n text-sm\n text-red-500\n hover:bg-gray-100\n dark:hover:bg-neutral-800\n ",
17681
17746
  children: [
17682
17747
  /* @__PURE__ */ jsxRuntime.jsx(bi.BiTrash, {}),
17683
17748
  "Delete"
@@ -17687,11 +17752,12 @@ function CommentMenu({
17687
17752
  canReport && /* @__PURE__ */ jsxRuntime.jsxs(
17688
17753
  "button",
17689
17754
  {
17755
+ type: "button",
17690
17756
  onClick: () => {
17691
17757
  setOpen(false);
17692
17758
  onReport?.();
17693
17759
  },
17694
- className: "w-full flex items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800",
17760
+ className: "\n w-full\n flex\n items-center\n gap-2\n px-4\n py-3\n text-sm\n hover:bg-gray-100\n dark:hover:bg-neutral-800\n ",
17695
17761
  children: [
17696
17762
  /* @__PURE__ */ jsxRuntime.jsx(bi.BiFlag, {}),
17697
17763
  "Report"
@@ -17709,25 +17775,37 @@ function CommentRepliesToggle({
17709
17775
  collapsed,
17710
17776
  onToggle
17711
17777
  }) {
17778
+ if (repliesCount <= 0) {
17779
+ return null;
17780
+ }
17781
+ const label = collapsed ? `View ${repliesCount} ${repliesCount === 1 ? "reply" : "replies"}` : "Hide replies";
17712
17782
  return /* @__PURE__ */ jsxRuntime.jsxs(
17713
17783
  "button",
17714
17784
  {
17785
+ type: "button",
17715
17786
  onClick: onToggle,
17716
- className: "inline-flex items-center gap-1 text-sm font-medium text-blue-500 hover:text-blue-400 transition-colors w-fit",
17787
+ className: "\n inline-flex\n items-center\n gap-1\n w-fit\n text-sm\n font-medium\n text-blue-500\n hover:text-blue-400\n transition-colors\n ",
17717
17788
  children: [
17718
17789
  collapsed ? /* @__PURE__ */ jsxRuntime.jsx(bi.BiChevronDown, { size: 18 }) : /* @__PURE__ */ jsxRuntime.jsx(bi.BiChevronUp, { size: 18 }),
17719
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: collapsed ? `View ${repliesCount} repl${repliesCount > 1 ? "ies" : "y"}` : "Hide replies" })
17790
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
17720
17791
  ]
17721
17792
  }
17722
17793
  );
17723
17794
  }
17724
17795
  var CommentRepliesToggle_default = CommentRepliesToggle;
17725
- function ReplyComposer({ username, onSubmit }) {
17796
+ function ReplyComposer({
17797
+ avatar,
17798
+ username,
17799
+ loading,
17800
+ onSubmit
17801
+ }) {
17726
17802
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
17727
17803
  CommentComposer_default,
17728
17804
  {
17729
- placeholder: `Reply to @${username}`,
17805
+ avatar,
17806
+ loading,
17730
17807
  submitLabel: "Reply",
17808
+ placeholder: `Reply to @${username}`,
17731
17809
  onSubmit
17732
17810
  }
17733
17811
  ) });
@@ -17736,7 +17814,7 @@ var ReplyComposer_default = ReplyComposer;
17736
17814
 
17737
17815
  // src/compositions/comment-thread/constants/comment.constants.ts
17738
17816
  var MAX_COMMENT_DEPTH = 5;
17739
- var COMMENT_INDENTATION = 0;
17817
+ var COMMENT_INDENTATION = 24;
17740
17818
  function CommentItem({
17741
17819
  comment,
17742
17820
  depth = 0,
@@ -17751,7 +17829,7 @@ function CommentItem({
17751
17829
  const [isReplying, setIsReplying] = React4.useState(false);
17752
17830
  const resolvedDepth = Math.min(depth, MAX_COMMENT_DEPTH);
17753
17831
  async function handleReply(content) {
17754
- console.log(content);
17832
+ await onReply?.(comment, content);
17755
17833
  setIsReplying(false);
17756
17834
  }
17757
17835
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -17759,16 +17837,18 @@ function CommentItem({
17759
17837
  {
17760
17838
  className: "flex flex-col gap-4",
17761
17839
  style: {
17762
- paddingLeft: `${resolvedDepth * COMMENT_INDENTATION}px`
17840
+ paddingLeft: resolvedDepth * COMMENT_INDENTATION
17763
17841
  },
17764
17842
  children: [
17765
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col gap-3 min-w-0", children: [
17843
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-3 min-w-0", children: [
17766
17844
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
17767
17845
  /* @__PURE__ */ jsxRuntime.jsx(
17768
17846
  CommentHeader_default,
17769
17847
  {
17770
- avatar: comment.author.avatar,
17771
- username: comment.author.username,
17848
+ avatar: comment.author?.avatar,
17849
+ username: comment.author?.username ?? "unknown",
17850
+ firstName: comment.author?.firstName,
17851
+ lastName: comment.author?.lastName,
17772
17852
  createdAt: comment.createdAt,
17773
17853
  edited: comment.edited
17774
17854
  }
@@ -17791,18 +17871,19 @@ function CommentItem({
17791
17871
  {
17792
17872
  likes: comment.likes,
17793
17873
  dislikes: comment.dislikes,
17874
+ liked: comment.liked,
17875
+ disliked: comment.disliked,
17876
+ canReply: comment.permissions?.canReply,
17794
17877
  onLike: () => onLike?.(comment),
17795
17878
  onDislike: () => onDislike?.(comment),
17796
- onReply: () => {
17797
- setIsReplying((prev) => !prev);
17798
- onReply?.(comment);
17799
- }
17879
+ onReply: () => setIsReplying((prev) => !prev)
17800
17880
  }
17801
17881
  ),
17802
17882
  isReplying && /* @__PURE__ */ jsxRuntime.jsx(
17803
17883
  ReplyComposer_default,
17804
17884
  {
17805
- username: comment.author.username,
17885
+ avatar: comment.author?.avatar,
17886
+ username: comment.author?.username,
17806
17887
  onSubmit: handleReply
17807
17888
  }
17808
17889
  ),
@@ -17852,6 +17933,9 @@ function CommentList({
17852
17933
  onDelete,
17853
17934
  onReport
17854
17935
  }) {
17936
+ if (!comments.length) {
17937
+ return null;
17938
+ }
17855
17939
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-8", children: comments.map((comment) => /* @__PURE__ */ jsxRuntime.jsx(
17856
17940
  CommentItem_default,
17857
17941
  {
@@ -17868,52 +17952,142 @@ function CommentList({
17868
17952
  )) });
17869
17953
  }
17870
17954
  var CommentList_default = CommentList;
17871
- var CommentThreadContext = React4.createContext(null);
17872
- function useCommentThread(initialComments) {
17873
- const [comments, setComments] = React4.useState(initialComments);
17874
- return {
17875
- comments,
17876
- setComments
17877
- };
17955
+
17956
+ // src/compositions/comment-thread/utils/buildCommentTree.ts
17957
+ function buildCommentTree(comments) {
17958
+ const map = /* @__PURE__ */ new Map();
17959
+ const roots = [];
17960
+ comments.forEach((comment) => {
17961
+ map.set(comment.id, {
17962
+ ...comment,
17963
+ replies: []
17964
+ });
17965
+ });
17966
+ comments.forEach((comment) => {
17967
+ const current = map.get(comment.id);
17968
+ if (!comment.parentCommentId) {
17969
+ roots.push(current);
17970
+ return;
17971
+ }
17972
+ const parent = map.get(comment.parentCommentId);
17973
+ if (parent) {
17974
+ parent.replies.push(current);
17975
+ }
17976
+ });
17977
+ return roots;
17978
+ }
17979
+
17980
+ // src/compositions/comment-thread/utils/updateComment.ts
17981
+ function updateComment(comments, commentId, updater) {
17982
+ return comments.map((comment) => {
17983
+ if (comment.id === commentId) {
17984
+ return updater(comment);
17985
+ }
17986
+ return {
17987
+ ...comment,
17988
+ replies: comment.replies ? updateComment(comment.replies, commentId, updater) : []
17989
+ };
17990
+ });
17991
+ }
17992
+
17993
+ // src/compositions/comment-thread/utils/insertReply.ts
17994
+ function insertReply(comments, parentId, reply) {
17995
+ return comments.map((comment) => {
17996
+ if (comment.id === parentId) {
17997
+ return {
17998
+ ...comment,
17999
+ replies: [...comment.replies || [], reply]
18000
+ };
18001
+ }
18002
+ return {
18003
+ ...comment,
18004
+ replies: comment.replies ? insertReply(comment.replies, parentId, reply) : []
18005
+ };
18006
+ });
17878
18007
  }
17879
18008
  function CommentThread({
17880
- comments: initialComments = [],
17881
- onCommentCreate,
17882
- onLike,
17883
- onDislike,
17884
- onReply,
18009
+ currentUser,
18010
+ dataSource,
18011
+ loading = false,
17885
18012
  onEdit,
17886
18013
  onDelete,
17887
18014
  onReport
17888
18015
  }) {
17889
- const { comments, setComments } = useCommentThread(initialComments);
18016
+ const [comments, setComments] = React4.useState([]);
18017
+ const [isLoadingComments, setIsLoadingComments] = React4.useState(true);
18018
+ React4.useEffect(() => {
18019
+ async function loadComments() {
18020
+ try {
18021
+ const result = await dataSource.getComments();
18022
+ setComments(buildCommentTree(result));
18023
+ } finally {
18024
+ setIsLoadingComments(false);
18025
+ }
18026
+ }
18027
+ loadComments();
18028
+ }, [dataSource]);
17890
18029
  async function handleCreateComment(content) {
17891
- await onCommentCreate?.(content);
18030
+ const comment = await dataSource.createComment({
18031
+ content,
18032
+ parentCommentId: null
18033
+ });
18034
+ setComments((prev) => [comment, ...prev]);
17892
18035
  }
17893
- return /* @__PURE__ */ jsxRuntime.jsx(
17894
- CommentThreadContext.Provider,
17895
- {
17896
- value: {
18036
+ async function handleReply(parentComment, content) {
18037
+ const reply = await dataSource.createComment({
18038
+ content,
18039
+ parentCommentId: parentComment.id
18040
+ });
18041
+ setComments((prev) => insertReply(prev, parentComment.id, reply));
18042
+ }
18043
+ async function handleLike(comment) {
18044
+ const result = await dataSource.toggleLike(comment.id);
18045
+ setComments(
18046
+ (prev) => updateComment(prev, comment.id, (item) => ({
18047
+ ...item,
18048
+ likes: result.likes,
18049
+ dislikes: result.dislikes,
18050
+ liked: result.liked,
18051
+ disliked: result.disliked
18052
+ }))
18053
+ );
18054
+ }
18055
+ async function handleDislike(comment) {
18056
+ const result = await dataSource.toggleDislike(comment.id);
18057
+ setComments(
18058
+ (prev) => updateComment(prev, comment.id, (item) => ({
18059
+ ...item,
18060
+ likes: result.likes,
18061
+ dislikes: result.dislikes,
18062
+ liked: result.liked,
18063
+ disliked: result.disliked
18064
+ }))
18065
+ );
18066
+ }
18067
+ if (loading || isLoadingComments) {
18068
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-6", children: "Loading comments..." });
18069
+ }
18070
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-8", children: [
18071
+ /* @__PURE__ */ jsxRuntime.jsx(
18072
+ CommentComposer_default,
18073
+ {
18074
+ avatar: currentUser.avatar,
18075
+ onSubmit: handleCreateComment
18076
+ }
18077
+ ),
18078
+ /* @__PURE__ */ jsxRuntime.jsx(
18079
+ CommentList_default,
18080
+ {
17897
18081
  comments,
17898
- setComments
17899
- },
17900
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-8 w-full", children: [
17901
- /* @__PURE__ */ jsxRuntime.jsx(CommentComposer_default, { onSubmit: handleCreateComment }),
17902
- /* @__PURE__ */ jsxRuntime.jsx(
17903
- CommentList_default,
17904
- {
17905
- comments,
17906
- onLike,
17907
- onDislike,
17908
- onReply,
17909
- onEdit,
17910
- onDelete,
17911
- onReport
17912
- }
17913
- )
17914
- ] })
17915
- }
17916
- );
18082
+ onLike: handleLike,
18083
+ onDislike: handleDislike,
18084
+ onReply: handleReply,
18085
+ onEdit,
18086
+ onDelete,
18087
+ onReport
18088
+ }
18089
+ )
18090
+ ] });
17917
18091
  }
17918
18092
  var CommentThread_default = CommentThread;
17919
18093
  function DataViewTable({