elseware-ui 2.31.0 → 2.31.2

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.mjs CHANGED
@@ -22,6 +22,7 @@ import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
22
22
  import { MdUpload, MdOutlineDone, MdOutlineRemoveCircleOutline } from 'react-icons/md';
23
23
  import '@mdxeditor/editor/style.css';
24
24
  import { MDXEditor, headingsPlugin, linkPlugin, listsPlugin, imagePlugin, tablePlugin, linkDialogPlugin, thematicBreakPlugin, quotePlugin, toolbarPlugin, UndoRedo, Separator, BlockTypeSelect, BoldItalicUnderlineToggles, StrikeThroughSupSubToggles, ListsToggle, CreateLink, InsertImage, InsertTable, InsertThematicBreak, markdownShortcutPlugin, frontmatterPlugin, codeBlockPlugin, codeMirrorPlugin } from '@mdxeditor/editor';
25
+ import { BiDotsVerticalRounded, BiEdit, BiTrash, BiFlag, BiSolidLike, BiLike, BiSolidDislike, BiDislike, BiChevronDown, BiChevronUp } from 'react-icons/bi';
25
26
 
26
27
  // node_modules/clsx/dist/clsx.mjs
27
28
  function r(e) {
@@ -15713,7 +15714,7 @@ function Tags({
15713
15714
  var Tags_default = Tags;
15714
15715
  function TextArea({
15715
15716
  placeholder,
15716
- limit,
15717
+ limit = 1e3,
15717
15718
  styles,
15718
15719
  shape,
15719
15720
  ...props
@@ -17457,6 +17458,824 @@ function ShowMore({ text, limit }) {
17457
17458
  ] });
17458
17459
  }
17459
17460
  var ShowMore_default = ShowMore;
17461
+ function CommentComposer({
17462
+ avatar,
17463
+ placeholder = "Add a comment...",
17464
+ submitLabel = "Comment",
17465
+ disabled,
17466
+ loading,
17467
+ onSubmit
17468
+ }) {
17469
+ const [value, setValue] = useState("");
17470
+ const [focused, setFocused] = useState(false);
17471
+ async function handleSubmit() {
17472
+ const content = value.trim();
17473
+ if (!content) {
17474
+ return;
17475
+ }
17476
+ await onSubmit?.(content);
17477
+ setValue("");
17478
+ setFocused(false);
17479
+ }
17480
+ function handleCancel() {
17481
+ setValue("");
17482
+ setFocused(false);
17483
+ }
17484
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
17485
+ /* @__PURE__ */ jsx(Avatar, { src: avatar, size: "md" }),
17486
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-3", children: [
17487
+ /* @__PURE__ */ jsx(
17488
+ "textarea",
17489
+ {
17490
+ value,
17491
+ disabled: disabled || loading,
17492
+ placeholder,
17493
+ onFocus: () => setFocused(true),
17494
+ onChange: (e) => setValue(e.target.value),
17495
+ 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 "
17496
+ }
17497
+ ),
17498
+ (focused || value.length > 0) && /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
17499
+ /* @__PURE__ */ jsx(
17500
+ Button_default,
17501
+ {
17502
+ text: "Cancel",
17503
+ variant: "secondary",
17504
+ appearance: "ghost",
17505
+ disabled: loading,
17506
+ onClick: handleCancel
17507
+ }
17508
+ ),
17509
+ /* @__PURE__ */ jsx(
17510
+ Button_default,
17511
+ {
17512
+ text: submitLabel,
17513
+ variant: "primary",
17514
+ loading,
17515
+ disabled: !value.trim(),
17516
+ onClick: handleSubmit
17517
+ }
17518
+ )
17519
+ ] })
17520
+ ] })
17521
+ ] });
17522
+ }
17523
+ var CommentComposer_default = CommentComposer;
17524
+ function CommentActions({
17525
+ likes = 0,
17526
+ dislikes = 0,
17527
+ liked = false,
17528
+ disliked = false,
17529
+ canReply = true,
17530
+ onLike,
17531
+ onDislike,
17532
+ onReply
17533
+ }) {
17534
+ return /* @__PURE__ */ jsxs(
17535
+ "div",
17536
+ {
17537
+ className: "\n flex\n items-center\n gap-5\n text-sm\n text-gray-500\n dark:text-gray-400\n ",
17538
+ children: [
17539
+ /* @__PURE__ */ jsxs(
17540
+ "button",
17541
+ {
17542
+ onClick: onLike,
17543
+ className: `
17544
+ inline-flex
17545
+ items-center
17546
+ gap-1
17547
+ transition-colors
17548
+ ${liked ? "text-blue-500" : "hover:text-gray-900 dark:hover:text-gray-100"}
17549
+ `,
17550
+ children: [
17551
+ liked ? /* @__PURE__ */ jsx(BiSolidLike, { size: 18 }) : /* @__PURE__ */ jsx(BiLike, { size: 18 }),
17552
+ /* @__PURE__ */ jsx("span", { children: likes })
17553
+ ]
17554
+ }
17555
+ ),
17556
+ /* @__PURE__ */ jsxs(
17557
+ "button",
17558
+ {
17559
+ onClick: onDislike,
17560
+ className: `
17561
+ inline-flex
17562
+ items-center
17563
+ gap-1
17564
+ transition-colors
17565
+ ${disliked ? "text-red-500" : "hover:text-gray-900 dark:hover:text-gray-100"}
17566
+ `,
17567
+ children: [
17568
+ disliked ? /* @__PURE__ */ jsx(BiSolidDislike, { size: 18 }) : /* @__PURE__ */ jsx(BiDislike, { size: 18 }),
17569
+ /* @__PURE__ */ jsx("span", { children: dislikes })
17570
+ ]
17571
+ }
17572
+ ),
17573
+ canReply && /* @__PURE__ */ jsx(
17574
+ "button",
17575
+ {
17576
+ onClick: onReply,
17577
+ className: "\n font-medium\n transition-colors\n hover:text-gray-900\n dark:hover:text-gray-100\n ",
17578
+ children: "Reply"
17579
+ }
17580
+ )
17581
+ ]
17582
+ }
17583
+ );
17584
+ }
17585
+ var CommentActions_default = CommentActions;
17586
+ function CommentContent({ content }) {
17587
+ return /* @__PURE__ */ jsx(
17588
+ "div",
17589
+ {
17590
+ className: "\n text-sm\n leading-relaxed\n whitespace-pre-wrap\n break-words\n text-gray-700\n dark:text-gray-300\n ",
17591
+ children: content
17592
+ }
17593
+ );
17594
+ }
17595
+ var CommentContent_default = CommentContent;
17596
+
17597
+ // src/compositions/comment-thread/utils/formatCommentDate.ts
17598
+ function formatCommentDate(date) {
17599
+ const now = /* @__PURE__ */ new Date();
17600
+ const target = new Date(date);
17601
+ const seconds = Math.floor((now.getTime() - target.getTime()) / 1e3);
17602
+ const intervals = [
17603
+ {
17604
+ label: "year",
17605
+ seconds: 31536e3
17606
+ },
17607
+ {
17608
+ label: "month",
17609
+ seconds: 2592e3
17610
+ },
17611
+ {
17612
+ label: "day",
17613
+ seconds: 86400
17614
+ },
17615
+ {
17616
+ label: "hour",
17617
+ seconds: 3600
17618
+ },
17619
+ {
17620
+ label: "minute",
17621
+ seconds: 60
17622
+ }
17623
+ ];
17624
+ for (const interval of intervals) {
17625
+ const count = Math.floor(seconds / interval.seconds);
17626
+ if (count >= 1) {
17627
+ return `${count} ${interval.label}${count > 1 ? "s" : ""} ago`;
17628
+ }
17629
+ }
17630
+ return "Just now";
17631
+ }
17632
+ function CommentHeader({
17633
+ avatar,
17634
+ username,
17635
+ firstName,
17636
+ lastName,
17637
+ createdAt,
17638
+ edited
17639
+ }) {
17640
+ const displayName = firstName || lastName ? `${firstName ?? ""} ${lastName ?? ""}`.trim() : username;
17641
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
17642
+ /* @__PURE__ */ jsx(Avatar, { src: avatar, size: "sm" }),
17643
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap min-w-0", children: [
17644
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-sm text-gray-900 dark:text-gray-100", children: displayName }),
17645
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
17646
+ "@",
17647
+ username
17648
+ ] }),
17649
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "\xB7" }),
17650
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: formatCommentDate(createdAt) }),
17651
+ edited && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: "(edited)" })
17652
+ ] })
17653
+ ] });
17654
+ }
17655
+ var CommentHeader_default = CommentHeader;
17656
+ function CommentMenu({
17657
+ canEdit,
17658
+ canDelete,
17659
+ canReport,
17660
+ onEdit,
17661
+ onDelete,
17662
+ onReport
17663
+ }) {
17664
+ const [open, setOpen] = useState(false);
17665
+ const ref = useRef(null);
17666
+ useEffect(() => {
17667
+ function handleOutsideClick(event) {
17668
+ if (ref.current && !ref.current.contains(event.target)) {
17669
+ setOpen(false);
17670
+ }
17671
+ }
17672
+ document.addEventListener("mousedown", handleOutsideClick);
17673
+ return () => document.removeEventListener("mousedown", handleOutsideClick);
17674
+ }, []);
17675
+ const hasActions = canEdit || canDelete || canReport;
17676
+ if (!hasActions) {
17677
+ return null;
17678
+ }
17679
+ return /* @__PURE__ */ jsxs("div", { ref, className: "relative", children: [
17680
+ /* @__PURE__ */ jsx(
17681
+ "button",
17682
+ {
17683
+ type: "button",
17684
+ onClick: () => setOpen((prev) => !prev),
17685
+ className: "\n text-gray-500\n hover:text-gray-900\n dark:hover:text-gray-100\n transition-colors\n ",
17686
+ children: /* @__PURE__ */ jsx(BiDotsVerticalRounded, { size: 18 })
17687
+ }
17688
+ ),
17689
+ open && /* @__PURE__ */ jsxs(
17690
+ "div",
17691
+ {
17692
+ 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 ",
17693
+ children: [
17694
+ canEdit && /* @__PURE__ */ jsxs(
17695
+ "button",
17696
+ {
17697
+ type: "button",
17698
+ onClick: () => {
17699
+ setOpen(false);
17700
+ onEdit?.();
17701
+ },
17702
+ 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 ",
17703
+ children: [
17704
+ /* @__PURE__ */ jsx(BiEdit, {}),
17705
+ "Edit"
17706
+ ]
17707
+ }
17708
+ ),
17709
+ canDelete && /* @__PURE__ */ jsxs(
17710
+ "button",
17711
+ {
17712
+ type: "button",
17713
+ onClick: () => {
17714
+ setOpen(false);
17715
+ onDelete?.();
17716
+ },
17717
+ 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 ",
17718
+ children: [
17719
+ /* @__PURE__ */ jsx(BiTrash, {}),
17720
+ "Delete"
17721
+ ]
17722
+ }
17723
+ ),
17724
+ canReport && /* @__PURE__ */ jsxs(
17725
+ "button",
17726
+ {
17727
+ type: "button",
17728
+ onClick: () => {
17729
+ setOpen(false);
17730
+ onReport?.();
17731
+ },
17732
+ 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 ",
17733
+ children: [
17734
+ /* @__PURE__ */ jsx(BiFlag, {}),
17735
+ "Report"
17736
+ ]
17737
+ }
17738
+ )
17739
+ ]
17740
+ }
17741
+ )
17742
+ ] });
17743
+ }
17744
+ var CommentMenu_default = CommentMenu;
17745
+ function CommentRepliesToggle({
17746
+ repliesCount,
17747
+ collapsed,
17748
+ onToggle
17749
+ }) {
17750
+ if (repliesCount <= 0) {
17751
+ return null;
17752
+ }
17753
+ const label = collapsed ? `View ${repliesCount} ${repliesCount === 1 ? "reply" : "replies"}` : "Hide replies";
17754
+ return /* @__PURE__ */ jsxs(
17755
+ "button",
17756
+ {
17757
+ type: "button",
17758
+ onClick: onToggle,
17759
+ 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 ",
17760
+ children: [
17761
+ collapsed ? /* @__PURE__ */ jsx(BiChevronDown, { size: 18 }) : /* @__PURE__ */ jsx(BiChevronUp, { size: 18 }),
17762
+ /* @__PURE__ */ jsx("span", { children: label })
17763
+ ]
17764
+ }
17765
+ );
17766
+ }
17767
+ var CommentRepliesToggle_default = CommentRepliesToggle;
17768
+ function ReplyComposer({
17769
+ avatar,
17770
+ username,
17771
+ loading,
17772
+ onSubmit
17773
+ }) {
17774
+ return /* @__PURE__ */ jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsx(
17775
+ CommentComposer_default,
17776
+ {
17777
+ avatar,
17778
+ loading,
17779
+ submitLabel: "Reply",
17780
+ placeholder: `Reply to @${username}`,
17781
+ onSubmit
17782
+ }
17783
+ ) });
17784
+ }
17785
+ var ReplyComposer_default = ReplyComposer;
17786
+
17787
+ // src/compositions/comment-thread/constants/comment.constants.ts
17788
+ var MAX_COMMENT_DEPTH = 5;
17789
+ var COMMENT_INDENTATION = 24;
17790
+ function CommentItem({
17791
+ comment,
17792
+ depth = 0,
17793
+ onLike,
17794
+ onDislike,
17795
+ onReply,
17796
+ onEdit,
17797
+ onDelete,
17798
+ onReport
17799
+ }) {
17800
+ const [collapsed, setCollapsed] = useState(false);
17801
+ const [isReplying, setIsReplying] = useState(false);
17802
+ const resolvedDepth = Math.min(depth, MAX_COMMENT_DEPTH);
17803
+ async function handleReply(content) {
17804
+ await onReply?.(comment, content);
17805
+ setIsReplying(false);
17806
+ }
17807
+ return /* @__PURE__ */ jsxs(
17808
+ "div",
17809
+ {
17810
+ className: "flex flex-col gap-4",
17811
+ style: {
17812
+ paddingLeft: resolvedDepth * COMMENT_INDENTATION
17813
+ },
17814
+ children: [
17815
+ /* @__PURE__ */ jsx("div", { className: "flex gap-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-3 min-w-0", children: [
17816
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
17817
+ /* @__PURE__ */ jsx(
17818
+ CommentHeader_default,
17819
+ {
17820
+ avatar: comment.author?.avatar,
17821
+ username: comment.author?.username ?? "unknown",
17822
+ firstName: comment.author?.firstName,
17823
+ lastName: comment.author?.lastName,
17824
+ createdAt: comment.createdAt,
17825
+ edited: comment.edited
17826
+ }
17827
+ ),
17828
+ /* @__PURE__ */ jsx(
17829
+ CommentMenu_default,
17830
+ {
17831
+ canEdit: comment.permissions?.canEdit,
17832
+ canDelete: comment.permissions?.canDelete,
17833
+ canReport: comment.permissions?.canReport,
17834
+ onEdit: () => onEdit?.(comment),
17835
+ onDelete: () => onDelete?.(comment),
17836
+ onReport: () => onReport?.(comment)
17837
+ }
17838
+ )
17839
+ ] }),
17840
+ /* @__PURE__ */ jsx(CommentContent_default, { content: comment.content }),
17841
+ /* @__PURE__ */ jsx(
17842
+ CommentActions_default,
17843
+ {
17844
+ likes: comment.likes,
17845
+ dislikes: comment.dislikes,
17846
+ liked: comment.liked,
17847
+ disliked: comment.disliked,
17848
+ canReply: comment.permissions?.canReply,
17849
+ onLike: () => onLike?.(comment),
17850
+ onDislike: () => onDislike?.(comment),
17851
+ onReply: () => setIsReplying((prev) => !prev)
17852
+ }
17853
+ ),
17854
+ isReplying && /* @__PURE__ */ jsx(
17855
+ ReplyComposer_default,
17856
+ {
17857
+ avatar: comment.author?.avatar,
17858
+ username: comment.author?.username,
17859
+ onSubmit: handleReply
17860
+ }
17861
+ ),
17862
+ !!comment.replies?.length && /* @__PURE__ */ jsx(
17863
+ CommentRepliesToggle_default,
17864
+ {
17865
+ collapsed,
17866
+ repliesCount: comment.replies.length,
17867
+ onToggle: () => setCollapsed((prev) => !prev)
17868
+ }
17869
+ )
17870
+ ] }) }),
17871
+ !collapsed && !!comment.replies?.length && /* @__PURE__ */ jsx(
17872
+ "div",
17873
+ {
17874
+ className: cn(
17875
+ "border-l border-gray-200 dark:border-neutral-800",
17876
+ "pl-4 ml-5"
17877
+ ),
17878
+ children: /* @__PURE__ */ jsx(
17879
+ CommentList_default,
17880
+ {
17881
+ comments: comment.replies,
17882
+ depth: depth + 1,
17883
+ onLike,
17884
+ onDislike,
17885
+ onReply,
17886
+ onEdit,
17887
+ onDelete,
17888
+ onReport
17889
+ }
17890
+ )
17891
+ }
17892
+ )
17893
+ ]
17894
+ }
17895
+ );
17896
+ }
17897
+ var CommentItem_default = CommentItem;
17898
+ function CommentList({
17899
+ comments,
17900
+ depth = 0,
17901
+ onLike,
17902
+ onDislike,
17903
+ onReply,
17904
+ onEdit,
17905
+ onDelete,
17906
+ onReport
17907
+ }) {
17908
+ if (!comments.length) {
17909
+ return null;
17910
+ }
17911
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-8", children: comments.map((comment) => /* @__PURE__ */ jsx(
17912
+ CommentItem_default,
17913
+ {
17914
+ comment,
17915
+ depth,
17916
+ onLike,
17917
+ onDislike,
17918
+ onReply,
17919
+ onEdit,
17920
+ onDelete,
17921
+ onReport
17922
+ },
17923
+ comment.id
17924
+ )) });
17925
+ }
17926
+ var CommentList_default = CommentList;
17927
+ function EditCommentModal({
17928
+ show,
17929
+ comment,
17930
+ handleCloseModal,
17931
+ onSubmit
17932
+ }) {
17933
+ if (!comment) return null;
17934
+ return /* @__PURE__ */ jsx(Modal_default, { title: "Edit Comment", show, handleOnClose: handleCloseModal, children: /* @__PURE__ */ jsx(
17935
+ Form,
17936
+ {
17937
+ enableReinitialize: true,
17938
+ initialValues: {
17939
+ content: comment.content
17940
+ },
17941
+ onSubmit: async (values) => {
17942
+ await onSubmit?.(comment, values.content);
17943
+ handleCloseModal();
17944
+ },
17945
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
17946
+ /* @__PURE__ */ jsx(TextArea_default, { name: "content", rows: 5, placeholder: "Comment" }),
17947
+ /* @__PURE__ */ jsx(Button_default, { type: "submit", variant: "success", text: "Save Changes" })
17948
+ ] })
17949
+ }
17950
+ ) });
17951
+ }
17952
+ var EditCommentModal_default = EditCommentModal;
17953
+ function DeleteCommentModal({
17954
+ show,
17955
+ comment,
17956
+ handleCloseModal,
17957
+ onDelete
17958
+ }) {
17959
+ if (!comment) return null;
17960
+ return /* @__PURE__ */ jsx(Modal_default, { title: "Delete Comment", show, handleOnClose: handleCloseModal, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
17961
+ /* @__PURE__ */ jsx(Typography.Paragraph, { children: "Are you sure you want to permanently delete this comment?" }),
17962
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-end", children: [
17963
+ /* @__PURE__ */ jsx(Button_default, { text: "Cancel", appearance: "ghost", onClick: handleCloseModal }),
17964
+ /* @__PURE__ */ jsx(
17965
+ Button_default,
17966
+ {
17967
+ text: "Delete",
17968
+ variant: "danger",
17969
+ onClick: async () => {
17970
+ await onDelete?.(comment);
17971
+ handleCloseModal();
17972
+ }
17973
+ }
17974
+ )
17975
+ ] })
17976
+ ] }) });
17977
+ }
17978
+ var DeleteCommentModal_default = DeleteCommentModal;
17979
+ var REPORT_REASONS = [
17980
+ {
17981
+ label: "Spam",
17982
+ value: "spam"
17983
+ },
17984
+ {
17985
+ label: "Harassment",
17986
+ value: "harassment"
17987
+ },
17988
+ {
17989
+ label: "Hate Speech",
17990
+ value: "hate_speech"
17991
+ },
17992
+ {
17993
+ label: "Misleading Information",
17994
+ value: "misleading_information"
17995
+ },
17996
+ {
17997
+ label: "Other",
17998
+ value: "other"
17999
+ }
18000
+ ];
18001
+ function ReportCommentModal({
18002
+ show,
18003
+ comment,
18004
+ handleCloseModal,
18005
+ onSubmit
18006
+ }) {
18007
+ if (!comment) return null;
18008
+ return /* @__PURE__ */ jsx(Modal_default, { title: "Report Comment", show, handleOnClose: handleCloseModal, children: /* @__PURE__ */ jsx(
18009
+ Form,
18010
+ {
18011
+ initialValues: {
18012
+ reason: "",
18013
+ description: ""
18014
+ },
18015
+ onSubmit: async (values) => {
18016
+ await onSubmit?.(comment, {
18017
+ reason: values.reason,
18018
+ description: values.description
18019
+ });
18020
+ handleCloseModal();
18021
+ },
18022
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
18023
+ /* @__PURE__ */ jsx(Select_default, { name: "reason", placeholder: "Reason", options: REPORT_REASONS }),
18024
+ /* @__PURE__ */ jsx(
18025
+ TextArea_default,
18026
+ {
18027
+ name: "description",
18028
+ rows: 4,
18029
+ placeholder: "Additional details"
18030
+ }
18031
+ ),
18032
+ /* @__PURE__ */ jsx(Button_default, { type: "submit", variant: "danger", text: "Submit Report" })
18033
+ ] })
18034
+ }
18035
+ ) });
18036
+ }
18037
+ var ReportCommentModal_default = ReportCommentModal;
18038
+ function useCommentModals() {
18039
+ const [showEditModal, setShowEditModal] = useState(false);
18040
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
18041
+ const [showReportModal, setShowReportModal] = useState(false);
18042
+ return {
18043
+ showEditModal,
18044
+ openEditModal: () => setShowEditModal(true),
18045
+ closeEditModal: () => setShowEditModal(false),
18046
+ showDeleteModal,
18047
+ openDeleteModal: () => setShowDeleteModal(true),
18048
+ closeDeleteModal: () => setShowDeleteModal(false),
18049
+ showReportModal,
18050
+ openReportModal: () => setShowReportModal(true),
18051
+ closeReportModal: () => setShowReportModal(false)
18052
+ };
18053
+ }
18054
+ function useCommentSelection() {
18055
+ const [selectedComment, setSelectedComment] = useState(
18056
+ null
18057
+ );
18058
+ function selectComment(comment) {
18059
+ setSelectedComment(comment);
18060
+ }
18061
+ function clearSelection() {
18062
+ setSelectedComment(null);
18063
+ }
18064
+ return {
18065
+ selectedComment,
18066
+ selectComment,
18067
+ clearSelection
18068
+ };
18069
+ }
18070
+
18071
+ // src/compositions/comment-thread/utils/buildCommentTree.ts
18072
+ function buildCommentTree(comments) {
18073
+ const map = /* @__PURE__ */ new Map();
18074
+ const roots = [];
18075
+ comments.forEach((comment) => {
18076
+ map.set(comment.id, {
18077
+ ...comment,
18078
+ replies: []
18079
+ });
18080
+ });
18081
+ comments.forEach((comment) => {
18082
+ const current = map.get(comment.id);
18083
+ if (!comment.parentCommentId) {
18084
+ roots.push(current);
18085
+ return;
18086
+ }
18087
+ const parent = map.get(comment.parentCommentId);
18088
+ if (parent) {
18089
+ parent.replies.push(current);
18090
+ }
18091
+ });
18092
+ return roots;
18093
+ }
18094
+
18095
+ // src/compositions/comment-thread/utils/updateComment.ts
18096
+ function updateComment(comments, commentId, updater) {
18097
+ return comments.map((comment) => {
18098
+ if (comment.id === commentId) {
18099
+ return updater(comment);
18100
+ }
18101
+ return {
18102
+ ...comment,
18103
+ replies: comment.replies ? updateComment(comment.replies, commentId, updater) : []
18104
+ };
18105
+ });
18106
+ }
18107
+
18108
+ // src/compositions/comment-thread/utils/removeComment.ts
18109
+ function removeComment(comments, commentId) {
18110
+ return comments.filter((comment) => comment.id !== commentId).map((comment) => ({
18111
+ ...comment,
18112
+ replies: comment.replies ? removeComment(comment.replies, commentId) : []
18113
+ }));
18114
+ }
18115
+
18116
+ // src/compositions/comment-thread/utils/insertReply.ts
18117
+ function insertReply(comments, parentId, reply) {
18118
+ return comments.map((comment) => {
18119
+ if (comment.id === parentId) {
18120
+ return {
18121
+ ...comment,
18122
+ replies: [...comment.replies || [], reply]
18123
+ };
18124
+ }
18125
+ return {
18126
+ ...comment,
18127
+ replies: comment.replies ? insertReply(comment.replies, parentId, reply) : []
18128
+ };
18129
+ });
18130
+ }
18131
+ function CommentThread({
18132
+ postId,
18133
+ currentUser,
18134
+ dataSource,
18135
+ loading = false,
18136
+ onEdit,
18137
+ onDelete,
18138
+ onReport
18139
+ }) {
18140
+ const [comments, setComments] = useState([]);
18141
+ const [isLoadingComments, setIsLoadingComments] = useState(true);
18142
+ const selection = useCommentSelection();
18143
+ const modals = useCommentModals();
18144
+ useEffect(() => {
18145
+ async function loadComments() {
18146
+ try {
18147
+ const result = await dataSource.getComments();
18148
+ setComments(buildCommentTree(result));
18149
+ } finally {
18150
+ setIsLoadingComments(false);
18151
+ }
18152
+ }
18153
+ loadComments();
18154
+ }, [dataSource]);
18155
+ async function handleCreateComment(content) {
18156
+ const comment = await dataSource.createComment({
18157
+ content,
18158
+ parentCommentId: null
18159
+ });
18160
+ setComments((prev) => [comment, ...prev]);
18161
+ }
18162
+ async function handleReply(parentComment, content) {
18163
+ const reply = await dataSource.createComment({
18164
+ content,
18165
+ parentCommentId: parentComment.id
18166
+ });
18167
+ setComments((prev) => insertReply(prev, parentComment.id, reply));
18168
+ }
18169
+ async function handleLike(comment) {
18170
+ const result = await dataSource.toggleLike(comment.id);
18171
+ setComments(
18172
+ (prev) => updateComment(prev, comment.id, (item) => ({
18173
+ ...item,
18174
+ likes: result.likes,
18175
+ dislikes: result.dislikes,
18176
+ liked: result.liked,
18177
+ disliked: result.disliked
18178
+ }))
18179
+ );
18180
+ }
18181
+ async function handleDislike(comment) {
18182
+ const result = await dataSource.toggleDislike(comment.id);
18183
+ setComments(
18184
+ (prev) => updateComment(prev, comment.id, (item) => ({
18185
+ ...item,
18186
+ likes: result.likes,
18187
+ dislikes: result.dislikes,
18188
+ liked: result.liked,
18189
+ disliked: result.disliked
18190
+ }))
18191
+ );
18192
+ }
18193
+ async function handleUpdateComment(comment, content) {
18194
+ const updated = await dataSource.updateComment({
18195
+ id: comment.id,
18196
+ content
18197
+ });
18198
+ setComments((prev) => updateComment(prev, comment.id, () => updated));
18199
+ }
18200
+ async function handleDeleteComment(comment) {
18201
+ await dataSource.deleteComment(comment.id);
18202
+ setComments((prev) => removeComment(prev, comment.id));
18203
+ onDelete?.(comment);
18204
+ }
18205
+ async function handleReportComment(comment, payload) {
18206
+ await dataSource.reportComment?.(
18207
+ comment.id,
18208
+ payload.reason,
18209
+ payload.description
18210
+ );
18211
+ onReport?.(comment);
18212
+ }
18213
+ if (loading || isLoadingComments) {
18214
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6", children: "Loading comments..." });
18215
+ }
18216
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
18217
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-8", children: [
18218
+ /* @__PURE__ */ jsx(
18219
+ CommentComposer_default,
18220
+ {
18221
+ avatar: currentUser.avatar,
18222
+ onSubmit: handleCreateComment
18223
+ }
18224
+ ),
18225
+ /* @__PURE__ */ jsx(
18226
+ CommentList_default,
18227
+ {
18228
+ comments,
18229
+ onLike: handleLike,
18230
+ onDislike: handleDislike,
18231
+ onReply: handleReply,
18232
+ onEdit: (comment) => {
18233
+ selection.selectComment(comment);
18234
+ onEdit?.(comment);
18235
+ modals.openEditModal();
18236
+ },
18237
+ onDelete: (comment) => {
18238
+ selection.selectComment(comment);
18239
+ modals.openDeleteModal();
18240
+ },
18241
+ onReport: (comment) => {
18242
+ selection.selectComment(comment);
18243
+ onReport?.(comment);
18244
+ modals.openReportModal();
18245
+ }
18246
+ }
18247
+ )
18248
+ ] }),
18249
+ /* @__PURE__ */ jsx(
18250
+ EditCommentModal_default,
18251
+ {
18252
+ show: modals.showEditModal,
18253
+ comment: selection.selectedComment,
18254
+ handleCloseModal: modals.closeEditModal,
18255
+ onSubmit: handleUpdateComment
18256
+ }
18257
+ ),
18258
+ /* @__PURE__ */ jsx(
18259
+ DeleteCommentModal_default,
18260
+ {
18261
+ show: modals.showDeleteModal,
18262
+ comment: selection.selectedComment,
18263
+ handleCloseModal: modals.closeDeleteModal,
18264
+ onDelete: handleDeleteComment
18265
+ }
18266
+ ),
18267
+ /* @__PURE__ */ jsx(
18268
+ ReportCommentModal_default,
18269
+ {
18270
+ show: modals.showReportModal,
18271
+ comment: selection.selectedComment,
18272
+ handleCloseModal: modals.closeReportModal,
18273
+ onSubmit: handleReportComment
18274
+ }
18275
+ )
18276
+ ] });
18277
+ }
18278
+ var CommentThread_default = CommentThread;
17460
18279
  function DataViewTable({
17461
18280
  title,
17462
18281
  columns,
@@ -18268,6 +19087,6 @@ function ScrollToTop({
18268
19087
  }
18269
19088
  var ScrollToTop_default = ScrollToTop;
18270
19089
 
18271
- export { Accordion_default as Accordion, AsyncComponentWrapper_default as AsyncComponentWrapper, Avatar, Backdrop_default as Backdrop, Badge, BarChart_default as BarChart, Block_default as Block, BlockGroup_default as BlockGroup, BoxNav_default as BoxNav, BoxNavItem_default as BoxNavItem, Brand_default as Brand, Breadcrumb, BreadcrumbItem_default as BreadcrumbItem, Button_default as Button, Caption, Card_default as Card, CardContent_default as CardContent, CardFooter_default as CardFooter, CardHeader_default as CardHeader, Chapter, Checkbox_default as Checkbox, Chip, CloudinaryImage_default as CloudinaryImage, CloudinaryProvider, CloudinaryVideo_default as CloudinaryVideo, Code, Content, ContentArea_default as ContentArea, DataView, DataViewTable_default as DataViewTable, DateSelector_default as DateSelector, DefaultLayout_default as DefaultLayout, Display, DocumentationPanel_default as DocumentationPanel, Drawer, DrawerToggler_default as DrawerToggler, EUIDevLayout_default as EUIDevLayout, EUIProvider, Flag, Flex, FlexCol_default as FlexCol, FlexRow_default as FlexRow, Footer, FooterNav_default as FooterNav, FooterNavGroup_default as FooterNavGroup, FooterNavItem, FooterNavItemContext, FooterNavItemTitle, Form, FormResponse_default as FormResponse, GenericLayout_default as GenericLayout, GlowWrapper_default as GlowWrapper, Graph, GraphEdge, GraphNode, GraphRenderer, Grid_default as Grid, Header, HeaderNav_default as HeaderNav, HeaderNavGroup_default as HeaderNavGroup, HeaderNavItem, HeaderNavItemContext, HeaderNavItemTitle, HomeLayout_default as HomeLayout, Image2 as Image, ImageInput_default as ImageInput, Info_default as Info, Input, InputFile, InputLabel_default as InputLabel, InputList, InputListGroup, InputResponse_default as InputResponse, Label, Layout, Lead, LineChart_default as LineChart, Link, List_default as List, ListItem_default as ListItem, MarkdownEditor_default as MarkdownEditor, MarkdownProvider_default as MarkdownProvider, MarkdownTOC_default as MarkdownTOC, MarkdownViewer_default as MarkdownViewer, Menu, MenuGroup, MenuItem, MenuItemTitle, Modal_default as Modal, MotionSurface_default as MotionSurface, MultiImageInput_default as MultiImageInput, NumericRating_default as NumericRating, Overline, Paragraph, PieChart_default as PieChart, PriceTag, ProgressBar, ProgressBarRating_default as ProgressBarRating, Quote, Radio_default as Radio, RouteTab_default as RouteTab, RouteTabs_default as RouteTabs, ScrollToTop_default as ScrollToTop, Section, Select_default as Select, ShapeSwitch, ShowMore_default as ShowMore, Sidebar_default as Sidebar, SidebarLayout_default as SidebarLayout, SidemenuLayout_default as SidemenuLayout, Skeleton, Slider_default as Slider, StarRating_default as StarRating, StarRatingDistribution_default as StarRatingDistribution, StarRatingInput_default as StarRatingInput, Switch_default as Switch, TNDropdown, TNDropdownItem, TNDropdownTitle, TNGroup, Table_default as Table, Tag_default as Tag, Tags_default as Tags, TextArea_default as TextArea, ThemeContext, ThemeProvider, ThemeSwitch, TitleBanner, Toast, Tooltip4 as Tooltip, TopNav, Transition4 as Transition, TransitionDropdown_default as TransitionDropdown, TransitionFadeIn_default as TransitionFadeIn, Typography, UnderConstructionBanner, ValueBadge_default as ValueBadge, WorldMap, WorldMapCountryTable, YoutubeVideo_default as YoutubeVideoPlayer, cn, createForceLayout, createGridLayout, createTreeLayout, enumValues, getCurrencySymbol, getLayout, isMatch, isRenderFn, normalize, registerLayout, resolveAppearanceStyles, resolveWithGlobal, sendToast, useClickOutside, useCloudinaryConfig, useCurrentTheme_default as useCurrentTheme, useDrawer_default as useDrawer, useEUIConfig, useIsMobile_default as useIsMobile, useModal_default as useModal, useTheme_default as useTheme };
19090
+ export { Accordion_default as Accordion, AsyncComponentWrapper_default as AsyncComponentWrapper, Avatar, Backdrop_default as Backdrop, Badge, BarChart_default as BarChart, Block_default as Block, BlockGroup_default as BlockGroup, BoxNav_default as BoxNav, BoxNavItem_default as BoxNavItem, Brand_default as Brand, Breadcrumb, BreadcrumbItem_default as BreadcrumbItem, Button_default as Button, Caption, Card_default as Card, CardContent_default as CardContent, CardFooter_default as CardFooter, CardHeader_default as CardHeader, Chapter, Checkbox_default as Checkbox, Chip, CloudinaryImage_default as CloudinaryImage, CloudinaryProvider, CloudinaryVideo_default as CloudinaryVideo, Code, CommentThread_default as CommentThread, Content, ContentArea_default as ContentArea, DataView, DataViewTable_default as DataViewTable, DateSelector_default as DateSelector, DefaultLayout_default as DefaultLayout, Display, DocumentationPanel_default as DocumentationPanel, Drawer, DrawerToggler_default as DrawerToggler, EUIDevLayout_default as EUIDevLayout, EUIProvider, Flag, Flex, FlexCol_default as FlexCol, FlexRow_default as FlexRow, Footer, FooterNav_default as FooterNav, FooterNavGroup_default as FooterNavGroup, FooterNavItem, FooterNavItemContext, FooterNavItemTitle, Form, FormResponse_default as FormResponse, GenericLayout_default as GenericLayout, GlowWrapper_default as GlowWrapper, Graph, GraphEdge, GraphNode, GraphRenderer, Grid_default as Grid, Header, HeaderNav_default as HeaderNav, HeaderNavGroup_default as HeaderNavGroup, HeaderNavItem, HeaderNavItemContext, HeaderNavItemTitle, HomeLayout_default as HomeLayout, Image2 as Image, ImageInput_default as ImageInput, Info_default as Info, Input, InputFile, InputLabel_default as InputLabel, InputList, InputListGroup, InputResponse_default as InputResponse, Label, Layout, Lead, LineChart_default as LineChart, Link, List_default as List, ListItem_default as ListItem, MarkdownEditor_default as MarkdownEditor, MarkdownProvider_default as MarkdownProvider, MarkdownTOC_default as MarkdownTOC, MarkdownViewer_default as MarkdownViewer, Menu, MenuGroup, MenuItem, MenuItemTitle, Modal_default as Modal, MotionSurface_default as MotionSurface, MultiImageInput_default as MultiImageInput, NumericRating_default as NumericRating, Overline, Paragraph, PieChart_default as PieChart, PriceTag, ProgressBar, ProgressBarRating_default as ProgressBarRating, Quote, Radio_default as Radio, RouteTab_default as RouteTab, RouteTabs_default as RouteTabs, ScrollToTop_default as ScrollToTop, Section, Select_default as Select, ShapeSwitch, ShowMore_default as ShowMore, Sidebar_default as Sidebar, SidebarLayout_default as SidebarLayout, SidemenuLayout_default as SidemenuLayout, Skeleton, Slider_default as Slider, StarRating_default as StarRating, StarRatingDistribution_default as StarRatingDistribution, StarRatingInput_default as StarRatingInput, Switch_default as Switch, TNDropdown, TNDropdownItem, TNDropdownTitle, TNGroup, Table_default as Table, Tag_default as Tag, Tags_default as Tags, TextArea_default as TextArea, ThemeContext, ThemeProvider, ThemeSwitch, TitleBanner, Toast, Tooltip4 as Tooltip, TopNav, Transition4 as Transition, TransitionDropdown_default as TransitionDropdown, TransitionFadeIn_default as TransitionFadeIn, Typography, UnderConstructionBanner, ValueBadge_default as ValueBadge, WorldMap, WorldMapCountryTable, YoutubeVideo_default as YoutubeVideoPlayer, cn, createForceLayout, createGridLayout, createTreeLayout, enumValues, getCurrencySymbol, getLayout, isMatch, isRenderFn, normalize, registerLayout, resolveAppearanceStyles, resolveWithGlobal, sendToast, useClickOutside, useCloudinaryConfig, useCurrentTheme_default as useCurrentTheme, useDrawer_default as useDrawer, useEUIConfig, useIsMobile_default as useIsMobile, useModal_default as useModal, useTheme_default as useTheme };
18272
19091
  //# sourceMappingURL=index.mjs.map
18273
19092
  //# sourceMappingURL=index.mjs.map