From 4038e280a1e7429f7651b8110542471649e1626d Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 22 Jan 2022 18:16:36 +0100 Subject: [PATCH 01/21] feat: monaco editor Signed-off-by: peterpeterparker --- .gitignore | 2 + CHANGELOG.md | 4 + README.md | 1 + package-lock.json | 28 +++++++ studio/package-lock.json | 18 +++++ studio/package.json | 1 + .../app-code-editor/app-code-editor.scss | 9 +++ .../app-code-editor/app-code-editor.tsx | 77 +++++++++++++++++++ studio/src/app/plugins/code.plugin.ts | 25 ++---- studio/src/app/utils/editor/plugin.utils.ts | 18 +++++ studio/src/components.d.ts | 13 ++++ studio/stencil.config.ts | 6 +- webcomponents/monaco-editor/CHANGELOG.md | 5 ++ webcomponents/monaco-editor/LICENSE | 21 +++++ webcomponents/monaco-editor/README.md | 67 ++++++++++++++++ webcomponents/monaco-editor/esbuild.js | 33 ++++++++ webcomponents/monaco-editor/package.json | 46 +++++++++++ .../monaco-editor/src/components.d.ts | 38 +++++++++ .../src/components/monaco-editor.scss | 11 +++ .../src/components/monaco-editor.tsx | 51 ++++++++++++ webcomponents/monaco-editor/src/index.html | 43 +++++++++++ webcomponents/monaco-editor/src/index.ts | 1 + .../monaco-editor/src/interface.d.ts | 1 + webcomponents/monaco-editor/stencil.config.ts | 28 +++++++ webcomponents/monaco-editor/tsconfig.json | 5 ++ 25 files changed, 532 insertions(+), 20 deletions(-) create mode 100644 studio/src/app/modals/editor/app-code-editor/app-code-editor.scss create mode 100644 studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx create mode 100644 webcomponents/monaco-editor/CHANGELOG.md create mode 100644 webcomponents/monaco-editor/LICENSE create mode 100644 webcomponents/monaco-editor/README.md create mode 100644 webcomponents/monaco-editor/esbuild.js create mode 100644 webcomponents/monaco-editor/package.json create mode 100644 webcomponents/monaco-editor/src/components.d.ts create mode 100644 webcomponents/monaco-editor/src/components/monaco-editor.scss create mode 100644 webcomponents/monaco-editor/src/components/monaco-editor.tsx create mode 100644 webcomponents/monaco-editor/src/index.html create mode 100644 webcomponents/monaco-editor/src/index.ts create mode 100644 webcomponents/monaco-editor/src/interface.d.ts create mode 100644 webcomponents/monaco-editor/stencil.config.ts create mode 100644 webcomponents/monaco-editor/tsconfig.json diff --git a/.gitignore b/.gitignore index f6b22d143..ad3c707c0 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ config.prod.json **/assetlinks.prod.json storybook-static/ + +webcomponents/monaco-editor/workers diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d870df2e..cd9065d8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ - color - inline-editor +### Web Components: New + +- monaco-editor: v1.0.0 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/main/webcomponents/monaco-editor/CHANGELOG.md)) + ### Others - utils: v4.10.0 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/main/utils/utils/CHANGELOG.md)) diff --git a/README.md b/README.md index a62baa77a..c929bb0ca 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ If you like the project, you can become a sponsor at [Open Collective](https://o | **Lazy image** | [`@deckdeckgo/lazy-img`](https://www.npmjs.com/package/@deckdeckgo/lazy-img) | [![version](https://img.shields.io/npm/v/@deckdeckgo/lazy-img/latest.svg?color=success)](https://www.npmjs.com/package/@deckdeckgo/lazy-img) | [`README`](webcomponents/lazy-img/README.md) | [`CHANGELOG`](webcomponents/lazy-img/CHANGELOG.md) | | **Math** | [`@deckdeckgo/math`](https://www.npmjs.com/package/@deckdeckgo/math) | [![version](https://img.shields.io/npm/v/@deckdeckgo/math/latest.svg?color=success)](https://www.npmjs.com/package/@deckdeckgo/math) | [`README`](webcomponents/math/README.md) | [`CHANGELOG`](webcomponents/math/CHANGELOG.md) | | **Markdown** | [`@deckdeckgo/markdown`](https://www.npmjs.com/package/@deckdeckgo/markdown) | [![version](https://img.shields.io/npm/v/@deckdeckgo/markdown/latest.svg?color=success)](https://www.npmjs.com/package/@deckdeckgo/markdown) | [`README`](webcomponents/markdown/README.md) | [`CHANGELOG`](webcomponents/markdown/CHANGELOG.md) | +| **Monaco Editor** | [`@deckdeckgo/monaco-editor`](https://www.npmjs.com/package/@deckdeckgo/monaco-editor) | [![version](https://img.shields.io/npm/v/@deckdeckgo/monaco-editor/latest.svg?color=success)](https://www.npmjs.com/package/@deckdeckgo/monaco-editor) | [`README`](webcomponents/monaco-editor/README.md) | [`CHANGELOG`](webcomponents/monaco-editor/CHANGELOG.md) | | **Pager** | [`@deckdeckgo/pager`](https://www.npmjs.com/package/@deckdeckgo/pager) | [![version](https://img.shields.io/npm/v/@deckdeckgo/pager/latest.svg?color=success)](https://www.npmjs.com/package/@deckdeckgo/pager) | [`README`](webcomponents/pager/README.md) | [`CHANGELOG`](webcomponents/pager/CHANGELOG.md) | | **Remote** | [`@deckdeckgo/remote`](https://www.npmjs.com/package/@deckdeckgo/remote) | [![version](https://img.shields.io/npm/v/@deckdeckgo/remote/latest.svg?color=success)](https://www.npmjs.com/package/@deckdeckgo/remote) | [`README`](webcomponents/remote/README.md) | [`CHANGELOG`](webcomponents/remote/CHANGELOG.md) | | **Social Image** | [`@deckdeckgo/social-img`](https://www.npmjs.com/package/@deckdeckgo/social-img) | [![version](https://img.shields.io/npm/v/@deckdeckgo/social-img/latest.svg?color=success)](https://www.npmjs.com/package/@deckdeckgo/social-img) | [`README`](webcomponents/social-img/README.md) | [`CHANGELOG`](webcomponents/social-img/CHANGELOG.md) | diff --git a/package-lock.json b/package-lock.json index 40c956fb1..932f1e0b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -652,6 +652,10 @@ "resolved": "webcomponents/math", "link": true }, + "node_modules/@deckdeckgo/monaco-editor": { + "resolved": "webcomponents/monaco-editor", + "link": true + }, "node_modules/@deckdeckgo/pager": { "resolved": "webcomponents/pager", "link": true @@ -7622,6 +7626,11 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/monaco-editor": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz", + "integrity": "sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==" + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -12003,6 +12012,14 @@ "@types/katex": "^0.11.1" } }, + "webcomponents/monaco-editor": { + "name": "@deckdeckgo/monaco-editor", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "monaco-editor": "^0.31.1" + } + }, "webcomponents/pager": { "name": "@deckdeckgo/pager", "version": "1.1.0", @@ -12768,6 +12785,12 @@ "katex": "^0.13.18" } }, + "@deckdeckgo/monaco-editor": { + "version": "file:webcomponents/monaco-editor", + "requires": { + "monaco-editor": "*" + } + }, "@deckdeckgo/pager": { "version": "file:webcomponents/pager" }, @@ -18489,6 +18512,11 @@ "minimist": "^1.2.5" } }, + "monaco-editor": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz", + "integrity": "sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==" + }, "mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", diff --git a/studio/package-lock.json b/studio/package-lock.json index 9be50750e..27c6ddda7 100644 --- a/studio/package-lock.json +++ b/studio/package-lock.json @@ -20,6 +20,7 @@ "@deckdeckgo/lazy-img": "^3.2.0", "@deckdeckgo/markdown": "^2.1.0", "@deckdeckgo/math": "^2.1.0", + "@deckdeckgo/monaco-editor": "file:../webcomponents/monaco-editor", "@deckdeckgo/remote": "^2.2.0", "@deckdeckgo/slide-aspect-ratio": "^3.4.0", "@deckdeckgo/slide-author": "^2.4.0", @@ -135,6 +136,13 @@ "@deckdeckgo/utils": "^3.0.0" } }, + "../webcomponents/monaco-editor": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "monaco-editor": "^0.31.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", @@ -1879,6 +1887,10 @@ "katex": "^0.13.11" } }, + "node_modules/@deckdeckgo/monaco-editor": { + "resolved": "../webcomponents/monaco-editor", + "link": true + }, "node_modules/@deckdeckgo/remote": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@deckdeckgo/remote/-/remote-2.2.0.tgz", @@ -5696,6 +5708,12 @@ "katex": "^0.13.11" } }, + "@deckdeckgo/monaco-editor": { + "version": "file:../webcomponents/monaco-editor", + "requires": { + "monaco-editor": "^0.31.1" + } + }, "@deckdeckgo/remote": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@deckdeckgo/remote/-/remote-2.2.0.tgz", diff --git a/studio/package.json b/studio/package.json index a5f623520..62dd990c4 100644 --- a/studio/package.json +++ b/studio/package.json @@ -33,6 +33,7 @@ "@deckdeckgo/lazy-img": "^3.2.0", "@deckdeckgo/markdown": "^2.1.0", "@deckdeckgo/math": "^2.1.0", + "@deckdeckgo/monaco-editor": "file:../webcomponents/monaco-editor", "@deckdeckgo/remote": "^2.2.0", "@deckdeckgo/slide-aspect-ratio": "^3.4.0", "@deckdeckgo/slide-author": "^2.4.0", diff --git a/studio/src/app/modals/editor/app-code-editor/app-code-editor.scss b/studio/src/app/modals/editor/app-code-editor/app-code-editor.scss new file mode 100644 index 000000000..7a5285180 --- /dev/null +++ b/studio/src/app/modals/editor/app-code-editor/app-code-editor.scss @@ -0,0 +1,9 @@ +app-code-editor { + @import "../../../../global/theme/editor/editor-modal"; + + deckgo-monaco-editor { + border: 1px solid #dedede; + display: block; + height: 100%; + } +} diff --git a/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx b/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx new file mode 100644 index 000000000..d239e79be --- /dev/null +++ b/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx @@ -0,0 +1,77 @@ +import {Component, ComponentInterface, Element, Fragment, h, Listen} from '@stencil/core'; + +import i18n from '../../../stores/i18n.store'; + +import {AppIcon} from '../../../components/core/app-icon/app-icon'; + +import '@deckdeckgo/monaco-editor'; + +// @ts-ignore +self.MonacoEnvironment = { + getWorkerUrl: function (_moduleId, label) { + if (label === 'json') { + return './build/json.worker.js'; + } + if (label === 'css' || label === 'scss' || label === 'less') { + return './build/css.worker.js'; + } + if (label === 'html' || label === 'handlebars' || label === 'razor') { + return './build/html.worker.js'; + } + if (label === 'typescript' || label === 'javascript') { + return './build/ts.worker.js'; + } + return './build/editor.worker.js'; + } +}; + +@Component({ + tag: 'app-code-editor', + styleUrl: 'app-code-editor.scss' +}) +export class AppCodeEditor implements ComponentInterface { + @Element() el: HTMLElement; + + componentDidLoad() { + history.pushState({modal: true}, null); + } + + @Listen('popstate', {target: 'window'}) + async handleHardwareBackButton(_e: PopStateEvent) { + await this.closeModal(); + } + + private async closeModal() { + await (this.el.closest('ion-modal') as HTMLIonModalElement).dismiss(); + } + + render() { + return ( + + + + + await this.closeModal()} aria-label={i18n.state.core.close}> + + + + {i18n.state.editor.code} + + + + + + + + +
+ + {i18n.state.core.save} + +
+
+
+
+ ); + } +} diff --git a/studio/src/app/plugins/code.plugin.ts b/studio/src/app/plugins/code.plugin.ts index 22e6d311c..ed30d421d 100644 --- a/studio/src/app/plugins/code.plugin.ts +++ b/studio/src/app/plugins/code.plugin.ts @@ -1,24 +1,11 @@ -import {createEmptyElement, StyloPlugin, StyloPluginCreateParagraphsParams, transformParagraph} from '@deckdeckgo/stylo'; +import {StyloPlugin, StyloPluginCreateParagraphsParams} from '@deckdeckgo/stylo'; -const createSlottedCode = (): HTMLElement => { - const code: HTMLElement = document.createElement('code'); - code.setAttribute('slot', 'code'); - return code; -}; +import i18n from '../stores/i18n.store'; + +import {openCodeModal} from '../utils/editor/plugin.utils'; export const code: StyloPlugin = { - text: 'code', + text: i18n.state.editor.code, icon: 'code', - createParagraphs: async ({container, paragraph}: StyloPluginCreateParagraphsParams) => { - const code: HTMLElement = document.createElement('deckgo-highlight-code'); - - code.setAttribute('editable', 'true'); - code.append(createSlottedCode()); - - transformParagraph({ - elements: [code, createEmptyElement({nodeName: 'div'})], - paragraph, - container - }); - } + createParagraphs: (pluginParams: StyloPluginCreateParagraphsParams) => openCodeModal({pluginParams}) }; diff --git a/studio/src/app/utils/editor/plugin.utils.ts b/studio/src/app/utils/editor/plugin.utils.ts index e96b2f1bd..782a578ca 100644 --- a/studio/src/app/utils/editor/plugin.utils.ts +++ b/studio/src/app/utils/editor/plugin.utils.ts @@ -57,3 +57,21 @@ export const openPluginModal = async ({ await modal.present(); }; + +export const openCodeModal = async ({pluginParams}: {pluginParams: StyloPluginCreateParagraphsParams}) => { + const modal: HTMLIonModalElement = await modalController.create({ + component: 'app-code-editor' + }); + + modal.onDidDismiss().then(({data: unsplashImage}: OverlayEventDetail) => { + const {container, paragraph} = pluginParams; + + createParagraphImage({ + image: unsplashImage, + container, + paragraph + }); + }); + + await modal.present(); +}; diff --git a/studio/src/components.d.ts b/studio/src/components.d.ts index 79c5e9b8e..dc6df0118 100644 --- a/studio/src/components.d.ts +++ b/studio/src/components.d.ts @@ -94,6 +94,8 @@ export namespace Components { "codeDidChange": EventEmitter; "selectedElement": HTMLElement; } + interface AppCodeEditor { + } interface AppCodeLanguages { "codeDidChange": EventEmitter; "currentLanguage": PrismLanguage | undefined; @@ -548,6 +550,12 @@ declare global { prototype: HTMLAppCodeElement; new (): HTMLAppCodeElement; }; + interface HTMLAppCodeEditorElement extends Components.AppCodeEditor, HTMLStencilElement { + } + var HTMLAppCodeEditorElement: { + prototype: HTMLAppCodeEditorElement; + new (): HTMLAppCodeEditorElement; + }; interface HTMLAppCodeLanguagesElement extends Components.AppCodeLanguages, HTMLStencilElement { } var HTMLAppCodeLanguagesElement: { @@ -1234,6 +1242,7 @@ declare global { "app-close-menu": HTMLAppCloseMenuElement; "app-cloud-wait": HTMLAppCloudWaitElement; "app-code": HTMLAppCodeElement; + "app-code-editor": HTMLAppCodeEditorElement; "app-code-languages": HTMLAppCodeLanguagesElement; "app-color": HTMLAppColorElement; "app-color-chart": HTMLAppColorChartElement; @@ -1443,6 +1452,8 @@ declare namespace LocalJSX { "codeDidChange"?: EventEmitter; "selectedElement"?: HTMLElement; } + interface AppCodeEditor { + } interface AppCodeLanguages { "codeDidChange"?: EventEmitter; "currentLanguage"?: PrismLanguage | undefined; @@ -1849,6 +1860,7 @@ declare namespace LocalJSX { "app-close-menu": AppCloseMenu; "app-cloud-wait": AppCloudWait; "app-code": AppCode; + "app-code-editor": AppCodeEditor; "app-code-languages": AppCodeLanguages; "app-color": AppColor; "app-color-chart": AppColorChart; @@ -1985,6 +1997,7 @@ declare module "@stencil/core" { "app-close-menu": LocalJSX.AppCloseMenu & JSXBase.HTMLAttributes; "app-cloud-wait": LocalJSX.AppCloudWait & JSXBase.HTMLAttributes; "app-code": LocalJSX.AppCode & JSXBase.HTMLAttributes; + "app-code-editor": LocalJSX.AppCodeEditor & JSXBase.HTMLAttributes; "app-code-languages": LocalJSX.AppCodeLanguages & JSXBase.HTMLAttributes; "app-color": LocalJSX.AppColor & JSXBase.HTMLAttributes; "app-color-chart": LocalJSX.AppColorChart & JSXBase.HTMLAttributes; diff --git a/studio/stencil.config.ts b/studio/stencil.config.ts index 49ba3b356..d819c19bd 100644 --- a/studio/stencil.config.ts +++ b/studio/stencil.config.ts @@ -38,7 +38,11 @@ export const config: Config = { serviceWorker: { swSrc: 'src/sw.js' }, - copy: [{src: 'robots.txt'}, {src: `${assetLinks}`, dest: `.well-known/assetlinks.json`}] + copy: [ + {src: 'robots.txt'}, + {src: `${assetLinks}`, dest: `.well-known/assetlinks.json`}, + {src: `${__dirname}/node_modules/@deckdeckgo/monaco-editor/workers/`, dest: `${__dirname}/www/build`} + ] } ], globalScript: globalScript, diff --git a/webcomponents/monaco-editor/CHANGELOG.md b/webcomponents/monaco-editor/CHANGELOG.md new file mode 100644 index 000000000..fd3ff444e --- /dev/null +++ b/webcomponents/monaco-editor/CHANGELOG.md @@ -0,0 +1,5 @@ +# 1.0.0 (2021-12-16) + +### Features + +- Hello World 👋 diff --git a/webcomponents/monaco-editor/LICENSE b/webcomponents/monaco-editor/LICENSE new file mode 100644 index 000000000..eff418e7f --- /dev/null +++ b/webcomponents/monaco-editor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 David Dal Busco and Nicolas Mattia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/webcomponents/monaco-editor/README.md b/webcomponents/monaco-editor/README.md new file mode 100644 index 000000000..841462d40 --- /dev/null +++ b/webcomponents/monaco-editor/README.md @@ -0,0 +1,67 @@ +[![npm][npm-badge]][npm-badge-url] +[![license][npm-license]][npm-license-url] + +[npm-badge]: https://img.shields.io/npm/v/@deckdeckgo/monaco-editor +[npm-badge-url]: https://www.npmjs.com/package/@deckdeckgo/monaco-editor +[npm-license]: https://img.shields.io/npm/l/@deckdeckgo/monaco-editor +[npm-license-url]: https://github.com/deckgo/deckdeckgo/blob/main/webcomponenents/monaco-editor/LICENSE + +# DeckDeckGo - Monaco Editor + +A web component to easily embed the [Monaco Editor](https://microsoft.github.io/monaco-editor/). + +## Installation + +``` +npm i @deckdeckgo/monaco-editor +``` + +## Usage + +1. Import the component in your application, for example with an `import` script. + +``` +import @deckdeckgo/monaco-editor +``` + +2. Copy the pre-compiled workers to your `public` folder or a sub-folder. + +``` +copy: [ + {src: `${__dirname}/node_modules/@deckdeckgo/monaco-editor/workers/`, dest: `${__dirname}/public`} +] +``` + +3. Configure where the worker scripts are located i.e. add following script in your application (update the path `./` if you have copied the scripts in sub-folders). + +``` +self.MonacoEnvironment = { + getWorkerUrl: function (_moduleId, label) { + if (label === 'json') { + return './json.worker.js'; + } + if (label === 'css' || label === 'scss' || label === 'less') { + return './css.worker.js'; + } + if (label === 'html' || label === 'handlebars' || label === 'razor') { + return './html.worker.js'; + } + if (label === 'typescript' || label === 'javascript') { + return './ts.worker.js'; + } + return './editor.worker.js'; + } +}; +``` + +4. Use the component + +``` + +``` + +## License + +MIT © [David Dal Busco](mailto:david.dalbusco@outlook.com) and [Nicolas Mattia](mailto:nicolas@nmattia.com) + +[deckdeckgo]: https://deckdeckgo.com diff --git a/webcomponents/monaco-editor/esbuild.js b/webcomponents/monaco-editor/esbuild.js new file mode 100644 index 000000000..11621e5bf --- /dev/null +++ b/webcomponents/monaco-editor/esbuild.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +const esbuild = require('esbuild'); +const path = require('path'); +const {copyFileSync} = require('fs'); + +const ROOT = path.join(process.cwd(), '..', '..', 'node_modules/monaco-editor/esm/vs'); +const WORKERS = path.join(process.cwd(), 'workers'); +const WWW = path.join(process.cwd(), 'www', 'build'); + +esbuild + .build({ + entryPoints: { + 'editor.worker': `${ROOT}/editor/editor.worker.js`, + 'json.worker': `${ROOT}/language/json/json.worker`, + 'css.worker': `${ROOT}/language/css/css.worker`, + 'html.worker': `${ROOT}/language/html/html.worker`, + 'ts.worker': `${ROOT}/language/typescript/ts.worker` + }, + entryNames: '[name]', + outdir: WORKERS, + bundle: true, + sourcemap: false, + minify: true, + target: ['esnext'] + }) + .catch(() => process.exit(1)); + +copyFileSync(`${WORKERS}/editor.worker.js`, `${WWW}/editor.worker.js`); +copyFileSync(`${WORKERS}/json.worker.js`, `${WWW}/json.worker.js`); +copyFileSync(`${WORKERS}/css.worker.js`, `${WWW}/css.worker.js`); +copyFileSync(`${WORKERS}/html.worker.js`, `${WWW}/html.worker.js`); +copyFileSync(`${WORKERS}/ts.worker.js`, `${WWW}/ts.worker.js`); diff --git a/webcomponents/monaco-editor/package.json b/webcomponents/monaco-editor/package.json new file mode 100644 index 000000000..b00fe10a9 --- /dev/null +++ b/webcomponents/monaco-editor/package.json @@ -0,0 +1,46 @@ +{ + "name": "@deckdeckgo/monaco-editor", + "version": "1.0.0", + "description": "A Monaco Editor web component", + "main": "dist/index.cjs.js", + "module": "dist/index.js", + "es2015": "dist/esm/index.js", + "es2017": "dist/esm/index.js", + "jsnext:main": "dist/esm/index.js", + "types": "dist/types/components.d.ts", + "collection": "dist/collection/collection-manifest.json", + "collection:main": "dist/collection/index.js", + "unpkg": "dist/elements/elements.esm.js", + "files": [ + "dist/", + "workers/", + "README.md", + "LICENSE" + ], + "scripts": { + "build:workers": "node esbuild.js", + "build": "stencil build && npm run build:workers", + "start": "stencil build --dev --watch --serve", + "test": "stencil test --spec --e2e", + "test.watch": "stencil test --spec --e2e --watchAll" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/deckgo/deckdeckgo.git", + "directory": "webcomponents/monaco-editor" + }, + "author": "David Dal Busco", + "license": "MIT", + "bugs": { + "url": "https://github.com/deckgo/deckdeckgo" + }, + "homepage": "https://deckdeckgo.com", + "keywords": [ + "editor", + "monaco-editor", + "vscode" + ], + "dependencies": { + "monaco-editor": "^0.31.1" + } +} diff --git a/webcomponents/monaco-editor/src/components.d.ts b/webcomponents/monaco-editor/src/components.d.ts new file mode 100644 index 000000000..e5c060455 --- /dev/null +++ b/webcomponents/monaco-editor/src/components.d.ts @@ -0,0 +1,38 @@ +/* eslint-disable */ +/* tslint:disable */ +/** + * This is an autogenerated file created by the Stencil compiler. + * It contains typing information for all components that exist in this project. + */ +import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; +export namespace Components { + interface DeckgoMonacoEditor { + "save": () => Promise; + } +} +declare global { + interface HTMLDeckgoMonacoEditorElement extends Components.DeckgoMonacoEditor, HTMLStencilElement { + } + var HTMLDeckgoMonacoEditorElement: { + prototype: HTMLDeckgoMonacoEditorElement; + new (): HTMLDeckgoMonacoEditorElement; + }; + interface HTMLElementTagNameMap { + "deckgo-monaco-editor": HTMLDeckgoMonacoEditorElement; + } +} +declare namespace LocalJSX { + interface DeckgoMonacoEditor { + } + interface IntrinsicElements { + "deckgo-monaco-editor": DeckgoMonacoEditor; + } +} +export { LocalJSX as JSX }; +declare module "@stencil/core" { + export namespace JSX { + interface IntrinsicElements { + "deckgo-monaco-editor": LocalJSX.DeckgoMonacoEditor & JSXBase.HTMLAttributes; + } + } +} diff --git a/webcomponents/monaco-editor/src/components/monaco-editor.scss b/webcomponents/monaco-editor/src/components/monaco-editor.scss new file mode 100644 index 000000000..7c758f13c --- /dev/null +++ b/webcomponents/monaco-editor/src/components/monaco-editor.scss @@ -0,0 +1,11 @@ +@import "~monaco-editor/min/vs/editor/editor.main.css"; + +:host { + --editor-width: 100%; + --editor-height: 100%; +} + +main { + width: var(--editor-width); + height: var(--editor-height); +} diff --git a/webcomponents/monaco-editor/src/components/monaco-editor.tsx b/webcomponents/monaco-editor/src/components/monaco-editor.tsx new file mode 100644 index 000000000..b98b1c213 --- /dev/null +++ b/webcomponents/monaco-editor/src/components/monaco-editor.tsx @@ -0,0 +1,51 @@ +import {Component, Element, h, ComponentInterface, Host, Method} from '@stencil/core'; + +import * as monaco from 'monaco-editor'; + +@Component({ + tag: 'deckgo-monaco-editor', + styleUrl: 'monaco-editor.scss', + shadow: true +}) +export class MonacoEditor implements ComponentInterface { + @Element() + private el: HTMLElement; + + private editor?: monaco.editor.IStandaloneCodeEditor; + + async componentDidLoad() { + const div = this.el.shadowRoot.querySelector('main'); + + this.editor = monaco.editor.create(div, { + value: "// First line\nfunction hello() {\n\talert('Hello world!');\n}\n// Last line", + language: 'javascript', + + scrollBeyondLastLine: false, + readOnly: false, + theme: 'vs-light', + + minimap: { + enabled: false + }, + + automaticLayout: true + }); + } + + disconnectedCallback() { + this.editor?.dispose(); + } + + @Method() + async save(): Promise { + return this.editor?.getValue(); + } + + render() { + return ( + +
+
+ ); + } +} diff --git a/webcomponents/monaco-editor/src/index.html b/webcomponents/monaco-editor/src/index.html new file mode 100644 index 000000000..ec0baae49 --- /dev/null +++ b/webcomponents/monaco-editor/src/index.html @@ -0,0 +1,43 @@ + + + + + + DeckDeckGo + + + + + + + + + + + + diff --git a/webcomponents/monaco-editor/src/index.ts b/webcomponents/monaco-editor/src/index.ts new file mode 100644 index 000000000..07635cbbc --- /dev/null +++ b/webcomponents/monaco-editor/src/index.ts @@ -0,0 +1 @@ +export * from './components'; diff --git a/webcomponents/monaco-editor/src/interface.d.ts b/webcomponents/monaco-editor/src/interface.d.ts new file mode 100644 index 000000000..07635cbbc --- /dev/null +++ b/webcomponents/monaco-editor/src/interface.d.ts @@ -0,0 +1 @@ +export * from './components'; diff --git a/webcomponents/monaco-editor/stencil.config.ts b/webcomponents/monaco-editor/stencil.config.ts new file mode 100644 index 000000000..2bb77e0a9 --- /dev/null +++ b/webcomponents/monaco-editor/stencil.config.ts @@ -0,0 +1,28 @@ +import {Config} from '@stencil/core'; + +import {sass} from '@stencil/sass'; +import {postcss} from '@stencil/postcss'; +// @ts-ignore +import autoprefixer from 'autoprefixer'; + +export const config: Config = { + namespace: 'monaco-editor', + outputTargets: [ + { + type: 'dist' + }, + { + type: 'www', + serviceWorker: null + } + ], + plugins: [ + sass(), + postcss({ + plugins: [autoprefixer()] + }) + ], + devServer: { + openBrowser: false + } +}; diff --git a/webcomponents/monaco-editor/tsconfig.json b/webcomponents/monaco-editor/tsconfig.json new file mode 100644 index 000000000..dad416bdb --- /dev/null +++ b/webcomponents/monaco-editor/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.json", + "include": ["src"], + "exclude": ["node_modules"] +} From 167db1dfa4d314539764cb12e005cce4793fcfb0 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sun, 23 Jan 2022 09:46:34 +0100 Subject: [PATCH 02/21] feat: code as slot and options Signed-off-by: peterpeterparker --- .../monaco-editor/src/components.d.ts | 5 ++- .../src/components/monaco-editor.scss | 12 +++--- .../src/components/monaco-editor.tsx | 42 ++++++++++++------- webcomponents/monaco-editor/src/index.html | 7 +++- webcomponents/monaco-editor/src/index.ts | 3 +- .../monaco-editor/src/interface.d.ts | 1 + .../monaco-editor/src/types/options.ts | 3 ++ 7 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 webcomponents/monaco-editor/src/types/options.ts diff --git a/webcomponents/monaco-editor/src/components.d.ts b/webcomponents/monaco-editor/src/components.d.ts index e5c060455..44f249d67 100644 --- a/webcomponents/monaco-editor/src/components.d.ts +++ b/webcomponents/monaco-editor/src/components.d.ts @@ -5,9 +5,11 @@ * It contains typing information for all components that exist in this project. */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; +import { MonacoEditorOptions } from "./types/options"; export namespace Components { interface DeckgoMonacoEditor { - "save": () => Promise; + "options": MonacoEditorOptions; + "save": () => Promise; } } declare global { @@ -23,6 +25,7 @@ declare global { } declare namespace LocalJSX { interface DeckgoMonacoEditor { + "options"?: MonacoEditorOptions; } interface IntrinsicElements { "deckgo-monaco-editor": DeckgoMonacoEditor; diff --git a/webcomponents/monaco-editor/src/components/monaco-editor.scss b/webcomponents/monaco-editor/src/components/monaco-editor.scss index 7c758f13c..97a11860d 100644 --- a/webcomponents/monaco-editor/src/components/monaco-editor.scss +++ b/webcomponents/monaco-editor/src/components/monaco-editor.scss @@ -1,11 +1,13 @@ @import "~monaco-editor/min/vs/editor/editor.main.css"; :host { - --editor-width: 100%; - --editor-height: 100%; } -main { - width: var(--editor-width); - height: var(--editor-height); +article { + width: var(--monaco-editor-width, 100%); + height: var(--monaco-editor-height, 100%); +} + +::slotted(*) { + display: none; } diff --git a/webcomponents/monaco-editor/src/components/monaco-editor.tsx b/webcomponents/monaco-editor/src/components/monaco-editor.tsx index b98b1c213..ca902e89f 100644 --- a/webcomponents/monaco-editor/src/components/monaco-editor.tsx +++ b/webcomponents/monaco-editor/src/components/monaco-editor.tsx @@ -1,7 +1,9 @@ -import {Component, Element, h, ComponentInterface, Host, Method} from '@stencil/core'; +import {Component, h, ComponentInterface, Host, Method, Prop, Element} from '@stencil/core'; import * as monaco from 'monaco-editor'; +import {MonacoEditorOptions} from '../types/options'; + @Component({ tag: 'deckgo-monaco-editor', styleUrl: 'monaco-editor.scss', @@ -11,24 +13,34 @@ export class MonacoEditor implements ComponentInterface { @Element() private el: HTMLElement; + @Prop() + options: MonacoEditorOptions; + private editor?: monaco.editor.IStandaloneCodeEditor; - async componentDidLoad() { - const div = this.el.shadowRoot.querySelector('main'); + private div!: HTMLDivElement; - this.editor = monaco.editor.create(div, { - value: "// First line\nfunction hello() {\n\talert('Hello world!');\n}\n// Last line", - language: 'javascript', + private readonly defaultOptions: monaco.editor.IStandaloneEditorConstructionOptions = { + language: 'javascript', - scrollBeyondLastLine: false, - readOnly: false, - theme: 'vs-light', + scrollBeyondLastLine: false, + readOnly: false, + theme: 'vs-light', - minimap: { - enabled: false - }, + minimap: { + enabled: false + }, + + automaticLayout: true + }; + + async componentDidLoad() { + const slottedCode: HTMLElement = this.el.querySelector(':scope > *:first-of-type'); - automaticLayout: true + this.editor = monaco.editor.create(this.div, { + value: slottedCode?.innerHTML.trim() || '', + ...this.defaultOptions, + ...(this.options || {}) }); } @@ -37,14 +49,14 @@ export class MonacoEditor implements ComponentInterface { } @Method() - async save(): Promise { + async save(): Promise { return this.editor?.getValue(); } render() { return ( -
+
(this.div = el as HTMLDivElement)}>
); } diff --git a/webcomponents/monaco-editor/src/index.html b/webcomponents/monaco-editor/src/index.html index ec0baae49..52760f5d6 100644 --- a/webcomponents/monaco-editor/src/index.html +++ b/webcomponents/monaco-editor/src/index.html @@ -38,6 +38,11 @@ - + + function toggleSrcLineNumbers() { const elem = document.getElementById('deckGoCode'); if (elem) { elem.lineNumbers = + !elem.lineNumbers; } } + diff --git a/webcomponents/monaco-editor/src/index.ts b/webcomponents/monaco-editor/src/index.ts index 07635cbbc..dd1b93456 100644 --- a/webcomponents/monaco-editor/src/index.ts +++ b/webcomponents/monaco-editor/src/index.ts @@ -1 +1,2 @@ -export * from './components'; +export type {Components, JSX} from './components'; +export * from './types/options'; diff --git a/webcomponents/monaco-editor/src/interface.d.ts b/webcomponents/monaco-editor/src/interface.d.ts index 07635cbbc..8470e4659 100644 --- a/webcomponents/monaco-editor/src/interface.d.ts +++ b/webcomponents/monaco-editor/src/interface.d.ts @@ -1 +1,2 @@ export * from './components'; +export * from './types/options'; diff --git a/webcomponents/monaco-editor/src/types/options.ts b/webcomponents/monaco-editor/src/types/options.ts new file mode 100644 index 000000000..c8520f4b6 --- /dev/null +++ b/webcomponents/monaco-editor/src/types/options.ts @@ -0,0 +1,3 @@ +import * as monaco from 'monaco-editor'; + +export interface MonacoEditorOptions extends monaco.editor.IStandaloneEditorConstructionOptions {} From 9c205109b5a1ba63fce4e02f0f8361fb28030b59 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sun, 23 Jan 2022 10:06:49 +0100 Subject: [PATCH 03/21] feat: create highlight-code plugin Signed-off-by: peterpeterparker --- .../app-code-editor/app-code-editor.tsx | 15 ++++++++++---- studio/src/app/utils/editor/plugin.utils.ts | 20 +++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx b/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx index d239e79be..ca3d7cfc3 100644 --- a/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx +++ b/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx @@ -32,6 +32,8 @@ self.MonacoEnvironment = { export class AppCodeEditor implements ComponentInterface { @Element() el: HTMLElement; + private codeEditor: HTMLDeckgoMonacoEditorElement | null; + componentDidLoad() { history.pushState({modal: true}, null); } @@ -41,8 +43,13 @@ export class AppCodeEditor implements ComponentInterface { await this.closeModal(); } - private async closeModal() { - await (this.el.closest('ion-modal') as HTMLIonModalElement).dismiss(); + private async closeModal(data?: {code: string}) { + await (this.el.closest('ion-modal') as HTMLIonModalElement).dismiss(data); + } + + private async save() { + const code: string | undefined = await this.codeEditor?.save(); + await this.closeModal({code}); } render() { @@ -60,12 +67,12 @@ export class AppCodeEditor implements ComponentInterface { - + (this.codeEditor = el)}>
- + await this.save()}> {i18n.state.core.save}
diff --git a/studio/src/app/utils/editor/plugin.utils.ts b/studio/src/app/utils/editor/plugin.utils.ts index 782a578ca..4c2d9f65f 100644 --- a/studio/src/app/utils/editor/plugin.utils.ts +++ b/studio/src/app/utils/editor/plugin.utils.ts @@ -63,13 +63,25 @@ export const openCodeModal = async ({pluginParams}: {pluginParams: StyloPluginCr component: 'app-code-editor' }); - modal.onDidDismiss().then(({data: unsplashImage}: OverlayEventDetail) => { + modal.onDidDismiss().then(({data}: OverlayEventDetail) => { const {container, paragraph} = pluginParams; - createParagraphImage({ - image: unsplashImage, + const {code: innerHTML} = data || {code: '\u200B'}; + + const code: HTMLElement = document.createElement('deckgo-highlight-code'); + code.setAttribute('editable', 'true'); + + const slot: HTMLElement = document.createElement('code'); + slot.setAttribute('slot', 'code'); + slot.innerHTML = innerHTML; + + code.append(slot); + + transformParagraph({ + elements: [code, createEmptyElement({nodeName: 'div'})], + paragraph, container, - paragraph + focus: 'last' }); }); From 8239c9dc1c176a3151167444a4dbf3c1101acec4 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sun, 23 Jan 2022 10:31:20 +0100 Subject: [PATCH 04/21] feat: focus editor Signed-off-by: peterpeterparker --- .../editor/app-code-editor/app-code-editor.tsx | 5 +++++ webcomponents/monaco-editor/src/components.d.ts | 2 ++ .../monaco-editor/src/components/monaco-editor.tsx | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx b/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx index ca3d7cfc3..13d6565af 100644 --- a/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx +++ b/studio/src/app/modals/editor/app-code-editor/app-code-editor.tsx @@ -43,6 +43,11 @@ export class AppCodeEditor implements ComponentInterface { await this.closeModal(); } + @Listen('editorDidLoad') + onEditorDidLoad() { + setTimeout(async () => await this.codeEditor?.setFocus(), 500); + } + private async closeModal(data?: {code: string}) { await (this.el.closest('ion-modal') as HTMLIonModalElement).dismiss(data); } diff --git a/webcomponents/monaco-editor/src/components.d.ts b/webcomponents/monaco-editor/src/components.d.ts index 44f249d67..05fc48767 100644 --- a/webcomponents/monaco-editor/src/components.d.ts +++ b/webcomponents/monaco-editor/src/components.d.ts @@ -10,6 +10,7 @@ export namespace Components { interface DeckgoMonacoEditor { "options": MonacoEditorOptions; "save": () => Promise; + "setFocus": () => Promise; } } declare global { @@ -25,6 +26,7 @@ declare global { } declare namespace LocalJSX { interface DeckgoMonacoEditor { + "onEditorDidLoad"?: (event: CustomEvent) => void; "options"?: MonacoEditorOptions; } interface IntrinsicElements { diff --git a/webcomponents/monaco-editor/src/components/monaco-editor.tsx b/webcomponents/monaco-editor/src/components/monaco-editor.tsx index ca902e89f..43b083543 100644 --- a/webcomponents/monaco-editor/src/components/monaco-editor.tsx +++ b/webcomponents/monaco-editor/src/components/monaco-editor.tsx @@ -1,4 +1,4 @@ -import {Component, h, ComponentInterface, Host, Method, Prop, Element} from '@stencil/core'; +import {Component, h, ComponentInterface, Host, Method, Prop, Element, EventEmitter, Event} from '@stencil/core'; import * as monaco from 'monaco-editor'; @@ -16,6 +16,9 @@ export class MonacoEditor implements ComponentInterface { @Prop() options: MonacoEditorOptions; + @Event() + editorDidLoad: EventEmitter; + private editor?: monaco.editor.IStandaloneCodeEditor; private div!: HTMLDivElement; @@ -34,7 +37,7 @@ export class MonacoEditor implements ComponentInterface { automaticLayout: true }; - async componentDidLoad() { + componentDidLoad() { const slottedCode: HTMLElement = this.el.querySelector(':scope > *:first-of-type'); this.editor = monaco.editor.create(this.div, { @@ -42,12 +45,19 @@ export class MonacoEditor implements ComponentInterface { ...this.defaultOptions, ...(this.options || {}) }); + + this.editorDidLoad.emit(); } disconnectedCallback() { this.editor?.dispose(); } + @Method() + async setFocus() { + this.editor?.focus(); + } + @Method() async save(): Promise { return this.editor?.getValue(); From 40ad07baa42351a03b6df3ff77781a236d6f2ba5 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sun, 23 Jan 2022 14:35:04 +0100 Subject: [PATCH 05/21] feat: replace editable feature with a button Signed-off-by: peterpeterparker --- webcomponents/highlight-code/package.json | 2 +- ...nfig.languages.js => config.languages.mjs} | 6 +- .../highlight-code/src/components.d.ts | 32 ++++- .../src/components/edit/edit.scss | 37 ++++++ .../src/components/edit/edit.tsx | 29 +++++ .../src/components/edit/readme.md | 47 ++++++++ .../deckdeckgo-highlight-code.scss | 50 ++------ .../deckdeckgo-highlight-code.tsx | 113 ++---------------- .../src/components/highlight-code/readme.md | 34 ++++-- webcomponents/highlight-code/src/index.html | 22 ++-- 10 files changed, 192 insertions(+), 180 deletions(-) rename webcomponents/highlight-code/scripts/{config.languages.js => config.languages.mjs} (93%) create mode 100644 webcomponents/highlight-code/src/components/edit/edit.scss create mode 100644 webcomponents/highlight-code/src/components/edit/edit.tsx create mode 100644 webcomponents/highlight-code/src/components/edit/readme.md diff --git a/webcomponents/highlight-code/package.json b/webcomponents/highlight-code/package.json index c032fbd23..73aaf5b94 100644 --- a/webcomponents/highlight-code/package.json +++ b/webcomponents/highlight-code/package.json @@ -17,7 +17,7 @@ "LICENSE" ], "scripts": { - "config": "node scripts/config.languages.js && prettier --write ./src/declarations/deckdeckgo-highlight-code-languages.tsx", + "config": "node scripts/config.languages.mjs && prettier --write ./src/declarations/deckdeckgo-highlight-code-languages.tsx", "build": "npm run config && stencil build --docs", "start": "npm run config && stencil build --dev --watch --serve", "test": "npm run config && stencil test --spec --e2e", diff --git a/webcomponents/highlight-code/scripts/config.languages.js b/webcomponents/highlight-code/scripts/config.languages.mjs similarity index 93% rename from webcomponents/highlight-code/scripts/config.languages.js rename to webcomponents/highlight-code/scripts/config.languages.mjs index d4bb4528d..a719f14f4 100644 --- a/webcomponents/highlight-code/scripts/config.languages.js +++ b/webcomponents/highlight-code/scripts/config.languages.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node -const fs = require('fs'); -const fetch = require('node-fetch'); +import {writeFile} from 'fs'; +import fetch from 'node-fetch'; const langInterfaces = `export interface DeckdeckgoHighlightCodeLanguageAlias { [index: string]: string; @@ -74,7 +74,7 @@ function getLanguageRequire(language) { export const deckdeckgoHighlightCodeLanguages: DeckdeckgoHighlightCodeLanguages = ${JSON.stringify(filteredLanguages)}`; - fs.writeFile(`./src/declarations/deckdeckgo-highlight-code-languages.tsx`, languagesEnum, 'utf8', (err) => { + writeFile(`./src/declarations/deckdeckgo-highlight-code-languages.tsx`, languagesEnum, 'utf8', (err) => { if (err) return console.log(err); }); } catch (e) { diff --git a/webcomponents/highlight-code/src/components.d.ts b/webcomponents/highlight-code/src/components.d.ts index 7fc4d8fb1..b2bfbc2e2 100644 --- a/webcomponents/highlight-code/src/components.d.ts +++ b/webcomponents/highlight-code/src/components.d.ts @@ -10,9 +10,13 @@ import { DeckdeckgoHighlightCodeCarbonTheme } from "./declarations/deckdeckgo-hi export namespace Components { interface DeckgoHighlightCode { /** - * In case you would like to set the code component as being editable + * Display a button user can click to edit the code. Edition has to find place on the comsumer side, the button emits an event */ "editable": boolean; + /** + * An optional label for the `aria-label` attribute of the editable button + */ + "editableLabel": string; "hide": () => Promise; "hideAll": () => Promise; /** @@ -51,6 +55,9 @@ export namespace Components { */ "theme": DeckdeckgoHighlightCodeCarbonTheme; } + interface DeckgoHighlightCodeEdit { + "label": string; + } } declare global { interface HTMLDeckgoHighlightCodeElement extends Components.DeckgoHighlightCode, HTMLStencilElement { @@ -59,16 +66,27 @@ declare global { prototype: HTMLDeckgoHighlightCodeElement; new (): HTMLDeckgoHighlightCodeElement; }; + interface HTMLDeckgoHighlightCodeEditElement extends Components.DeckgoHighlightCodeEdit, HTMLStencilElement { + } + var HTMLDeckgoHighlightCodeEditElement: { + prototype: HTMLDeckgoHighlightCodeEditElement; + new (): HTMLDeckgoHighlightCodeEditElement; + }; interface HTMLElementTagNameMap { "deckgo-highlight-code": HTMLDeckgoHighlightCodeElement; + "deckgo-highlight-code-edit": HTMLDeckgoHighlightCodeEditElement; } } declare namespace LocalJSX { interface DeckgoHighlightCode { /** - * In case you would like to set the code component as being editable + * Display a button user can click to edit the code. Edition has to find place on the comsumer side, the button emits an event */ "editable"?: boolean; + /** + * An optional label for the `aria-label` attribute of the editable button + */ + "editableLabel"?: string; /** * If you wish to highlight some lines of your code. The lines number should be provided as a number (one line) or numbers separated with coma or dash (many lines), group separated with space. For example: 1 3,5 8 14-17 which highlight lines 1, 3 to 5, 8 and 14 to 17 */ @@ -81,10 +99,6 @@ declare namespace LocalJSX { * Display the number of the lines of code */ "lineNumbers"?: boolean; - /** - * Emitted when the code was edited (see attribute editable). Propagate the root component itself - */ - "onCodeDidChange"?: (event: CustomEvent) => void; /** * Emitted when a language could not be loaded. The component fallback to javascript language to display the code anyway. */ @@ -103,8 +117,13 @@ declare namespace LocalJSX { */ "theme"?: DeckdeckgoHighlightCodeCarbonTheme; } + interface DeckgoHighlightCodeEdit { + "label"?: string; + "onEditCode"?: (event: CustomEvent) => void; + } interface IntrinsicElements { "deckgo-highlight-code": DeckgoHighlightCode; + "deckgo-highlight-code-edit": DeckgoHighlightCodeEdit; } } export { LocalJSX as JSX }; @@ -112,6 +131,7 @@ declare module "@stencil/core" { export namespace JSX { interface IntrinsicElements { "deckgo-highlight-code": LocalJSX.DeckgoHighlightCode & JSXBase.HTMLAttributes; + "deckgo-highlight-code-edit": LocalJSX.DeckgoHighlightCodeEdit & JSXBase.HTMLAttributes; } } } diff --git a/webcomponents/highlight-code/src/components/edit/edit.scss b/webcomponents/highlight-code/src/components/edit/edit.scss new file mode 100644 index 000000000..ff6ca10c9 --- /dev/null +++ b/webcomponents/highlight-code/src/components/edit/edit.scss @@ -0,0 +1,37 @@ +:host { + display: block; + + position: absolute; + inset: auto 0 0 auto; +} + +button { + width: 30px; + height: 30px; + display: flex; + justify-content: center; + align-items: center; + margin: 0 16px; + border-radius: 50%; + + background: #f4f5f8; + color: #000000; + + border: 1px solid transparent; + outline: none; + + box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12); + + isolation: isolate; + + overflow: hidden; + + cursor: pointer; + + transition: transform 0.15s ease-out; + + &:active { + box-shadow: none; + transform: translateX(1px) translateY(1px); + } +} diff --git a/webcomponents/highlight-code/src/components/edit/edit.tsx b/webcomponents/highlight-code/src/components/edit/edit.tsx new file mode 100644 index 000000000..c5dd1a6d8 --- /dev/null +++ b/webcomponents/highlight-code/src/components/edit/edit.tsx @@ -0,0 +1,29 @@ +import {Component, h, Event, EventEmitter, Prop} from '@stencil/core'; + +/** + * @part edit-button: A CSS :part to access the button + * @part edit-icon: A CSS :part to access the SVG icon rendered within the button + */ +@Component({ + tag: 'deckgo-highlight-code-edit', + styleUrl: 'edit.scss', + shadow: true +}) +export class Edit { + @Prop() + label: string; + + @Event() + editCode: EventEmitter; + + render() { + return ( + + ); + } +} diff --git a/webcomponents/highlight-code/src/components/edit/readme.md b/webcomponents/highlight-code/src/components/edit/readme.md new file mode 100644 index 000000000..695c80b59 --- /dev/null +++ b/webcomponents/highlight-code/src/components/edit/readme.md @@ -0,0 +1,47 @@ +# deckgo-highlight-code-edit + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| -------- | --------- | ----------- | -------- | ----------- | +| `label` | `label` | | `string` | `undefined` | + + +## Events + +| Event | Description | Type | +| ---------- | ----------- | ------------------- | +| `editCode` | | `CustomEvent` | + + +## Shadow Parts + +| Part | Description | +| ---------------------------------------------------------------------------- | ----------- | +| `"edit-button"` | | +| `"edit-button: A CSS :part to access the button"` | | +| `"edit-icon"` | | +| `"edit-icon: A CSS :part to access the SVG icon rendered within the button"` | | + + +## Dependencies + +### Used by + + - [deckgo-highlight-code](../highlight-code) + +### Graph +```mermaid +graph TD; + deckgo-highlight-code --> deckgo-highlight-code-edit + style deckgo-highlight-code-edit fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.scss b/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.scss index fc3ca5833..77f45674f 100644 --- a/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.scss +++ b/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.scss @@ -6,22 +6,6 @@ } } -:host([editable]) { - /** - * @prop --deckgo-editable-cursor: The mouse cursor displayed when hovering the editable element - * @default text - */ - cursor: var(--deckgo-editable-cursor, text); - - code:empty:not(:focus):after { - /** - * @prop --deckgo-highlight-code-empty-text: Place holder in case the editable is set to true - * @default Click to add your code - */ - content: var(--deckgo-highlight-code-empty-text, "Click to add your code"); - } -} - :host(.deckgo-highlight-code-carbon) { /** * @prop --deckgo-highlight-code-carbon-display: The display property of the host container @@ -132,16 +116,16 @@ code { /** - * @prop : Place holder in case the editable is set to true - * @default + * @prop --deckgo-highlight-code-font-family: Ubuntu font family + * @default Ubuntu mono */ font-family: var(--deckgo-highlight-code-font-family, "Ubuntu mono"); > div.line-number { &:before { /** - * @prop : Place holder in case the editable is set to true - * @default + * @prop --deckgo-highlight-code-ubuntu-background: Ubunut line number background + * @default #4c1e3d */ background: var(--deckgo-highlight-code-ubuntu-background, #4c1e3d); } @@ -152,21 +136,20 @@ :host(.deckgo-highlight-code-ubuntu) ::slotted([slot="code"]) { /** - * @prop : Place holder in case the editable is set to true - * @default + * @prop --deckgo-highlight-code-ubuntu-color: Ubuntu color + * @default #ddd */ color: var(--deckgo-highlight-code-ubuntu-color, #ddd); } div.container { /** - * @prop : Place holder in case the editable is set to true - * @default + * @prop --deckgo-highlight-code-color: Ubuntu container color + * @default inherit */ color: var(--deckgo-highlight-code-color, inherit); /** - * @prop : Place holder in case the editable is set to true - * @default + * @prop --deckgo-highlight-code-background: Ubuntu code background */ background: var(--deckgo-highlight-code-background); padding: var(--deckgo-highlight-code-padding, 0 16px); @@ -515,11 +498,6 @@ div.carbon { * @default #ff5f56 */ background: var(--deckgo-highlight-code-carbon-header-button-red-background, #ff5f56); - /** - * @prop --deckgo-highlight-code-carbon-header-button-red-border: The border of the first button of the card header - * @default 0.5px solid #e0443e - */ - border: var(--deckgo-highlight-code-carbon-header-button-red-border, 0.5px solid #e0443e); } &.yellow { @@ -528,11 +506,6 @@ div.carbon { * @default #ffbd2e */ background: var(--deckgo-highlight-code-carbon-header-button-yellow-background, #ffbd2e); - /** - * @prop --deckgo-highlight-code-carbon-header-button-yellow-border: The border of the second button of the card header - * @default 0.5px solid #dea123 - */ - border: var(--deckgo-highlight-code-carbon-header-button-yellow-border, 0.5px solid #dea123); } &.green { @@ -541,11 +514,6 @@ div.carbon { * @default #27c93f */ background: var(--deckgo-highlight-code-carbon-header-button-green-background, #27c93f); - /** - * @prop --deckgo-highlight-code-carbon-header-button-green-border: The color of the third button of the card header - * @default 0.5px solid #1aab29 - */ - border: var(--deckgo-highlight-code-carbon-header-button-green-border, 0.5px solid #1aab29); } } } diff --git a/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.tsx b/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.tsx index 1b3d4b99d..d4b8d9f86 100644 --- a/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.tsx +++ b/webcomponents/highlight-code/src/components/highlight-code/deckdeckgo-highlight-code.tsx @@ -1,6 +1,5 @@ import {Component, Prop, Watch, Element, Method, EventEmitter, Event, Listen, State, h, Host} from '@stencil/core'; -import {catchTab, debounce, getSelection, moveCursorToEnd} from '@deckdeckgo/utils'; import {DeckDeckGoRevealComponent} from '@deckdeckgo/slide-utils'; import {loadTheme} from '../../utils/themes-loader.utils'; @@ -39,12 +38,6 @@ export class DeckdeckgoHighlightCode implements DeckDeckGoRevealComponent { @Event() prismLanguageError: EventEmitter; - /** - * Emitted when the code was edited (see attribute editable). Propagate the root component itself - */ - @Event() - codeDidChange: EventEmitter; - /** * Define the language to be used for the syntax highlighting. The list of supported languages is defined by Prism.js */ @@ -66,10 +59,15 @@ export class DeckdeckgoHighlightCode implements DeckDeckGoRevealComponent { @Prop({reflect: true}) terminal: DeckdeckgoHighlightCodeTerminal = DeckdeckgoHighlightCodeTerminal.CARBON; /** - * In case you would like to set the code component as being editable + * Display a button user can click to edit the code. Edition has to find place on the comsumer side, the button emits an event */ @Prop() editable: boolean = false; + /** + * An optional label for the `aria-label` attribute of the editable button + */ + @Prop() editableLabel: string; + /** * The theme of the selected terminal (applied only in case of carbon) */ @@ -87,8 +85,6 @@ export class DeckdeckgoHighlightCode implements DeckDeckGoRevealComponent { private refContainer!: HTMLDivElement; - private readonly debounceUpdateSlot: () => void; - private highlightGroup: number | undefined = undefined; /** @@ -100,18 +96,8 @@ export class DeckdeckgoHighlightCode implements DeckDeckGoRevealComponent { @State() private highlightRows: {start: number; end: number} | undefined = undefined; - private editFocused: boolean = false; - - constructor() { - this.debounceUpdateSlot = debounce(async () => { - await this.copyCodeToSlot(); - }, 500); - } - - async componentWillLoad() { - await loadGoogleFonts(this.terminal); - - await this.loadTheme(); + componentWillLoad() { + Promise.all([loadGoogleFonts(this.terminal), this.loadTheme()]).then(() => {}); } async componentDidLoad() { @@ -304,62 +290,6 @@ export class DeckdeckgoHighlightCode implements DeckDeckGoRevealComponent { }; } - private async applyCode() { - if (!this.editable) { - return; - } - - this.editFocused = false; - - await this.copyCodeToSlot(); - - this.parseSlottedCode(); - - this.codeDidChange.emit(this.el); - } - - private inputCode() { - if (!this.editable) { - return; - } - - this.debounceUpdateSlot(); - } - - private async copyCodeToSlot() { - const code: HTMLElement | null = this.el.querySelector(":scope > [slot='code']"); - - if (!code) { - return; - } - - // Avoid duplicating new lines on new entries - this.refCode?.querySelectorAll('br')?.forEach((node: HTMLBRElement) => (node.outerHTML = '\u200B')); - - code.innerHTML = this.refCode?.innerText - .replace(/\u200B/g, '') - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); - } - - private edit() { - if (!this.editable || this.editFocused) { - return; - } - - if (!this.refCode) { - return; - } - - this.editFocused = true; - - this.refCode.focus(); - moveCursorToEnd(this.refCode); - } - /** * @internal Used when integrated in DeckDeckGo presentations. Call `nextHighlight()`. */ @@ -453,26 +383,6 @@ export class DeckdeckgoHighlightCode implements DeckDeckGoRevealComponent { }; } - @Listen('copy', {target: 'window'}) - onCopyCleanZeroWidthSpaces($event: ClipboardEvent) { - const {target, clipboardData} = $event; - - if (!target || !clipboardData || !this.el.isEqualNode(target as Node)) { - return; - } - - const selection: Selection | null = getSelection(); - - if (!selection) { - return; - } - - $event.preventDefault(); - - const text: string = selection.toString().replace(/\u200B/g, ''); - clipboardData.setData('text/plain', text); - } - render() { const hostClass = { 'deckgo-highlight-code-carbon': this.terminal === DeckdeckgoHighlightCodeTerminal.CARBON, @@ -484,19 +394,16 @@ export class DeckdeckgoHighlightCode implements DeckDeckGoRevealComponent { } return ( - this.edit()}> + {this.renderCarbon()} {this.renderUbuntu()} {this.renderHighlightStyle()}
(this.refContainer = el as HTMLDivElement)}> 0 ? 'highlight' : undefined} - contentEditable={this.editable} - onBlur={async () => await this.applyCode()} - onInput={() => this.inputCode()} - onKeyDown={($event: KeyboardEvent) => catchTab($event)} ref={(el: HTMLElement | null) => (this.refCode = el as HTMLElement)}> + {this.editable && }
); diff --git a/webcomponents/highlight-code/src/components/highlight-code/readme.md b/webcomponents/highlight-code/src/components/highlight-code/readme.md index f9078d5f2..d421ed7ec 100644 --- a/webcomponents/highlight-code/src/components/highlight-code/readme.md +++ b/webcomponents/highlight-code/src/components/highlight-code/readme.md @@ -62,7 +62,8 @@ If you are displaying your code in an Ubuntu terminal, you could also displays a | Property | Attribute | Description | Type | Default | | ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | -| `editable` | `editable` | In case you would like to set the code component as being editable | `boolean` | `false` | +| `editable` | `editable` | Display a button user can click to edit the code. Edition has to find place on the comsumer side, the button emits an event | `boolean` | `false` | +| `editableLabel` | `editable-label` | An optional label for the `aria-label` attribute of the editable button | `string` | `undefined` | | `highlightLines` | `highlight-lines` | If you wish to highlight some lines of your code. The lines number should be provided as a number (one line) or numbers separated with coma or dash (many lines), group separated with space. For example: 1 3,5 8 14-17 which highlight lines 1, 3 to 5, 8 and 14 to 17 | `string` | `undefined` | | `language` | `language` | Define the language to be used for the syntax highlighting. The list of supported languages is defined by Prism.js | `string` | `'javascript'` | | `lineNumbers` | `line-numbers` | Display the number of the lines of code | `boolean` | `false` | @@ -72,11 +73,10 @@ If you are displaying your code in an Ubuntu terminal, you could also displays a ## Events -| Event | Description | Type | -| --------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------- | -| `codeDidChange` | Emitted when the code was edited (see attribute editable). Propagate the root component itself | `CustomEvent` | -| `prismLanguageError` | Emitted when a language could not be loaded. The component fallback to javascript language to display the code anyway. | `CustomEvent` | -| `prismLanguageLoaded` | Emitted when a language is fetched and loaded | `CustomEvent` | +| Event | Description | Type | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------------- | +| `prismLanguageError` | Emitted when a language could not be loaded. The component fallback to javascript language to display the code anyway. | `CustomEvent` | +| `prismLanguageLoaded` | Emitted when a language is fetched and loaded | `CustomEvent` | ## Methods @@ -124,7 +124,7 @@ Type: `Promise` | Name | Description | | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | -| `--deckgo-editable-cursor` | The mouse cursor displayed when hovering the editable element @default text | +| `--deckgo-highlight-code-background` | Ubuntu code background | | `--deckgo-highlight-code-border-radius` | The border radius of the displayed code | | `--deckgo-highlight-code-carbon-background` | The background property of the host container @default #282a36 | | `--deckgo-highlight-code-carbon-border` | The border property of the host container | @@ -134,19 +134,17 @@ Type: `Promise` | `--deckgo-highlight-code-carbon-display` | The display property of the host container @default block | | `--deckgo-highlight-code-carbon-header-button-border-radius` | The border-radius of a button of the card header @default 50% | | `--deckgo-highlight-code-carbon-header-button-green-background` | The background of the third button of the card header @default #27c93f | -| `--deckgo-highlight-code-carbon-header-button-green-border` | The color of the third button of the card header @default 0.5px solid #1aab29 | | `--deckgo-highlight-code-carbon-header-button-height` | The height of a button of the card header @default 0.75em | | `--deckgo-highlight-code-carbon-header-button-margin` | The margin of a button of the card header @default 0.5em 0.375em 0.5em 0 | | `--deckgo-highlight-code-carbon-header-button-red-background` | The background of the first button of the card header @default #ff5f56 | -| `--deckgo-highlight-code-carbon-header-button-red-border` | The border of the first button of the card header @default 0.5px solid #e0443e | | `--deckgo-highlight-code-carbon-header-button-width` | The width of a button of the card header @default 0.75em | | `--deckgo-highlight-code-carbon-header-button-yellow-background` | The background of the second button of the card header @default #ffbd2e | -| `--deckgo-highlight-code-carbon-header-button-yellow-border` | The border of the second button of the card header @default 0.5px solid #dea123 | | `--deckgo-highlight-code-carbon-header-margin` | The margin property of the card header @default 0 | | `--deckgo-highlight-code-carbon-header-padding` | The padding property of the card header. @default 8px 16px | | `--deckgo-highlight-code-carbon-margin` | The margin property of the host container @default 16px 0 | | `--deckgo-highlight-code-carbon-overflow` | The overflow property of the host container. @default auto | | `--deckgo-highlight-code-carbon-toolbar-display` | The display property of the toolbar container @default bloack | +| `--deckgo-highlight-code-color` | Ubuntu container color @default inherit | | `--deckgo-highlight-code-container-align-items` | The attribute align-items of the code's container | | `--deckgo-highlight-code-container-display` | The attribute display of the code's container @default bloack | | `--deckgo-highlight-code-container-flex-direction` | The attribute flex-direction of the code's container | @@ -155,8 +153,7 @@ Type: `Promise` | `--deckgo-highlight-code-container-width` | The attribute width of the code's container | | `--deckgo-highlight-code-direction` | The direction of the displayed code @default ltr | | `--deckgo-highlight-code-display` | The display property of the code @default block | -| `--deckgo-highlight-code-empty-text` | Place holder in case the editable is set to true @default Click to add your code | -| `--deckgo-highlight-code-font-family` | The family of the font for the code @default monospace | +| `--deckgo-highlight-code-font-family` | Ubuntu font family @default Ubuntu mono | | `--deckgo-highlight-code-font-size` | The size of the font for the code | | `--deckgo-highlight-code-height` | Height property of the shadowed code block @default 100% | | `--deckgo-highlight-code-line-background` | The background of the lines you wish to highlight | @@ -224,6 +221,19 @@ Type: `Promise` | `--deckgo-lowlight-code-line-opacity` | The opacity of the lines you do not wish to highlight @default 0.32 | +## Dependencies + +### Depends on + +- [deckgo-highlight-code-edit](../edit) + +### Graph +```mermaid +graph TD; + deckgo-highlight-code --> deckgo-highlight-code-edit + style deckgo-highlight-code fill:#f9f,stroke:#333,stroke-width:4px +``` + ---------------------------------------------- *Built with [StencilJS](https://stenciljs.com/)* diff --git a/webcomponents/highlight-code/src/index.html b/webcomponents/highlight-code/src/index.html index 0bb57891a..bf558d35c 100644 --- a/webcomponents/highlight-code/src/index.html +++ b/webcomponents/highlight-code/src/index.html @@ -31,6 +31,12 @@

Editable:

} + +