From de2cc20c59a37e565976b297c48fa82c09ea5e62 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 13 May 2022 04:57:07 +0200 Subject: [PATCH 01/10] Change how resizable images work (#1859) * Add ResizableImage.svelte in ts/editable * Set image constrained via attributes instead of managed style sheet * Implement new constrained size method * Turn WithImageConstrained.svelte into normal ts file * Introduce removeStyleProperties Removes "style" attribute if all style properties were cleared * Avoid --editor-width and use one variable set on container * Disable shrinking if already smaller than shrunken size * Add button to restore image to original size * Don't allow restoring original size if no custom width set * Bottom-center HandleLabel * Satisfy svelte-check --- ftl/core/editing.ftl | 2 + ts/editable/ResizableImage.svelte | 11 + ts/editable/index.ts | 1 + ts/editor/HandleLabel.svelte | 52 +---- ts/editor/NoteEditor.svelte | 2 +- ts/editor/editor-toolbar/BoldButton.svelte | 10 +- .../HighlightColorButton.svelte | 10 +- ts/editor/editor-toolbar/ItalicButton.svelte | 10 +- .../editor-toolbar/SubscriptButton.svelte | 9 +- .../editor-toolbar/SuperscriptButton.svelte | 9 +- .../editor-toolbar/TextColorButton.svelte | 10 +- ts/editor/image-overlay/ImageHandle.svelte | 175 ++++++++++----- ts/editor/image-overlay/SizeSelect.svelte | 25 ++- .../image-overlay/WithImageConstrained.svelte | 203 ------------------ ts/editor/image-overlay/icons.ts | 1 + ts/editor/surround.ts | 13 -- ts/lib/styling.ts | 31 +++ 17 files changed, 225 insertions(+), 349 deletions(-) create mode 100644 ts/editable/ResizableImage.svelte delete mode 100644 ts/editor/image-overlay/WithImageConstrained.svelte create mode 100644 ts/lib/styling.ts diff --git a/ftl/core/editing.ftl b/ftl/core/editing.ftl index df5d391db..6e4d9086a 100644 --- a/ftl/core/editing.ftl +++ b/ftl/core/editing.ftl @@ -15,6 +15,7 @@ editing-customize-card-templates = Customize Card Templates editing-customize-fields = Customize Fields editing-cut = Cut editing-double-click-image = double-click image +editing-double-click-to-expand = (double-click to expand) editing-edit-current = Edit Current editing-edit-html = Edit HTML editing-fields = Fields @@ -38,6 +39,7 @@ editing-outdent = Decrease indent editing-paste = Paste editing-record-audio = Record audio editing-remove-formatting = Remove formatting +editing-restore-original-size = Restore original size editing-select-remove-formatting = Select formatting to remove editing-show-duplicates = Show Duplicates editing-subscript = Subscript diff --git a/ts/editable/ResizableImage.svelte b/ts/editable/ResizableImage.svelte new file mode 100644 index 000000000..36b06871b --- /dev/null +++ b/ts/editable/ResizableImage.svelte @@ -0,0 +1,11 @@ + + diff --git a/ts/editable/index.ts b/ts/editable/index.ts index 8fa7331d7..f8fa11f71 100644 --- a/ts/editable/index.ts +++ b/ts/editable/index.ts @@ -5,3 +5,4 @@ import "./editable-base.css"; /* only imported for the CSS */ import "./ContentEditable.svelte"; import "./Mathjax.svelte"; +import "./ResizableImage.svelte"; diff --git a/ts/editor/HandleLabel.svelte b/ts/editor/HandleLabel.svelte index 4be58bb74..5b023c69a 100644 --- a/ts/editor/HandleLabel.svelte +++ b/ts/editor/HandleLabel.svelte @@ -4,53 +4,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> -
+
diff --git a/ts/editor/NoteEditor.svelte b/ts/editor/NoteEditor.svelte index 754628b29..b58d740c6 100644 --- a/ts/editor/NoteEditor.svelte +++ b/ts/editor/NoteEditor.svelte @@ -339,7 +339,7 @@ the AddCards dialog) should be implemented in the user of this component. }} bind:this={richTextInputs[index]} > - + diff --git a/ts/editor/editor-toolbar/BoldButton.svelte b/ts/editor/editor-toolbar/BoldButton.svelte index edd07a307..d1b0f51e7 100644 --- a/ts/editor/editor-toolbar/BoldButton.svelte +++ b/ts/editor/editor-toolbar/BoldButton.svelte @@ -9,9 +9,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type { MatchType } from "../../domlib/surround"; import * as tr from "../../lib/ftl"; import { getPlatformString } from "../../lib/shortcuts"; + import { removeStyleProperties } from "../../lib/styling"; import { context as noteEditorContext } from "../NoteEditor.svelte"; import { editingInputIsRichText } from "../rich-text-input"; - import { removeEmptyStyle, Surrounder } from "../surround"; + import { Surrounder } from "../surround"; import { context as editorToolbarContext } from "./EditorToolbar.svelte"; import { boldIcon } from "./icons"; @@ -25,9 +26,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const fontWeight = element.style.fontWeight; if (fontWeight === "bold" || Number(fontWeight) >= 400) { return match.clear((): void => { - element.style.removeProperty("font-weight"); - - if (removeEmptyStyle(element) && element.className.length === 0) { + if ( + removeStyleProperties(element, "font-weight") && + element.className.length === 0 + ) { match.remove(); } }); diff --git a/ts/editor/editor-toolbar/HighlightColorButton.svelte b/ts/editor/editor-toolbar/HighlightColorButton.svelte index f1313d54e..d6514a043 100644 --- a/ts/editor/editor-toolbar/HighlightColorButton.svelte +++ b/ts/editor/editor-toolbar/HighlightColorButton.svelte @@ -11,9 +11,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } from "../../domlib/surround"; import { bridgeCommand } from "../../lib/bridgecommand"; import * as tr from "../../lib/ftl"; + import { removeStyleProperties } from "../../lib/styling"; import { context as noteEditorContext } from "../NoteEditor.svelte"; import { editingInputIsRichText } from "../rich-text-input"; - import { removeEmptyStyle, Surrounder } from "../surround"; + import { Surrounder } from "../surround"; import ColorPicker from "./ColorPicker.svelte"; import type { RemoveFormat } from "./EditorToolbar.svelte"; import { context as editorToolbarContext } from "./EditorToolbar.svelte"; @@ -45,9 +46,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html match.setCache(value); match.clear((): void => { - element.style.removeProperty("background-color"); - - if (removeEmptyStyle(element) && element.className.length === 0) { + if ( + removeStyleProperties(element, "background-color") && + element.className.length === 0 + ) { match.remove(); } }); diff --git a/ts/editor/editor-toolbar/ItalicButton.svelte b/ts/editor/editor-toolbar/ItalicButton.svelte index a4d9fd7d8..965453292 100644 --- a/ts/editor/editor-toolbar/ItalicButton.svelte +++ b/ts/editor/editor-toolbar/ItalicButton.svelte @@ -9,9 +9,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import type { MatchType } from "../../domlib/surround"; import * as tr from "../../lib/ftl"; import { getPlatformString } from "../../lib/shortcuts"; + import { removeStyleProperties } from "../../lib/styling"; import { context as noteEditorContext } from "../NoteEditor.svelte"; import { editingInputIsRichText } from "../rich-text-input"; - import { removeEmptyStyle, Surrounder } from "../surround"; + import { Surrounder } from "../surround"; import { context as editorToolbarContext } from "./EditorToolbar.svelte"; import { italicIcon } from "./icons"; @@ -24,9 +25,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html if (["italic", "oblique"].includes(element.style.fontStyle)) { return match.clear((): void => { - element.style.removeProperty("font-style"); - - if (removeEmptyStyle(element) && element.className.length === 0) { + if ( + removeStyleProperties(element, "font-style") && + element.className.length === 0 + ) { return match.remove(); } }); diff --git a/ts/editor/editor-toolbar/SubscriptButton.svelte b/ts/editor/editor-toolbar/SubscriptButton.svelte index df1316a20..5fdb113b4 100644 --- a/ts/editor/editor-toolbar/SubscriptButton.svelte +++ b/ts/editor/editor-toolbar/SubscriptButton.svelte @@ -4,7 +4,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> - {#await sheetPromise then sheet} - { - updateSizesWithDimensions(); - dropdownObject.update(); - }} - let:toggleActualSize - let:active + image={activeImage} + on:mount={(event) => createDropdown(event.detail.selection)} > - {#if activeImage} - createDropdown(event.detail.selection)} - > - + { + if (shrinkingDisabled) { + return; + } + toggleActualSize(); + updateSizesWithDimensions(); + dropdownObject.update(); + }} + /> - - {actualWidth}×{actualHeight} - {#if customDimensions} - (Original: {naturalWidth}×{naturalHeight}) - {/if} - + + {#if isSizeConstrained} + {tr.editingDoubleClickToExpand()} + {:else} + {actualWidth}×{actualHeight} + {#if customDimensions} + (Original: {naturalWidth}×{naturalHeight}) + {/if} + {/if} + - { - if (active) { - setPointerCapture(event); - } - }} - on:pointermove={(event) => { - resize(event); - updateSizesWithDimensions(); - dropdownObject.update(); - }} - /> - - - - - - {/if} - - {/await} + { + if (!isSizeConstrained) { + setPointerCapture(event); + } + }} + on:pointermove={(event) => { + resize(event); + updateSizesWithDimensions(); + dropdownObject.update(); + }} + /> + + + + { + toggleActualSize(); + updateSizesWithDimensions(); + dropdownObject.update(); + }} + on:imageclear={() => { + clearActualSize(); + updateSizesWithDimensions(); + dropdownObject.update(); + }} + /> + + {/if} diff --git a/ts/editor/image-overlay/SizeSelect.svelte b/ts/editor/image-overlay/SizeSelect.svelte index 9afacc1ff..022b3aad5 100644 --- a/ts/editor/image-overlay/SizeSelect.svelte +++ b/ts/editor/image-overlay/SizeSelect.svelte @@ -3,29 +3,38 @@ Copyright: Ankitects Pty Ltd and contributors License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html --> {@html icon} dispatch("imagetoggle")} + --border-left-radius="5px">{@html icon} + + dispatch("imageclear")} + --border-right-radius="5px">{@html sizeClear} diff --git a/ts/editor/image-overlay/WithImageConstrained.svelte b/ts/editor/image-overlay/WithImageConstrained.svelte deleted file mode 100644 index d0d6cb935..000000000 --- a/ts/editor/image-overlay/WithImageConstrained.svelte +++ /dev/null @@ -1,203 +0,0 @@ - - - -{#if activeImage} - -{/if} diff --git a/ts/editor/image-overlay/icons.ts b/ts/editor/image-overlay/icons.ts index 9fd979fe7..fa1019d56 100644 --- a/ts/editor/image-overlay/icons.ts +++ b/ts/editor/image-overlay/icons.ts @@ -4,5 +4,6 @@ export { default as floatLeftIcon } from "@mdi/svg/svg/format-float-left.svg"; export { default as floatNoneIcon } from "@mdi/svg/svg/format-float-none.svg"; export { default as floatRightIcon } from "@mdi/svg/svg/format-float-right.svg"; +export { default as sizeClear } from "@mdi/svg/svg/image-remove.svg"; export { default as sizeActual } from "@mdi/svg/svg/image-size-select-actual.svg"; export { default as sizeMinimized } from "@mdi/svg/svg/image-size-select-large.svg"; diff --git a/ts/editor/surround.ts b/ts/editor/surround.ts index e09aeffd3..9044dde34 100644 --- a/ts/editor/surround.ts +++ b/ts/editor/surround.ts @@ -243,19 +243,6 @@ export class Surrounder { } } -/** - * @returns True, if element has no style attribute (anymore). - */ -export function removeEmptyStyle(element: HTMLElement | SVGElement): boolean { - if (element.style.cssText.length === 0) { - element.removeAttribute("style"); - // Calling `.hasAttribute` right after `.removeAttribute` might return true. - return true; - } - - return false; -} - registerPackage("anki/surround", { Surrounder, }); diff --git a/ts/lib/styling.ts b/ts/lib/styling.ts new file mode 100644 index 000000000..cac1df4c8 --- /dev/null +++ b/ts/lib/styling.ts @@ -0,0 +1,31 @@ +// Copyright: Ankitects Pty Ltd and contributors +// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +/** + * @returns True, if element has no style attribute (anymore). + */ +function removeEmptyStyle(element: HTMLElement | SVGElement): boolean { + if (element.style.cssText.length === 0) { + element.removeAttribute("style"); + // Calling `.hasAttribute` right after `.removeAttribute` might return true. + return true; + } + + return false; +} + +/** + * Will remove the style attribute, if all properties were removed. + * + * @returns True, if element has no style attributes anymore + */ +export function removeStyleProperties( + element: HTMLElement | SVGElement, + ...props: string[] +): boolean { + for (const prop of props) { + element.style.removeProperty(prop); + } + + return removeEmptyStyle(element); +} From 52438fe4c9ae074176f063bf2fff88afb5416809 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 13 May 2022 05:02:03 +0200 Subject: [PATCH 02/10] Move focus into HTML editor when shown (#1861) * Move focus into input field, when input is shown * Change trapFocusOut to move focus into available inputs - This means that e.g. closing the HTML editor with focus in it will focus the visual editor in turn * Prevent Control+A unselecting tag editor when no tags exist --- ts/editor/EditingArea.svelte | 51 ++++++++++++++++--- ts/editor/NoteEditor.svelte | 22 +++++++- ts/editor/PlainTextBadge.svelte | 5 +- ts/editor/RichTextBadge.svelte | 8 ++- .../plain-text-input/PlainTextInput.svelte | 18 +++++-- .../rich-text-input/RichTextInput.svelte | 17 ++++++- ts/editor/tag-editor/TagEditor.svelte | 18 ++++++- ts/editor/tag-editor/TagInput.svelte | 6 +-- 8 files changed, 120 insertions(+), 25 deletions(-) diff --git a/ts/editor/EditingArea.svelte b/ts/editor/EditingArea.svelte index be5654a79..8aa156bed 100644 --- a/ts/editor/EditingArea.svelte +++ b/ts/editor/EditingArea.svelte @@ -7,7 +7,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import contextProperty from "../sveltelib/context-property"; - export interface EditingInputAPI { + export interface FocusableInputAPI { readonly name: string; focusable: boolean; /** @@ -22,6 +22,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html refocus(): void; } + export interface EditingInputAPI extends FocusableInputAPI { + /** + * Check whether blurred target belongs to an editing input. + * The editing area can then restore focus to this input. + * + * @returns An editing input api that is associated with the event target. + */ + getInputAPI(target: EventTarget): Promise; + } + export interface EditingAreaAPI { content: Writable; editingInputs: Writable; @@ -36,7 +46,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + + diff --git a/ts/editor/editor-toolbar/icons.ts b/ts/editor/editor-toolbar/icons.ts index 0bc292d07..e00a2df79 100644 --- a/ts/editor/editor-toolbar/icons.ts +++ b/ts/editor/editor-toolbar/icons.ts @@ -24,8 +24,6 @@ export { default as underlineIcon } from "bootstrap-icons/icons/type-underline.s export const arrowIcon = ''; -export { default as incrementClozeIcon } from "../../icons/contain-plus.svg"; -export { default as clozeIcon } from "@mdi/svg/svg/contain.svg"; export { default as functionIcon } from "@mdi/svg/svg/function-variant.svg"; export { default as paperclipIcon } from "@mdi/svg/svg/paperclip.svg"; export { default as micIcon } from "bootstrap-icons/icons/mic.svg"; diff --git a/ts/editor/editor-toolbar/index.ts b/ts/editor/editor-toolbar/index.ts index a3e53d674..d85d25bce 100644 --- a/ts/editor/editor-toolbar/index.ts +++ b/ts/editor/editor-toolbar/index.ts @@ -3,3 +3,4 @@ export type { EditorToolbarAPI } from "./EditorToolbar.svelte"; export { default as EditorToolbar, editorToolbar } from "./EditorToolbar.svelte"; +export { default as ClozeButtons } from "./EditorToolbar.svelte"; diff --git a/ts/editor/icons.ts b/ts/editor/icons.ts index 61e00169e..f05de24b0 100644 --- a/ts/editor/icons.ts +++ b/ts/editor/icons.ts @@ -3,8 +3,10 @@ /// +export { default as incrementClozeIcon } from "../icons/contain-plus.svg"; export { default as alertIcon } from "@mdi/svg/svg/alert.svg"; export { default as htmlOn } from "@mdi/svg/svg/code-tags.svg"; +export { default as clozeIcon } from "@mdi/svg/svg/contain.svg"; export { default as richTextOff } from "@mdi/svg/svg/eye-off-outline.svg"; export { default as richTextOn } from "@mdi/svg/svg/eye-outline.svg"; export { default as stickyOff } from "@mdi/svg/svg/pin-off-outline.svg"; diff --git a/ts/editor/mathjax-overlay/MathjaxButtons.svelte b/ts/editor/mathjax-overlay/MathjaxButtons.svelte index 95cc8b2b6..a801572d2 100644 --- a/ts/editor/mathjax-overlay/MathjaxButtons.svelte +++ b/ts/editor/mathjax-overlay/MathjaxButtons.svelte @@ -10,6 +10,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import IconButton from "../../components/IconButton.svelte"; import { hasBlockAttribute } from "../../lib/dom"; import * as tr from "../../lib/ftl"; + import ClozeButtons from "../ClozeButtons.svelte"; import { blockIcon, deleteIcon, inlineIcon } from "./icons"; export let element: Element; @@ -48,6 +49,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html > + +
+ +