@collabchron/notiq 0.2.0

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 (188) hide show
  1. package/README.md +71 -0
  2. package/components.json +21 -0
  3. package/eslint.config.mjs +16 -0
  4. package/next.config.ts +12 -0
  5. package/package.json +108 -0
  6. package/postcss.config.mjs +5 -0
  7. package/public/file.svg +1 -0
  8. package/public/globe.svg +1 -0
  9. package/public/images/icons/plus.svg +10 -0
  10. package/public/next.svg +1 -0
  11. package/public/vercel.svg +1 -0
  12. package/public/window.svg +1 -0
  13. package/src/app/actions.ts +2 -0
  14. package/src/app/api/ai/route.ts +175 -0
  15. package/src/app/api/edgestore/[...edgestore]/route.ts +28 -0
  16. package/src/app/favicon.ico +0 -0
  17. package/src/app/globals.css +205 -0
  18. package/src/app/layout.tsx +38 -0
  19. package/src/app/page.tsx +12 -0
  20. package/src/components/editor/Core.tsx +220 -0
  21. package/src/components/editor/hooks/instructions-messages.ts +300 -0
  22. package/src/components/editor/hooks/use-mobile.ts +19 -0
  23. package/src/components/editor/hooks/useReport.ts +67 -0
  24. package/src/components/editor/hooks/useResizeObservert.ts +22 -0
  25. package/src/components/editor/index.tsx +39 -0
  26. package/src/components/editor/lexical-on-change.tsx +28 -0
  27. package/src/components/editor/nodes/CollapsibleNode/CollapsibleContainerNode.ts +92 -0
  28. package/src/components/editor/nodes/CollapsibleNode/CollapsibleContentNode.ts +65 -0
  29. package/src/components/editor/nodes/CollapsibleNode/CollapsibleTitleNode.ts +105 -0
  30. package/src/components/editor/nodes/EquationNode/EquationComponent.tsx +143 -0
  31. package/src/components/editor/nodes/EquationNode/EquationNode.tsx +170 -0
  32. package/src/components/editor/nodes/ExcalidrawNode/ExcalidrawComponent.tsx +228 -0
  33. package/src/components/editor/nodes/ExcalidrawNode/ExcalidrawImage.tsx +137 -0
  34. package/src/components/editor/nodes/ExcalidrawNode/ImageResizer.tsx +317 -0
  35. package/src/components/editor/nodes/ExcalidrawNode/index.tsx +204 -0
  36. package/src/components/editor/nodes/FigmaNode/FigmaNode.tsx +134 -0
  37. package/src/components/editor/nodes/Hint/HintComponet.tsx +221 -0
  38. package/src/components/editor/nodes/Hint/index.tsx +190 -0
  39. package/src/components/editor/nodes/ImageNode/index.tsx +328 -0
  40. package/src/components/editor/nodes/InlineImageNode/InlineImageComponent.tsx +383 -0
  41. package/src/components/editor/nodes/InlineImageNode/InlineImageNode.css +94 -0
  42. package/src/components/editor/nodes/InlineImageNode/InlineImageNode.tsx +309 -0
  43. package/src/components/editor/nodes/LayoutNode/LayoutContainerNode.ts +146 -0
  44. package/src/components/editor/nodes/LayoutNode/LayoutItemNode.ts +79 -0
  45. package/src/components/editor/nodes/PollNode/index.tsx +204 -0
  46. package/src/components/editor/nodes/Stepper/index.tsx +260 -0
  47. package/src/components/editor/nodes/TweetNode/index.tsx +214 -0
  48. package/src/components/editor/nodes/index.ts +81 -0
  49. package/src/components/editor/plugins/AutoEmbedPlugin/index.tsx +350 -0
  50. package/src/components/editor/plugins/AutoLinkPlugin/index.tsx +56 -0
  51. package/src/components/editor/plugins/CodeActionMenuPlugin/components/CopyButton.tsx +70 -0
  52. package/src/components/editor/plugins/CodeActionMenuPlugin/components/PrettierButton.tsx +192 -0
  53. package/src/components/editor/plugins/CodeActionMenuPlugin/index.tsx +217 -0
  54. package/src/components/editor/plugins/CodeActionMenuPlugin/utils.ts +26 -0
  55. package/src/components/editor/plugins/CodeHighlightPlugin/index.ts +21 -0
  56. package/src/components/editor/plugins/CollapsiblePlugin/Collapsible.css +76 -0
  57. package/src/components/editor/plugins/CollapsiblePlugin/index.ts +228 -0
  58. package/src/components/editor/plugins/DragDropPastePlugin/index.tsx +44 -0
  59. package/src/components/editor/plugins/DraggableBlockPlugin/index.tsx +52 -0
  60. package/src/components/editor/plugins/EquationsPlugin/index.tsx +85 -0
  61. package/src/components/editor/plugins/ExcalidrawPlugin/index.tsx +98 -0
  62. package/src/components/editor/plugins/FigmaPlugin/index.tsx +42 -0
  63. package/src/components/editor/plugins/FloatingLinkEditorPlugin/index.tsx +445 -0
  64. package/src/components/editor/plugins/FloatingTextFormatToolbarPlugin/index.tsx +275 -0
  65. package/src/components/editor/plugins/ImagesPlugin/index.tsx +222 -0
  66. package/src/components/editor/plugins/InlineImagePlugin/index.tsx +351 -0
  67. package/src/components/editor/plugins/LayoutPlugin/index.tsx +238 -0
  68. package/src/components/editor/plugins/LinkPlugin/index.tsx +36 -0
  69. package/src/components/editor/plugins/LinkWithMetaData/index.tsx +271 -0
  70. package/src/components/editor/plugins/MarkdownShortcutPlugin/index.tsx +11 -0
  71. package/src/components/editor/plugins/MarkdownTransformers/index.tsx +304 -0
  72. package/src/components/editor/plugins/PollPlugin/index.tsx +49 -0
  73. package/src/components/editor/plugins/ShortcutsPlugin/index.tsx +180 -0
  74. package/src/components/editor/plugins/ShortcutsPlugin/shortcuts.ts +253 -0
  75. package/src/components/editor/plugins/SlashCommand/index.tsx +621 -0
  76. package/src/components/editor/plugins/SpeechToTextPlugin/index.ts +127 -0
  77. package/src/components/editor/plugins/TabFocusPlugin/index.ts +58 -0
  78. package/src/components/editor/plugins/TableCellActionMenuPlugin/index.tsx +759 -0
  79. package/src/components/editor/plugins/TableCellResizer/index.tsx +438 -0
  80. package/src/components/editor/plugins/TableHoverActionsPlugin/index.tsx +314 -0
  81. package/src/components/editor/plugins/TablePlugin/index.tsx +99 -0
  82. package/src/components/editor/plugins/ToolbarPlugin/index.tsx +522 -0
  83. package/src/components/editor/plugins/TwitterPlugin/index.ts +35 -0
  84. package/src/components/editor/plugins/YouTubeNode/index.tsx +179 -0
  85. package/src/components/editor/plugins/YouTubePlugin/index.ts +41 -0
  86. package/src/components/editor/themes/editor-theme.ts +113 -0
  87. package/src/components/editor/themes/theme.css +377 -0
  88. package/src/components/editor/utils/ai.ts +291 -0
  89. package/src/components/editor/utils/canUseDOM.ts +12 -0
  90. package/src/components/editor/utils/editorFormatting.ts +282 -0
  91. package/src/components/editor/utils/environment.ts +50 -0
  92. package/src/components/editor/utils/extract-data.ts +166 -0
  93. package/src/components/editor/utils/getAllLexicalChildren.ts +13 -0
  94. package/src/components/editor/utils/getDOMRangeRect.ts +27 -0
  95. package/src/components/editor/utils/getSelectedNode.ts +27 -0
  96. package/src/components/editor/utils/gif.ts +29 -0
  97. package/src/components/editor/utils/invariant.ts +15 -0
  98. package/src/components/editor/utils/setFloatingElemPosition.ts +51 -0
  99. package/src/components/editor/utils/setFloatingElemPositionForLinkEditor.ts +40 -0
  100. package/src/components/editor/utils/setNodePlaceholderFromSelection/getNodePlaceholder.ts +51 -0
  101. package/src/components/editor/utils/setNodePlaceholderFromSelection/setNodePlaceholderFromSelection.ts +15 -0
  102. package/src/components/editor/utils/setNodePlaceholderFromSelection/setPlaceholderOnSelection.ts +114 -0
  103. package/src/components/editor/utils/setNodePlaceholderFromSelection/styles.css +6 -0
  104. package/src/components/editor/utils/url.ts +109 -0
  105. package/src/components/editor/utils/useLayoutEffect.ts +13 -0
  106. package/src/components/providers/QueryProvider.tsx +15 -0
  107. package/src/components/providers/SharedHistoryContext.tsx +28 -0
  108. package/src/components/providers/ToolbarContext.tsx +123 -0
  109. package/src/components/providers/theme-provider.tsx +11 -0
  110. package/src/components/theme/ModeToggle.tsx +40 -0
  111. package/src/components/ui/FileInput.tsx +40 -0
  112. package/src/components/ui/Input.css +32 -0
  113. package/src/components/ui/Select.css +42 -0
  114. package/src/components/ui/Select.tsx +36 -0
  115. package/src/components/ui/TextInput.tsx +48 -0
  116. package/src/components/ui/ai/ai-button.tsx +574 -0
  117. package/src/components/ui/ai/border.tsx +99 -0
  118. package/src/components/ui/ai/placeholder-input-vanish.tsx +282 -0
  119. package/src/components/ui/button.tsx +89 -0
  120. package/src/components/ui/card.tsx +76 -0
  121. package/src/components/ui/checkbox.tsx +30 -0
  122. package/src/components/ui/command.tsx +153 -0
  123. package/src/components/ui/dialog/Dialog.css +25 -0
  124. package/src/components/ui/dialog/Dialog.tsx +34 -0
  125. package/src/components/ui/dialog.tsx +122 -0
  126. package/src/components/ui/drop-downs/background-color.tsx +183 -0
  127. package/src/components/ui/drop-downs/block-format.tsx +159 -0
  128. package/src/components/ui/drop-downs/code.tsx +42 -0
  129. package/src/components/ui/drop-downs/color.tsx +177 -0
  130. package/src/components/ui/drop-downs/font-size.tsx +138 -0
  131. package/src/components/ui/drop-downs/font.tsx +155 -0
  132. package/src/components/ui/drop-downs/index.tsx +122 -0
  133. package/src/components/ui/drop-downs/insert-node.tsx +213 -0
  134. package/src/components/ui/drop-downs/text-align.tsx +123 -0
  135. package/src/components/ui/drop-downs/text-format.tsx +104 -0
  136. package/src/components/ui/dropdown-menu.tsx +201 -0
  137. package/src/components/ui/equation/EquationEditor.css +38 -0
  138. package/src/components/ui/equation/EquationEditor.tsx +56 -0
  139. package/src/components/ui/equation/KatexEquationAlterer.css +41 -0
  140. package/src/components/ui/equation/KatexEquationAlterer.tsx +83 -0
  141. package/src/components/ui/equation/KatexRenderer.tsx +66 -0
  142. package/src/components/ui/excalidraw/ExcalidrawModal.css +64 -0
  143. package/src/components/ui/excalidraw/ExcalidrawModal.tsx +234 -0
  144. package/src/components/ui/excalidraw/Modal.css +62 -0
  145. package/src/components/ui/excalidraw/Modal.tsx +110 -0
  146. package/src/components/ui/hover-card.tsx +29 -0
  147. package/src/components/ui/image/error-image.tsx +17 -0
  148. package/src/components/ui/image/file-upload.tsx +240 -0
  149. package/src/components/ui/image/image-resizer.tsx +297 -0
  150. package/src/components/ui/image/image-toolbar.tsx +264 -0
  151. package/src/components/ui/image/index.tsx +408 -0
  152. package/src/components/ui/image/lazy-image.tsx +68 -0
  153. package/src/components/ui/image/lazy-video.tsx +71 -0
  154. package/src/components/ui/input.tsx +22 -0
  155. package/src/components/ui/models/custom-dialog.tsx +320 -0
  156. package/src/components/ui/models/insert-gif.tsx +90 -0
  157. package/src/components/ui/models/insert-image.tsx +52 -0
  158. package/src/components/ui/models/insert-poll.tsx +29 -0
  159. package/src/components/ui/models/insert-table.tsx +62 -0
  160. package/src/components/ui/models/use-model.tsx +91 -0
  161. package/src/components/ui/poll/poll-component.tsx +304 -0
  162. package/src/components/ui/popover.tsx +33 -0
  163. package/src/components/ui/progress.tsx +28 -0
  164. package/src/components/ui/scroll-area.tsx +48 -0
  165. package/src/components/ui/separator.tsx +31 -0
  166. package/src/components/ui/skeleton.tsx +15 -0
  167. package/src/components/ui/sonner.tsx +31 -0
  168. package/src/components/ui/stepper/step.tsx +179 -0
  169. package/src/components/ui/stepper/stepper.tsx +89 -0
  170. package/src/components/ui/textarea.tsx +22 -0
  171. package/src/components/ui/toggle.tsx +71 -0
  172. package/src/components/ui/tooltip.tsx +32 -0
  173. package/src/components/ui/write/text-format-floting-toolbar.tsx +346 -0
  174. package/src/lib/edgestore.ts +9 -0
  175. package/src/lib/pinecone-client.ts +0 -0
  176. package/src/lib/utils.ts +6 -0
  177. package/src/utils/docSerialization.ts +77 -0
  178. package/src/utils/emoji-list.ts +16615 -0
  179. package/src/utils/getDOMRangeRect.ts +27 -0
  180. package/src/utils/getSelectedNode.ts +27 -0
  181. package/src/utils/getThemeSelector.ts +25 -0
  182. package/src/utils/isMobileWidth.ts +7 -0
  183. package/src/utils/joinClasses.ts +13 -0
  184. package/src/utils/setFloatingElemPosition.ts +74 -0
  185. package/src/utils/setFloatingElemPositionForLinkEditor.ts +46 -0
  186. package/src/utils/swipe.ts +127 -0
  187. package/src/utils/url.ts +38 -0
  188. package/tsconfig.json +27 -0
@@ -0,0 +1,62 @@
1
+ import { Button } from "@/components/ui/button";
2
+ import { Input } from "@/components/ui/input";
3
+ import { INSERT_TABLE_COMMAND } from "@lexical/table";
4
+ import { LexicalEditor } from "lexical";
5
+ import { JSX, useEffect, useState } from "react";
6
+
7
+ export function InsertTable({
8
+ activeEditor,
9
+ onClose,
10
+ }: {
11
+ activeEditor: LexicalEditor;
12
+ onClose: () => void;
13
+ }): JSX.Element {
14
+ const [rows, setRows] = useState("5");
15
+ const [columns, setColumns] = useState("5");
16
+ const [isDisabled, setIsDisabled] = useState(true);
17
+
18
+ useEffect(() => {
19
+ const row = Number(rows);
20
+ const column = Number(columns);
21
+ if (row && row > 0 && row <= 500 && column && column > 0 && column <= 50) {
22
+ setIsDisabled(false);
23
+ } else {
24
+ setIsDisabled(true);
25
+ }
26
+ }, [rows, columns]);
27
+
28
+ const onClick = () => {
29
+ activeEditor.dispatchCommand(INSERT_TABLE_COMMAND, {
30
+ columns,
31
+ rows,
32
+ });
33
+
34
+ onClose();
35
+ };
36
+
37
+ return (
38
+ <div className="flex flex-col gap-y-2 ">
39
+ <Input
40
+ placeholder={"# of rows (1-500)"}
41
+ onChange={(e) => {
42
+ setRows(e.target.value);
43
+ }}
44
+ value={rows}
45
+ data-test-id="table-modal-rows"
46
+ type="number"
47
+ />
48
+ <Input
49
+ placeholder={"# of columns (1-50)"}
50
+ onChange={(e) => {
51
+ setColumns(e.target.value);
52
+ }}
53
+ value={columns}
54
+ data-test-id="table-modal-columns"
55
+ type="number"
56
+ />
57
+ <Button className="w-full" disabled={isDisabled} onClick={onClick}>
58
+ Confirm
59
+ </Button>
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,91 @@
1
+ "use client";
2
+
3
+ import { useCallback, useMemo, useState,JSX } from "react";
4
+ import {
5
+ Dialog,
6
+ DialogDescription,
7
+ DialogContent,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DialogClose,
11
+ } from "./custom-dialog";
12
+
13
+ export default function useModal(): [
14
+ JSX.Element | null,
15
+ (
16
+ title?: string | null,
17
+ description?: string | null,
18
+ getContent?: (onClose: () => void) => JSX.Element,
19
+ isDilog?:boolean
20
+
21
+ ) => void,
22
+ boolean
23
+ ] {
24
+ const [isOpen, setIsOpen] = useState(false);
25
+ const [modalContent, setModalContent] = useState<{
26
+ title?: string | null;
27
+ description?: string | null;
28
+ content?: JSX.Element;
29
+ isDilog?:boolean
30
+ } | null>(null);
31
+
32
+ const onClose = useCallback(() => {
33
+ setIsOpen(false);
34
+ setModalContent(null);
35
+ }, []);
36
+
37
+ const showModal = useCallback(
38
+ (
39
+ title?: string | null,
40
+ description?: string | null,
41
+ getContent?: (onClose: () => void) => JSX.Element,
42
+ isDilog?:boolean
43
+
44
+ ) => {
45
+ setIsOpen(true);
46
+ setModalContent({
47
+ title,
48
+ description,
49
+ content: getContent ? getContent(onClose) : undefined,
50
+ isDilog:isDilog
51
+ });
52
+ },
53
+ [onClose]
54
+ );
55
+
56
+ const modal = useMemo(() => {
57
+ if (!isOpen || !modalContent) {
58
+ return null;
59
+ }
60
+
61
+
62
+ const { title, description, content,isDilog } = modalContent;
63
+ if(!isDilog){
64
+ return <>{content}</>
65
+ }
66
+ return (
67
+ <Dialog open={isOpen} onOpenChange={setIsOpen}>
68
+ <DialogContent className="w-full max-w-md bg-white p-6 dark:bg-zinc-900">
69
+ {(title || description) && (
70
+ <DialogHeader>
71
+ {title && (
72
+ <DialogTitle className="text-zinc-900 dark:text-white">
73
+ {title}
74
+ </DialogTitle>
75
+ )}
76
+ {description && (
77
+ <DialogDescription className="text-zinc-600 dark:text-zinc-400">
78
+ {description}
79
+ </DialogDescription>
80
+ )}
81
+ </DialogHeader>
82
+ )}
83
+ <div className="mt-2">{content}</div>
84
+ <DialogClose />
85
+ </DialogContent>
86
+ </Dialog>
87
+ );
88
+ }, [isOpen, modalContent]);
89
+
90
+ return [modal, showModal, isOpen];
91
+ }
@@ -0,0 +1,304 @@
1
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
2
+ import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection";
3
+ import { mergeRegister } from "@lexical/utils";
4
+ import {
5
+ $getNodeByKey,
6
+ $getSelection,
7
+ $isNodeSelection,
8
+ BaseSelection,
9
+ CLICK_COMMAND,
10
+ COMMAND_PRIORITY_LOW,
11
+ KEY_BACKSPACE_COMMAND,
12
+ KEY_DELETE_COMMAND,
13
+ NodeKey,
14
+ } from "lexical";
15
+ import * as React from "react";
16
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
17
+ import { Button } from "@/components/ui/button";
18
+ import { Input } from "@/components/ui/input";
19
+ import { Checkbox } from "@/components/ui/checkbox";
20
+ import { X } from "lucide-react";
21
+ import { useLexicalEditable } from "@lexical/react/useLexicalEditable";
22
+ import { useMutation } from "@tanstack/react-query";
23
+
24
+ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
25
+ import { $isPollNode, createPollOption, Option, Options, PollNode } from "../../editor/nodes/PollNode";
26
+
27
+ function getTotalVotes(options: Options): number {
28
+ return options.reduce((totalVotes, next) => {
29
+ return totalVotes + next.votes.length;
30
+ }, 0);
31
+ }
32
+
33
+ function PollOptionComponent({
34
+ option,
35
+ index,
36
+ options,
37
+ totalVotes,
38
+ withPollNode,
39
+ isEditable,
40
+ }: {
41
+ index: number;
42
+ option: Option;
43
+ options: Options;
44
+ totalVotes: number;
45
+ withPollNode: (
46
+ cb: (pollNode: PollNode) => void,
47
+ onSelect?: () => void
48
+ ) => void;
49
+ isEditable: boolean;
50
+ }): React.JSX.Element {
51
+ const userId = "1212312" // change it with user ID
52
+ const checkboxRef = useRef(null);
53
+ const votesArray = option.votes;
54
+ const checkedIndex = votesArray.indexOf(userId!);
55
+ const checked = checkedIndex !== -1;
56
+ const votes = votesArray.length;
57
+ const text = option.text;
58
+ const [editor] = useLexicalComposerContext();
59
+
60
+
61
+ const { mutate, isPending } = useMutation({
62
+ mutationFn: async () => {
63
+
64
+ try {
65
+
66
+ await new Promise<void>((resolve) => {
67
+ withPollNode((node) => {
68
+ node.toggleVote(option, userId!);
69
+ }, resolve);
70
+ });
71
+ return
72
+ // const currentState = editor.getEditorState();
73
+ // await updateContent(id, currentState.toJSON(), true);
74
+ } catch (error) {
75
+ await new Promise<void>((resolve) => {
76
+ withPollNode((node) => {
77
+ node.toggleVote(option, userId!);
78
+ }, resolve);
79
+ });
80
+ throw error;
81
+ }
82
+ },
83
+ });
84
+
85
+ return (
86
+ <div className="flex flex-row items-center justify-center gap-x-2">
87
+ <div>
88
+ <Checkbox
89
+ disabled={!userId}
90
+ ref={checkboxRef}
91
+ onCheckedChange={() => {
92
+ mutate();
93
+ }}
94
+ checked={checked}
95
+ />
96
+ </div>
97
+ <div className="flex relative overflow-hidden">
98
+ <div
99
+ className="h-9 overflow-hidden bg-blue-500/30 dark:bg-blue-300/50 rounded-md absolute top-0 left-0 w-full py-2 transition-all duration-1000"
100
+ style={{ width: `${votes === 0 ? 0 : (votes / totalVotes) * 100}%` }}
101
+ />
102
+ <span className=" absolute text-xs font-bold right-2 top-1">
103
+ {votes > 0 && (votes === 1 ? "1 vote" : `${votes} votes`)}
104
+ </span>
105
+ <Input
106
+ className=" overflow-hidden ring-0 outline-none bg-transparent"
107
+ value={text}
108
+ disabled={!isEditable}
109
+ onChange={(e) => {
110
+ const target = e.target;
111
+ const value = target.value;
112
+ const selectionStart = target.selectionStart;
113
+ const selectionEnd = target.selectionEnd;
114
+ withPollNode(
115
+ (node) => {
116
+ node.setOptionText(option, value);
117
+ },
118
+ () => {
119
+ target.selectionStart = selectionStart;
120
+ target.selectionEnd = selectionEnd;
121
+ }
122
+ );
123
+ }}
124
+ placeholder={`Option ${index + 1}`}
125
+ />
126
+ </div>
127
+ {isEditable && (
128
+ <Button
129
+ size={"sm"}
130
+ variant={"ghost"}
131
+ disabled={options.length < 3 || !isEditable || isPending}
132
+ aria-label="Remove"
133
+ className="mx-1"
134
+ onClick={() => {
135
+ withPollNode((node) => {
136
+ node.deleteOption(option);
137
+ });
138
+ }}
139
+ >
140
+ <X />
141
+ </Button>
142
+ )}
143
+ </div>
144
+ );
145
+ }
146
+
147
+ export default function PollComponent({
148
+ question,
149
+ options,
150
+ nodeKey,
151
+ }: {
152
+ nodeKey: NodeKey;
153
+ options: Options;
154
+ question: string;
155
+ }): React.JSX.Element {
156
+ const [editor] = useLexicalComposerContext();
157
+ const [internalOptions, setInternalOptions] = useState(options);
158
+ const [version, setVersion] = useState(0);
159
+ const totalVotes = useMemo(() => getTotalVotes(options), [options, version]);
160
+ const [isSelected, setSelected, clearSelection] =
161
+ useLexicalNodeSelection(nodeKey);
162
+ const [selection, setSelection] = useState<BaseSelection | null>(null);
163
+ const ref = useRef(null);
164
+ const isEditable = useLexicalEditable();
165
+
166
+ const $onDelete = useCallback(
167
+ (payload: KeyboardEvent) => {
168
+ const deleteSelection = $getSelection();
169
+ if (isSelected && $isNodeSelection(deleteSelection)) {
170
+ const event: KeyboardEvent = payload;
171
+ event.preventDefault();
172
+ deleteSelection.getNodes().forEach((node) => {
173
+ if ($isPollNode(node)) {
174
+ node.remove();
175
+ }
176
+ });
177
+ }
178
+ return false;
179
+ },
180
+ [isSelected]
181
+ );
182
+
183
+ useEffect(() => {
184
+ return editor.registerUpdateListener(({ editorState }) => {
185
+ editorState.read(() => {
186
+ const node = $getNodeByKey(nodeKey);
187
+ if ($isPollNode(node)) {
188
+ setInternalOptions([...node.__options]);
189
+ setVersion((v) => v + 1);
190
+ }
191
+ });
192
+ });
193
+ }, [editor, nodeKey]);
194
+
195
+ useEffect(() => {
196
+ return mergeRegister(
197
+ editor.registerUpdateListener(({ editorState }) => {
198
+ setSelection(editorState.read(() => $getSelection()));
199
+ }),
200
+ editor.registerCommand<MouseEvent>(
201
+ CLICK_COMMAND,
202
+ (payload) => {
203
+ const event = payload;
204
+
205
+ if (event.target === ref.current) {
206
+ if (!event.shiftKey) {
207
+ clearSelection();
208
+ }
209
+ setSelected(!isSelected);
210
+ return true;
211
+ }
212
+
213
+ return false;
214
+ },
215
+ COMMAND_PRIORITY_LOW
216
+ ),
217
+ editor.registerCommand(
218
+ KEY_DELETE_COMMAND,
219
+ $onDelete,
220
+ COMMAND_PRIORITY_LOW
221
+ ),
222
+ editor.registerCommand(
223
+ KEY_BACKSPACE_COMMAND,
224
+ $onDelete,
225
+ COMMAND_PRIORITY_LOW
226
+ )
227
+ );
228
+ }, [clearSelection, editor, isSelected, nodeKey, $onDelete, setSelected]);
229
+ const withPollNode = (
230
+ cb: (node: PollNode) => void,
231
+ onUpdate?: () => void
232
+ ): Promise<void> => {
233
+ return new Promise((resolve) => {
234
+ editor.update(
235
+ () => {
236
+ const node = $getNodeByKey(nodeKey);
237
+ if ($isPollNode(node)) {
238
+ cb(node);
239
+ node.markDirty();
240
+ }
241
+ },
242
+ {
243
+ onUpdate: () => {
244
+ onUpdate?.();
245
+ resolve();
246
+ },
247
+ tag: "history-merge",
248
+ }
249
+ );
250
+ });
251
+ };
252
+
253
+ const addOption = () => {
254
+ withPollNode((node) => {
255
+ node.addOption(createPollOption());
256
+ });
257
+ };
258
+
259
+ const handleQuestionChange = useCallback(
260
+ (event: any) => {
261
+ const newQuestion = event.target.value || "";
262
+ withPollNode((node) => {
263
+ node.setQuestion(newQuestion);
264
+ });
265
+ },
266
+ [withPollNode]
267
+ );
268
+ return (
269
+ <Card>
270
+ <CardHeader className="text-center">
271
+ <CardTitle
272
+ suppressContentEditableWarning
273
+ onChange={handleQuestionChange}
274
+ contentEditable={isEditable}
275
+ >
276
+ {question}
277
+ </CardTitle>
278
+ </CardHeader>
279
+ <CardContent className=" space-y-2">
280
+ {options.map((option, index) => {
281
+ const key = `${option.uid}-${option.votes.length}`;
282
+ return (
283
+ <PollOptionComponent
284
+ key={key}
285
+ withPollNode={withPollNode}
286
+ option={option}
287
+ index={index}
288
+ options={options}
289
+ totalVotes={totalVotes}
290
+ isEditable={isEditable}
291
+ />
292
+ );
293
+ })}
294
+ </CardContent>
295
+ {isEditable && (
296
+ <CardFooter className="flex items-center justify-center">
297
+ <Button disabled={!isEditable} onClick={addOption}>
298
+ Add Option
299
+ </Button>
300
+ </CardFooter>
301
+ )}
302
+ </Card>
303
+ );
304
+ }
@@ -0,0 +1,33 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Popover = PopoverPrimitive.Root
9
+
10
+ const PopoverTrigger = PopoverPrimitive.Trigger
11
+
12
+ const PopoverAnchor = PopoverPrimitive.Anchor
13
+
14
+ const PopoverContent = React.forwardRef<
15
+ React.ElementRef<typeof PopoverPrimitive.Content>,
16
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
17
+ >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
18
+ <PopoverPrimitive.Portal>
19
+ <PopoverPrimitive.Content
20
+ ref={ref}
21
+ align={align}
22
+ sideOffset={sideOffset}
23
+ className={cn(
24
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ </PopoverPrimitive.Portal>
30
+ ))
31
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName
32
+
33
+ export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
@@ -0,0 +1,28 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as ProgressPrimitive from "@radix-ui/react-progress"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Progress = React.forwardRef<
9
+ React.ElementRef<typeof ProgressPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
11
+ >(({ className, value, ...props }, ref) => (
12
+ <ProgressPrimitive.Root
13
+ ref={ref}
14
+ className={cn(
15
+ "relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
16
+ className
17
+ )}
18
+ {...props}
19
+ >
20
+ <ProgressPrimitive.Indicator
21
+ className="h-full w-full flex-1 bg-primary transition-all"
22
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
23
+ />
24
+ </ProgressPrimitive.Root>
25
+ ))
26
+ Progress.displayName = ProgressPrimitive.Root.displayName
27
+
28
+ export { Progress }
@@ -0,0 +1,48 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const ScrollArea = React.forwardRef<
9
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
11
+ >(({ className, children, ...props }, ref) => (
12
+ <ScrollAreaPrimitive.Root
13
+ ref={ref}
14
+ className={cn("relative overflow-hidden", className)}
15
+ {...props}
16
+ >
17
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
18
+ {children}
19
+ </ScrollAreaPrimitive.Viewport>
20
+ <ScrollBar />
21
+ <ScrollAreaPrimitive.Corner />
22
+ </ScrollAreaPrimitive.Root>
23
+ ))
24
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
25
+
26
+ const ScrollBar = React.forwardRef<
27
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
28
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
29
+ >(({ className, orientation = "vertical", ...props }, ref) => (
30
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
31
+ ref={ref}
32
+ orientation={orientation}
33
+ className={cn(
34
+ "flex touch-none select-none transition-colors",
35
+ orientation === "vertical" &&
36
+ "h-full w-2.5 border-l border-l-transparent p-[1px]",
37
+ orientation === "horizontal" &&
38
+ "h-2.5 flex-col border-t border-t-transparent p-[1px]",
39
+ className
40
+ )}
41
+ {...props}
42
+ >
43
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
44
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
45
+ ))
46
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
47
+
48
+ export { ScrollArea, ScrollBar }
@@ -0,0 +1,31 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SeparatorPrimitive from "@radix-ui/react-separator"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Separator = React.forwardRef<
9
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
11
+ >(
12
+ (
13
+ { className, orientation = "horizontal", decorative = true, ...props },
14
+ ref
15
+ ) => (
16
+ <SeparatorPrimitive.Root
17
+ ref={ref}
18
+ decorative={decorative}
19
+ orientation={orientation}
20
+ className={cn(
21
+ "shrink-0 bg-border",
22
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ )
28
+ )
29
+ Separator.displayName = SeparatorPrimitive.Root.displayName
30
+
31
+ export { Separator }
@@ -0,0 +1,15 @@
1
+ import { cn } from "@/lib/utils"
2
+
3
+ function Skeleton({
4
+ className,
5
+ ...props
6
+ }: React.HTMLAttributes<HTMLDivElement>) {
7
+ return (
8
+ <div
9
+ className={cn("animate-pulse rounded-md bg-primary/10", className)}
10
+ {...props}
11
+ />
12
+ )
13
+ }
14
+
15
+ export { Skeleton }
@@ -0,0 +1,31 @@
1
+ "use client"
2
+
3
+ import { useTheme } from "next-themes"
4
+ import { Toaster as Sonner } from "sonner"
5
+
6
+ type ToasterProps = React.ComponentProps<typeof Sonner>
7
+
8
+ const Toaster = ({ ...props }: ToasterProps) => {
9
+ const { theme = "system" } = useTheme()
10
+
11
+ return (
12
+ <Sonner
13
+ theme={theme as ToasterProps["theme"]}
14
+ className="toaster group"
15
+ toastOptions={{
16
+ classNames: {
17
+ toast:
18
+ "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
19
+ description: "group-[.toast]:text-muted-foreground",
20
+ actionButton:
21
+ "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
22
+ cancelButton:
23
+ "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
24
+ },
25
+ }}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ export { Toaster }