Implement Maximum image size mechanism
This commit is contained in:
parent
8a8cd4ee38
commit
a1df49b11e
@ -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
|
||||||
|
@ -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"],
|
||||||
)
|
)
|
||||||
|
@ -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}×{height} (Original: {naturalWidth}×{naturalHeight})
|
{width}×{height} (Original: {naturalWidth}×{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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
51
ts/editor/ImageHandleSizeSelect.svelte
Normal file
51
ts/editor/ImageHandleSizeSelect.svelte
Normal 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>
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
Loading…
Reference in New Issue
Block a user