1
0
Fork 0
mirror of https://github.com/kou029w/quot.git synced 2025-01-19 00:18:09 +00:00

テキストの編集に対応

This commit is contained in:
Nebel 2022-08-28 17:49:49 +09:00
parent 34f4d26c84
commit e50e65c839
5 changed files with 227 additions and 21 deletions

158
app/package-lock.json generated
View file

@ -9,8 +9,10 @@
"version": "0.0.0",
"dependencies": {
"@exampledev/new.css": "^1.1.3",
"@lexical/plain-text": "^0.3.11",
"esbuild": "^0.15.5",
"esbuild-register": "^3.3.3",
"lexical": "^0.3.11",
"solid-js": "^1.4.8"
},
"devDependencies": {
@ -27,6 +29,90 @@
"resolved": "https://registry.npmjs.org/@exampledev/new.css/-/new.css-1.1.3.tgz",
"integrity": "sha512-qhbGfqBRwUlM6MCSaJdUfjq86opNCMvM+6kVvs6S0kYhy0V8dKbe4rDMIklEJGuMc5QH5OuPjdCReu9I0tim2w=="
},
"node_modules/@lexical/clipboard": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.3.11.tgz",
"integrity": "sha512-ly+9R2Rccz80eV9Gu0hOi701fXIyU50t+S7OR0Ensos09oAmQPae6poFkvS3k36tH+leLgk0hRZ47pB/+ejENA==",
"peer": true,
"dependencies": {
"@lexical/html": "0.3.11",
"@lexical/list": "0.3.11",
"@lexical/selection": "0.3.11",
"@lexical/utils": "0.3.11"
},
"peerDependencies": {
"lexical": "0.3.11"
}
},
"node_modules/@lexical/html": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/html/-/html-0.3.11.tgz",
"integrity": "sha512-cw6IjI+OQr6Dwm1Mvkws0HsMOZ0lIky/DIhSt7ZAcUya+N1sis3kNIhg5Gijvpao+/n17QPgOwWS2sE7PFESmg==",
"peer": true,
"dependencies": {
"@lexical/selection": "0.3.11"
},
"peerDependencies": {
"lexical": "0.3.11"
}
},
"node_modules/@lexical/list": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/list/-/list-0.3.11.tgz",
"integrity": "sha512-l79kqwFRTuUx+fNxRmYaqP27tB7va/MAKoRL1Jzv1EYBZjz0fEHxpaRBG7Y5JHSMMFRQrJEhL4hKMs2EiCecvQ==",
"peer": true,
"dependencies": {
"@lexical/utils": "0.3.11"
},
"peerDependencies": {
"lexical": "0.3.11"
}
},
"node_modules/@lexical/plain-text": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/plain-text/-/plain-text-0.3.11.tgz",
"integrity": "sha512-AfCHJPtY0g/gknYDCU2uZ5NHASSp1Gg7Ho7OuAi3sVOuVqs+SHI+ZkeC3y7MxrGhOP3TnZ+EsltAbToEQclSDA==",
"peerDependencies": {
"@lexical/clipboard": "0.3.11",
"@lexical/selection": "0.3.11",
"@lexical/utils": "0.3.11",
"lexical": "0.3.11"
}
},
"node_modules/@lexical/selection": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/selection/-/selection-0.3.11.tgz",
"integrity": "sha512-6SlXUmLP6K2E1OQFS2QcOiGe1fZV4o1vFLynq/st7BVzpQ1/bDMp2lnsnNWyA0H6v36n+wVUvipKsyFcrDtH/w==",
"peer": true,
"peerDependencies": {
"lexical": "0.3.11"
}
},
"node_modules/@lexical/table": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/table/-/table-0.3.11.tgz",
"integrity": "sha512-/6nDug81vfs6eI3u23JTRcCny1fsJ/gjM/ljERd6+f9rP+yXbStMKIyAsWebz6ZSskoyl5LIj8Po3PParhCdGg==",
"peer": true,
"dependencies": {
"@lexical/utils": "0.3.11"
},
"peerDependencies": {
"lexical": "0.3.11"
}
},
"node_modules/@lexical/utils": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/utils/-/utils-0.3.11.tgz",
"integrity": "sha512-J3HQjPSYg33Yd+g/SUx9Selqn27qCmXN253+0lIEH8R8pn2A+Pe4vrk+/DSr/URuN1GUq+jjLWci3SDsA4h3xQ==",
"peer": true,
"dependencies": {
"@lexical/list": "0.3.11",
"@lexical/table": "0.3.11"
},
"peerDependencies": {
"lexical": "0.3.11"
}
},
"node_modules/@tsconfig/node18-strictest-esm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tsconfig/node18-strictest-esm/-/node18-strictest-esm-1.0.0.tgz",
@ -97,6 +183,11 @@
"esbuild": ">=0.12 <1"
}
},
"node_modules/lexical": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/lexical/-/lexical-0.3.11.tgz",
"integrity": "sha512-HZvQ2T3g0jWBX6MC/A0HY1N7NMvR+FrmTfR4vn6WnoCg56UPlkgIX3GKa6rCyGOAnOtykXRPJ831JWEKJUHalQ=="
},
"node_modules/solid-js": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.4.8.tgz",
@ -122,6 +213,68 @@
"resolved": "https://registry.npmjs.org/@exampledev/new.css/-/new.css-1.1.3.tgz",
"integrity": "sha512-qhbGfqBRwUlM6MCSaJdUfjq86opNCMvM+6kVvs6S0kYhy0V8dKbe4rDMIklEJGuMc5QH5OuPjdCReu9I0tim2w=="
},
"@lexical/clipboard": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.3.11.tgz",
"integrity": "sha512-ly+9R2Rccz80eV9Gu0hOi701fXIyU50t+S7OR0Ensos09oAmQPae6poFkvS3k36tH+leLgk0hRZ47pB/+ejENA==",
"peer": true,
"requires": {
"@lexical/html": "0.3.11",
"@lexical/list": "0.3.11",
"@lexical/selection": "0.3.11",
"@lexical/utils": "0.3.11"
}
},
"@lexical/html": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/html/-/html-0.3.11.tgz",
"integrity": "sha512-cw6IjI+OQr6Dwm1Mvkws0HsMOZ0lIky/DIhSt7ZAcUya+N1sis3kNIhg5Gijvpao+/n17QPgOwWS2sE7PFESmg==",
"peer": true,
"requires": {
"@lexical/selection": "0.3.11"
}
},
"@lexical/list": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/list/-/list-0.3.11.tgz",
"integrity": "sha512-l79kqwFRTuUx+fNxRmYaqP27tB7va/MAKoRL1Jzv1EYBZjz0fEHxpaRBG7Y5JHSMMFRQrJEhL4hKMs2EiCecvQ==",
"peer": true,
"requires": {
"@lexical/utils": "0.3.11"
}
},
"@lexical/plain-text": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/plain-text/-/plain-text-0.3.11.tgz",
"integrity": "sha512-AfCHJPtY0g/gknYDCU2uZ5NHASSp1Gg7Ho7OuAi3sVOuVqs+SHI+ZkeC3y7MxrGhOP3TnZ+EsltAbToEQclSDA==",
"requires": {}
},
"@lexical/selection": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/selection/-/selection-0.3.11.tgz",
"integrity": "sha512-6SlXUmLP6K2E1OQFS2QcOiGe1fZV4o1vFLynq/st7BVzpQ1/bDMp2lnsnNWyA0H6v36n+wVUvipKsyFcrDtH/w==",
"peer": true,
"requires": {}
},
"@lexical/table": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/table/-/table-0.3.11.tgz",
"integrity": "sha512-/6nDug81vfs6eI3u23JTRcCny1fsJ/gjM/ljERd6+f9rP+yXbStMKIyAsWebz6ZSskoyl5LIj8Po3PParhCdGg==",
"peer": true,
"requires": {
"@lexical/utils": "0.3.11"
}
},
"@lexical/utils": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@lexical/utils/-/utils-0.3.11.tgz",
"integrity": "sha512-J3HQjPSYg33Yd+g/SUx9Selqn27qCmXN253+0lIEH8R8pn2A+Pe4vrk+/DSr/URuN1GUq+jjLWci3SDsA4h3xQ==",
"peer": true,
"requires": {
"@lexical/list": "0.3.11",
"@lexical/table": "0.3.11"
}
},
"@tsconfig/node18-strictest-esm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tsconfig/node18-strictest-esm/-/node18-strictest-esm-1.0.0.tgz",
@ -174,6 +327,11 @@
"integrity": "sha512-eFHOkutgIMJY5gc8LUp/7c+LLlDqzNi9T6AwCZ2WKKl3HmT+5ef3ZRyPPxDOynInML0fgaC50yszPKfPnjC0NQ==",
"requires": {}
},
"lexical": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/lexical/-/lexical-0.3.11.tgz",
"integrity": "sha512-HZvQ2T3g0jWBX6MC/A0HY1N7NMvR+FrmTfR4vn6WnoCg56UPlkgIX3GKa6rCyGOAnOtykXRPJ831JWEKJUHalQ=="
},
"solid-js": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.4.8.tgz",

View file

@ -12,8 +12,10 @@
"packageManager": "npm@8.15.0",
"dependencies": {
"@exampledev/new.css": "^1.1.3",
"@lexical/plain-text": "^0.3.11",
"esbuild": "^0.15.5",
"esbuild-register": "^3.3.3",
"lexical": "^0.3.11",
"solid-js": "^1.4.8"
},
"devDependencies": {

View file

@ -1,3 +1,10 @@
.editor {
inline-size: 100%;
padding: 1rem;
box-sizing: border-box;
background-color: var(--nc-bg-2);
color: var(--nc-tx-2);
border-width: 0.75rem;
border-top-style: solid;
border-top-color: var(--nc-bg-3);
box-shadow: 0 1px var(--nc-bg-3);
}

View file

@ -1,19 +1,43 @@
import {
$createParagraphNode,
$createTextNode,
$getRoot,
createEditor,
} from "lexical";
import { registerPlainText } from "@lexical/plain-text";
import { onCleanup, onMount } from "solid-js";
import type Pages from "../protocol/pages";
import "./editor.css";
const editor = createEditor();
function ref(el: HTMLElement) {
editor.setRootElement(el);
}
export default (props: {
id: number;
title: string;
text: string;
onUpdatePage: (content: Pages.RequestContentPage) => void;
}) => (
<textarea
id={String(props.id)}
class="editor"
autofocus
onInput={(e) => {
const text = e.currentTarget.value;
const lines = text.split("\n");
props.onUpdatePage({ id: props.id, title: lines[0] ?? "", text });
e.currentTarget.setAttribute("rows", String(Math.max(2, lines.length)));
}}
></textarea>
);
}) => {
const initialEditorState = () => {
const root = $getRoot();
if (root.getFirstChild()) return;
const paragraphNode = $createParagraphNode();
const textNode = $createTextNode(props.text);
paragraphNode.append(textNode);
root.append(paragraphNode);
};
onCleanup(registerPlainText(editor, initialEditorState));
onMount(() => {
onCleanup(
editor.registerTextContentListener((text) => {
const [title] = text.split("\n");
props.onUpdatePage({ id: props.id, title: title ?? "", text });
})
);
editor.focus();
});
return <article ref={ref} class="editor" contenteditable />;
};

View file

@ -1,7 +1,6 @@
import { createResource } from "solid-js";
import type Pages from "../protocol/pages";
import Editor from "../components/editor";
import Card from "../components/card";
import beforeunload from "../helpers/beforeunload";
import throttle from "../helpers/throttle";
@ -23,6 +22,14 @@ async function updatePage(
return res.ok;
}
async function deletePage(id: number): Promise<boolean> {
const res = await fetch(
new URL(`/pages?id=eq.${id}`, import.meta.env.QUOT_API_URL),
{ method: "DELETE" }
);
return res.ok;
}
async function fetchPage(id: number): Promise<Pages.ResponsePage> {
const res = await fetch(
new URL(`/pages?id=eq.${id}`, import.meta.env.QUOT_API_URL)
@ -32,13 +39,12 @@ async function fetchPage(id: number): Promise<Pages.ResponsePage> {
}
export default (props: { id: number }) => {
const [page, { refetch }] = createResource(props.id, fetchPage);
const [page] = createResource(props.id, fetchPage);
const throttledUpdate = throttle(
async (id: number, content: Pages.RequestContentPage) => {
if (await updatePage(id, content)) {
if (await (content.text ? updatePage(id, content) : deletePage(id))) {
unblock();
refetch();
window.history.replaceState({}, "", `/${id}`);
window.history.replaceState({}, "", `/${content.text ? id : "new"}`);
}
},
intervalMs
@ -51,8 +57,17 @@ export default (props: { id: number }) => {
return (
<main>
<Editor id={props.id} onUpdatePage={onUpdatePage} />
{() => <Card id={props.id} title="" text="" {...page()} />}
{() =>
!page.loading && (
<Editor
id={props.id}
title=""
text=""
{...page()}
onUpdatePage={onUpdatePage}
/>
)
}
</main>
);
};