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-align-left = Align left
editing-align-right = Align right
@ -17,7 +18,7 @@ editing-edit-html = Edit HTML
editing-fields = Fields
editing-float-left = Float left
editing-float-right = Float right
editing-float-remove = Remove float
editing-float-none = No float
editing-html-editor = HTML Editor
editing-indent = Increase indent
editing-italic-text = Italic text

View File

@ -120,9 +120,12 @@ copy_mdi_icons(
"color-helper.svg",
# image handle
"format-float-none.svg",
"format-float-left.svg",
"format-float-right.svg",
"close-circle-outline.svg",
"image-size-select-large.svg",
"image-size-select-actual.svg",
],
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">
import ImageHandleButtons from "./ImageHandleButtons.svelte";
import ImageHandleSizeSelect from "./ImageHandleSizeSelect.svelte";
import { getContext } from "svelte";
import { nightModeKey } from "components/context-keys";
export let image: HTMLImageElement | null = null;
export let container: HTMLElement | null = null;
let hidden = true;
export let imageRule: CSSStyleRule | null = null;
export let container: HTMLElement;
let naturalWidth = 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;
$: if (image) {
const imageRect = image.getBoundingClientRect();
updateSizes();
}
function updateSizes() {
const imageRect = image!.getBoundingClientRect();
const containerRect = (
container ?? document.documentElement
).getBoundingClientRect();
naturalWidth = image.naturalWidth;
naturalHeight = image.naturalHeight;
naturalWidth = image!.naturalWidth;
naturalHeight = image!.naturalHeight;
containerTop = containerRect.top;
containerLeft = containerRect.left;
top = imageRect.top - containerTop;
left = imageRect.left - containerLeft;
width = image.clientWidth;
height = image.clientHeight;
hidden = false;
} else {
hidden = true;
width = image!.clientWidth;
height = image!.clientHeight;
}
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);
let active: boolean = false;
</script>
{#if image}
{#if image && imageRule}
<div
style="--top: {top}px; --left: {left}px; --width: {width}px; --height: {height}px;"
class="image-handle-selection"
{hidden}
>
<div class="image-handle-bg" />
<div class="image-handle-buttons">
<ImageHandleButtons bind:float={image.style.float} />
</div>
<div class="image-handle-size-select">
<ImageHandleSizeSelect
{image}
{imageRule}
bind:active
on:update={updateSizes}
/>
</div>
<div class="image-handle-dimensions">
{width}&times;{height} (Original: {naturalWidth}&times;{naturalHeight})
</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-sw is-active"
class:active
class="image-handle-control image-handle-control-sw"
on:pointerdown={setPointerCapture}
on:pointermove={resize}
/>
<div
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:pointermove={resize}
/>
@ -131,12 +143,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
left: 3px;
}
.image-handle-size-select {
bottom: 3px;
left: 3px;
}
.image-handle-dimensions {
pointer-events: none;
user-select: none;
bottom: 3px;
right: 3px;
font-size: 13px;
background-color: rgba(0 0 0 / 0.3);
border-color: black;
border-radius: 0.25rem;
@ -148,14 +166,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
height: 7px;
border: 1px solid black;
&.is-active {
&.active {
background-color: black;
}
&.nightMode {
border-color: white;
&.is-active {
&.active {
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-right: none;
&.is-active {
&.active {
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-left: none;
&.is-active {
&.active {
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 IconButton from "components/IconButton.svelte";
import { floatLeftIcon, floatRightIcon, resetIcon } from "./icons";
import { floatNoneIcon, floatLeftIcon, floatRightIcon } from "./icons";
export let float: string;
</script>
<ButtonToolbar size={1.6} wrap={false}>
<ButtonGroup>
<ButtonGroupItem>
<IconButton
tooltip={tr.editingFloatNone()}
active={float === "" || float === "none"}
on:click={() => (float = "")}>{@html floatNoneIcon}</IconButton
>
</ButtonGroupItem>
<ButtonGroupItem>
<IconButton
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
>
</ButtonGroupItem>
<ButtonGroupItem>
<IconButton
tooltip={tr.editingFloatRemove()}
active={float === "" || float === "none"}
on:click={() => (float = "")}>{@html resetIcon}</IconButton
>
</ButtonGroupItem>
</ButtonGroup>
</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 {
baseStyle: HTMLStyleElement;
baseRule?: CSSStyleRule;
imageRule?: CSSStyleRule;
constructor() {
super();
@ -27,8 +29,16 @@ export class EditableContainer extends HTMLDivElement {
}
connectedCallback(): void {
const baseStyleSheet = this.baseStyle.sheet as CSSStyleSheet;
baseStyleSheet.insertRule("anki-editable {}", 0);
const sheet = this.baseStyle.sheet as CSSStyleSheet;
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 {
@ -36,17 +46,17 @@ export class EditableContainer extends HTMLDivElement {
}
setBaseColor(color: string): void {
const styleSheet = this.baseStyle.sheet as CSSStyleSheet;
const firstRule = styleSheet.cssRules[0] as CSSStyleRule;
firstRule.style.color = color;
if (this.baseRule) {
this.baseRule.style.color = color;
}
}
setBaseStyling(fontFamily: string, fontSize: string, direction: string): void {
const styleSheet = this.baseStyle.sheet as CSSStyleSheet;
const firstRule = styleSheet.cssRules[0] as CSSStyleRule;
firstRule.style.fontFamily = fontFamily;
firstRule.style.fontSize = fontSize;
firstRule.style.direction = direction;
if (this.baseRule) {
this.baseRule.style.fontFamily = fontFamily;
this.baseRule.style.fontSize = fontSize;
this.baseRule.style.direction = direction;
}
}
isRightToLeft(): boolean {

View File

@ -47,7 +47,9 @@ export class EditingArea extends HTMLDivElement {
this.imageHandle = new ImageHandle({
target: this,
anchor: this.editableContainer,
props: { container: this.editable },
props: {
container: this.editable,
},
context,
} as any);
@ -168,10 +170,12 @@ export class EditingArea extends HTMLDivElement {
if (event.target instanceof HTMLImageElement) {
(this.imageHandle as any).$set({
image: event.target,
imageRule: this.editableContainer.imageRule,
});
} else {
(this.imageHandle as any).$set({
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>';
// image handle
export { default as floatNoneIcon } from "./format-float-none.svg";
export { default as floatLeftIcon } from "./format-float-left.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";