@valbuild/ui 0.26.0 → 0.27.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 (138) hide show
  1. package/package.json +7 -3
  2. package/server/.tmp/assets/index-082e6676.css +1 -0
  3. package/server/.tmp/assets/index-3108ab2a.js +197 -0
  4. package/{index.html → server/.tmp/index.html} +3 -1
  5. package/.babelrc.json +0 -10
  6. package/.storybook/main.js +0 -25
  7. package/.storybook/preview-head.html +0 -6
  8. package/.storybook/preview.js +0 -33
  9. package/.storybook/theme.css +0 -34
  10. package/CHANGELOG.md +0 -0
  11. package/components.json +0 -16
  12. package/fix-server-hack.js +0 -54
  13. package/fullscreen.vite.config.ts +0 -9
  14. package/jest.config.js +0 -4
  15. package/postcss.config.js +0 -6
  16. package/rollup.config.js +0 -23
  17. package/server.vite.config.ts +0 -31
  18. package/src/App.tsx +0 -73
  19. package/src/assets/icons/Bold.tsx +0 -23
  20. package/src/assets/icons/Chevron.tsx +0 -28
  21. package/src/assets/icons/FontColor.tsx +0 -30
  22. package/src/assets/icons/ImageIcon.tsx +0 -29
  23. package/src/assets/icons/Italic.tsx +0 -24
  24. package/src/assets/icons/Logo.tsx +0 -103
  25. package/src/assets/icons/Section.tsx +0 -41
  26. package/src/assets/icons/Strikethrough.tsx +0 -22
  27. package/src/assets/icons/TextIcon.tsx +0 -20
  28. package/src/assets/icons/Underline.tsx +0 -22
  29. package/src/assets/icons/Undo.tsx +0 -20
  30. package/src/components/Button.tsx +0 -68
  31. package/src/components/Checkbox.tsx +0 -51
  32. package/src/components/DraggableList.stories.tsx +0 -20
  33. package/src/components/DraggableList.tsx +0 -95
  34. package/src/components/Dropdown.tsx +0 -101
  35. package/src/components/EditButton.tsx +0 -10
  36. package/src/components/ErrorText.tsx +0 -3
  37. package/src/components/ExpandLogo.tsx +0 -72
  38. package/src/components/Grid.stories.tsx +0 -43
  39. package/src/components/Grid.tsx +0 -139
  40. package/src/components/RichTextEditor/ContentEditable.tsx +0 -117
  41. package/src/components/RichTextEditor/Nodes/ImageNode.tsx +0 -100
  42. package/src/components/RichTextEditor/Plugins/AutoFocus.tsx +0 -12
  43. package/src/components/RichTextEditor/Plugins/ImagePlugin.tsx +0 -45
  44. package/src/components/RichTextEditor/Plugins/LinkEditorPlugin.tsx +0 -58
  45. package/src/components/RichTextEditor/Plugins/Toolbar.tsx +0 -412
  46. package/src/components/RichTextEditor/RichTextEditor.tsx +0 -105
  47. package/src/components/UploadModal.tsx +0 -109
  48. package/src/components/User.tsx +0 -17
  49. package/src/components/ValFormField.tsx +0 -574
  50. package/src/components/ValFullscreen.tsx +0 -1278
  51. package/src/components/ValMenu.tsx +0 -92
  52. package/src/components/ValOverlay.tsx +0 -488
  53. package/src/components/ValOverlayContext.tsx +0 -80
  54. package/src/components/ValWindow.stories.tsx +0 -146
  55. package/src/components/ValWindow.tsx +0 -220
  56. package/src/components/dashboard/DashboardButton.tsx +0 -25
  57. package/src/components/dashboard/DashboardDropdown.tsx +0 -59
  58. package/src/components/dashboard/Dropdown.stories.tsx +0 -11
  59. package/src/components/dashboard/Dropdown.tsx +0 -70
  60. package/src/components/dashboard/FormGroup.stories.tsx +0 -37
  61. package/src/components/dashboard/FormGroup.tsx +0 -42
  62. package/src/components/dashboard/Grid2.stories.tsx +0 -56
  63. package/src/components/dashboard/Grid2.tsx +0 -72
  64. package/src/components/dashboard/Tree.stories.tsx +0 -91
  65. package/src/components/dashboard/Tree.tsx +0 -72
  66. package/src/components/dashboard/ValDashboardEditor.tsx +0 -269
  67. package/src/components/dashboard/ValDashboardGrid.tsx +0 -142
  68. package/src/components/dashboard/ValTreeNavigator.tsx +0 -253
  69. package/src/components/forms/Form.tsx +0 -126
  70. package/src/components/forms/FormContainer.tsx +0 -24
  71. package/src/components/forms/ImageForm.tsx +0 -195
  72. package/src/components/forms/TextArea.tsx +0 -24
  73. package/src/components/ui/accordion.tsx +0 -58
  74. package/src/components/ui/alert-dialog.tsx +0 -139
  75. package/src/components/ui/avatar.tsx +0 -48
  76. package/src/components/ui/button.tsx +0 -56
  77. package/src/components/ui/calendar.tsx +0 -62
  78. package/src/components/ui/card.tsx +0 -86
  79. package/src/components/ui/checkbox.tsx +0 -28
  80. package/src/components/ui/command.tsx +0 -153
  81. package/src/components/ui/dialog.tsx +0 -120
  82. package/src/components/ui/dropdown-menu.tsx +0 -198
  83. package/src/components/ui/form.tsx +0 -177
  84. package/src/components/ui/input.tsx +0 -24
  85. package/src/components/ui/label.tsx +0 -24
  86. package/src/components/ui/popover.tsx +0 -29
  87. package/src/components/ui/progress.tsx +0 -26
  88. package/src/components/ui/radio-group.tsx +0 -42
  89. package/src/components/ui/scroll-area.tsx +0 -51
  90. package/src/components/ui/select.tsx +0 -119
  91. package/src/components/ui/switch.tsx +0 -27
  92. package/src/components/ui/tabs.tsx +0 -53
  93. package/src/components/ui/toggle.tsx +0 -43
  94. package/src/components/ui/tooltip.tsx +0 -28
  95. package/src/components/usePatch.ts +0 -86
  96. package/src/components/useTheme.ts +0 -45
  97. package/src/dto/SerializedSchema.ts +0 -69
  98. package/src/dto/Session.ts +0 -12
  99. package/src/dto/SessionMode.ts +0 -5
  100. package/src/dto/Tree.ts +0 -18
  101. package/src/exports.ts +0 -6
  102. package/src/index.css +0 -115
  103. package/src/index.tsx +0 -14
  104. package/src/lib/IValStore.ts +0 -6
  105. package/src/lib/utils.ts +0 -6
  106. package/src/main.jsx +0 -10
  107. package/src/richtext/conversion/conversion.test.ts +0 -146
  108. package/src/richtext/conversion/lexicalToRichTextSource.test.ts +0 -89
  109. package/src/richtext/conversion/lexicalToRichTextSource.ts +0 -285
  110. package/src/richtext/conversion/parseRichTextSource.test.ts +0 -469
  111. package/src/richtext/conversion/parseRichTextSource.ts +0 -233
  112. package/src/richtext/conversion/richTextSourceToLexical.test.ts +0 -381
  113. package/src/richtext/conversion/richTextSourceToLexical.ts +0 -293
  114. package/src/richtext/shadowRootPolyFill.js +0 -115
  115. package/src/server.ts +0 -70
  116. package/src/stories/Button.stories.tsx +0 -20
  117. package/src/stories/Checkbox.stories.tsx +0 -14
  118. package/src/stories/Dropdown.stories.tsx +0 -23
  119. package/src/stories/Introduction.mdx +0 -221
  120. package/src/stories/RichTextEditor.stories.tsx +0 -24
  121. package/src/stories/assets/code-brackets.svg +0 -1
  122. package/src/stories/assets/colors.svg +0 -1
  123. package/src/stories/assets/comments.svg +0 -1
  124. package/src/stories/assets/direction.svg +0 -1
  125. package/src/stories/assets/flow.svg +0 -1
  126. package/src/stories/assets/plugin.svg +0 -1
  127. package/src/stories/assets/repo.svg +0 -1
  128. package/src/stories/assets/stackalt.svg +0 -1
  129. package/src/utils/Remote.ts +0 -15
  130. package/src/utils/imageMimeType.ts +0 -23
  131. package/src/utils/readImage.ts +0 -54
  132. package/src/utils/resolvePath.ts +0 -32
  133. package/src/vite-env.d.ts +0 -1
  134. package/src/vite-index.tsx +0 -7
  135. package/src/vite-server.ts +0 -42
  136. package/tailwind.config.js +0 -83
  137. package/tsconfig.json +0 -19
  138. package/vite.config.ts +0 -43
@@ -1,412 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
- import {
3
- INSERT_CHECK_LIST_COMMAND,
4
- INSERT_ORDERED_LIST_COMMAND,
5
- INSERT_UNORDERED_LIST_COMMAND,
6
- REMOVE_LIST_COMMAND,
7
- $isListNode,
8
- ListNode,
9
- } from "@lexical/list";
10
- import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
11
- import {
12
- $createHeadingNode,
13
- $isHeadingNode,
14
- HeadingTagType,
15
- } from "@lexical/rich-text";
16
- import {
17
- $getSelectionStyleValueForProperty,
18
- $patchStyleText,
19
- $setBlocksType,
20
- } from "@lexical/selection";
21
- import {
22
- $findMatchingParent,
23
- $getNearestBlockElementAncestorOrThrow,
24
- mergeRegister,
25
- $getNearestNodeOfType,
26
- } from "@lexical/utils";
27
- import {
28
- $createParagraphNode,
29
- $getSelection,
30
- $isRangeSelection,
31
- $isRootOrShadowRoot,
32
- $isTextNode,
33
- COMMAND_PRIORITY_CRITICAL,
34
- DEPRECATED_$isGridSelection,
35
- FORMAT_TEXT_COMMAND,
36
- LexicalEditor,
37
- NodeKey,
38
- REDO_COMMAND,
39
- SELECTION_CHANGE_COMMAND,
40
- SerializedLexicalNode,
41
- UNDO_COMMAND,
42
- } from "lexical";
43
- import { SerializedEditorState } from "lexical/LexicalEditorState";
44
- import { FC, useCallback, useEffect, useState } from "react";
45
- import Bold from "../../../assets/icons/Bold";
46
- import ImageIcon from "../../../assets/icons/ImageIcon";
47
- import Italic from "../../../assets/icons/Italic";
48
- import Strikethrough from "../../../assets/icons/Strikethrough";
49
- import Underline from "../../../assets/icons/Underline";
50
- import Undo from "../../../assets/icons/Undo";
51
- import Button from "../../Button";
52
- import Dropdown from "../../Dropdown";
53
- import UploadModal from "../../UploadModal";
54
- import { INSERT_IMAGE_COMMAND } from "./ImagePlugin";
55
- import { readImage } from "../../../utils/readImage";
56
- import { $isLinkNode, LinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link";
57
- import { getSelectedNode } from "./LinkEditorPlugin";
58
- import { Check, Link, X } from "react-feather";
59
-
60
- export interface ToolbarSettingsProps {
61
- fontsFamilies?: string[];
62
- fontSizes?: string[];
63
- colors?: string[];
64
- onEditor?: (editor: LexicalEditor) => void;
65
- }
66
-
67
- const Toolbar: FC<ToolbarSettingsProps> = ({
68
- fontSizes,
69
- fontsFamilies,
70
- onEditor,
71
- colors,
72
- }) => {
73
- const [editor] = useLexicalComposerContext();
74
- useEffect(() => {
75
- if (onEditor) {
76
- onEditor(editor);
77
- }
78
- }, [editor]);
79
- const [activeEditor, setActiveEditor] = useState(editor);
80
- const [selectedElementKey, setSelectedElementKey] = useState<NodeKey | null>(
81
- null
82
- );
83
- const [isBold, setIsBold] = useState(false);
84
- const [isItalic, setIsItalic] = useState(false);
85
- const [isStrikethrough, setIsStrikethrough] = useState(false);
86
- const [isUnderline, setIsUnderline] = useState(false);
87
- const [fontSize, setFontSize] = useState<string>("15px");
88
- const [fontColor, setFontColor] = useState<string>("#000");
89
- const [fontFamily, setFontFamily] = useState<string>("Sans");
90
- const [blockType, setBlockType] =
91
- useState<keyof typeof blockTypes>("paragraph");
92
-
93
- const [showModal, setShowModal] = useState<boolean>(false);
94
- const [inputUrl, setInputUrl] = useState<boolean>(false);
95
- const [uploadMode, setUploadMode] = useState<"url" | "file">("url");
96
- const [file, setFile] = useState<File | null>(null);
97
-
98
- const [url, setUrl] = useState<string | null>(null);
99
-
100
- const dispatchLinkChange = (url: string | null) => {
101
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
102
- };
103
-
104
- const blockTypes: { [key: string]: string } = {
105
- paragraph: "Normal",
106
- h1: "Heading 1",
107
- h2: "Heading 2",
108
- h3: "Heading 3",
109
- h4: "Heading 4",
110
- h5: "Heading 5",
111
- h6: "Heading 6",
112
- number: "Numbered List",
113
- bullet: "Bulleted List",
114
- };
115
- const blockTypesLookup: { [key: string]: string } = {};
116
-
117
- for (const key in blockTypes) {
118
- blockTypesLookup[blockTypes[key]] = key;
119
- }
120
-
121
- const updateToolbar = useCallback(() => {
122
- const selection = $getSelection();
123
- if ($isRangeSelection(selection)) {
124
- const anchorNode = selection.anchor.getNode();
125
- let element =
126
- anchorNode.getKey() === "root"
127
- ? anchorNode
128
- : $findMatchingParent(anchorNode, (e) => {
129
- const parent = e.getParent();
130
- return parent !== null && $isRootOrShadowRoot(parent);
131
- });
132
-
133
- if (element === null) {
134
- element = anchorNode.getTopLevelElementOrThrow();
135
- }
136
-
137
- // LINK STUFF
138
- const node = getSelectedNode(selection);
139
- const linkParent = $findMatchingParent(
140
- node,
141
- $isLinkNode
142
- ) as LinkNode | null;
143
- if (linkParent !== null) {
144
- const href = linkParent.getURL();
145
- setUrl(href);
146
- } else {
147
- setUrl(null);
148
- }
149
- // ====================
150
-
151
- const elementKey = element.getKey();
152
- const elementDOM = activeEditor.getElementByKey(elementKey);
153
- setIsBold(selection.hasFormat("bold"));
154
- setIsItalic(selection.hasFormat("italic"));
155
- setIsStrikethrough(selection.hasFormat("strikethrough"));
156
- setIsUnderline(selection.hasFormat("underline"));
157
- if (elementDOM !== null) {
158
- setSelectedElementKey(elementKey);
159
- if ($isListNode(element)) {
160
- const parentList = $getNearestNodeOfType<ListNode>(
161
- anchorNode,
162
- ListNode
163
- );
164
- const type = parentList
165
- ? parentList.getListType()
166
- : element.getListType();
167
- setBlockType(type);
168
- } else {
169
- const type = $isHeadingNode(element)
170
- ? element.getTag()
171
- : element.getType();
172
- if (type in blockTypes) {
173
- setBlockType(type as keyof typeof blockTypes);
174
- }
175
- }
176
- }
177
-
178
- // setFontSize(
179
- // $getSelectionStyleValueForProperty(selection, "font-size", "15px")
180
- // );
181
- // setFontColor(
182
- // $getSelectionStyleValueForProperty(selection, "color", "#000")
183
- // );
184
- // setFontFamily(
185
- // $getSelectionStyleValueForProperty(selection, "font-family", "Arial")
186
- // );
187
- }
188
- }, [activeEditor]);
189
-
190
- useEffect(() => {
191
- return mergeRegister(
192
- editor.registerUpdateListener(({ editorState }) => {
193
- editorState.read(() => {
194
- updateToolbar();
195
- });
196
- })
197
- );
198
- }, [updateToolbar, activeEditor, editor]);
199
-
200
- useEffect(() => {
201
- return editor.registerCommand(
202
- SELECTION_CHANGE_COMMAND,
203
- (_payload, newEditor) => {
204
- updateToolbar();
205
- setActiveEditor(newEditor);
206
- return false;
207
- },
208
- COMMAND_PRIORITY_CRITICAL
209
- );
210
- }, [editor, updateToolbar]);
211
-
212
- const formatText = (format: keyof typeof blockTypes) => {
213
- if (["h1", "h2", "h3", "h4", "h5", "h6"].includes(format as string)) {
214
- if (blockType !== format) {
215
- editor.update(() => {
216
- const selection = $getSelection();
217
- if ($isRangeSelection(selection)) {
218
- $setBlocksType(selection, () => {
219
- return $createHeadingNode(format as HeadingTagType);
220
- });
221
- }
222
- });
223
- }
224
- } else if (format === "paragraph" && blockType !== "paragraph") {
225
- editor.update(() => {
226
- const selection = $getSelection();
227
- if (
228
- $isRangeSelection(selection) ||
229
- DEPRECATED_$isGridSelection(selection)
230
- ) {
231
- $setBlocksType(selection, () => $createParagraphNode());
232
- }
233
- });
234
- } else {
235
- if (format === "number" && blockType !== "number") {
236
- editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
237
- } else if (format === "bullet" && blockType !== "bullet") {
238
- editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
239
- } else {
240
- editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
241
- }
242
- }
243
- };
244
-
245
- const clearFormatting = useCallback(() => {
246
- editor.update(() => {
247
- const selection = $getSelection();
248
- if ($isRangeSelection(selection)) {
249
- const anchor = selection.anchor;
250
- const focus = selection.focus;
251
- const nodes = selection.getNodes();
252
-
253
- if (anchor.key === focus.key && anchor.offset === focus.offset) {
254
- return;
255
- }
256
- nodes.forEach((node, idx) => {
257
- if ($isTextNode(node)) {
258
- if (idx === 0 && anchor.offset !== 0) {
259
- node = node.splitText(anchor.offset)[1] || node;
260
- }
261
- if (idx === nodes.length - 1) {
262
- node = node.splitText(focus.offset)[0] || node;
263
- }
264
-
265
- if (node.__style !== "") {
266
- node.setStyle("");
267
- }
268
- if (node.__format !== 0) {
269
- node.setFormat(0);
270
- $getNearestBlockElementAncestorOrThrow(node).setFormat("");
271
- }
272
- } else if ($isHeadingNode(node)) {
273
- node.replace($createParagraphNode(), true);
274
- }
275
- });
276
- }
277
- });
278
- }, [activeEditor]);
279
-
280
- const changeFontFamily = (fontFamily: string) => {
281
- editor.update(() => {
282
- const selection = $getSelection();
283
- if ($isRangeSelection(selection)) {
284
- $patchStyleText(selection, {
285
- ["font-family"]: fontFamily,
286
- });
287
- }
288
- });
289
- };
290
-
291
- const changeFontSize = (fontSize: string) => {
292
- editor.update(() => {
293
- const selection = $getSelection();
294
- if ($isRangeSelection(selection)) {
295
- $patchStyleText(selection, {
296
- ["font-size"]: fontSize,
297
- });
298
- }
299
- });
300
- };
301
-
302
- return (
303
- <div className="sticky top-0 border-b bg-background border-highlight flex flex-col">
304
- <div className="flex flex-row gap-1">
305
- <Dropdown
306
- options={Object.values(blockTypes)}
307
- label={
308
- blockTypes[blockType as string] ?? blockType + " (not supported)"
309
- }
310
- onChange={(selectedOption) => {
311
- formatText(blockTypesLookup[selectedOption]);
312
- }}
313
- />
314
- <Button
315
- variant="primary"
316
- onClick={(ev) => {
317
- ev.preventDefault();
318
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold");
319
- }}
320
- active={isBold}
321
- icon={<Bold className={`${isBold && "stroke-[3px]"}`} />}
322
- />
323
- <Button
324
- active={isStrikethrough}
325
- onClick={(ev) => {
326
- ev.preventDefault();
327
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, "strikethrough");
328
- }}
329
- icon={
330
- <Strikethrough className={`${isStrikethrough && "stroke-[2px]"}`} />
331
- }
332
- />
333
- <Button
334
- active={isItalic}
335
- onClick={(ev) => {
336
- ev.preventDefault();
337
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic");
338
- }}
339
- icon={<Italic className={`${isItalic && "stroke-[3px]"}`} />}
340
- />
341
- <Button
342
- active={url !== null}
343
- onClick={(ev) => {
344
- ev.preventDefault();
345
- setUrl("");
346
- dispatchLinkChange("");
347
- }}
348
- icon={
349
- <Link
350
- width={12}
351
- height={12}
352
- className={`${url !== null && "stroke-[3px]"}`}
353
- />
354
- }
355
- />
356
- <label className="flex items-center justify-center">
357
- <ImageIcon />
358
- <input
359
- type="file"
360
- hidden={true}
361
- onChange={(ev) => {
362
- ev.preventDefault();
363
-
364
- readImage(ev)
365
- .then((res) => {
366
- editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
367
- ...res,
368
- });
369
- })
370
- .catch((err) => {
371
- console.error("Error reading image", err);
372
- });
373
- }}
374
- />
375
- </label>
376
- </div>
377
- {url !== null && (
378
- <div className="flex flex-row p-2">
379
- <input
380
- type="text"
381
- placeholder="Enter URL"
382
- className="w-1/3 text-primary bg-background px-2"
383
- value={url}
384
- onChange={(ev) => {
385
- ev.preventDefault();
386
- setUrl(ev.target.value);
387
- }}
388
- ></input>
389
- <Button
390
- variant="primary"
391
- onClick={(ev) => {
392
- ev.preventDefault();
393
- // empty url will remove link
394
- dispatchLinkChange(url || null);
395
- }}
396
- icon={<Check size={14} />}
397
- />
398
- <Button
399
- variant="primary"
400
- onClick={(ev) => {
401
- ev.preventDefault();
402
- dispatchLinkChange(null);
403
- }}
404
- icon={<X size={14} />}
405
- />
406
- </div>
407
- )}
408
- </div>
409
- );
410
- };
411
-
412
- export default Toolbar;
@@ -1,105 +0,0 @@
1
- "use client";
2
-
3
- /* eslint-disable @typescript-eslint/no-explicit-any */
4
- import { LexicalEditor } from "lexical";
5
- import { ListItemNode, ListNode } from "@lexical/list";
6
- import { LexicalComposer } from "@lexical/react/LexicalComposer";
7
- import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
8
- import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
9
- import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
10
- import { ListPlugin } from "@lexical/react/LexicalListPlugin";
11
- import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
12
- import { FC } from "react";
13
- import LexicalContentEditable from "./ContentEditable";
14
- import { ImageNode } from "./Nodes/ImageNode";
15
- import { AutoFocus } from "./Plugins/AutoFocus";
16
- import ImagesPlugin from "./Plugins/ImagePlugin";
17
- import Toolbar from "./Plugins/Toolbar";
18
- import { AnyRichTextOptions, RichTextSource } from "@valbuild/core";
19
- import { HeadingNode } from "@lexical/rich-text";
20
- import { richTextSourceToLexical } from "../../richtext/conversion/richTextSourceToLexical";
21
- import { useValOverlayContext } from "../ValOverlayContext";
22
- import { parseRichTextSource } from "../../exports";
23
- import { LinkNode } from "@lexical/link";
24
- import LinkEditorPlugin from "./Plugins/LinkEditorPlugin";
25
-
26
- export interface RichTextEditorProps {
27
- richtext: RichTextSource<AnyRichTextOptions>;
28
- onEditor?: (editor: LexicalEditor) => void; // Not the ideal way of passing the editor to the upper context, we need it to be able to save
29
- }
30
-
31
- function onError(error: any) {
32
- console.error(error);
33
- }
34
-
35
- const TOOLBAR_HEIGHT = 28;
36
-
37
- export const RichTextEditor: FC<RichTextEditorProps> = ({
38
- richtext,
39
- onEditor,
40
- }) => {
41
- const { windowSize } = useValOverlayContext();
42
- const prePopulatedState = (editor: LexicalEditor) => {
43
- editor.setEditorState(
44
- editor.parseEditorState({
45
- root: richTextSourceToLexical(parseRichTextSource(richtext)),
46
- })
47
- );
48
- };
49
- const initialConfig = {
50
- namespace: "val",
51
- editorState: prePopulatedState,
52
- nodes: [HeadingNode, ImageNode, ListNode, ListItemNode, LinkNode],
53
- theme: {
54
- text: {
55
- bold: "font-semibold",
56
- underline: "underline",
57
- italic: "italic",
58
- strikethrough: "line-through",
59
- underlineStrikethrough: "underline line-through",
60
- },
61
- list: {
62
- listitem: "ml-[20px]",
63
- ol: "list-decimal",
64
- ul: "list-disc",
65
- },
66
- heading: {
67
- h1: "text-4xl font-bold",
68
- h2: "text-3xl font-bold",
69
- h3: "text-2xl font-bold",
70
- h4: "text-xl font-bold",
71
- h5: "text-lg font-bold",
72
- h6: "text-md font-bold",
73
- },
74
- link: "text-highlight underline",
75
- },
76
- onError,
77
- };
78
- return (
79
- <LexicalComposer initialConfig={initialConfig}>
80
- <AutoFocus />
81
- <Toolbar onEditor={onEditor} />
82
- <RichTextPlugin
83
- contentEditable={
84
- <div
85
- className="font-sans border-b text-primary border-highlight"
86
- style={{
87
- minHeight: windowSize?.innerHeight
88
- ? windowSize?.innerHeight - TOOLBAR_HEIGHT
89
- : undefined,
90
- }}
91
- >
92
- <LexicalContentEditable className="p-4 outline-none bg-fill" />
93
- </div>
94
- }
95
- placeholder={<div className="">Enter some text...</div>}
96
- ErrorBoundary={LexicalErrorBoundary}
97
- />
98
- <LinkPlugin />
99
- <LinkEditorPlugin />
100
- <ListPlugin />
101
- <ImagesPlugin />
102
- <HistoryPlugin />
103
- </LexicalComposer>
104
- );
105
- };
@@ -1,109 +0,0 @@
1
- import { FC, useEffect, useState } from "react";
2
- import Button from "./Button";
3
-
4
- interface UploadModalProps {
5
- showModal: boolean;
6
- setShowModal: React.Dispatch<React.SetStateAction<boolean>>;
7
- uploadImage: (url: string, alt?: string) => void;
8
- }
9
-
10
- const UploadModal: FC<UploadModalProps> = ({
11
- showModal,
12
- setShowModal,
13
- uploadImage,
14
- }) => {
15
- const [uploadUrl, setUploadUrl] = useState<boolean>(true);
16
- const [url, setUrl] = useState<string>("");
17
-
18
- useEffect(() => {
19
- setUrl("");
20
- }, [uploadUrl]);
21
-
22
- const loadImage = (files: FileList | null) => {
23
- const reader = new FileReader();
24
- reader.onload = function () {
25
- if (typeof reader.result === "string") {
26
- setUrl(reader.result);
27
- }
28
- return "";
29
- };
30
- if (files !== null) {
31
- reader.readAsDataURL(files[0]);
32
- }
33
- };
34
-
35
- const onSubmit = () => {
36
- if (url) {
37
- uploadImage(url);
38
- setUrl("");
39
- setShowModal(false);
40
- setUploadUrl(true);
41
- }
42
- };
43
-
44
- return (
45
- <div className="absolute z-10 flex flex-col justify-center items-center top-[50%] left-[50%] font-mono">
46
- {showModal && (
47
- <div className="flex flex-col items-center justify-center">
48
- <div className="flex flex-col items-start justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
49
- <div className="fixed inset-0 transition-opacity">
50
- <div className="absolute inset-0 bg-gray-500 opacity-75" />
51
- </div>
52
-
53
- <div className="flex flex-col items-center justify-between bg-fill rounded-lg transform transition-all min-h-[300px] min-w-[500px] h-full px-5 py-7">
54
- <div className="flex flex-col items-center w-full gap-5">
55
- <div className="mb-4">
56
- <Button
57
- variant={uploadUrl ? "primary" : "secondary"}
58
- onClick={() => setUploadUrl(true)}
59
- >
60
- Paste URL
61
- </Button>
62
- <Button
63
- variant={uploadUrl ? "secondary" : "primary"}
64
- onClick={() => setUploadUrl(false)}
65
- >
66
- Upload file
67
- </Button>
68
- </div>
69
- {uploadUrl ? (
70
- <div className="flex flex-col items-center justify-center w-full gap-5">
71
- <label className="text-primary">Upload URL</label>
72
- <input
73
- className="w-full h-10 rounded-lg bg-border"
74
- value={url}
75
- onChange={(event) => setUrl(event.target.value)}
76
- />
77
- </div>
78
- ) : (
79
- <div className="flex flex-col items-center justify-center w-full gap-5">
80
- <label className="text-primary">Choose File</label>
81
- <input
82
- className="h-10 rounded-lg w-fit"
83
- type="file"
84
- onChange={(e) => loadImage(e.target.files)}
85
- />
86
- </div>
87
- )}
88
- </div>
89
- <div className="flex flex-row items-center justify-center gap-5 ">
90
- <Button variant="secondary" onClick={() => setShowModal(false)}>
91
- Cancel
92
- </Button>
93
- <Button
94
- variant="primary"
95
- disabled={url === ""}
96
- onClick={() => onSubmit()}
97
- >
98
- Upload
99
- </Button>
100
- </div>
101
- </div>
102
- </div>
103
- </div>
104
- )}
105
- </div>
106
- );
107
- };
108
-
109
- export default UploadModal;
@@ -1,17 +0,0 @@
1
- import { FC } from "react";
2
-
3
- const User: FC<{ name: string }> = ({ name }) => {
4
- return (
5
- <div className="flex flex-row items-center gap-2">
6
- <div className="w-[32px] h-[32px] rounded-full bg-light-gray flex justify-center items-center">
7
- {name
8
- .split(" ")
9
- .map((name) => name.charAt(0))
10
- .join("")}
11
- </div>
12
- <div className="text-white">{name}</div>
13
- </div>
14
- );
15
- };
16
-
17
- export default User;