Skip to content

Commit

Permalink
Code Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
R1c4rdCo5t4 committed Mar 29, 2024
1 parent 2ed1f39 commit f7c62d2
Show file tree
Hide file tree
Showing 25 changed files with 143 additions and 107 deletions.
2 changes: 1 addition & 1 deletion code/client/dev-dist/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ define(['./workbox-fda11f75'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "index.html",
"revision": "0.pmdqsqcitr8"
"revision": "0.45ptkuepdho"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
Expand Down
26 changes: 26 additions & 0 deletions code/client/src/editor/components/cursors/Cursor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Selection } from '@notespace/shared/types/cursor';

type CursorProps = {
selection: Selection;
color: string;
};

function Cursor({ selection, color }: CursorProps) {
// TODO: Get absolute position of cursor in pixels using selection and render cursor/selection accordingly
console.log(selection);
return (
<div
className="cursor"
style={{
position: 'absolute',
top: `px`,
left: `px`,
width: '2px',
height: '1.5em',
backgroundColor: color,
}}
/>
);
}

export default Cursor;
7 changes: 7 additions & 0 deletions code/client/src/editor/components/cursors/CursorData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Selection } from '../../../../../shared/types/cursor.ts';

export type CursorData = {
id: string;
selection: Selection;
color: string;
};
48 changes: 6 additions & 42 deletions code/client/src/editor/components/cursors/Cursors.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,24 @@
import { useState } from 'react';
import './CursorsManager.scss';
import useSocketListeners from '../../../socket/useSocketListeners.ts';

interface Cursor {
id: string;
position: { line: number; column: number };
color: string;
}
import { CursorData } from '@editor/components/cursors/CursorData.ts';
import Cursor from '@editor/components/cursors/Cursor.tsx';

function Cursors() {
const [cursors, setCursors] = useState<Cursor[]>([]);
const [cursors, setCursors] = useState<CursorData[]>([]);

const handleCursorChange = (cursor: Cursor) => {
const onCursorChange = (cursor: CursorData) => {
setCursors(prevCursors => {
const updatedCursors = prevCursors.filter(c => c.id !== cursor.id);
return [...updatedCursors, cursor];
});
};

useSocketListeners({
cursorChange: handleCursorChange,
cursorChange: onCursorChange,
});

const getCharacterWidth = (textarea: HTMLElement) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')!;
const computedStyle = getComputedStyle(textarea);
ctx.font = computedStyle.font;
return ctx.measureText('W').width;
};

const renderCursors = () => {
return cursors.map(cursor => {
const textarea = document.querySelector('textarea')!;
const fontSize = parseFloat(getComputedStyle(textarea).fontSize);
const lineHeight = fontSize * 1.2;
const characterWidth = getCharacterWidth(textarea);
const top = textarea.offsetTop + (cursor.position.line - 1) * lineHeight - 2;
const left = textarea.offsetLeft + (cursor.position.column - 1) * characterWidth + 1;
return (
<div
className="cursor"
key={cursor.id}
style={{
position: 'absolute',
top: `${top}px`,
left: `${left}px`,
width: '2px',
height: '1.5em',
backgroundColor: cursor.color,
}}
/>
);
});
};
return <>{renderCursors()}</>;
return cursors.map(cursor => <Cursor key={cursor.id} color={cursor.color} selection={cursor.selection} />);
}

export default Cursors;
2 changes: 1 addition & 1 deletion code/client/src/editor/crdt/fugue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type StyleOperation,
} from '@notespace/shared/crdt/types/operations';
import { type Node, type Id } from '@notespace/shared/crdt/types/nodes';
import { type Style } from '@notespace/shared/crdt/types/styles';
import { type Style } from '../../../../shared/types/styles';
import { FugueTree } from '@notespace/shared/crdt/FugueTree';
import { generateReplicaId } from './utils';
import { socket } from '@src/socket/socket';
Expand Down
2 changes: 1 addition & 1 deletion code/client/src/editor/crdt/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Style } from '@notespace/shared/crdt/types/styles';
import { type Style } from '../../../../shared/types/styles.ts';

export type InsertNode = {
value: string;
Expand Down
2 changes: 1 addition & 1 deletion code/client/src/editor/crdt/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { range } from 'lodash';
import type { Style } from '@notespace/shared/crdt/types/styles';
import type { Style } from '../../../../shared/types/styles.ts';
import type { InsertNode } from '@editor/crdt/types';

const BASE64CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
Expand Down
7 changes: 4 additions & 3 deletions code/client/src/editor/slate/SlateEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Editable, Slate, withReact } from 'slate-react';
import useInputHandlers from '@editor/slate/hooks/useInputHandlers';
import useInputHandlers from '@editor/slate/hooks/useInputHandlers.ts';
import useFugue from '@editor/hooks/useFugue';
import useEvents from '@editor/hooks/useEvents';
import useRenderers from '@editor/slate/hooks/useRenderers';
import './SlateEditor.scss';
import Toolbar from '@editor/slate/toolbar/Toolbar';
import { withHistory } from 'slate-history';
import useEditor from '@editor/slate/hooks/useEditor';
import { withMarkdown } from '@editor/slate/plugins/markdown/withMarkdown';
import { toSlate } from '@editor/slate/utils/toSlate';
import './SlateEditor.scss';

const initialValue = [
{
Expand All @@ -20,7 +20,7 @@ const initialValue = [
function SlateEditor() {
const editor = useEditor(withHistory, withReact, withMarkdown);
const fugue = useFugue();
const { onKeyDown, onPaste, onCut } = useInputHandlers(editor, fugue);
const { onKeyDown, onPaste, onCut, onSelect } = useInputHandlers(editor, fugue);
const { renderElement, renderLeaf } = useRenderers();

useEvents(fugue, () => {
Expand All @@ -46,6 +46,7 @@ function SlateEditor() {
onKeyDown={onKeyDown}
onPaste={onPaste}
onCut={onCut}
onSelect={onSelect}
/>
</Slate>
</div>
Expand Down
107 changes: 75 additions & 32 deletions code/client/src/editor/slate/hooks/useInputHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { type Editor } from 'slate';
import { getSelection } from '../utils/selection';
import { isEqual } from 'lodash';
import { insertNode } from '@src/editor/crdt/utils';
import { Cursor, Selection } from '@notespace/shared/types/cursor';
import { socket } from '@src/socket/socket.ts';

const hotkeys: Record<string, string> = {
b: 'bold',
Expand All @@ -15,37 +17,73 @@ const hotkeys: Record<string, string> = {
function useInputHandlers(editor: Editor, fugue: Fugue) {
function onKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
if (e.ctrlKey) return shortcutHandler(e);

const selection = getSelection(editor);
const { start, end } = selection;
switch (e.key) {
case 'Enter':
fugue.insertLocal(start, insertNode('\n', []));
onEnter(selection.start);
break;
case 'Backspace': {
const startPosition = { line: 0, column: 0 };
if (isEqual(startPosition, start) && isEqual(startPosition, end)) break;
fugue.deleteLocal(selection);
onBackspace(selection);
break;
}
case 'Tab':
e.preventDefault();
editor.insertText('\t');
fugue.insertLocal(start, insertNode('\t', []));
onTab(selection.start);
break;
default: {
if (e.key.length !== 1) break;
if (selection.start.column !== selection.end.column) {
fugue.deleteLocal(selection); // replace selection
}
const previousNode = fugue.getNodeByCursor(start);
const styles = previousNode?.styles || [];
fugue.insertLocal(start, insertNode(e.key, styles));
onKey(e.key, selection);
break;
}
}
}

function shortcutHandler(event: React.KeyboardEvent<HTMLDivElement>) {
switch (event.key) {
case 'z':
onUndo();
break;
case 'y':
onRedo();
break;
case 'Backspace':
onCtrlBackspace();
break;
case 'Delete':
onCtrlDelete();
break;
default: {
onFormat(event.key);
}
}
}

function onKey(key: string, selection: Selection) {
if (selection.start.column !== selection.end.column) {
fugue.deleteLocal(selection); // replace selection
}
const previousNode = fugue.getNodeByCursor(selection.start);
const styles = previousNode?.styles || [];
fugue.insertLocal(selection.start, insertNode(key, styles));
}

function onEnter(cursor: Cursor) {
fugue.insertLocal(cursor, insertNode('\n', []));
}

function onBackspace(selection: Selection) {
const startPosition = { line: 0, column: 0 };
if ([startPosition, selection.start, selection.end].every(isEqual)) {
return; // beginning of document
}
fugue.deleteLocal(selection);
}

function onTab(cursor: Cursor) {
editor.insertText('\t');
fugue.insertLocal(cursor, insertNode('\t', []));
}

function onPaste(e: React.ClipboardEvent<HTMLDivElement>) {
const clipboardData = e.clipboardData?.getData('text');
if (!clipboardData) return;
Expand All @@ -56,33 +94,38 @@ function useInputHandlers(editor: Editor, fugue: Fugue) {
}

function onCut() {
const selection = getSelection(editor); // problem here
fugue.deleteLocal(selection); // TODO: Fix this
const selection = getSelection(editor);
fugue.deleteLocal(selection);
}

function onUndo() {
// TODO: Implement undo
// TODO: Implement undo (broadcast to other clients)
}

function onRedo() {
// TODO: Implement redo
// TODO: Implement redo (broadcast to other clients)
}

function shortcutHandler(event: React.KeyboardEvent<HTMLDivElement>) {
switch (event.key) {
case 'z':
onUndo();
break;
case 'y':
onRedo();
break;
default: {
const mark = hotkeys[event.key];
CustomEditor.toggleMark(editor, mark, fugue);
}
}
function onCtrlBackspace() {
// TODO: Implement delete word to the left of the cursor (broadcast to other clients)
}
return { onKeyDown, onPaste, onCut };

function onCtrlDelete() {
// TODO: Implement delete word to the right of the cursor (broadcast to other clients)
}

function onFormat(key: string) {
const mark = hotkeys[key];
if (!mark) return;
CustomEditor.toggleMark(editor, mark, fugue);
}

function onSelect() {
const selection = getSelection(editor);
socket.emit('cursorChange', selection);
}

return { onKeyDown, onPaste, onCut, onSelect };
}

export default useInputHandlers;
2 changes: 1 addition & 1 deletion code/client/src/editor/slate/model/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type BaseEditor, type Descendant } from 'slate';
import { type ReactEditor } from 'slate-react';
import { type HistoryEditor } from 'slate-history';
import { type BlockStyle } from '@notespace/shared/crdt/types/styles';
import { type BlockStyle } from '../../../../../shared/types/styles.ts';

export interface CustomFormat {
bold?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion code/client/src/editor/slate/model/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type Descendant, Editor, Range } from 'slate';
import { type CustomText } from '@editor/slate/model/types.ts';
import { type BlockStyle } from '@notespace/shared/crdt/types/styles';
import { type BlockStyle } from '../../../../../shared/types/styles.ts';

/**
* Creates a descendant object.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type ReactNode } from 'react';
import { BlockStyles } from '@notespace/shared/crdt/types/styles';
import { BlockStyles } from '../../../../../../../shared/types/styles.ts';
import { RenderElementProps } from 'slate-react';
import {
Blockquote,
Expand Down
2 changes: 1 addition & 1 deletion code/client/src/editor/slate/plugins/markdown/shortcuts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type CustomElement } from '@editor/slate/model/types.ts';
import { BlockStyles, InlineStyles } from '@notespace/shared/crdt/types/styles';
import { BlockStyles, InlineStyles } from '../../../../../../shared/types/styles.ts';
import { type Editor, Element, Range, Text, Transforms } from 'slate';
import { createDescendant } from '@editor/slate/model/utils.ts';

Expand Down
17 changes: 8 additions & 9 deletions code/client/src/editor/slate/utils/selection.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { Editor, Range, Point, Text, Node, Path } from 'slate';
import { Cursor, Selection } from '../model/cursor.ts';

function emptySelection(): Selection {
return {
start: { line: 0, column: 0 },
end: { line: 0, column: 0 },
};
}
import { Cursor, Selection } from '@notespace/shared/types/cursor';

export function isSelected(editor: Editor) {
if (!editor.selection) return false;
Expand Down Expand Up @@ -58,6 +51,12 @@ function getAbsoluteOffset(editor: Editor, point: Point): number {
offset = 0;
}
}

return offset;
}

function emptySelection(): Selection {
return {
start: { line: 0, column: 0 },
end: { line: 0, column: 0 },
};
}
2 changes: 1 addition & 1 deletion code/client/src/editor/slate/utils/toSlate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Descendant } from 'slate';
import type { Style, BlockStyle } from '../../../../../shared/crdt/types/styles';
import type { Style, BlockStyle } from '../../../../../shared/types/styles.ts';
import type { CustomText } from '@editor/slate/model/types.ts';
import { isEmpty, isEqual } from 'lodash';
import { createChildren, createDescendant } from '@editor/slate/model/utils.ts';
Expand Down
3 changes: 1 addition & 2 deletions code/client/src/socket/socket.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { io } from 'socket.io-client';

const SOCKET_SERVER_URL = 'ws://localhost:8080';
const socketConfig = {};
export const socket = io(SOCKET_SERVER_URL, socketConfig);
export const socket = io(SOCKET_SERVER_URL);
Loading

0 comments on commit f7c62d2

Please sign in to comment.