@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,27 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ export function getDOMRangeRect(
9
+ nativeSelection: Selection,
10
+ rootElement: HTMLElement,
11
+ ): DOMRect {
12
+ const domRange = nativeSelection.getRangeAt(0);
13
+
14
+ let rect;
15
+
16
+ if (nativeSelection.anchorNode === rootElement) {
17
+ let inner = rootElement;
18
+ while (inner.firstElementChild != null) {
19
+ inner = inner.firstElementChild as HTMLElement;
20
+ }
21
+ rect = inner.getBoundingClientRect();
22
+ } else {
23
+ rect = domRange.getBoundingClientRect();
24
+ }
25
+
26
+ return rect;
27
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import {$isAtNodeEnd} from '@lexical/selection';
9
+ import {ElementNode, RangeSelection, TextNode} from 'lexical';
10
+
11
+ export function getSelectedNode(
12
+ selection: RangeSelection,
13
+ ): TextNode | ElementNode {
14
+ const anchor = selection.anchor;
15
+ const focus = selection.focus;
16
+ const anchorNode = selection.anchor.getNode();
17
+ const focusNode = selection.focus.getNode();
18
+ if (anchorNode === focusNode) {
19
+ return anchorNode;
20
+ }
21
+ const isBackward = selection.isBackward();
22
+ if (isBackward) {
23
+ return $isAtNodeEnd(focus) ? anchorNode : focusNode;
24
+ } else {
25
+ return $isAtNodeEnd(anchor) ? anchorNode : focusNode;
26
+ }
27
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import {EditorThemeClasses} from 'lexical';
10
+
11
+ export function getThemeSelector(
12
+ getTheme: () => EditorThemeClasses | null | undefined,
13
+ name: keyof EditorThemeClasses,
14
+ ): string {
15
+ const className = getTheme()?.[name];
16
+ if (typeof className !== 'string') {
17
+ throw new Error(
18
+ `getThemeClass: required theme property ${name} not defined`,
19
+ );
20
+ }
21
+ return className
22
+ .split(/\s+/g)
23
+ .map((cls) => `.${cls}`)
24
+ .join();
25
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ export default function joinClasses(
10
+ ...args: Array<string | boolean | null | undefined>
11
+ ) {
12
+ return args.filter(Boolean).join(' ');
13
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ const VERTICAL_GAP = 10;
9
+ const HORIZONTAL_OFFSET = 5;
10
+
11
+ export function setFloatingElemPosition(
12
+ targetRect: DOMRect | null,
13
+ floatingElem: HTMLElement,
14
+ anchorElem: HTMLElement,
15
+ isLink: boolean = false,
16
+ verticalGap: number = VERTICAL_GAP,
17
+ horizontalOffset: number = HORIZONTAL_OFFSET,
18
+ ): void {
19
+ const scrollerElem = anchorElem.parentElement;
20
+
21
+ if (targetRect === null || !scrollerElem) {
22
+ floatingElem.style.opacity = '0';
23
+ floatingElem.style.transform = 'translate(-10000px, -10000px)';
24
+ return;
25
+ }
26
+
27
+ const floatingElemRect = floatingElem.getBoundingClientRect();
28
+ const anchorElementRect = anchorElem.getBoundingClientRect();
29
+ const editorScrollerRect = scrollerElem.getBoundingClientRect();
30
+
31
+ let top = targetRect.top - floatingElemRect.height - verticalGap;
32
+ let left = targetRect.left - horizontalOffset;
33
+
34
+ // Check if text is end-aligned
35
+ const selection = window.getSelection();
36
+ if (selection && selection.rangeCount > 0) {
37
+ const range = selection.getRangeAt(0);
38
+ const textNode = range.startContainer;
39
+ if (textNode.nodeType === Node.ELEMENT_NODE || textNode.parentElement) {
40
+ const textElement =
41
+ textNode.nodeType === Node.ELEMENT_NODE
42
+ ? (textNode as Element)
43
+ : (textNode.parentElement as Element);
44
+ const textAlign = window.getComputedStyle(textElement).textAlign;
45
+
46
+ if (textAlign === 'right' || textAlign === 'end') {
47
+ // For end-aligned text, position the toolbar relative to the text end
48
+ left = targetRect.right - floatingElemRect.width + horizontalOffset;
49
+ }
50
+ }
51
+ }
52
+
53
+ if (top < editorScrollerRect.top) {
54
+ // adjusted height for link element if the element is at top
55
+ top +=
56
+ floatingElemRect.height +
57
+ targetRect.height +
58
+ verticalGap * (isLink ? 9 : 2);
59
+ }
60
+
61
+ if (left + floatingElemRect.width > editorScrollerRect.right) {
62
+ left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;
63
+ }
64
+
65
+ if (left < editorScrollerRect.left) {
66
+ left = editorScrollerRect.left + horizontalOffset;
67
+ }
68
+
69
+ top -= anchorElementRect.top;
70
+ left -= anchorElementRect.left;
71
+
72
+ floatingElem.style.opacity = '1';
73
+ floatingElem.style.transform = `translate(${left}px, ${top}px)`;
74
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ const VERTICAL_GAP = 10;
9
+ const HORIZONTAL_OFFSET = 5;
10
+
11
+ export function setFloatingElemPositionForLinkEditor(
12
+ targetRect: DOMRect | null,
13
+ floatingElem: HTMLElement,
14
+ anchorElem: HTMLElement,
15
+ verticalGap: number = VERTICAL_GAP,
16
+ horizontalOffset: number = HORIZONTAL_OFFSET,
17
+ ): void {
18
+ const scrollerElem = anchorElem.parentElement;
19
+
20
+ if (targetRect === null || !scrollerElem) {
21
+ floatingElem.style.opacity = '0';
22
+ floatingElem.style.transform = 'translate(-10000px, -10000px)';
23
+ return;
24
+ }
25
+
26
+ const floatingElemRect = floatingElem.getBoundingClientRect();
27
+ const anchorElementRect = anchorElem.getBoundingClientRect();
28
+ const editorScrollerRect = scrollerElem.getBoundingClientRect();
29
+
30
+ let top = targetRect.top - verticalGap;
31
+ let left = targetRect.left - horizontalOffset;
32
+
33
+ if (top < editorScrollerRect.top) {
34
+ top += floatingElemRect.height + targetRect.height + verticalGap * 2;
35
+ }
36
+
37
+ if (left + floatingElemRect.width > editorScrollerRect.right) {
38
+ left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;
39
+ }
40
+
41
+ top -= anchorElementRect.top;
42
+ left -= anchorElementRect.left;
43
+
44
+ floatingElem.style.opacity = '1';
45
+ floatingElem.style.transform = `translate(${left}px, ${top}px)`;
46
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ type Force = [number, number];
10
+ type Listener = (force: Force, e: TouchEvent) => void;
11
+ type ElementValues = {
12
+ start: null | Force;
13
+ listeners: Set<Listener>;
14
+ handleTouchstart: (e: TouchEvent) => void;
15
+ handleTouchend: (e: TouchEvent) => void;
16
+ };
17
+
18
+ const elements = new WeakMap<HTMLElement, ElementValues>();
19
+
20
+ function readTouch(e: TouchEvent): [number, number] | null {
21
+ const touch = e.changedTouches[0];
22
+ if (touch === undefined) {
23
+ return null;
24
+ }
25
+ return [touch.clientX, touch.clientY];
26
+ }
27
+
28
+ function addListener(element: HTMLElement, cb: Listener): () => void {
29
+ let elementValues = elements.get(element);
30
+ if (elementValues === undefined) {
31
+ const listeners = new Set<Listener>();
32
+ const handleTouchstart = (e: TouchEvent) => {
33
+ if (elementValues !== undefined) {
34
+ elementValues.start = readTouch(e);
35
+ }
36
+ };
37
+ const handleTouchend = (e: TouchEvent) => {
38
+ if (elementValues === undefined) {
39
+ return;
40
+ }
41
+ const start = elementValues.start;
42
+ if (start === null) {
43
+ return;
44
+ }
45
+ const end = readTouch(e);
46
+ for (const listener of listeners) {
47
+ if (end !== null) {
48
+ listener([end[0] - start[0], end[1] - start[1]], e);
49
+ }
50
+ }
51
+ };
52
+ element.addEventListener('touchstart', handleTouchstart);
53
+ element.addEventListener('touchend', handleTouchend);
54
+
55
+ elementValues = {
56
+ handleTouchend,
57
+ handleTouchstart,
58
+ listeners,
59
+ start: null,
60
+ };
61
+ elements.set(element, elementValues);
62
+ }
63
+ elementValues.listeners.add(cb);
64
+ return () => deleteListener(element, cb);
65
+ }
66
+
67
+ function deleteListener(element: HTMLElement, cb: Listener): void {
68
+ const elementValues = elements.get(element);
69
+ if (elementValues === undefined) {
70
+ return;
71
+ }
72
+ const listeners = elementValues.listeners;
73
+ listeners.delete(cb);
74
+ if (listeners.size === 0) {
75
+ elements.delete(element);
76
+ element.removeEventListener('touchstart', elementValues.handleTouchstart);
77
+ element.removeEventListener('touchend', elementValues.handleTouchend);
78
+ }
79
+ }
80
+
81
+ export function addSwipeLeftListener(
82
+ element: HTMLElement,
83
+ cb: (_force: number, e: TouchEvent) => void,
84
+ ) {
85
+ return addListener(element, (force, e) => {
86
+ const [x, y] = force;
87
+ if (x < 0 && -x > Math.abs(y)) {
88
+ cb(x, e);
89
+ }
90
+ });
91
+ }
92
+
93
+ export function addSwipeRightListener(
94
+ element: HTMLElement,
95
+ cb: (_force: number, e: TouchEvent) => void,
96
+ ) {
97
+ return addListener(element, (force, e) => {
98
+ const [x, y] = force;
99
+ if (x > 0 && x > Math.abs(y)) {
100
+ cb(x, e);
101
+ }
102
+ });
103
+ }
104
+
105
+ export function addSwipeUpListener(
106
+ element: HTMLElement,
107
+ cb: (_force: number, e: TouchEvent) => void,
108
+ ) {
109
+ return addListener(element, (force, e) => {
110
+ const [x, y] = force;
111
+ if (y < 0 && -y > Math.abs(x)) {
112
+ cb(x, e);
113
+ }
114
+ });
115
+ }
116
+
117
+ export function addSwipeDownListener(
118
+ element: HTMLElement,
119
+ cb: (_force: number, e: TouchEvent) => void,
120
+ ) {
121
+ return addListener(element, (force, e) => {
122
+ const [x, y] = force;
123
+ if (y > 0 && y > Math.abs(x)) {
124
+ cb(x, e);
125
+ }
126
+ });
127
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ const SUPPORTED_URL_PROTOCOLS = new Set([
10
+ 'http:',
11
+ 'https:',
12
+ 'mailto:',
13
+ 'sms:',
14
+ 'tel:',
15
+ ]);
16
+
17
+ export function sanitizeUrl(url: string): string {
18
+ try {
19
+ const parsedUrl = new URL(url);
20
+ // eslint-disable-next-line no-script-url
21
+ if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) {
22
+ return 'about:blank';
23
+ }
24
+ } catch {
25
+ return url;
26
+ }
27
+ return url;
28
+ }
29
+
30
+ // Source: https://stackoverflow.com/a/8234912/2013580
31
+ const urlRegExp = new RegExp(
32
+ /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/,
33
+ );
34
+ export function validateUrl(url: string): boolean {
35
+ // TODO Fix UI for link insertion; it should never default to an invalid URL such as https://.
36
+ // Maybe show a dialog where they user can type the URL before inserting it.
37
+ return url === 'https://' || urlRegExp.test(url);
38
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./src/*"]
23
+ }
24
+ },
25
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26
+ "exclude": ["node_modules"]
27
+ }