diff --git a/components/Blocks.tsx b/components/Blocks.tsx
index 077c84ec..dd987e22 100644
--- a/components/Blocks.tsx
+++ b/components/Blocks.tsx
@@ -49,7 +49,7 @@ export function Blocks(props: { entityID: string }) {
?.focus();
}, 10);
} else {
- focusBlock(lastBlock, "end", "bottom");
+ focusBlock(lastBlock, { type: "end" });
}
}
}}
@@ -68,6 +68,7 @@ export function Blocks(props: { entityID: string }) {
);
})}
+
);
}
@@ -147,14 +148,20 @@ function Block(props: BlockProps) {
e.preventDefault();
let block = props.nextBlock;
if (block && useUIState.getState().selectedBlock.length <= 1)
- focusBlock(block, useEditorStates.getState().lastXPosition, "top");
+ focusBlock(block, {
+ type: "top",
+ left: useEditorStates.getState().lastXPosition,
+ });
if (!block) return;
}
if (e.key === "ArrowUp") {
e.preventDefault();
let block = props.previousBlock;
if (block && useUIState.getState().selectedBlock.length <= 1) {
- focusBlock(block, useEditorStates.getState().lastXPosition, "bottom");
+ focusBlock(block, {
+ type: "bottom",
+ left: useEditorStates.getState().lastXPosition,
+ });
}
if (!block) return;
}
@@ -164,7 +171,7 @@ function Block(props: BlockProps) {
r.mutate.removeBlock({ blockEntity: props.entityID });
useUIState.getState().closeCard(props.entityID);
let block = props.previousBlock;
- if (block) focusBlock(block, "end", "bottom");
+ if (block) focusBlock(block, { type: "end" });
}
if (e.key === "Enter") {
let newEntityID = crypto.randomUUID();
@@ -260,11 +267,25 @@ export function HeadingBlock(props: BlockProps) {
);
}
-export function focusBlock(
- block: Block,
- left: number | "end" | "start",
- top: "top" | "bottom",
-) {
+type Position =
+ | {
+ type: "start";
+ }
+ | { type: "end" }
+ | {
+ type: "coord";
+ top: number;
+ left: number;
+ }
+ | {
+ type: "top";
+ left: number;
+ }
+ | {
+ type: "bottom";
+ left: number;
+ };
+export function focusBlock(block: Block, position: Position) {
if (block.type !== "text" && block.type !== "heading") {
useUIState.getState().setSelectedBlock(block);
return true;
@@ -272,21 +293,42 @@ export function focusBlock(
let nextBlockID = block.value;
let nextBlock = useEditorStates.getState().editorStates[nextBlockID];
if (!nextBlock || !nextBlock.view) return;
- nextBlock.view.focus();
+ nextBlock.view.dom.focus({ preventScroll: true });
let nextBlockViewClientRect = nextBlock.view.dom.getBoundingClientRect();
let tr = nextBlock.editor.tr;
- let pos =
- left === "end"
- ? { pos: tr.doc.content.size - 1 }
- : left === "start"
- ? { pos: 1 }
- : nextBlock.view.posAtCoords({
- top:
- top === "top"
- ? nextBlockViewClientRect.top + 12
- : nextBlockViewClientRect.bottom - 12,
- left,
- });
+ let pos: { pos: number } | null = null;
+ switch (position.type) {
+ case "end": {
+ pos = { pos: tr.doc.content.size - 1 };
+ break;
+ }
+ case "start": {
+ pos = { pos: 1 };
+ break;
+ }
+ case "top": {
+ pos = nextBlock.view.posAtCoords({
+ top: nextBlockViewClientRect.top + 12,
+ left: position.left,
+ });
+ break;
+ }
+ case "bottom": {
+ pos = nextBlock.view.posAtCoords({
+ top: nextBlockViewClientRect.bottom - 12,
+ left: position.left,
+ });
+ break;
+ }
+ case "coord": {
+ pos = nextBlock.view.posAtCoords({
+ top: position.top,
+ left: position.left,
+ });
+ break;
+ }
+ }
+
let newState = nextBlock.editor.apply(
tr.setSelection(TextSelection.create(tr.doc, pos?.pos || 0)),
);
diff --git a/components/TextBlock/index.tsx b/components/TextBlock/index.tsx
index ab15e2bf..ecad83c0 100644
--- a/components/TextBlock/index.tsx
+++ b/components/TextBlock/index.tsx
@@ -60,14 +60,18 @@ export function IOSBS(props: BlockProps) {
let selected = useUIState((s) =>
s.selectedBlock.find((b) => b.value === props.entityID),
);
- if (selected) return null;
+ let [initialRender, setInitialRender] = useState(true);
+ useEffect(() => {
+ setInitialRender(false);
+ }, []);
+ if (selected || initialRender || !isIOS()) return null;
return (
{
e.preventDefault();
let target = e.target;
+ console.log("what");
focusBlock(props, {
type: "coord",
top: e.clientY,
diff --git a/src/state/useEditorState.ts b/src/state/useEditorState.ts
new file mode 100644
index 00000000..fe716d5e
--- /dev/null
+++ b/src/state/useEditorState.ts
@@ -0,0 +1,31 @@
+import { create } from "zustand";
+import { EditorState } from "prosemirror-state";
+import { EditorView } from "prosemirror-view";
+export let useEditorStates = create(() => ({
+ lastXPosition: 0,
+ editorStates: {} as {
+ [entity: string]:
+ | {
+ editor: InstanceType;
+ view?: InstanceType;
+ }
+ | undefined;
+ },
+}));
+
+export const setEditorState = (
+ entityID: string,
+ s: {
+ editor: InstanceType;
+ },
+) => {
+ useEditorStates.setState((oldState) => {
+ let existingState = oldState.editorStates[entityID];
+ return {
+ editorStates: {
+ ...oldState.editorStates,
+ [entityID]: { ...existingState, ...s },
+ },
+ };
+ });
+};
diff --git a/src/utils/focusBlock.ts b/src/utils/focusBlock.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/src/utils/isVisible.ts b/src/utils/isVisible.ts
new file mode 100644
index 00000000..a95f52da
--- /dev/null
+++ b/src/utils/isVisible.ts
@@ -0,0 +1,18 @@
+export async function isVisible(el: Element) {
+ return new Promise((resolve) => {
+ const observer = new IntersectionObserver(
+ (entries, observer) => {
+ entries.forEach((entry) => {
+ resolve(entry.isIntersecting);
+ observer.unobserve(entry.target);
+ });
+ },
+ {
+ root: null, // Use the viewport as the root
+ threshold: 0.1, // Trigger when 10% of the element is visible
+ },
+ );
+
+ observer.observe(el);
+ });
+}