a tool for shared writing and social publishing
1import {
2 AsyncValueAutosizeTextarea,
3 AutosizeTextareaProps,
4} from "components/utils/AutosizeTextarea";
5import { BlockProps } from "./Block";
6import { getCoordinatesInTextarea } from "src/utils/getCoordinatesInTextarea";
7import { focusBlock } from "src/utils/focusBlock";
8import { generateKeyBetween } from "fractional-indexing";
9import { v7 } from "uuid";
10import { elementId } from "src/utils/elementId";
11import { Replicache } from "replicache";
12import { ReplicacheMutators } from "src/replicache";
13
14type BaseTextareaBlockProps = AutosizeTextareaProps & {
15 block: Pick<
16 BlockProps,
17 "previousBlock" | "nextBlock" | "parent" | "position" | "nextPosition"
18 >;
19 rep?: Replicache<ReplicacheMutators> | null;
20 permissionSet?: string;
21};
22
23export function BaseTextareaBlock(props: BaseTextareaBlockProps) {
24 let { block, rep, permissionSet, ...passDownProps } = props;
25 return (
26 <AsyncValueAutosizeTextarea
27 {...passDownProps}
28 noWrap
29 onKeyDown={(e) => {
30 // Shift-Enter or Ctrl-Enter: create new text block below and focus it
31 if (
32 (e.shiftKey || e.ctrlKey || e.metaKey) &&
33 e.key === "Enter" &&
34 rep &&
35 permissionSet
36 ) {
37 e.preventDefault();
38 let newEntityID = v7();
39 rep.mutate.addBlock({
40 parent: block.parent,
41 type: "text",
42 factID: v7(),
43 permission_set: permissionSet,
44 position: generateKeyBetween(
45 block.position,
46 block.nextPosition || null,
47 ),
48 newEntityID,
49 });
50
51 setTimeout(() => {
52 document.getElementById(elementId.block(newEntityID).text)?.focus();
53 }, 10);
54 return true;
55 }
56
57 if (e.key === "ArrowUp") {
58 let selection = e.currentTarget.selectionStart;
59
60 let lastLineBeforeCursor = e.currentTarget.value
61 .slice(0, selection)
62 .lastIndexOf("\n");
63 if (lastLineBeforeCursor !== -1) return;
64 let block = props.block.previousBlock;
65 let coord = getCoordinatesInTextarea(e.currentTarget, selection);
66 if (block) {
67 focusBlock(block, {
68 left: coord.left + e.currentTarget.getBoundingClientRect().left,
69 type: "bottom",
70 });
71 return true;
72 }
73 }
74 if (e.key === "ArrowDown") {
75 let selection = e.currentTarget.selectionStart;
76
77 let lastLine = e.currentTarget.value.lastIndexOf("\n");
78 let lastLineBeforeCursor = e.currentTarget.value
79 .slice(0, selection)
80 .lastIndexOf("\n");
81 if (lastLine !== lastLineBeforeCursor) return;
82 e.preventDefault();
83 let block = props.block.nextBlock;
84
85 let coord = getCoordinatesInTextarea(e.currentTarget, selection);
86 console.log(coord);
87 if (block) {
88 focusBlock(block, {
89 left: coord.left + e.currentTarget.getBoundingClientRect().left,
90 type: "top",
91 });
92 return true;
93 }
94 }
95 }}
96 />
97 );
98}