diff --git a/global.css b/global.css index 7b7188b..6448ec2 100644 --- a/global.css +++ b/global.css @@ -11,8 +11,6 @@ body { font-family: 'Open Sans', 'Helvetica', sans-serif; - max-width: 800px; - margin: 0 auto; padding: 5px; } diff --git a/markdown-panel/index.html b/markdown-panel/index.html new file mode 100644 index 0000000..a4f6119 --- /dev/null +++ b/markdown-panel/index.html @@ -0,0 +1,13 @@ + + + + + Markdown Panel + + + + + + + + diff --git a/markdown-panel/markdown-panel.ts b/markdown-panel/markdown-panel.ts new file mode 100644 index 0000000..8fbf7e7 --- /dev/null +++ b/markdown-panel/markdown-panel.ts @@ -0,0 +1,75 @@ +import { LitElement, html, css } from 'lit-element' +import { customElement, state } from 'lit/decorators.js' +import { unsafeHTML } from 'lit/directives/unsafe-html.js' +import { marked } from 'marked' + +@customElement('markdown-panel') +export class MarkdownPanel extends LitElement { + @state({ type: String }) + mode: string = 'view' + + @state({ type: String }) + markdown = '' + + static styles = css` + p { + margin-top: 0; + margin-bottom: 16px; + } + + textarea { + width: 100%; + height: 50vh; + } + + button { + padding: 10px 20px; + } + + h1, + h2, + h3, + h4 { + font-family: 'Raleway', sans-serif; + margin-top: 0; + } + ` + + constructor() { + super() + grist.ready({ + onEditOptions: () => { + this.mode = 'edit' + }, + }) + grist.onOptions((options, interaction) => { + if (options.markdown) { + this.markdown = options.markdown + } else { + this.mode = 'edit' + } + }) + } + + saveText() { + const textarea = this.shadowRoot!.querySelector('textarea')! + grist.setOption('markdown', textarea.value) + this.markdown = textarea.value + this.mode = 'view' + } + + protected render() { + const rendered_markdown = unsafeHTML(marked(this.markdown) as string) + if (this.mode === 'edit') { + return html` + +
+ + +
+ ` + } else { + return html`
${rendered_markdown}
` + } + } +} diff --git a/package-lock.json b/package-lock.json index 19be164..2ec545e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.4", "dependencies": { "@vitejs/plugin-vue": "^5.0.4", + "lit": "^3.1.3", + "marked": "^12.0.2", "moment": "^2.30.1", "qrcode": "^1.5.3", "vue": "^3.4.27" @@ -380,6 +382,19 @@ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz", + "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==" + }, + "node_modules/@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", @@ -584,6 +599,11 @@ "optional": true, "peer": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, "node_modules/@vitejs/plugin-vue": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", @@ -851,6 +871,34 @@ "node": ">=8" } }, + "node_modules/lit": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.3.tgz", + "integrity": "sha512-l4slfspEsnCcHVRTvaP7YnkTZEZggNFywLEIhQaGhYDczG+tu/vlgm/KaWIEjIp+ZyV20r2JnZctMb8LeLCG7Q==", + "dependencies": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.0.4", + "lit-html": "^3.1.2" + } + }, + "node_modules/lit-element": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.5.tgz", + "integrity": "sha512-iTWskWZEtn9SyEf4aBG6rKT8GABZMrTWop1+jopsEOgEcugcXJGKuX5bEbkq9qfzY+XB4MAgCaSPwnNpdsNQ3Q==", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.1.2" + } + }, + "node_modules/lit-html": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.3.tgz", + "integrity": "sha512-FwIbqDD8O/8lM4vUZ4KvQZjPPNx7V1VhT7vmRB8RBAO0AU6wuTVdoXiu2CivVjEGdugvcbPNBLtPE1y0ifplHA==", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -870,6 +918,17 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", diff --git a/package.json b/package.json index 469531b..1faf4d5 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ }, "dependencies": { "@vitejs/plugin-vue": "^5.0.4", + "lit": "^3.1.3", + "marked": "^12.0.2", "moment": "^2.30.1", "qrcode": "^1.5.3", "vue": "^3.4.27" diff --git a/tent-sheet/tent-sheet.css b/tent-sheet/tent-sheet.css index a9d33aa..a669548 100644 --- a/tent-sheet/tent-sheet.css +++ b/tent-sheet/tent-sheet.css @@ -4,6 +4,8 @@ html { } body { + max-width: 800px; + margin: 0 auto; border: 1px solid #999; border-radius: 5px; padding-top: 0px; diff --git a/tsconfig.json b/tsconfig.json index a42c85c..deac7ab 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,9 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2022", "types": ["vite/client", "node"], - "useDefineForClassFields": true, "module": "esnext", - "lib": ["ES2020", "DOM", "DOM.Iterable", "WebWorker"], + "lib": ["ES2022", "DOM", "DOM.Iterable", "WebWorker"], "skipLibCheck": true, /* Bundler mode */ @@ -18,7 +17,10 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + + "experimentalDecorators": true, + "useDefineForClassFields": false, }, "ts-node": { "esm": true diff --git a/vite.config.ts b/vite.config.ts index 2dd964b..94f02e9 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,6 +7,7 @@ export default defineConfig({ input: { 'index': resolve(__dirname, 'index.html'), 'tent-sheet': resolve(__dirname, 'tent-sheet/index.html'), + 'markdown-panel': resolve(__dirname, 'markdown-panel/index.html'), }, }, },