Implement Maximum image size mechanism

This commit is contained in:
Henrik Giesel 2021-07-21 14:13:56 +02:00 committed by Damien Elmes
parent 8a8cd4ee38
commit a1df49b11e
8 changed files with 132 additions and 42 deletions

View File

@ -1,3 +1,4 @@
editing-actual-size = Actual size
editing-add-media = Add Media editing-add-media = Add Media
editing-align-left = Align left editing-align-left = Align left
editing-align-right = Align right editing-align-right = Align right
@ -17,7 +18,7 @@ editing-edit-html = Edit HTML
editing-fields = Fields editing-fields = Fields
editing-float-left = Float left editing-float-left = Float left
editing-float-right = Float right editing-float-right = Float right
editing-float-remove = Remove float editing-float-none = No float
editing-html-editor = HTML Editor editing-html-editor = HTML Editor
editing-indent = Increase indent editing-indent = Increase indent
editing-italic-text = Italic text editing-italic-text = Italic text

View File

@ -120,9 +120,12 @@ copy_mdi_icons(
"color-helper.svg", "color-helper.svg",
# image handle # image handle
"format-float-none.svg",
"format-float-left.svg", "format-float-left.svg",
"format-float-right.svg", "format-float-right.svg",
"close-circle-outline.svg",
"image-size-select-large.svg",
"image-size-select-actual.svg",
], ],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )

View File

@ -4,13 +4,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="typescript"> <script lang="typescript">
import ImageHandleButtons from "./ImageHandleButtons.svelte"; import ImageHandleButtons from "./ImageHandleButtons.svelte";
import ImageHandleSizeSelect from "./ImageHandleSizeSelect.svelte";
import { getContext } from "svelte"; import { getContext } from "svelte";
import { nightModeKey } from "components/context-keys"; import { nightModeKey } from "components/context-keys";
export let image: HTMLImageElement | null = null; export let image: HTMLImageElement | null = null;
export let container: HTMLElement | null = null; export let imageRule: CSSStyleRule | null = null;
export let container: HTMLElement;
let hidden = true;
let naturalWidth = 0; let naturalWidth = 0;
let naturalHeight = 0; let naturalHeight = 0;
@ -24,23 +25,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let height = 0; let height = 0;
$: if (image) { $: if (image) {
const imageRect = image.getBoundingClientRect(); updateSizes();
}
function updateSizes() {
const imageRect = image!.getBoundingClientRect();
const containerRect = ( const containerRect = (
container ?? document.documentElement container ?? document.documentElement
).getBoundingClientRect(); ).getBoundingClientRect();
naturalWidth = image.naturalWidth; naturalWidth = image!.naturalWidth;
naturalHeight = image.naturalHeight; naturalHeight = image!.naturalHeight;
containerTop = containerRect.top; containerTop = containerRect.top;
containerLeft = containerRect.left; containerLeft = containerRect.left;
top = imageRect.top - containerTop; top = imageRect.top - containerTop;
left = imageRect.left - containerLeft; left = imageRect.left - containerLeft;
width = image.clientWidth; width = image!.clientWidth;
height = image.clientHeight; height = image!.clientHeight;
hidden = false;
} else {
hidden = true;
} }
function setPointerCapture(event: PointerEvent): void { function setPointerCapture(event: PointerEvent): void {
@ -75,18 +77,26 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
const nightMode = getContext(nightModeKey); const nightMode = getContext(nightModeKey);
let active: boolean = false;
</script> </script>
{#if image} {#if image && imageRule}
<div <div
style="--top: {top}px; --left: {left}px; --width: {width}px; --height: {height}px;" style="--top: {top}px; --left: {left}px; --width: {width}px; --height: {height}px;"
class="image-handle-selection" class="image-handle-selection"
{hidden}
> >
<div class="image-handle-bg" /> <div class="image-handle-bg" />
<div class="image-handle-buttons"> <div class="image-handle-buttons">
<ImageHandleButtons bind:float={image.style.float} /> <ImageHandleButtons bind:float={image.style.float} />
</div> </div>
<div class="image-handle-size-select">
<ImageHandleSizeSelect
{image}
{imageRule}
bind:active
on:update={updateSizes}
/>
</div>
<div class="image-handle-dimensions"> <div class="image-handle-dimensions">
{width}&times;{height} (Original: {naturalWidth}&times;{naturalHeight}) {width}&times;{height} (Original: {naturalWidth}&times;{naturalHeight})
</div> </div>
@ -94,13 +104,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<div class:nightMode class="image-handle-control image-handle-control-ne" /> <div class:nightMode class="image-handle-control image-handle-control-ne" />
<div <div
class:nightMode class:nightMode
class="image-handle-control image-handle-control-sw is-active" class:active
class="image-handle-control image-handle-control-sw"
on:pointerdown={setPointerCapture} on:pointerdown={setPointerCapture}
on:pointermove={resize} on:pointermove={resize}
/> />
<div <div
class:nightMode class:nightMode
class="image-handle-control image-handle-control-se is-active" class:active
class="image-handle-control image-handle-control-se"
on:pointerdown={setPointerCapture} on:pointerdown={setPointerCapture}
on:pointermove={resize} on:pointermove={resize}
/> />
@ -131,12 +143,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
left: 3px; left: 3px;
} }
.image-handle-size-select {
bottom: 3px;
left: 3px;
}
.image-handle-dimensions { .image-handle-dimensions {
pointer-events: none; pointer-events: none;
user-select: none; user-select: none;
bottom: 3px; bottom: 3px;
right: 3px; right: 3px;
font-size: 13px;
background-color: rgba(0 0 0 / 0.3); background-color: rgba(0 0 0 / 0.3);
border-color: black; border-color: black;
border-radius: 0.25rem; border-radius: 0.25rem;
@ -148,14 +166,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
height: 7px; height: 7px;
border: 1px solid black; border: 1px solid black;
&.is-active { &.active {
background-color: black; background-color: black;
} }
&.nightMode { &.nightMode {
border-color: white; border-color: white;
&.is-active { &.active {
background-color: white; background-color: white;
} }
} }
@ -181,7 +199,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
border-top: none; border-top: none;
border-right: none; border-right: none;
&.is-active { &.active {
cursor: sw-resize; cursor: sw-resize;
} }
} }
@ -192,7 +210,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
border-top: none; border-top: none;
border-left: none; border-left: none;
&.is-active { &.active {
cursor: se-resize; cursor: se-resize;
} }
} }

View File

@ -10,13 +10,21 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import ButtonGroupItem from "components/ButtonGroupItem.svelte"; import ButtonGroupItem from "components/ButtonGroupItem.svelte";
import IconButton from "components/IconButton.svelte"; import IconButton from "components/IconButton.svelte";
import { floatLeftIcon, floatRightIcon, resetIcon } from "./icons"; import { floatNoneIcon, floatLeftIcon, floatRightIcon } from "./icons";
export let float: string; export let float: string;
</script> </script>
<ButtonToolbar size={1.6} wrap={false}> <ButtonToolbar size={1.6} wrap={false}>
<ButtonGroup> <ButtonGroup>
<ButtonGroupItem>
<IconButton
tooltip={tr.editingFloatNone()}
active={float === "" || float === "none"}
on:click={() => (float = "")}>{@html floatNoneIcon}</IconButton
>
</ButtonGroupItem>
<ButtonGroupItem> <ButtonGroupItem>
<IconButton <IconButton
tooltip={tr.editingFloatLeft()} tooltip={tr.editingFloatLeft()}
@ -32,13 +40,5 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
on:click={() => (float = "right")}>{@html floatRightIcon}</IconButton on:click={() => (float = "right")}>{@html floatRightIcon}</IconButton
> >
</ButtonGroupItem> </ButtonGroupItem>
<ButtonGroupItem>
<IconButton
tooltip={tr.editingFloatRemove()}
active={float === "" || float === "none"}
on:click={() => (float = "")}>{@html resetIcon}</IconButton
>
</ButtonGroupItem>
</ButtonGroup> </ButtonGroup>
</ButtonToolbar> </ButtonToolbar>

View File

@ -0,0 +1,51 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import * as tr from "lib/i18n";
import ButtonGroup from "components/ButtonGroup.svelte";
import ButtonGroupItem from "components/ButtonGroupItem.svelte";
import IconButton from "components/IconButton.svelte";
import { createEventDispatcher } from "svelte";
import { sizeActual, sizeMinimized } from "./icons";
export let image: HTMLImageElement;
export let imageRule: CSSStyleRule;
const selector = `:not([src="${image.getAttribute("src")}"])`;
export let active = imageRule.selectorText.includes(selector);
$: icon = active ? sizeActual : sizeMinimized;
const dispatch = createEventDispatcher();
function toggleActualSize() {
if (!image.hasAttribute("src")) {
return;
}
if (active) {
imageRule.selectorText = imageRule.selectorText.replace(selector, "");
active = false;
} else {
imageRule.selectorText += selector;
active = true;
}
dispatch("update");
}
</script>
<ButtonGroup size={1.6}>
<ButtonGroupItem>
<IconButton
{active}
tooltip={tr.editingActualSize()}
on:click={toggleActualSize}>{@html icon}</IconButton
>
</ButtonGroupItem>
</ButtonGroup>

View File

@ -7,6 +7,8 @@
export class EditableContainer extends HTMLDivElement { export class EditableContainer extends HTMLDivElement {
baseStyle: HTMLStyleElement; baseStyle: HTMLStyleElement;
baseRule?: CSSStyleRule;
imageRule?: CSSStyleRule;
constructor() { constructor() {
super(); super();
@ -27,8 +29,16 @@ export class EditableContainer extends HTMLDivElement {
} }
connectedCallback(): void { connectedCallback(): void {
const baseStyleSheet = this.baseStyle.sheet as CSSStyleSheet; const sheet = this.baseStyle.sheet as CSSStyleSheet;
baseStyleSheet.insertRule("anki-editable {}", 0); const baseIndex = sheet.insertRule("anki-editable {}");
this.baseRule = sheet.cssRules[baseIndex] as CSSStyleRule;
const imageIndex = sheet.insertRule("anki-editable img {}");
this.imageRule = sheet.cssRules[imageIndex] as CSSStyleRule;
this.imageRule.style.setProperty("width", "auto", "important");
this.imageRule.style.setProperty("height", "auto", "important");
this.imageRule.style.setProperty("max-width", "min(250px, 100%)", "important");
this.imageRule.style.setProperty("max-height", "min(250px, 100%)", "important");
} }
initialize(color: string): void { initialize(color: string): void {
@ -36,17 +46,17 @@ export class EditableContainer extends HTMLDivElement {
} }
setBaseColor(color: string): void { setBaseColor(color: string): void {
const styleSheet = this.baseStyle.sheet as CSSStyleSheet; if (this.baseRule) {
const firstRule = styleSheet.cssRules[0] as CSSStyleRule; this.baseRule.style.color = color;
firstRule.style.color = color; }
} }
setBaseStyling(fontFamily: string, fontSize: string, direction: string): void { setBaseStyling(fontFamily: string, fontSize: string, direction: string): void {
const styleSheet = this.baseStyle.sheet as CSSStyleSheet; if (this.baseRule) {
const firstRule = styleSheet.cssRules[0] as CSSStyleRule; this.baseRule.style.fontFamily = fontFamily;
firstRule.style.fontFamily = fontFamily; this.baseRule.style.fontSize = fontSize;
firstRule.style.fontSize = fontSize; this.baseRule.style.direction = direction;
firstRule.style.direction = direction; }
} }
isRightToLeft(): boolean { isRightToLeft(): boolean {

View File

@ -47,7 +47,9 @@ export class EditingArea extends HTMLDivElement {
this.imageHandle = new ImageHandle({ this.imageHandle = new ImageHandle({
target: this, target: this,
anchor: this.editableContainer, anchor: this.editableContainer,
props: { container: this.editable }, props: {
container: this.editable,
},
context, context,
} as any); } as any);
@ -168,10 +170,12 @@ export class EditingArea extends HTMLDivElement {
if (event.target instanceof HTMLImageElement) { if (event.target instanceof HTMLImageElement) {
(this.imageHandle as any).$set({ (this.imageHandle as any).$set({
image: event.target, image: event.target,
imageRule: this.editableContainer.imageRule,
}); });
} else { } else {
(this.imageHandle as any).$set({ (this.imageHandle as any).$set({
image: null, image: null,
imageRule: null,
}); });
} }
} }

View File

@ -35,6 +35,9 @@ export const arrowIcon =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="transparent" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2 5l6 6 6-6"/></svg>'; '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="transparent" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2 5l6 6 6-6"/></svg>';
// image handle // image handle
export { default as floatNoneIcon } from "./format-float-none.svg";
export { default as floatLeftIcon } from "./format-float-left.svg"; export { default as floatLeftIcon } from "./format-float-left.svg";
export { default as floatRightIcon } from "./format-float-right.svg"; export { default as floatRightIcon } from "./format-float-right.svg";
export { default as resetIcon } from "./close-circle-outline.svg";
export { default as sizeActual } from "./image-size-select-actual.svg";
export { default as sizeMinimized } from "./image-size-select-large.svg";