From de2cc20c59a37e565976b297c48fa82c09ea5e62 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 13 May 2022 04:57:07 +0200 Subject: [PATCH] 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); +}