@wealthx/shadcn 1.5.16 → 1.5.18

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.
Files changed (90) hide show
  1. package/.turbo/turbo-build.log +130 -130
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-UJMVXREM.mjs → chunk-2UA4CPE5.mjs} +1 -1
  4. package/dist/{chunk-DYSVJ473.mjs → chunk-2VTOF7PW.mjs} +1 -1
  5. package/dist/{chunk-6MDNL5I2.mjs → chunk-45V7X563.mjs} +3 -3
  6. package/dist/{chunk-RKHKBEE6.mjs → chunk-5BU7TNB4.mjs} +2 -2
  7. package/dist/{chunk-B7DD3ODQ.mjs → chunk-7T4TYUO3.mjs} +2 -2
  8. package/dist/{chunk-JGBV3XMQ.mjs → chunk-ANERULZS.mjs} +2 -2
  9. package/dist/{chunk-LRQSY3TP.mjs → chunk-C4ESZLGT.mjs} +142 -69
  10. package/dist/{chunk-5YVJSKFH.mjs → chunk-DKH35J4U.mjs} +2 -2
  11. package/dist/{chunk-MXP2RX2V.mjs → chunk-ET4MTPIY.mjs} +1 -1
  12. package/dist/{chunk-QHAMVWDG.mjs → chunk-G7YCLJ53.mjs} +2 -2
  13. package/dist/{chunk-XAS6KBIG.mjs → chunk-JJSA772M.mjs} +1 -1
  14. package/dist/{chunk-MNMSHB2J.mjs → chunk-K3XP5ETH.mjs} +2 -2
  15. package/dist/{chunk-I4P7RXAE.mjs → chunk-KJQ3BVTB.mjs} +1 -1
  16. package/dist/{chunk-MUV4EGDW.mjs → chunk-KPGARKFC.mjs} +1 -1
  17. package/dist/{chunk-R6U246E4.mjs → chunk-L7IZ3YHI.mjs} +1 -1
  18. package/dist/{chunk-P7CEBZM6.mjs → chunk-LSRGA5BI.mjs} +19 -5
  19. package/dist/{chunk-GMF7INNS.mjs → chunk-LWYMZHN7.mjs} +2 -2
  20. package/dist/{chunk-XMP24PWA.mjs → chunk-NXZ2F4JA.mjs} +1 -1
  21. package/dist/{chunk-4X4MGYHE.mjs → chunk-PUGQVHQL.mjs} +1 -1
  22. package/dist/{chunk-LPVXO3TD.mjs → chunk-RFWP2325.mjs} +2 -2
  23. package/dist/{chunk-3KLJ4XRE.mjs → chunk-RPOIXMHW.mjs} +1 -1
  24. package/dist/{chunk-IFSQWDRN.mjs → chunk-SH5L5VG6.mjs} +1 -1
  25. package/dist/{chunk-FW4U543X.mjs → chunk-STDCXTUU.mjs} +2 -2
  26. package/dist/{chunk-2KNQZG5S.mjs → chunk-SU6TPDEU.mjs} +1 -1
  27. package/dist/{chunk-K4WDPVFY.mjs → chunk-WZFBLRNP.mjs} +1 -1
  28. package/dist/{chunk-UYRHYJPX.mjs → chunk-XNX3XJ2F.mjs} +1 -1
  29. package/dist/components/ui/about-you-form.js +17 -4
  30. package/dist/components/ui/about-you-form.mjs +3 -3
  31. package/dist/components/ui/add-column-modal.js +17 -4
  32. package/dist/components/ui/add-column-modal.mjs +2 -2
  33. package/dist/components/ui/ai-conversations.js +458 -345
  34. package/dist/components/ui/ai-conversations.mjs +2 -1
  35. package/dist/components/ui/appointment-action-dialogs.js +17 -4
  36. package/dist/components/ui/appointment-action-dialogs.mjs +2 -2
  37. package/dist/components/ui/appointment-availability-settings.js +17 -4
  38. package/dist/components/ui/appointment-availability-settings.mjs +4 -4
  39. package/dist/components/ui/appointment-book-dialog.js +17 -4
  40. package/dist/components/ui/appointment-book-dialog.mjs +3 -3
  41. package/dist/components/ui/appointment-detail-sheet.js +17 -4
  42. package/dist/components/ui/appointment-detail-sheet.mjs +3 -3
  43. package/dist/components/ui/appointment-upcoming-card.js +17 -4
  44. package/dist/components/ui/appointment-upcoming-card.mjs +2 -2
  45. package/dist/components/ui/backoffice-signup-steps.js +17 -4
  46. package/dist/components/ui/backoffice-signup-steps.mjs +4 -4
  47. package/dist/components/ui/bank-statement-generate-dialog.js +17 -4
  48. package/dist/components/ui/bank-statement-generate-dialog.mjs +5 -5
  49. package/dist/components/ui/chat-widget.js +17 -4
  50. package/dist/components/ui/chat-widget.mjs +3 -3
  51. package/dist/components/ui/contact-alert-dialog/index.js +17 -4
  52. package/dist/components/ui/contact-alert-dialog/index.mjs +3 -3
  53. package/dist/components/ui/create-contact-modal.js +17 -4
  54. package/dist/components/ui/create-contact-modal.mjs +3 -3
  55. package/dist/components/ui/date-picker.mjs +2 -2
  56. package/dist/components/ui/expense-detail-item.mjs +3 -3
  57. package/dist/components/ui/expense-work-details.mjs +3 -3
  58. package/dist/components/ui/field.js +17 -4
  59. package/dist/components/ui/field.mjs +2 -2
  60. package/dist/components/ui/form-primitives.js +17 -4
  61. package/dist/components/ui/form-primitives.mjs +2 -2
  62. package/dist/components/ui/frontend-signup-steps.js +17 -4
  63. package/dist/components/ui/frontend-signup-steps.mjs +6 -6
  64. package/dist/components/ui/income-work-details.mjs +3 -3
  65. package/dist/components/ui/label.js +20 -4
  66. package/dist/components/ui/label.mjs +1 -1
  67. package/dist/components/ui/opportunity-edit-modals.js +17 -4
  68. package/dist/components/ui/opportunity-edit-modals.mjs +5 -5
  69. package/dist/components/ui/opportunity-summary-tab.js +17 -4
  70. package/dist/components/ui/opportunity-summary-tab.mjs +6 -6
  71. package/dist/components/ui/pipeline-dialogs.mjs +3 -3
  72. package/dist/components/ui/property-report-dialog.js +17 -4
  73. package/dist/components/ui/property-report-dialog.mjs +4 -4
  74. package/dist/components/ui/review-alerts-dialog.js +17 -4
  75. package/dist/components/ui/review-alerts-dialog.mjs +2 -2
  76. package/dist/components/ui/savings-goal-modal.mjs +2 -2
  77. package/dist/components/ui/share-details-dialog.js +17 -4
  78. package/dist/components/ui/share-details-dialog.mjs +2 -2
  79. package/dist/components/ui/signup-form-primitives.js +17 -4
  80. package/dist/components/ui/signup-form-primitives.mjs +3 -3
  81. package/dist/components/ui/two-fa-setup-form.js +17 -4
  82. package/dist/components/ui/two-fa-setup-form.mjs +3 -3
  83. package/dist/index.js +1360 -1281
  84. package/dist/index.mjs +35 -35
  85. package/dist/styles.css +1 -1
  86. package/package.json +9 -1
  87. package/src/components/ui/ai-conversations.tsx +161 -83
  88. package/src/components/ui/label.tsx +18 -3
  89. package/src/styles/styles-css.ts +1 -1
  90. package/dist/{chunk-TRM3KIHT.mjs → chunk-UMF6LLQK.mjs} +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wealthx/shadcn",
3
- "version": "1.5.16",
3
+ "version": "1.5.18",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -18,6 +18,11 @@
18
18
  "@fontsource-variable/figtree": "^5.2.10",
19
19
  "@react-awesome-query-builder/ui": "6.7.0-alpha.0",
20
20
  "@tanstack/react-table": "8.21.3",
21
+ "@tiptap/extension-link": "^3.23.6",
22
+ "@tiptap/extension-underline": "^3.23.6",
23
+ "@tiptap/pm": "^3.23.6",
24
+ "@tiptap/react": "^3.23.6",
25
+ "@tiptap/starter-kit": "^3.23.6",
21
26
  "chart.js": "^4.5.1",
22
27
  "chartjs-plugin-datalabels": "^2.2.0",
23
28
  "class-variance-authority": "^0.7.1",
@@ -27,6 +32,9 @@
27
32
  "lucide-react": "^0.577.0",
28
33
  "react-chartjs-2": "^5.3.1",
29
34
  "react-day-picker": "^9.14.0",
35
+ "react-markdown": "^10.1.0",
36
+ "rehype-raw": "^7.0.0",
37
+ "rehype-sanitize": "^6.0.0",
30
38
  "sonner": "^2.0.7",
31
39
  "tailwind-merge": "^3.5.0",
32
40
  "tw-animate-css": "^1.4.0",
@@ -1,4 +1,11 @@
1
1
  import React, { useState } from "react";
2
+ import { useEditor, EditorContent } from "@tiptap/react";
3
+ import StarterKit from "@tiptap/starter-kit";
4
+ import TiptapUnderline from "@tiptap/extension-underline";
5
+ import TiptapLink from "@tiptap/extension-link";
6
+ import ReactMarkdown from "react-markdown";
7
+ import rehypeRaw from "rehype-raw";
8
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
2
9
  import {
3
10
  Archive,
4
11
  ArrowLeft,
@@ -51,6 +58,7 @@ import {
51
58
  DialogHeader,
52
59
  DialogTitle,
53
60
  } from "@/components/ui/dialog";
61
+ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
54
62
  import { Separator } from "@/components/ui/separator";
55
63
  import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
56
64
  import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
@@ -639,13 +647,15 @@ export function ChatBubble({ message, className }: ChatBubbleProps) {
639
647
  {/* Bubble */}
640
648
  <div
641
649
  className={cn(
642
- "px-3 py-2 text-sm leading-relaxed",
643
- isBot && "border border-border bg-muted/60 text-foreground",
644
- isVisitor && "border border-border bg-background text-foreground",
645
- isAdvisor && "bg-primary text-primary-foreground",
650
+ "px-3 py-2 text-sm leading-relaxed break-words [&_a]:underline [&_p]:m-0",
651
+ isBot && "border border-border bg-muted/60 text-foreground [&_a]:text-primary",
652
+ isVisitor && "border border-border bg-background text-foreground [&_a]:text-primary",
653
+ isAdvisor && "bg-primary text-primary-foreground [&_a]:text-primary-foreground",
646
654
  )}
647
655
  >
648
- {content}
656
+ <ReactMarkdown rehypePlugins={[rehypeRaw, [rehypeSanitize, defaultSchema]]}>
657
+ {content}
658
+ </ReactMarkdown>
649
659
  </div>
650
660
 
651
661
  {timestamp && (
@@ -692,6 +702,107 @@ export interface ChatComposerProps {
692
702
  className?: string;
693
703
  }
694
704
 
705
+ function ComposerToolbarButton({
706
+ label,
707
+ icon: Icon,
708
+ pressed,
709
+ onToggle,
710
+ }: {
711
+ label: string;
712
+ icon: React.ElementType;
713
+ pressed?: boolean;
714
+ onToggle?: () => void;
715
+ }) {
716
+ return (
717
+ <button
718
+ type="button"
719
+ aria-label={label}
720
+ aria-pressed={pressed}
721
+ onClick={onToggle}
722
+ className={cn(
723
+ "flex size-7 items-center justify-center transition-colors",
724
+ pressed
725
+ ? "bg-foreground text-background"
726
+ : "text-muted-foreground hover:bg-muted hover:text-foreground",
727
+ )}
728
+ >
729
+ <Icon className="size-3.5" />
730
+ </button>
731
+ );
732
+ }
733
+
734
+ function ComposerLinkPopover({ editor }: { editor: ReturnType<typeof useEditor> }) {
735
+ const [open, setOpen] = React.useState(false);
736
+ const [url, setUrl] = React.useState("");
737
+
738
+ const handleApply = () => {
739
+ if (url.trim()) {
740
+ editor?.chain().focus().setLink({ href: url.trim() }).run();
741
+ }
742
+ setOpen(false);
743
+ setUrl("");
744
+ };
745
+
746
+ return (
747
+ <Popover
748
+ open={open}
749
+ onOpenChange={(newOpen) => {
750
+ if (newOpen && editor?.isActive("link")) {
751
+ editor.chain().focus().unsetLink().run();
752
+ return;
753
+ }
754
+ if (newOpen) setUrl("");
755
+ setOpen(newOpen);
756
+ }}
757
+ >
758
+ <PopoverTrigger
759
+ aria-label="Insert link"
760
+ className={cn(
761
+ "flex size-7 items-center justify-center transition-colors",
762
+ editor?.isActive("link")
763
+ ? "bg-foreground text-background"
764
+ : "text-muted-foreground hover:bg-muted hover:text-foreground",
765
+ )}
766
+ >
767
+ <Link2 className="size-3.5" />
768
+ </PopoverTrigger>
769
+ <PopoverContent className="w-72 p-2" align="start">
770
+ <div className="flex items-center gap-1.5">
771
+ <input
772
+ type="url"
773
+ value={url}
774
+ onChange={(e) => setUrl(e.target.value)}
775
+ onKeyDown={(e) => e.key === "Enter" && handleApply()}
776
+ placeholder="https://"
777
+ className="min-w-0 flex-1 border border-border bg-transparent px-2 py-1.5 text-sm text-foreground outline-none placeholder:text-muted-foreground focus:border-primary"
778
+ autoFocus
779
+ />
780
+ <Button size="sm" className="h-8 px-3" onClick={handleApply}>
781
+ Apply
782
+ </Button>
783
+ </div>
784
+ </PopoverContent>
785
+ </Popover>
786
+ );
787
+ }
788
+
789
+ function ComposerEmailFieldRow({
790
+ label,
791
+ children,
792
+ }: {
793
+ label: string;
794
+ children: React.ReactNode;
795
+ }) {
796
+ return (
797
+ <div className="flex items-center gap-3 border-b border-border px-4 py-2.5">
798
+ <span className="w-14 shrink-0 text-sm text-muted-foreground">
799
+ {label}
800
+ </span>
801
+ {children}
802
+ </div>
803
+ );
804
+ }
805
+
695
806
  export function ChatComposer({
696
807
  mode,
697
808
  channel: channelProp = "chat",
@@ -715,61 +826,30 @@ export function ChatComposer({
715
826
  const [emailCc, setEmailCc] = React.useState("");
716
827
  const [showCc, setShowCc] = React.useState(false);
717
828
  const [emailSubject, setEmailSubject] = React.useState("");
718
- const [bold, setBold] = React.useState(false);
719
- const [italic, setItalic] = React.useState(false);
720
- const [underline, setUnderline] = React.useState(false);
829
+
830
+ const [, forceUpdate] = React.useReducer((x: number) => x + 1, 0);
831
+
832
+ const editor = useEditor({
833
+ extensions: [
834
+ StarterKit,
835
+ TiptapUnderline,
836
+ TiptapLink.configure({ openOnClick: false }),
837
+ ],
838
+ content: "",
839
+ onTransaction: () => forceUpdate(),
840
+ editorProps: {
841
+ attributes: {
842
+ class:
843
+ "min-h-[150px] px-4 py-3 text-base text-foreground outline-none prose prose-sm max-w-none [&_p]:m-0 [&_a]:text-primary [&_a]:underline [&_a]:cursor-pointer",
844
+ },
845
+ },
846
+ });
721
847
 
722
848
  const handleChannelChange = (c: AiConvChannel) => {
723
849
  setChannel(c);
724
850
  onChannelChange?.(c);
725
851
  };
726
852
 
727
- function ToolbarButton({
728
- label,
729
- icon: Icon,
730
- pressed,
731
- onToggle,
732
- }: {
733
- label: string;
734
- icon: React.ElementType;
735
- pressed?: boolean;
736
- onToggle?: () => void;
737
- }) {
738
- return (
739
- <button
740
- type="button"
741
- aria-label={label}
742
- aria-pressed={pressed}
743
- onClick={onToggle}
744
- className={cn(
745
- "flex size-7 items-center justify-center transition-colors",
746
- pressed
747
- ? "bg-foreground text-background"
748
- : "text-muted-foreground hover:bg-muted hover:text-foreground",
749
- )}
750
- >
751
- <Icon className="size-3.5" />
752
- </button>
753
- );
754
- }
755
-
756
- function EmailFieldRow({
757
- label,
758
- children,
759
- }: {
760
- label: string;
761
- children: React.ReactNode;
762
- }) {
763
- return (
764
- <div className="flex items-center gap-3 border-b border-border px-4 py-2.5">
765
- <span className="w-14 shrink-0 text-sm text-muted-foreground">
766
- {label}
767
- </span>
768
- {children}
769
- </div>
770
- );
771
- }
772
-
773
853
  return (
774
854
  <div
775
855
  className={cn(
@@ -822,7 +902,7 @@ export function ChatComposer({
822
902
  )}
823
903
  aria-hidden={channel !== "email"}
824
904
  >
825
- <EmailFieldRow label="To">
905
+ <ComposerEmailFieldRow label="To">
826
906
  <input
827
907
  type="email"
828
908
  value={emailTo}
@@ -838,9 +918,9 @@ export function ChatComposer({
838
918
  CC
839
919
  <ChevronDown className="size-3.5" />
840
920
  </button>
841
- </EmailFieldRow>
921
+ </ComposerEmailFieldRow>
842
922
  {showCc && (
843
- <EmailFieldRow label="CC">
923
+ <ComposerEmailFieldRow label="CC">
844
924
  <input
845
925
  type="email"
846
926
  value={emailCc}
@@ -848,9 +928,9 @@ export function ChatComposer({
848
928
  placeholder="CC email"
849
929
  className="min-w-0 flex-1 bg-transparent text-base text-foreground outline-none placeholder:text-muted-foreground"
850
930
  />
851
- </EmailFieldRow>
931
+ </ComposerEmailFieldRow>
852
932
  )}
853
- <EmailFieldRow label="Subject">
933
+ <ComposerEmailFieldRow label="Subject">
854
934
  <input
855
935
  type="text"
856
936
  value={emailSubject}
@@ -858,49 +938,47 @@ export function ChatComposer({
858
938
  placeholder="Email subject"
859
939
  className="min-w-0 flex-1 bg-transparent text-base text-foreground outline-none placeholder:text-muted-foreground"
860
940
  />
861
- </EmailFieldRow>
862
- <Textarea
863
- value={inputValue}
864
- onChange={(e) => onInputChange?.(e.target.value)}
865
- placeholder="Write your email..."
866
- rows={6}
867
- className="resize-y border-0 px-4 py-3 text-base shadow-none focus-visible:ring-0"
868
- />
941
+ </ComposerEmailFieldRow>
942
+ <EditorContent editor={editor} />
869
943
  <div className="flex items-center justify-between border-t border-border px-3 py-2">
870
944
  <div className="flex items-center gap-0.5">
871
- <ToolbarButton
945
+ <ComposerToolbarButton
872
946
  label="Bold"
873
947
  icon={Bold}
874
- pressed={bold}
875
- onToggle={() => setBold((v) => !v)}
948
+ pressed={editor?.isActive("bold")}
949
+ onToggle={() => editor?.chain().focus().toggleBold().run()}
876
950
  />
877
- <ToolbarButton
951
+ <ComposerToolbarButton
878
952
  label="Italic"
879
953
  icon={Italic}
880
- pressed={italic}
881
- onToggle={() => setItalic((v) => !v)}
954
+ pressed={editor?.isActive("italic")}
955
+ onToggle={() => editor?.chain().focus().toggleItalic().run()}
882
956
  />
883
- <ToolbarButton
957
+ <ComposerToolbarButton
884
958
  label="Underline"
885
959
  icon={Underline}
886
- pressed={underline}
887
- onToggle={() => setUnderline((v) => !v)}
960
+ pressed={editor?.isActive("underline")}
961
+ onToggle={() =>
962
+ editor?.chain().focus().toggleUnderline().run()
963
+ }
888
964
  />
889
965
  <Separator orientation="vertical" className="mx-1.5 h-4" />
890
- <ToolbarButton label="Link" icon={Link2} />
891
- <ToolbarButton label="Attach file" icon={Paperclip} />
966
+ <ComposerLinkPopover editor={editor} />
967
+ <ComposerToolbarButton label="Attach file" icon={Paperclip} />
892
968
  </div>
893
969
  <Button
894
970
  size="sm"
895
- onClick={() =>
971
+ onClick={() => {
972
+ const html = editor?.getHTML() ?? "";
896
973
  onSendEmail?.({
897
- content: inputValue,
974
+ content: html,
898
975
  to: emailTo,
899
976
  cc: emailCc,
900
977
  subject: emailSubject,
901
- })
902
- }
903
- disabled={!inputValue.trim() || !emailTo.trim()}
978
+ });
979
+ editor?.commands.clearContent();
980
+ }}
981
+ disabled={!editor || editor.isEmpty || !emailTo.trim()}
904
982
  >
905
983
  <Send className="mr-1.5 size-3.5" />
906
984
  Send Email
@@ -2,9 +2,17 @@ import { type ReactElement } from "react";
2
2
  import * as React from "react";
3
3
  import { cn } from "@/lib/utils";
4
4
 
5
- export type LabelProps = React.ComponentProps<"label">;
5
+ export type LabelProps = React.ComponentProps<"label"> & {
6
+ /** When true, renders a red asterisk after the label text to indicate a required field */
7
+ required?: boolean;
8
+ };
6
9
 
7
- function Label({ className, ...props }: LabelProps): ReactElement {
10
+ function Label({
11
+ className,
12
+ required,
13
+ children,
14
+ ...props
15
+ }: LabelProps): ReactElement {
8
16
  return (
9
17
  // eslint-disable-next-line jsx-a11y/label-has-associated-control -- htmlFor is passed by the consumer
10
18
  <label
@@ -14,7 +22,14 @@ function Label({ className, ...props }: LabelProps): ReactElement {
14
22
  )}
15
23
  data-slot="label"
16
24
  {...props}
17
- />
25
+ >
26
+ {children}
27
+ {required && (
28
+ <span aria-hidden="true" className="text-destructive">
29
+ *
30
+ </span>
31
+ )}
32
+ </label>
18
33
  );
19
34
  }
20
35