mirror of
https://github.com/kou029w/quot.git
synced 2025-01-19 00:18:09 +00:00
テキストの編集に対応
This commit is contained in:
parent
34f4d26c84
commit
e50e65c839
5 changed files with 227 additions and 21 deletions
158
app/package-lock.json
generated
158
app/package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 />;
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue