From 709c1be7a08803f5408ef2f68f3c07397262fc81 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 13:46:25 +0200 Subject: [PATCH 01/26] Add codable element --- ts/editor/codable.ts | 6 ++++++ ts/editor/editingArea.ts | 5 +++++ ts/editor/index.ts | 2 ++ 3 files changed, 13 insertions(+) create mode 100644 ts/editor/codable.ts diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts new file mode 100644 index 000000000..00a03562e --- /dev/null +++ b/ts/editor/codable.ts @@ -0,0 +1,6 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +export class Codable extends HTMLTextAreaElement { + connectedCallback(): void {} +} diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index b5345a630..6732cecae 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -6,6 +6,7 @@ */ import type { Editable } from "./editable"; +import type { Codable } from "./codable"; import { updateActiveButtons } from "./toolbar"; import { bridgeCommand } from "./lib"; @@ -23,6 +24,7 @@ function onCutOrCopy(): void { export class EditingArea extends HTMLDivElement { editable: Editable; + codable: Codable; baseStyle: HTMLStyleElement; constructor() { @@ -41,6 +43,9 @@ export class EditingArea extends HTMLDivElement { this.editable = document.createElement("anki-editable") as Editable; this.shadowRoot!.appendChild(this.editable); + + this.codable = document.createElement("textarea", { is: "anki-codable" }) as Codable; + this.shadowRoot!.appendChild(this.codable); } get ord(): number { diff --git a/ts/editor/index.ts b/ts/editor/index.ts index eb2980e2c..88295b255 100644 --- a/ts/editor/index.ts +++ b/ts/editor/index.ts @@ -18,6 +18,7 @@ import { EditorField } from "./editorField"; import { LabelContainer } from "./labelContainer"; import { EditingArea } from "./editingArea"; import { Editable } from "./editable"; +import { Codable } from "./codable"; import { initToolbar } from "./toolbar"; export { setNoteId, getNoteId } from "./noteId"; @@ -35,6 +36,7 @@ declare global { } customElements.define("anki-editable", Editable); +customElements.define("anki-codable", Codable, { extends: "textarea" }); customElements.define("anki-editing-area", EditingArea, { extends: "div" }); customElements.define("anki-label-container", LabelContainer, { extends: "div" }); customElements.define("anki-editor-field", EditorField, { extends: "div" }); From 8b15c81d347ec28cdfff8659d81900695e08aaae Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 14:44:03 +0200 Subject: [PATCH 02/26] Display CodeMirror in editingArea --- ts/editor/BUILD.bazel | 2 ++ ts/editor/codable.ts | 6 +++++- ts/editor/editable.scss | 2 ++ ts/licenses.json | 9 +++++++++ ts/package.json | 2 ++ ts/sass/BUILD.bazel | 2 +- ts/sass/codemirror/BUILD.bazel | 23 +++++++++++++++++++++++ ts/yarn.lock | 24 ++++++++++++++++++++++++ 8 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 ts/sass/codemirror/BUILD.bazel diff --git a/ts/editor/BUILD.bazel b/ts/editor/BUILD.bazel index 4ad87ce37..16aa2de1d 100644 --- a/ts/editor/BUILD.bazel +++ b/ts/editor/BUILD.bazel @@ -73,6 +73,8 @@ ts_library( "//ts/html-filter", "//ts:image_module_support", "@npm//svelte", + "@npm//@types/codemirror", + "@npm//codemirror", ] + svelte_names, ) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index 00a03562e..c6337ff48 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -1,6 +1,10 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +import CodeMirror from "codemirror/src/codemirror" + export class Codable extends HTMLTextAreaElement { - connectedCallback(): void {} + connectedCallback(): void { + CodeMirror.fromTextArea(this); + } } diff --git a/ts/editor/editable.scss b/ts/editor/editable.scss index 7489a48a4..00beb30c5 100644 --- a/ts/editor/editable.scss +++ b/ts/editor/editable.scss @@ -1,3 +1,5 @@ +@import 'ts/sass/codemirror/codemirror'; + @use 'ts/sass/scrollbar'; anki-editable { diff --git a/ts/licenses.json b/ts/licenses.json index 54792d39a..971abdcc7 100644 --- a/ts/licenses.json +++ b/ts/licenses.json @@ -147,6 +147,15 @@ "path": "node_modules/bootstrap", "licenseFile": "node_modules/bootstrap/LICENSE" }, + "codemirror@5.61.1": { + "licenses": "MIT", + "repository": "https://github.com/codemirror/CodeMirror", + "publisher": "Marijn Haverbeke", + "email": "marijnh@gmail.com", + "url": "http://marijnhaverbeke.nl", + "path": "node_modules/codemirror", + "licenseFile": "node_modules/codemirror/LICENSE" + }, "commander@7.2.0": { "licenses": "MIT", "repository": "https://github.com/tj/commander.js", diff --git a/ts/package.json b/ts/package.json index 8aa61d415..580d5f772 100644 --- a/ts/package.json +++ b/ts/package.json @@ -12,6 +12,7 @@ "@sqltools/formatter": "^1.2.2", "@tsconfig/svelte": "^1.0.10", "@types/bootstrap": "^5.0.12", + "@types/codemirror": "^5.60.0", "@types/d3": "^6.3.0", "@types/diff": "^5.0.0", "@types/jest": "^26.0.22", @@ -60,6 +61,7 @@ "@types/marked": "^2.0.2", "bootstrap": "=5.0.0-beta3", "bootstrap-icons": "^1.4.0", + "codemirror": "^5.61.1", "css-browser-selector": "^0.6.5", "d3": "^7.0.0", "intl-pluralrules": "^1.2.2", diff --git a/ts/sass/BUILD.bazel b/ts/sass/BUILD.bazel index f78576633..9c61c0b79 100644 --- a/ts/sass/BUILD.bazel +++ b/ts/sass/BUILD.bazel @@ -8,7 +8,7 @@ sass_library( "bootstrap-dark.scss", ], visibility = ["//visibility:public"], - deps = ["//ts/sass/bootstrap"], + deps = ["//ts/sass/bootstrap", "//ts/sass/codemirror"], ) sass_library( diff --git a/ts/sass/codemirror/BUILD.bazel b/ts/sass/codemirror/BUILD.bazel new file mode 100644 index 000000000..4577960c5 --- /dev/null +++ b/ts/sass/codemirror/BUILD.bazel @@ -0,0 +1,23 @@ +load("//ts:vendor.bzl", "pkg_from_name", "vendor_js_lib") +load("@io_bazel_rules_sass//:defs.bzl", "sass_library") + +# copy codemirror sass files in +vendor_js_lib( + name = "sass-sources", + include = [ + "lib/codemirror.css", + ], + base = "external/npm/node_modules/codemirror/", + pkg = pkg_from_name("codemirror"), + strip_prefix = "lib/", + visibility = ["//visibility:private"], +) + +# wrap them in a library +sass_library( + name = "codemirror", + srcs = [ + ":sass-sources", + ], + visibility = ["//visibility:public"], +) diff --git a/ts/yarn.lock b/ts/yarn.lock index 5fb4bd20b..53048b413 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -701,6 +701,13 @@ "@popperjs/core" "^2.9.2" "@types/jquery" "*" +"@types/codemirror@^5.60.0": + version "5.60.0" + resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.0.tgz#bf14b728449ebd355c17054262a083639a995710" + integrity sha512-xgzXZyCzedLRNC67/Nn8rpBtTFnAsX2C+Q/LGoH6zgcpF/LqdNHJMHEOhqT1bwUcSp6kQdOIuKzRbeW9DYhEhg== + dependencies: + "@types/tern" "*" + "@types/d3-array@*": version "2.12.1" resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-2.12.1.tgz#bee6857b812f1ecfd5e6832fd67f617b667dd024" @@ -921,6 +928,11 @@ resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.0.tgz#eb71e94feae62548282c4889308a3dfb57e36020" integrity sha512-jrm2K65CokCCX4NmowtA+MfXyuprZC13jbRuwprs6/04z/EcFg/MCwYdsHn+zgV4CQBiATiI7AEq7y1sZCtWKA== +"@types/estree@*": + version "0.0.48" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.48.tgz#18dc8091b285df90db2f25aa7d906cfc394b7f74" + integrity sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew== + "@types/geojson@*": version "7946.0.7" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad" @@ -1069,6 +1081,13 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== +"@types/tern@*": + version "0.23.3" + resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.3.tgz#4b54538f04a88c9ff79de1f6f94f575a7f339460" + integrity sha512-imDtS4TAoTcXk0g7u4kkWqedB3E4qpjXzCpD2LU5M5NAXHzCDsypyvXSaG7mM8DKYkCRa7tFp4tS/lp/Wo7Q3w== + dependencies: + "@types/estree" "*" + "@types/yargs-parser@*": version "20.2.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" @@ -1516,6 +1535,11 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +codemirror@^5.61.1: + version "5.61.1" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.1.tgz#ccfc8a43b8fcfb8b12e8e75b5ffde48d541406e0" + integrity sha512-+D1NZjAucuzE93vJGbAaXzvoBHwp9nJZWWWF9utjv25+5AZUiah6CIlfb4ikG4MoDsFsCG8niiJH5++OO2LgIQ== + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" From 678a5997e9f204f95feac803500d23b23c45183e Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 15:30:05 +0200 Subject: [PATCH 03/26] Connect HtmlEditButton to codable --- ts/editor/TemplateButtons.svelte | 31 ++++++++++++++++++++----------- ts/editor/codable.ts | 15 ++++++++++++--- ts/editor/editable.scss | 4 ++-- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index 3ee854e77..dd8a295cb 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -13,10 +13,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import DropdownItem from "components/DropdownItem.svelte"; import WithDropdownMenu from "components/WithDropdownMenu.svelte"; import WithShortcut from "components/WithShortcut.svelte"; + import WithState from "components/WithState.svelte"; import ClozeButton from "./ClozeButton.svelte"; import { wrap } from "./wrap"; import { appendInParentheses } from "./helpers"; + import { toggleHtmlEdit } from "./codable"; import { paperclipIcon, micIcon, functionIcon, xmlIcon } from "./icons"; export let api = {}; @@ -28,10 +30,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html function onRecord(): void { bridgeCommand("record"); } - - function onHtmlEdit(): void { - bridgeCommand("htmlEdit"); - } @@ -164,14 +162,25 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - { console.log(event); return true }} + let:state={active} + let:updateState > - {@html xmlIcon} - + { + toggleHtmlEdit(); + updateState(event); + }} + on:mount={createShortcut} + > + {@html xmlIcon} + + diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index c6337ff48..4c5bfdf68 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -3,8 +3,17 @@ import CodeMirror from "codemirror/src/codemirror" -export class Codable extends HTMLTextAreaElement { - connectedCallback(): void { - CodeMirror.fromTextArea(this); +const codables: Codable[] = []; + +export function toggleHtmlEdit() { + for (const codable of codables) { + CodeMirror.fromTextArea(codable); + } +} + +export class Codable extends HTMLTextAreaElement { + connectedCallback(): void { + this.setAttribute("hidden", ""); + codables.push(this); } } diff --git a/ts/editor/editable.scss b/ts/editor/editable.scss index 00beb30c5..4d2ccba42 100644 --- a/ts/editor/editable.scss +++ b/ts/editor/editable.scss @@ -1,7 +1,7 @@ -@import 'ts/sass/codemirror/codemirror'; - @use 'ts/sass/scrollbar'; +@import 'ts/sass/codemirror/codemirror'; + anki-editable { display: block; overflow-wrap: break-word; From 8a902944a85460dc06b1c5c6a2db42f87d5e00a4 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 17:08:33 +0200 Subject: [PATCH 04/26] Setup toggleHtmlEdit on editingArea --- ts/editor/TemplateButtons.svelte | 10 ++++++++-- ts/editor/codable.ts | 29 ++++++++++++++++++++++------- ts/editor/editingArea.ts | 4 ++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index dd8a295cb..53650b213 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -18,7 +18,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { wrap } from "./wrap"; import { appendInParentheses } from "./helpers"; - import { toggleHtmlEdit } from "./codable"; + import { forEditorField } from "."; import { paperclipIcon, micIcon, functionIcon, xmlIcon } from "./icons"; export let api = {}; @@ -30,6 +30,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html function onRecord(): void { bridgeCommand("record"); } + + function onHtmlEdit() { + forEditorField([], (field) => { + field.editingArea.toggleHtmlEdit(); + }) + } @@ -173,7 +179,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html iconSize={70} {active} on:click={(event) => { - toggleHtmlEdit(); + onHtmlEdit(); updateState(event); }} on:mount={createShortcut} diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index 4c5bfdf68..a84745bf9 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -3,17 +3,32 @@ import CodeMirror from "codemirror/src/codemirror" -const codables: Codable[] = []; - -export function toggleHtmlEdit() { - for (const codable of codables) { - CodeMirror.fromTextArea(codable); - } +const codeMirrorOptions = { + lineNumbers: true, + mode: "htmlmixed", } export class Codable extends HTMLTextAreaElement { + codeMirror: any; + connectedCallback(): void { this.setAttribute("hidden", ""); - codables.push(this); + } + + toggle(html: string): string { + return this.codeMirror ? this.teardown() : this.setup(html); + } + + setup(html: string): string { + this.innerHTML = html; + this.codeMirror = CodeMirror.fromTextArea(this, codeMirrorOptions); + return html; + } + + teardown(): string { + this.codeMirror.toTextArea(); + this.codeMirror = undefined; + console.log(this.innerHTML) + return this.innerHTML; } } diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index 6732cecae..720820a3e 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -123,4 +123,8 @@ export class EditingArea extends HTMLDivElement { blurEditable(): void { this.editable.blur(); } + + toggleHtmlEdit(): void { + this.fieldHTML = this.codable.toggle(this.fieldHTML); + } } From 7f76a98546c8222d312ca41e2990544e40747549 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 17:18:00 +0200 Subject: [PATCH 05/26] Apply the Monokai theme --- ts/editor/TemplateButtons.svelte | 7 +++++-- ts/editor/codable.ts | 10 ++++++---- ts/editor/editable.scss | 3 ++- ts/editor/editingArea.ts | 4 +++- ts/sass/codemirror/BUILD.bazel | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index 53650b213..f417443c2 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -34,7 +34,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html function onHtmlEdit() { forEditorField([], (field) => { field.editingArea.toggleHtmlEdit(); - }) + }); } @@ -170,7 +170,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html { console.log(event); return true }} + update={(event) => { + console.log(event); + return true; + }} let:state={active} let:updateState > diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index a84745bf9..641981a2d 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -1,12 +1,14 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import CodeMirror from "codemirror/src/codemirror" +import CodeMirror from "codemirror/src/codemirror"; const codeMirrorOptions = { lineNumbers: true, + lineWrapping: true, mode: "htmlmixed", -} + theme: "monokai", +}; export class Codable extends HTMLTextAreaElement { codeMirror: any; @@ -16,7 +18,7 @@ export class Codable extends HTMLTextAreaElement { } toggle(html: string): string { - return this.codeMirror ? this.teardown() : this.setup(html); + return this.codeMirror ? this.teardown() : this.setup(html); } setup(html: string): string { @@ -28,7 +30,7 @@ export class Codable extends HTMLTextAreaElement { teardown(): string { this.codeMirror.toTextArea(); this.codeMirror = undefined; - console.log(this.innerHTML) + console.log(this.innerHTML); return this.innerHTML; } } diff --git a/ts/editor/editable.scss b/ts/editor/editable.scss index 4d2ccba42..26c35131a 100644 --- a/ts/editor/editable.scss +++ b/ts/editor/editable.scss @@ -1,6 +1,7 @@ @use 'ts/sass/scrollbar'; -@import 'ts/sass/codemirror/codemirror'; +@import "ts/sass/codemirror/lib/codemirror"; +@import "ts/sass/codemirror/theme/monokai"; anki-editable { display: block; diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index 720820a3e..5fe738d18 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -44,7 +44,9 @@ export class EditingArea extends HTMLDivElement { this.editable = document.createElement("anki-editable") as Editable; this.shadowRoot!.appendChild(this.editable); - this.codable = document.createElement("textarea", { is: "anki-codable" }) as Codable; + this.codable = document.createElement("textarea", { + is: "anki-codable", + }) as Codable; this.shadowRoot!.appendChild(this.codable); } diff --git a/ts/sass/codemirror/BUILD.bazel b/ts/sass/codemirror/BUILD.bazel index 4577960c5..98eb96cbb 100644 --- a/ts/sass/codemirror/BUILD.bazel +++ b/ts/sass/codemirror/BUILD.bazel @@ -6,10 +6,10 @@ vendor_js_lib( name = "sass-sources", include = [ "lib/codemirror.css", + "theme", ], base = "external/npm/node_modules/codemirror/", pkg = pkg_from_name("codemirror"), - strip_prefix = "lib/", visibility = ["//visibility:private"], ) From 4daede2995b0893f6a7967622b7adeacc1c25762 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 17:49:50 +0200 Subject: [PATCH 06/26] Make Codable correctly update the editable state and its button --- ts/editor/TemplateButtons.svelte | 12 +++++++----- ts/editor/codable.ts | 21 ++++++++++++++++----- ts/editor/editingArea.ts | 7 ++++++- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index f417443c2..328fd0817 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -18,7 +18,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { wrap } from "./wrap"; import { appendInParentheses } from "./helpers"; - import { forEditorField } from "."; + import { forEditorField, getEditorField } from "."; import { paperclipIcon, micIcon, functionIcon, xmlIcon } from "./icons"; export let api = {}; @@ -31,6 +31,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html bridgeCommand("record"); } + function checkHtmlEdit() { + return getEditorField(0)!.editingArea.codable.active; + } + function onHtmlEdit() { forEditorField([], (field) => { field.editingArea.toggleHtmlEdit(); @@ -170,10 +174,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html { - console.log(event); - return true; - }} + update={checkHtmlEdit} let:state={active} let:updateState > @@ -186,6 +187,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html updateState(event); }} on:mount={createShortcut} + disables={false} > {@html xmlIcon} diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index 641981a2d..b1fe27e6a 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -10,8 +10,16 @@ const codeMirrorOptions = { theme: "monokai", }; +const parser = new DOMParser(); + export class Codable extends HTMLTextAreaElement { - codeMirror: any; + codeMirror: CodeMirror | undefined; + active: boolean; + + constructor() { + super(); + this.active = false; + } connectedCallback(): void { this.setAttribute("hidden", ""); @@ -22,15 +30,18 @@ export class Codable extends HTMLTextAreaElement { } setup(html: string): string { - this.innerHTML = html; + this.active = true; + this.value = html; this.codeMirror = CodeMirror.fromTextArea(this, codeMirrorOptions); - return html; + return ""; } teardown(): string { + this.active = false; this.codeMirror.toTextArea(); this.codeMirror = undefined; - console.log(this.innerHTML); - return this.innerHTML; + + const doc = parser.parseFromString(this.value, "text/html"); + return doc.documentElement.textContent!; } } diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index 5fe738d18..301403c59 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -127,6 +127,11 @@ export class EditingArea extends HTMLDivElement { } toggleHtmlEdit(): void { - this.fieldHTML = this.codable.toggle(this.fieldHTML); + const output = this.codable.toggle(this.fieldHTML); + console.log("succ output", output, this.ord); + if (output) { + console.log("writo"); + this.fieldHTML = output; + } } } From 74961ff11807e77976a04535c3848715b1ce4e1b Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 17:51:22 +0200 Subject: [PATCH 07/26] Make DOMParser correctly return innerHTML --- ts/editor/codable.ts | 2 +- ts/editor/editingArea.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index b1fe27e6a..c0d78a6a6 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -42,6 +42,6 @@ export class Codable extends HTMLTextAreaElement { this.codeMirror = undefined; const doc = parser.parseFromString(this.value, "text/html"); - return doc.documentElement.textContent!; + return doc.documentElement.innerHTML; } } diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index 301403c59..5d217aee9 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -128,9 +128,7 @@ export class EditingArea extends HTMLDivElement { toggleHtmlEdit(): void { const output = this.codable.toggle(this.fieldHTML); - console.log("succ output", output, this.ord); if (output) { - console.log("writo"); this.fieldHTML = output; } } From d80fc6a397697a010272127d5d44f80426dc373c Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 18:36:12 +0200 Subject: [PATCH 08/26] Successfully add monokai theming --- ts/editor/codable.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index c0d78a6a6..ca9cb0e62 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -1,7 +1,8 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import CodeMirror from "codemirror/src/codemirror"; +import * as CodeMirror from "codemirror/lib/codemirror"; +import "codemirror/mode/htmlmixed/htmlmixed"; const codeMirrorOptions = { lineNumbers: true, From 447e54d3aff28d2b1157e30b606eb194932e6a6f Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 19:08:14 +0200 Subject: [PATCH 09/26] Make editable hide correctly when activating codable --- ts/editor/codable.ts | 16 ++++++++-------- ts/editor/editable.scss | 4 ++++ ts/editor/editingArea.ts | 26 +++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index ca9cb0e62..383a0027e 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -5,10 +5,10 @@ import * as CodeMirror from "codemirror/lib/codemirror"; import "codemirror/mode/htmlmixed/htmlmixed"; const codeMirrorOptions = { - lineNumbers: true, - lineWrapping: true, mode: "htmlmixed", theme: "monokai", + lineNumbers: true, + lineWrapping: true, }; const parser = new DOMParser(); @@ -26,15 +26,15 @@ export class Codable extends HTMLTextAreaElement { this.setAttribute("hidden", ""); } - toggle(html: string): string { - return this.codeMirror ? this.teardown() : this.setup(html); - } - - setup(html: string): string { + setup(html: string): void { this.active = true; this.value = html; this.codeMirror = CodeMirror.fromTextArea(this, codeMirrorOptions); - return ""; + } + + focus(): void { + this.codeMirror.focus(); + this.codeMirror.setCursor(this.codeMirror.lineCount(), 0); } teardown(): string { diff --git a/ts/editor/editable.scss b/ts/editor/editable.scss index 26c35131a..7eba170b7 100644 --- a/ts/editor/editable.scss +++ b/ts/editor/editable.scss @@ -40,3 +40,7 @@ img.drawing { filter: unquote("invert() hue-rotate(180deg)"); } } + +[hidden] { + display: none; +} diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index 5d217aee9..f03f62dfd 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -10,6 +10,7 @@ import type { Codable } from "./codable"; import { updateActiveButtons } from "./toolbar"; import { bridgeCommand } from "./lib"; +import { caretToEnd } from "./helpers"; import { onInput, onKey, onKeyUp } from "./inputHandlers"; import { onFocus, onBlur } from "./focusHandlers"; @@ -126,10 +127,29 @@ export class EditingArea extends HTMLDivElement { this.editable.blur(); } + hasFocus(): boolean { + return document.activeElement === this; + } + toggleHtmlEdit(): void { - const output = this.codable.toggle(this.fieldHTML); - if (output) { - this.fieldHTML = output; + const hadFocus = this.hasFocus(); + + if (this.codable.active) { + const html = this.codable.teardown(); + this.fieldHTML = html; + + this.editable.hidden = false; + if (hadFocus) { + this.focusEditable(); + caretToEnd(this); + } + } else { + this.editable.hidden = true; + this.codable.setup(this.fieldHTML); + + if (hadFocus) { + this.codable.focus(); + } } } } From 95dad1abc1166492196bf58a4b2ef8f15855662c Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 20:27:34 +0200 Subject: [PATCH 10/26] Match and fold tags --- ts/editor/codable.ts | 7 +++++++ ts/editor/editable.scss | 1 + ts/sass/codemirror/BUILD.bazel | 1 + 3 files changed, 9 insertions(+) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index 383a0027e..e3d6345c7 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -3,12 +3,19 @@ import * as CodeMirror from "codemirror/lib/codemirror"; import "codemirror/mode/htmlmixed/htmlmixed"; +import "codemirror/addon/fold/foldcode"; +import "codemirror/addon/fold/foldgutter"; +import "codemirror/addon/fold/xml-fold"; +import "codemirror/addon/edit/matchtags.js"; const codeMirrorOptions = { mode: "htmlmixed", theme: "monokai", lineNumbers: true, lineWrapping: true, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], + matchTags: { bothTags: true }, }; const parser = new DOMParser(); diff --git a/ts/editor/editable.scss b/ts/editor/editable.scss index 7eba170b7..a868e10f9 100644 --- a/ts/editor/editable.scss +++ b/ts/editor/editable.scss @@ -2,6 +2,7 @@ @import "ts/sass/codemirror/lib/codemirror"; @import "ts/sass/codemirror/theme/monokai"; +@import "ts/sass/codemirror/addon/fold/foldgutter"; anki-editable { display: block; diff --git a/ts/sass/codemirror/BUILD.bazel b/ts/sass/codemirror/BUILD.bazel index 98eb96cbb..0ef981a1e 100644 --- a/ts/sass/codemirror/BUILD.bazel +++ b/ts/sass/codemirror/BUILD.bazel @@ -7,6 +7,7 @@ vendor_js_lib( include = [ "lib/codemirror.css", "theme", + "addon/fold/foldgutter.css", ], base = "external/npm/node_modules/codemirror/", pkg = pkg_from_name("codemirror"), From 7266f485d08108f99e95b03ed70629c8389360e7 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 20:47:33 +0200 Subject: [PATCH 11/26] Make Codable enter behavior the same as Editable --- ts/editor/codable.ts | 1 + ts/editor/editable.scss | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index e3d6345c7..def1b1c08 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -16,6 +16,7 @@ const codeMirrorOptions = { foldGutter: true, gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], matchTags: { bothTags: true }, + viewportMargin: Infinity, }; const parser = new DOMParser(); diff --git a/ts/editor/editable.scss b/ts/editor/editable.scss index a868e10f9..c502f7793 100644 --- a/ts/editor/editable.scss +++ b/ts/editor/editable.scss @@ -1,9 +1,5 @@ @use 'ts/sass/scrollbar'; -@import "ts/sass/codemirror/lib/codemirror"; -@import "ts/sass/codemirror/theme/monokai"; -@import "ts/sass/codemirror/addon/fold/foldgutter"; - anki-editable { display: block; overflow-wrap: break-word; @@ -45,3 +41,12 @@ img.drawing { [hidden] { display: none; } + +@import "ts/sass/codemirror/lib/codemirror"; +@import "ts/sass/codemirror/theme/monokai"; +@import "ts/sass/codemirror/addon/fold/foldgutter"; + +.CodeMirror { + height: auto; + padding: 6px 0; +} From a87f81f00bd711fe59aa4ffcfe7048851d6fb145 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 20:57:20 +0200 Subject: [PATCH 12/26] Only toggle the current field, not all --- ts/editor/TemplateButtons.svelte | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index 328fd0817..2fd298f56 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -18,7 +18,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { wrap } from "./wrap"; import { appendInParentheses } from "./helpers"; - import { forEditorField, getEditorField } from "."; + import { getCurrentField } from "."; import { paperclipIcon, micIcon, functionIcon, xmlIcon } from "./icons"; export let api = {}; @@ -32,13 +32,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } function checkHtmlEdit() { - return getEditorField(0)!.editingArea.codable.active; + const currentField = getCurrentField(); + return currentField ? currentField.codable.active : false; } function onHtmlEdit() { - forEditorField([], (field) => { - field.editingArea.toggleHtmlEdit(); - }); + const currentField = getCurrentField(); + if (currentField) { + currentField.toggleHtmlEdit(); + } } @@ -187,7 +189,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html updateState(event); }} on:mount={createShortcut} - disables={false} > {@html xmlIcon} From 9cbc1c33e7818992eb43589eea854a683144cf53 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 21:36:56 +0200 Subject: [PATCH 13/26] Make it so you don't have to close the HTML mode for html to be written back --- ts/editor/codable.ts | 22 +++++++++++++++---- ts/editor/editable.ts | 6 +++++- ts/editor/editingArea.ts | 43 ++++++++++++++++++++++++-------------- ts/editor/helpers.ts | 6 +++--- ts/editor/index.ts | 3 +-- ts/editor/inputHandlers.ts | 4 ++-- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index def1b1c08..c46c8fee0 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -21,6 +21,11 @@ const codeMirrorOptions = { const parser = new DOMParser(); +function parseHTML(html: string): string { + const doc = parser.parseFromString(html, "text/html"); + return doc.documentElement.innerHTML; +} + export class Codable extends HTMLTextAreaElement { codeMirror: CodeMirror | undefined; active: boolean; @@ -30,18 +35,29 @@ export class Codable extends HTMLTextAreaElement { this.active = false; } + set fieldHTML(content: string) { + this.value = content; + } + + get fieldHTML(): string { + return parseHTML(this.codeMirror.getValue()); + } + connectedCallback(): void { this.setAttribute("hidden", ""); } setup(html: string): void { this.active = true; - this.value = html; + this.fieldHTML = html; this.codeMirror = CodeMirror.fromTextArea(this, codeMirrorOptions); } focus(): void { this.codeMirror.focus(); + } + + caretToEnd(): void { this.codeMirror.setCursor(this.codeMirror.lineCount(), 0); } @@ -49,8 +65,6 @@ export class Codable extends HTMLTextAreaElement { this.active = false; this.codeMirror.toTextArea(); this.codeMirror = undefined; - - const doc = parser.parseFromString(this.value, "text/html"); - return doc.documentElement.innerHTML; + return parseHTML(this.value); } } diff --git a/ts/editor/editable.ts b/ts/editor/editable.ts index 680bdf35c..9161bf248 100644 --- a/ts/editor/editable.ts +++ b/ts/editor/editable.ts @@ -1,7 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import { nodeIsInline } from "./helpers"; +import { nodeIsInline, caretToEnd } from "./helpers"; function containsInlineContent(field: Element): boolean { if (field.childNodes.length === 0) { @@ -36,4 +36,8 @@ export class Editable extends HTMLElement { connectedCallback(): void { this.setAttribute("contenteditable", ""); } + + caretToEnd() { + caretToEnd(this); + } } diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index f03f62dfd..41ad580f5 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -10,7 +10,6 @@ import type { Codable } from "./codable"; import { updateActiveButtons } from "./toolbar"; import { bridgeCommand } from "./lib"; -import { caretToEnd } from "./helpers"; import { onInput, onKey, onKeyUp } from "./inputHandlers"; import { onFocus, onBlur } from "./focusHandlers"; @@ -51,16 +50,20 @@ export class EditingArea extends HTMLDivElement { this.shadowRoot!.appendChild(this.codable); } + get activeInput(): Editable | Codable { + return this.codable.active ? this.codable : this.editable; + } + get ord(): number { return Number(this.getAttribute("ord")); } set fieldHTML(content: string) { - this.editable.fieldHTML = content; + this.activeInput.fieldHTML = content; } get fieldHTML(): string { - return this.editable.fieldHTML; + return this.activeInput.fieldHTML; } connectedCallback(): void { @@ -119,12 +122,24 @@ export class EditingArea extends HTMLDivElement { return this.shadowRoot!.getSelection()!; } - focusEditable(): void { - this.editable.focus(); + focus(): void { + this.activeInput.focus(); } + blur(): void { + this.activeInput.blur(); + } + + /* legacy */ + focusEditable(): void { + focus(); + } blurEditable(): void { - this.editable.blur(); + blur(); + } + + caretToEnd(): void { + this.activeInput.caretToEnd(); } hasFocus(): boolean { @@ -135,21 +150,17 @@ export class EditingArea extends HTMLDivElement { const hadFocus = this.hasFocus(); if (this.codable.active) { - const html = this.codable.teardown(); - this.fieldHTML = html; - + this.fieldHTML = this.codable.teardown(); this.editable.hidden = false; - if (hadFocus) { - this.focusEditable(); - caretToEnd(this); - } } else { this.editable.hidden = true; + console.log("eyo", this.fieldHTML); this.codable.setup(this.fieldHTML); + } - if (hadFocus) { - this.codable.focus(); - } + if (hadFocus) { + this.focus(); + this.caretToEnd(); } } } diff --git a/ts/editor/helpers.ts b/ts/editor/helpers.ts index 871feb1c7..240a12611 100644 --- a/ts/editor/helpers.ts +++ b/ts/editor/helpers.ts @@ -69,11 +69,11 @@ export function nodeIsInline(node: Node): boolean { return !nodeIsElement(node) || INLINE_TAGS.includes(node.tagName); } -export function caretToEnd(currentField: EditingArea): void { +export function caretToEnd(node: Node): void { const range = document.createRange(); - range.selectNodeContents(currentField.editable); + range.selectNodeContents(node); range.collapse(false); - const selection = currentField.getSelection(); + const selection = (node.getRootNode() as Document | ShadowRoot).getSelection()!; selection.removeAllRanges(); selection.addRange(range); } diff --git a/ts/editor/index.ts b/ts/editor/index.ts index 88295b255..333b8415d 100644 --- a/ts/editor/index.ts +++ b/ts/editor/index.ts @@ -11,7 +11,6 @@ import { setupI18n, ModuleName } from "lib/i18n"; import "./fields.css"; -import { caretToEnd } from "./helpers"; import { saveField } from "./changeTimer"; import { EditorField } from "./editorField"; @@ -52,7 +51,7 @@ export function focusField(n: number): void { if (field) { field.editingArea.focusEditable(); - caretToEnd(field.editingArea); + field.editingArea.caretToEnd(); updateActiveButtons(new Event("manualfocus")); } } diff --git a/ts/editor/inputHandlers.ts b/ts/editor/inputHandlers.ts index f80011537..d717741b0 100644 --- a/ts/editor/inputHandlers.ts +++ b/ts/editor/inputHandlers.ts @@ -7,7 +7,7 @@ import { updateActiveButtons } from "./toolbar"; import { EditingArea } from "./editingArea"; -import { caretToEnd, nodeIsElement, getBlockElement } from "./helpers"; +import { nodeIsElement, getBlockElement } from "./helpers"; import { triggerChangeTimer } from "./changeTimer"; import { registerShortcut } from "lib/shortcuts"; @@ -59,7 +59,7 @@ export function onKey(evt: KeyboardEvent): void { function updateFocus(evt: FocusEvent) { const newFocusTarget = evt.target; if (newFocusTarget instanceof EditingArea) { - caretToEnd(newFocusTarget); + newFocusTarget.caretToEnd(); updateActiveButtons(evt); } } From f923660fc6bd26ea4577f433ef5e939bd7eedbae Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 21:45:11 +0200 Subject: [PATCH 14/26] Add autoclosetag addon for CodeMirror --- ts/editor/codable.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index c46c8fee0..c400450f1 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -7,6 +7,7 @@ import "codemirror/addon/fold/foldcode"; import "codemirror/addon/fold/foldgutter"; import "codemirror/addon/fold/xml-fold"; import "codemirror/addon/edit/matchtags.js"; +import "codemirror/addon/edit/closetag.js"; const codeMirrorOptions = { mode: "htmlmixed", @@ -16,6 +17,7 @@ const codeMirrorOptions = { foldGutter: true, gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], matchTags: { bothTags: true }, + autoCloseTags: true, viewportMargin: Infinity, }; From 94c789a5bfc72a1152ec1bbc588ad778cf2d4501 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 22:02:06 +0200 Subject: [PATCH 15/26] Have correct enter behavior in for Editable and Codable --- ts/editor/changeTimer.ts | 2 +- ts/editor/codable.ts | 14 +++++++++----- ts/editor/editable.ts | 12 +++++++++++- ts/editor/editingArea.ts | 25 +++++++++++++++++-------- ts/editor/focusHandlers.ts | 2 +- ts/editor/index.ts | 4 ++-- ts/editor/inputHandlers.ts | 13 ++++--------- 7 files changed, 45 insertions(+), 27 deletions(-) diff --git a/ts/editor/changeTimer.ts b/ts/editor/changeTimer.ts index 30e442e5d..577cc3c82 100644 --- a/ts/editor/changeTimer.ts +++ b/ts/editor/changeTimer.ts @@ -41,6 +41,6 @@ export function saveNow(keepFocus: boolean): void { saveField(currentField, "key"); } else { // triggers onBlur, which saves - currentField.blurEditable(); + currentField.blur(); } } diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index c400450f1..e7d4a10e0 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -55,6 +55,13 @@ export class Codable extends HTMLTextAreaElement { this.codeMirror = CodeMirror.fromTextArea(this, codeMirrorOptions); } + teardown(): string { + this.active = false; + this.codeMirror.toTextArea(); + this.codeMirror = undefined; + return parseHTML(this.value); + } + focus(): void { this.codeMirror.focus(); } @@ -63,10 +70,7 @@ export class Codable extends HTMLTextAreaElement { this.codeMirror.setCursor(this.codeMirror.lineCount(), 0); } - teardown(): string { - this.active = false; - this.codeMirror.toTextArea(); - this.codeMirror = undefined; - return parseHTML(this.value); + enterBehavior(): void { + /* default */ } } diff --git a/ts/editor/editable.ts b/ts/editor/editable.ts index 9161bf248..338d3f549 100644 --- a/ts/editor/editable.ts +++ b/ts/editor/editable.ts @@ -1,7 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import { nodeIsInline, caretToEnd } from "./helpers"; +import { nodeIsInline, caretToEnd, getBlockElement } from "./helpers"; function containsInlineContent(field: Element): boolean { if (field.childNodes.length === 0) { @@ -40,4 +40,14 @@ export class Editable extends HTMLElement { caretToEnd() { caretToEnd(this); } + + enterBehavior(event: KeyboardEvent): void { + if ( + !getBlockElement(this.getRootNode() as Document | ShadowRoot) !== + event.shiftKey + ) { + event.preventDefault(); + document.execCommand("insertLineBreak"); + } + } } diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index 41ad580f5..83287531e 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -130,14 +130,6 @@ export class EditingArea extends HTMLDivElement { this.activeInput.blur(); } - /* legacy */ - focusEditable(): void { - focus(); - } - blurEditable(): void { - blur(); - } - caretToEnd(): void { this.activeInput.caretToEnd(); } @@ -146,6 +138,10 @@ export class EditingArea extends HTMLDivElement { return document.activeElement === this; } + enterBehavior(event: KeyboardEvent): void { + this.activeInput.enterBehavior(event); + } + toggleHtmlEdit(): void { const hadFocus = this.hasFocus(); @@ -163,4 +159,17 @@ export class EditingArea extends HTMLDivElement { this.caretToEnd(); } } + + /** + * @deprecated Use focus instead + */ + focusEditable(): void { + focus(); + } + /** + * @deprecated Use blur instead + */ + blurEditable(): void { + blur(); + } } diff --git a/ts/editor/focusHandlers.ts b/ts/editor/focusHandlers.ts index 1c748f4fb..6d782cd9a 100644 --- a/ts/editor/focusHandlers.ts +++ b/ts/editor/focusHandlers.ts @@ -9,7 +9,7 @@ import { bridgeCommand } from "./lib"; export function onFocus(evt: FocusEvent): void { const currentField = evt.currentTarget as EditingArea; - currentField.focusEditable(); + currentField.focus(); bridgeCommand(`focus:${currentField.ord}`); enableButtons(); } diff --git a/ts/editor/index.ts b/ts/editor/index.ts index 333b8415d..1613c26ff 100644 --- a/ts/editor/index.ts +++ b/ts/editor/index.ts @@ -50,7 +50,7 @@ export function focusField(n: number): void { const field = getEditorField(n); if (field) { - field.editingArea.focusEditable(); + field.editingArea.focus(); field.editingArea.caretToEnd(); updateActiveButtons(new Event("manualfocus")); } @@ -61,7 +61,7 @@ export function focusIfField(x: number, y: number): boolean { for (let i = 0; i < elements.length; i++) { const elem = elements[i] as EditingArea; if (elem instanceof EditingArea) { - elem.focusEditable(); + elem.focus(); return true; } } diff --git a/ts/editor/inputHandlers.ts b/ts/editor/inputHandlers.ts index d717741b0..a1fee4f2e 100644 --- a/ts/editor/inputHandlers.ts +++ b/ts/editor/inputHandlers.ts @@ -7,7 +7,7 @@ import { updateActiveButtons } from "./toolbar"; import { EditingArea } from "./editingArea"; -import { nodeIsElement, getBlockElement } from "./helpers"; +import { nodeIsElement } from "./helpers"; import { triggerChangeTimer } from "./changeTimer"; import { registerShortcut } from "lib/shortcuts"; @@ -22,17 +22,12 @@ export function onKey(evt: KeyboardEvent): void { // esc clears focus, allowing dialog to close if (evt.code === "Escape") { - currentField.blurEditable(); - return; + return currentField.blur(); } // prefer
instead of
- if ( - evt.code === "Enter" && - !getBlockElement(currentField.shadowRoot!) !== evt.shiftKey - ) { - evt.preventDefault(); - document.execCommand("insertLineBreak"); + if (evt.code === "Enter") { + return currentField.enterBehavior(evt); } // // fix Ctrl+right/left handling in RTL fields From 817dee1a1bb3d9f01a876aed55c982259eee7bad Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Thu, 17 Jun 2021 23:12:15 +0200 Subject: [PATCH 16/26] Make different onPaste behavior for Editable and Codable --- ts/editor/codable.ts | 6 +++++- ts/editor/editable.ts | 8 +++++++- ts/editor/editingArea.ts | 19 ++++++++++--------- ts/editor/inputHandlers.ts | 2 +- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index e7d4a10e0..cd56d61b5 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -70,7 +70,11 @@ export class Codable extends HTMLTextAreaElement { this.codeMirror.setCursor(this.codeMirror.lineCount(), 0); } - enterBehavior(): void { + onEnter(): void { + /* default */ + } + + onPaste(): void { /* default */ } } diff --git a/ts/editor/editable.ts b/ts/editor/editable.ts index 338d3f549..6a4a34081 100644 --- a/ts/editor/editable.ts +++ b/ts/editor/editable.ts @@ -1,6 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +import { bridgeCommand } from "./lib"; import { nodeIsInline, caretToEnd, getBlockElement } from "./helpers"; function containsInlineContent(field: Element): boolean { @@ -41,7 +42,7 @@ export class Editable extends HTMLElement { caretToEnd(this); } - enterBehavior(event: KeyboardEvent): void { + onEnter(event: KeyboardEvent): void { if ( !getBlockElement(this.getRootNode() as Document | ShadowRoot) !== event.shiftKey @@ -50,4 +51,9 @@ export class Editable extends HTMLElement { document.execCommand("insertLineBreak"); } } + + onPaste(event: ClipboardEvent): void { + bridgeCommand("paste"); + event.preventDefault(); + } } diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index 83287531e..efe211764 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -13,11 +13,6 @@ import { bridgeCommand } from "./lib"; import { onInput, onKey, onKeyUp } from "./inputHandlers"; import { onFocus, onBlur } from "./focusHandlers"; -function onPaste(evt: ClipboardEvent): void { - bridgeCommand("paste"); - evt.preventDefault(); -} - function onCutOrCopy(): void { bridgeCommand("cutOrCopy"); } @@ -48,6 +43,8 @@ export class EditingArea extends HTMLDivElement { is: "anki-codable", }) as Codable; this.shadowRoot!.appendChild(this.codable); + + this.onPaste = this.onPaste.bind(this); } get activeInput(): Editable | Codable { @@ -72,7 +69,7 @@ export class EditingArea extends HTMLDivElement { this.addEventListener("input", onInput); this.addEventListener("focus", onFocus); this.addEventListener("blur", onBlur); - this.addEventListener("paste", onPaste); + this.addEventListener("paste", this.onPaste); this.addEventListener("copy", onCutOrCopy); this.addEventListener("oncut", onCutOrCopy); this.addEventListener("mouseup", updateActiveButtons); @@ -87,7 +84,7 @@ export class EditingArea extends HTMLDivElement { this.removeEventListener("input", onInput); this.removeEventListener("focus", onFocus); this.removeEventListener("blur", onBlur); - this.removeEventListener("paste", onPaste); + this.removeEventListener("paste", this.onPaste); this.removeEventListener("copy", onCutOrCopy); this.removeEventListener("oncut", onCutOrCopy); this.removeEventListener("mouseup", updateActiveButtons); @@ -138,8 +135,12 @@ export class EditingArea extends HTMLDivElement { return document.activeElement === this; } - enterBehavior(event: KeyboardEvent): void { - this.activeInput.enterBehavior(event); + onEnter(event: KeyboardEvent): void { + this.activeInput.onEnter(event); + } + + onPaste(event: ClipboardEvent): void { + this.activeInput.onPaste(event); } toggleHtmlEdit(): void { diff --git a/ts/editor/inputHandlers.ts b/ts/editor/inputHandlers.ts index a1fee4f2e..ae2c8c44f 100644 --- a/ts/editor/inputHandlers.ts +++ b/ts/editor/inputHandlers.ts @@ -27,7 +27,7 @@ export function onKey(evt: KeyboardEvent): void { // prefer
instead of
if (evt.code === "Enter") { - return currentField.enterBehavior(evt); + return currentField.onEnter(evt); } // // fix Ctrl+right/left handling in RTL fields From eeb954535fc6a80d76b9556c77043e01a40565cf Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 00:27:07 +0200 Subject: [PATCH 17/26] Disable Bold button in Codable --- ts/components/IconButton.svelte | 11 +++---- ts/components/WithContext.svelte | 16 +++++++++ ts/components/WithState.svelte | 13 ++++---- ts/editor/FormatInlineButtons.svelte | 49 +++++++++++++++++----------- ts/editor/codable.ts | 3 ++ ts/editor/contextKeys.ts | 4 +++ ts/editor/editable.ts | 6 ++++ ts/editor/toolbar.ts | 11 +++++++ 8 files changed, 81 insertions(+), 32 deletions(-) create mode 100644 ts/components/WithContext.svelte create mode 100644 ts/editor/contextKeys.ts diff --git a/ts/components/IconButton.svelte b/ts/components/IconButton.svelte index 6763def8f..7c02c44fe 100644 --- a/ts/components/IconButton.svelte +++ b/ts/components/IconButton.svelte @@ -3,9 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> + + diff --git a/ts/components/WithState.svelte b/ts/components/WithState.svelte index 97d22ee4c..14657087f 100644 --- a/ts/components/WithState.svelte +++ b/ts/components/WithState.svelte @@ -5,14 +5,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html @@ -27,24 +30,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - document.queryCommandState("bold")} - let:state={active} - let:updateState - > - { - document.execCommand("bold"); - updateState(event); - }} - on:mount={createShortcut} - > - {@html boldIcon} - - + + + document.queryCommandState("bold")} + let:state={active} + let:updateState + > + { + document.execCommand("bold"); + updateState(event); + }} + on:mount={createShortcut} + > + {@html boldIcon} + + + + diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index cd56d61b5..af9916555 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -9,6 +9,8 @@ import "codemirror/addon/fold/xml-fold"; import "codemirror/addon/edit/matchtags.js"; import "codemirror/addon/edit/closetag.js"; +import { setCodableButtons } from "./toolbar"; + const codeMirrorOptions = { mode: "htmlmixed", theme: "monokai", @@ -64,6 +66,7 @@ export class Codable extends HTMLTextAreaElement { focus(): void { this.codeMirror.focus(); + setCodableButtons(); } caretToEnd(): void { diff --git a/ts/editor/contextKeys.ts b/ts/editor/contextKeys.ts new file mode 100644 index 000000000..552cb9188 --- /dev/null +++ b/ts/editor/contextKeys.ts @@ -0,0 +1,4 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +export const inCodableKey = Symbol("inCodable"); diff --git a/ts/editor/editable.ts b/ts/editor/editable.ts index 6a4a34081..78a2e9dd9 100644 --- a/ts/editor/editable.ts +++ b/ts/editor/editable.ts @@ -3,6 +3,7 @@ import { bridgeCommand } from "./lib"; import { nodeIsInline, caretToEnd, getBlockElement } from "./helpers"; +import { setEditableButtons } from "./toolbar"; function containsInlineContent(field: Element): boolean { if (field.childNodes.length === 0) { @@ -38,6 +39,11 @@ export class Editable extends HTMLElement { this.setAttribute("contenteditable", ""); } + focus() { + super.focus(); + setEditableButtons(); + } + caretToEnd() { caretToEnd(this); } diff --git a/ts/editor/toolbar.ts b/ts/editor/toolbar.ts index 856d69014..ac6c18b42 100644 --- a/ts/editor/toolbar.ts +++ b/ts/editor/toolbar.ts @@ -7,12 +7,14 @@ */ import { disabledKey, nightModeKey } from "components/contextKeys"; +import { inCodableKey } from "./contextKeys"; import { writable } from "svelte/store"; import EditorToolbar from "./EditorToolbar.svelte"; import "./bootstrap.css"; const disabled = writable(false); +const inCodable = writable(false); export function initToolbar(i18n: Promise): Promise { let toolbarResolve: (value: EditorToolbar) => void; @@ -27,6 +29,7 @@ export function initToolbar(i18n: Promise): Promise { const context = new Map(); context.set(disabledKey, disabled); + context.set(inCodableKey, inCodable); context.set( nightModeKey, document.documentElement.classList.contains("night-mode") @@ -47,6 +50,14 @@ export function disableButtons(): void { disabled.set(true); } +export function setCodableButtons(): void { + inCodable.set(true); +} + +export function setEditableButtons(): void { + inCodable.set(false); +} + export { updateActiveButtons, clearActiveButtons, From 28679968f7eb6d67e7318a67e59c8d1574140c8c Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 01:17:43 +0200 Subject: [PATCH 18/26] Disable FormatBlock buttons for Codable --- ts/components/WithContext.svelte | 2 +- ts/editor/CommandIconButton.svelte | 79 +++++++++++++ ts/editor/FormatBlockButtons.svelte | 155 ++++++++----------------- ts/editor/FormatInlineButtons.svelte | 167 +++++---------------------- ts/editor/OnlyEditable.svelte | 15 +++ 5 files changed, 174 insertions(+), 244 deletions(-) create mode 100644 ts/editor/CommandIconButton.svelte create mode 100644 ts/editor/OnlyEditable.svelte diff --git a/ts/components/WithContext.svelte b/ts/components/WithContext.svelte index 111da8c1e..5cfba6123 100644 --- a/ts/components/WithContext.svelte +++ b/ts/components/WithContext.svelte @@ -6,7 +6,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type { Readable } from "svelte/store"; import { getContext } from "svelte"; - type T = unknown; + type T = boolean; export let key: Symbol | string; diff --git a/ts/editor/CommandIconButton.svelte b/ts/editor/CommandIconButton.svelte new file mode 100644 index 000000000..4781d2ccc --- /dev/null +++ b/ts/editor/CommandIconButton.svelte @@ -0,0 +1,79 @@ + + + + + {#if withoutShortcut && withoutState} + document.execCommand(key)}> + + + {:else if withoutShortcut} + document.queryCommandState(key)} + let:state={active} + let:updateState + > + { + document.execCommand(key); + updateState(event); + }} + > + + + + {:else if withoutState} + + document.execCommand(key)} + on:mount={createShortcut} + > + + + + {:else} + + document.queryCommandState(key)} + let:state={active} + let:updateState + > + { + document.execCommand(key); + updateState(event); + }} + on:mount={createShortcut} + > + + + + + {/if} + diff --git a/ts/editor/FormatBlockButtons.svelte b/ts/editor/FormatBlockButtons.svelte index 358b02ad5..f1d4376ea 100644 --- a/ts/editor/FormatBlockButtons.svelte +++ b/ts/editor/FormatBlockButtons.svelte @@ -11,8 +11,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import IconButton from "components/IconButton.svelte"; import ButtonDropdown from "components/ButtonDropdown.svelte"; import ButtonToolbarItem from "components/ButtonToolbarItem.svelte"; - import WithState from "components/WithState.svelte"; import WithDropdownMenu from "components/WithDropdownMenu.svelte"; + import OnlyEditable from "./OnlyEditable.svelte"; + import CommandIconButton from "./CommandIconButton.svelte"; import { getListItem } from "./helpers"; import { @@ -46,134 +47,66 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - document.queryCommandState("insertUnorderedList")} - let:state={active} - let:updateState + tooltip={tr.editingUnorderedList()} + withoutShortcut>{@html ulIcon} - { - document.execCommand("insertUnorderedList"); - updateState(event); - }} - > - {@html ulIcon} - - - document.queryCommandState("insertOrderedList")} - let:state={active} - let:updateState + tooltip={tr.editingOrderedList()} + withoutShortcut>{@html olIcon} - { - document.execCommand("insertOrderedList"); - updateState(event); - }} - > - {@html olIcon} - - - - {@html listOptionsIcon} - + + + {@html listOptionsIcon} + + - document.queryCommandState("justifyLeft")} - let:state={active} - let:updateState + tooltip={tr.editingAlignLeft()} + withoutShortcut + >{@html justifyLeftIcon} - { - document.execCommand("justifyLeft"); - updateState(event); - }} - > - {@html justifyLeftIcon} - - - - document.queryCommandState("justifyCenter")} - let:state={active} - let:updateState + tooltip={tr.editingCenter()} + withoutShortcut + >{@html justifyCenterIcon} - { - document.execCommand("justifyCenter"); - updateState(event); - }} - > - {@html justifyCenterIcon} - - - - document.queryCommandState("justifyRight")} - let:state={active} - let:updateState + tooltip={tr.editingAlignRight()} + withoutShortcut + >{@html justifyRightIcon} - { - document.execCommand("justifyRight"); - updateState(event); - }} - > - {@html justifyRightIcon} - - - document.queryCommandState("justifyFull")} - let:state={active} - let:updateState + tooltip={tr.editingJustify()} + withoutShortcut + >{@html justifyFullIcon} - { - document.execCommand("justifyFull"); - updateState(event); - }} - > - {@html justifyFullIcon} - - @@ -181,21 +114,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - {@html outdentIcon} - + + + {@html outdentIcon} + + - - {@html indentIcon} - + + + {@html indentIcon} + + diff --git a/ts/editor/FormatInlineButtons.svelte b/ts/editor/FormatInlineButtons.svelte index 1758ce1c5..47032b74e 100644 --- a/ts/editor/FormatInlineButtons.svelte +++ b/ts/editor/FormatInlineButtons.svelte @@ -7,10 +7,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import ButtonGroup from "components/ButtonGroup.svelte"; import ButtonGroupItem from "components/ButtonGroupItem.svelte"; - import IconButton from "components/IconButton.svelte"; - import WithShortcut from "components/WithShortcut.svelte"; - import WithContext from "components/WithContext.svelte"; - import WithState from "components/WithState.svelte"; + import CommandIconButton from "./CommandIconButton.svelte"; import { boldIcon, @@ -20,157 +17,57 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html subscriptIcon, eraserIcon, } from "./icons"; - import { appendInParentheses } from "./helpers"; - import { disabledKey } from "components/contextKeys"; - import { inCodableKey } from "./contextKeys"; export let api = {}; - - - - document.queryCommandState("bold")} - let:state={active} - let:updateState - > - { - document.execCommand("bold"); - updateState(event); - }} - on:mount={createShortcut} - > - {@html boldIcon} - - - - - + {@html boldIcon} - - document.queryCommandState("italic")} - let:state={active} - let:updateState - > - { - document.execCommand("italic"); - updateState(event); - }} - on:mount={createShortcut} - > - {@html italicIcon} - - - + {@html italicIcon} - - document.queryCommandState("underline")} - let:state={active} - let:updateState - > - { - document.execCommand("underline"); - updateState(event); - }} - on:mount={createShortcut} - > - {@html underlineIcon} - - - + {@html underlineIcon} - - document.queryCommandState("superscript")} - let:state={active} - let:updateState - > - { - document.execCommand("superscript"); - updateState(event); - }} - on:mount={createShortcut} - > - {@html superscriptIcon} - - - + {@html superscriptIcon} - - document.queryCommandState("subscript")} - let:state={active} - let:updateState - > - { - document.execCommand("subscript"); - updateState(event); - }} - on:mount={createShortcut} - > - {@html subscriptIcon} - - - + {@html subscriptIcon} - - { - document.execCommand("removeFormat"); - }} - on:mount={createShortcut} - > - {@html eraserIcon} - - + {@html eraserIcon} diff --git a/ts/editor/OnlyEditable.svelte b/ts/editor/OnlyEditable.svelte new file mode 100644 index 000000000..c36bfa86d --- /dev/null +++ b/ts/editor/OnlyEditable.svelte @@ -0,0 +1,15 @@ + + + + + + + + From c23665cf6395ac783227ec0b328aa33f7eb60447 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 01:32:46 +0200 Subject: [PATCH 19/26] Allow Tab and Shift+Tab on Codable --- ts/editor/codable.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index af9916555..f243085be 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -20,6 +20,7 @@ const codeMirrorOptions = { gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], matchTags: { bothTags: true }, autoCloseTags: true, + extraKeys: { Tab: false, "Shift-Tab": false }, viewportMargin: Infinity, }; From 2322d170fc1e49ea27f55cfbd15de54162b9e17b Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 01:44:15 +0200 Subject: [PATCH 20/26] Disable ColorButtons for Codable --- ts/editor/ColorButtons.svelte | 93 ++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/ts/editor/ColorButtons.svelte b/ts/editor/ColorButtons.svelte index 388c000ed..eac0cad46 100644 --- a/ts/editor/ColorButtons.svelte +++ b/ts/editor/ColorButtons.svelte @@ -11,6 +11,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import ColorPicker from "components/ColorPicker.svelte"; import WithShortcut from "components/WithShortcut.svelte"; import WithColorHelper from "./WithColorHelper.svelte"; + import OnlyEditable from "./OnlyEditable.svelte"; import { textColorIcon, highlightColorIcon, arrowIcon } from "./icons"; import { appendInParentheses } from "./helpers"; @@ -28,51 +29,61 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - - - {@html textColorIcon} - {@html colorHelperIcon} - - - + + + + + {@html textColorIcon} + {@html colorHelperIcon} + + + - - - - {@html arrowIcon} - - - - + + + + {@html arrowIcon} + + + + + - - - {@html highlightColorIcon} - {@html colorHelperIcon} - - + + + + {@html highlightColorIcon} + {@html colorHelperIcon} + + - - - {@html arrowIcon} - - - + + + {@html arrowIcon} + + + + From 8a07d3161dcec9d411749857e44076432b192455 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 02:02:01 +0200 Subject: [PATCH 21/26] Disable Media button for Codable, but keep wrapping and cloze key for now --- ts/editor/ClozeButton.svelte | 19 +++--- ts/editor/TemplateButtons.svelte | 99 +++++++++++++++++--------------- 2 files changed, 66 insertions(+), 52 deletions(-) diff --git a/ts/editor/ClozeButton.svelte b/ts/editor/ClozeButton.svelte index 003eb0191..b2c444257 100644 --- a/ts/editor/ClozeButton.svelte +++ b/ts/editor/ClozeButton.svelte @@ -4,9 +4,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - - {@html bracketsIcon} - + + + {@html bracketsIcon} + + diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index 2fd298f56..79cd7f398 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -5,6 +5,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index 79cd7f398..5ffe9f8ba 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -19,9 +19,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import OnlyEditable from "./OnlyEditable.svelte"; import ClozeButton from "./ClozeButton.svelte"; - import { wrap } from "./wrap"; - import { appendInParentheses } from "./helpers"; import { getCurrentField } from "."; + import { appendInParentheses } from "./helpers"; + import { wrapCurrent } from "./wrap"; import { paperclipIcon, micIcon, functionIcon, xmlIcon } from "./icons"; export let api = {}; @@ -98,7 +98,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let:shortcutLabel > wrap("\\(", "\\)")} + on:click={() => wrapCurrent("\\(", "\\)")} on:mount={createShortcut} > {tr.editingMathjaxInline()} @@ -112,7 +112,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let:shortcutLabel > wrap("\\[", "\\]")} + on:click={() => wrapCurrent("\\[", "\\]")} on:mount={createShortcut} > {tr.editingMathjaxBlock()} @@ -126,7 +126,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let:shortcutLabel > wrap("\\(\\ce{", "}\\)")} + on:click={() => wrapCurrent("\\(\\ce{", "}\\)")} on:mount={createShortcut} > {tr.editingMathjaxChemistry()} @@ -140,7 +140,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let:shortcutLabel > wrap("[latex]", "[/latex]")} + on:click={() => wrapCurrent("[latex]", "[/latex]")} on:mount={createShortcut} > {tr.editingLatex()} @@ -154,7 +154,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let:shortcutLabel > wrap("[$]", "[/$]")} + on:click={() => wrapCurrent("[$]", "[/$]")} on:mount={createShortcut} > {tr.editingLatexEquation()} @@ -168,7 +168,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let:shortcutLabel > wrap("[$$]", "[/$$]")} + on:click={(event) => wrapCurrent("[$$]", "[/$$]", event)} on:mount={createShortcut} > {tr.editingLatexMathEnv()} diff --git a/ts/editor/codable.ts b/ts/editor/codable.ts index f243085be..568ef6aaf 100644 --- a/ts/editor/codable.ts +++ b/ts/editor/codable.ts @@ -74,6 +74,11 @@ export class Codable extends HTMLTextAreaElement { this.codeMirror.setCursor(this.codeMirror.lineCount(), 0); } + surroundSelection(before: string, after: string): void { + const selection = this.codeMirror.getSelection(); + this.codeMirror.replaceSelection(before + selection + after); + } + onEnter(): void { /* default */ } diff --git a/ts/editor/editable.ts b/ts/editor/editable.ts index 78a2e9dd9..c537cb189 100644 --- a/ts/editor/editable.ts +++ b/ts/editor/editable.ts @@ -4,6 +4,7 @@ import { bridgeCommand } from "./lib"; import { nodeIsInline, caretToEnd, getBlockElement } from "./helpers"; import { setEditableButtons } from "./toolbar"; +import { wrap } from "./wrap"; function containsInlineContent(field: Element): boolean { if (field.childNodes.length === 0) { @@ -48,6 +49,10 @@ export class Editable extends HTMLElement { caretToEnd(this); } + surroundSelection(before: string, after: string): void { + wrap(before, after); + } + onEnter(event: KeyboardEvent): void { if ( !getBlockElement(this.getRootNode() as Document | ShadowRoot) !== diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index efe211764..a1d9f8f11 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -115,10 +115,6 @@ export class EditingArea extends HTMLDivElement { return firstRule.style.direction === "rtl"; } - getSelection(): Selection { - return this.shadowRoot!.getSelection()!; - } - focus(): void { this.activeInput.focus(); } @@ -135,6 +131,14 @@ export class EditingArea extends HTMLDivElement { return document.activeElement === this; } + getSelection(): Selection { + return this.shadowRoot!.getSelection()!; + } + + surroundSelection(before: string, after: string): void { + this.activeInput.surroundSelection(before, after); + } + onEnter(event: KeyboardEvent): void { this.activeInput.onEnter(event); } diff --git a/ts/editor/wrap.ts b/ts/editor/wrap.ts index 4255d4d80..8b67d776b 100644 --- a/ts/editor/wrap.ts +++ b/ts/editor/wrap.ts @@ -45,6 +45,11 @@ export function wrap(front: string, back: string): void { wrapInternal(front, back, false); } +export function wrapCurrent(front: string, back: string): void { + const currentField = getCurrentField()!; + currentField.surroundSelection(front, back); +} + /* currently unused */ export function wrapIntoText(front: string, back: string): void { wrapInternal(front, back, true); From 767af9e47841ad973931461da8583f3990200999 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 02:44:15 +0200 Subject: [PATCH 23/26] Satisfy linter --- ts/editor/BUILD.bazel | 1 + ts/editor/TemplateButtons.svelte | 2 +- ts/editor/editable.ts | 4 ++-- ts/editor/editingArea.ts | 1 - ts/editor/helpers.ts | 4 +++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ts/editor/BUILD.bazel b/ts/editor/BUILD.bazel index 16aa2de1d..805ba9e27 100644 --- a/ts/editor/BUILD.bazel +++ b/ts/editor/BUILD.bazel @@ -180,6 +180,7 @@ svelte_check( "//ts/sass:button_mixins_lib", "//ts/sass/bootstrap", "//ts/components:svelte_components", + "//ts/components", "@npm//@types/bootstrap", ], ) diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index 5ffe9f8ba..13f0f28b2 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -168,7 +168,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html let:shortcutLabel > wrapCurrent("[$$]", "[/$$]", event)} + on:click={() => wrapCurrent("[$$]", "[/$$]")} on:mount={createShortcut} > {tr.editingLatexMathEnv()} diff --git a/ts/editor/editable.ts b/ts/editor/editable.ts index c537cb189..03bad6d82 100644 --- a/ts/editor/editable.ts +++ b/ts/editor/editable.ts @@ -40,12 +40,12 @@ export class Editable extends HTMLElement { this.setAttribute("contenteditable", ""); } - focus() { + focus(): void { super.focus(); setEditableButtons(); } - caretToEnd() { + caretToEnd(): void { caretToEnd(this); } diff --git a/ts/editor/editingArea.ts b/ts/editor/editingArea.ts index a1d9f8f11..35ac59fe7 100644 --- a/ts/editor/editingArea.ts +++ b/ts/editor/editingArea.ts @@ -155,7 +155,6 @@ export class EditingArea extends HTMLDivElement { this.editable.hidden = false; } else { this.editable.hidden = true; - console.log("eyo", this.fieldHTML); this.codable.setup(this.fieldHTML); } diff --git a/ts/editor/helpers.ts b/ts/editor/helpers.ts index 240a12611..afee4a781 100644 --- a/ts/editor/helpers.ts +++ b/ts/editor/helpers.ts @@ -1,7 +1,9 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -import type { EditingArea } from "./editingArea"; +/* eslint +@typescript-eslint/no-non-null-assertion: "off", + */ export function nodeIsElement(node: Node): node is Element { return node.nodeType === Node.ELEMENT_NODE; From bfe2bdecc0920519e6bf60867854cfd5468ac34f Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 02:51:42 +0200 Subject: [PATCH 24/26] Disable function dropdown when no field selected --- ts/editor/TemplateButtons.svelte | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ts/editor/TemplateButtons.svelte b/ts/editor/TemplateButtons.svelte index 13f0f28b2..9de8256ba 100644 --- a/ts/editor/TemplateButtons.svelte +++ b/ts/editor/TemplateButtons.svelte @@ -87,9 +87,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html - - {@html functionIcon} - + + + {@html functionIcon} + + Date: Fri, 18 Jun 2021 02:59:45 +0200 Subject: [PATCH 25/26] Remove components from editor BUILD.bazel again --- ts/editor/BUILD.bazel | 1 - 1 file changed, 1 deletion(-) diff --git a/ts/editor/BUILD.bazel b/ts/editor/BUILD.bazel index 805ba9e27..16aa2de1d 100644 --- a/ts/editor/BUILD.bazel +++ b/ts/editor/BUILD.bazel @@ -180,7 +180,6 @@ svelte_check( "//ts/sass:button_mixins_lib", "//ts/sass/bootstrap", "//ts/components:svelte_components", - "//ts/components", "@npm//@types/bootstrap", ], ) From 3320aecddab8c993e99549c32cf71891aa0ab7b6 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 18 Jun 2021 03:12:02 +0200 Subject: [PATCH 26/26] Satisfy svelte_check --- ts/editor/BUILD.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/editor/BUILD.bazel b/ts/editor/BUILD.bazel index 16aa2de1d..d6aaaf4fa 100644 --- a/ts/editor/BUILD.bazel +++ b/ts/editor/BUILD.bazel @@ -179,7 +179,7 @@ svelte_check( ]) + [ "//ts/sass:button_mixins_lib", "//ts/sass/bootstrap", - "//ts/components:svelte_components", "@npm//@types/bootstrap", + "//ts/components", ], )