mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
Manage CSSStyleSheet from within SizeSelect where each image has one rule
This commit is contained in:
parent
e581d593d3
commit
d3e46e9da4
5 changed files with 329 additions and 116 deletions
|
@ -2,33 +2,25 @@
|
|||
Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="typescript">
|
||||
<script lang="ts">
|
||||
import ImageHandleFloat from "./ImageHandleFloat.svelte";
|
||||
import ImageHandleSizeSelect from "./ImageHandleSizeSelect.svelte";
|
||||
|
||||
import { onDestroy, getContext } from "svelte";
|
||||
import { nightModeKey } from "components/context-keys";
|
||||
|
||||
export let image: HTMLImageElement | null = null;
|
||||
export let imageRule: CSSStyleRule | null = null;
|
||||
export let container: HTMLElement;
|
||||
export let sheet: CSSStyleSheet;
|
||||
export let activeImage: HTMLImageElement | null = null;
|
||||
export let isRtl: boolean = false;
|
||||
|
||||
export let container: HTMLElement;
|
||||
|
||||
$: selector = `:not([src="${image?.getAttribute("src")}"])`;
|
||||
$: naturalWidth = image?.naturalWidth;
|
||||
$: naturalHeight = image?.naturalHeight;
|
||||
$: naturalWidth = activeImage?.naturalWidth;
|
||||
$: naturalHeight = activeImage?.naturalHeight;
|
||||
$: aspectRatio = naturalWidth && naturalHeight ? naturalWidth / naturalHeight : NaN;
|
||||
|
||||
$: showDimensions = image
|
||||
? parseInt(getComputedStyle(image).getPropertyValue("height")) >= 50
|
||||
: false;
|
||||
$: showDimensions = activeImage ? Number(activeImage!.height) >= 50 : false;
|
||||
|
||||
$: showFloat = image
|
||||
? parseInt(getComputedStyle(image).getPropertyValue("width")) >= 100
|
||||
: false;
|
||||
|
||||
$: active = imageRule?.selectorText.includes(selector) as boolean;
|
||||
$: showFloat = activeImage ? Number(activeImage!.width) >= 100 : false;
|
||||
|
||||
let actualWidth = "";
|
||||
let actualHeight = "";
|
||||
|
@ -42,39 +34,48 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
$: if (image) {
|
||||
$: if (activeImage) {
|
||||
updateSizes();
|
||||
} else {
|
||||
resetSizes();
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
if (image) {
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (activeImage) {
|
||||
updateSizes();
|
||||
}
|
||||
});
|
||||
|
||||
function startObserving() {
|
||||
observer.observe(container);
|
||||
resizeObserver.observe(container);
|
||||
}
|
||||
|
||||
function stopObserving() {
|
||||
observer.unobserve(container);
|
||||
resizeObserver.unobserve(container);
|
||||
}
|
||||
|
||||
startObserving();
|
||||
|
||||
function resetSizes() {
|
||||
top = 0;
|
||||
left = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
|
||||
function updateSizes() {
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const imageRect = image!.getBoundingClientRect();
|
||||
const imageRect = activeImage!.getBoundingClientRect();
|
||||
|
||||
containerTop = containerRect.top;
|
||||
containerLeft = containerRect.left;
|
||||
top = imageRect!.top - containerTop;
|
||||
left = imageRect!.left - containerLeft;
|
||||
width = image!.clientWidth;
|
||||
height = image!.clientHeight;
|
||||
width = activeImage!.clientWidth;
|
||||
height = activeImage!.clientHeight;
|
||||
|
||||
/* we do not want the actual width, but rather the intended display width */
|
||||
const widthProperty = image!.style.width;
|
||||
const widthProperty = activeImage!.style.width;
|
||||
let inPixel = false;
|
||||
customDimensions = false;
|
||||
|
||||
|
@ -91,7 +92,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
actualWidth = String(naturalWidth);
|
||||
}
|
||||
|
||||
const heightProperty = image!.style.height;
|
||||
const heightProperty = activeImage!.style.height;
|
||||
if (inPixel || heightProperty === "auto") {
|
||||
actualHeight = String(Math.trunc(Number(actualWidth) / aspectRatio));
|
||||
} else if (heightProperty) {
|
||||
|
@ -104,14 +105,43 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
}
|
||||
}
|
||||
|
||||
function setPointerCapture(event: PointerEvent): void {
|
||||
if (!active || event.pointerId !== 1) {
|
||||
return;
|
||||
}
|
||||
/* memoized position of image on resize start
|
||||
* prevents frantic behavior when image shift into the next/previous line */
|
||||
let getDragWidth: (event: PointerEvent) => number;
|
||||
let getDragHeight: (event: PointerEvent) => number;
|
||||
|
||||
stopObserving();
|
||||
(event.target as Element).setPointerCapture(event.pointerId);
|
||||
}
|
||||
const setPointerCapture =
|
||||
(north: boolean, west: boolean) =>
|
||||
(event: PointerEvent): void => {
|
||||
if (!active || event.pointerId !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const imageRect = activeImage!.getBoundingClientRect();
|
||||
|
||||
const originalContainerY = containerRect.top;
|
||||
const originalContainerX = containerRect.left;
|
||||
const originalY = imageRect!.top - containerTop;
|
||||
const originalX = imageRect!.left - containerLeft;
|
||||
|
||||
getDragWidth = (event) =>
|
||||
west
|
||||
? activeImage!.clientWidth -
|
||||
event.clientX +
|
||||
(originalContainerX + originalX)
|
||||
: event.clientX - originalContainerX - originalX;
|
||||
|
||||
getDragHeight = (event) =>
|
||||
north
|
||||
? activeImage!.clientHeight -
|
||||
event.clientY +
|
||||
(originalContainerY + originalY)
|
||||
: event.clientY - originalContainerY - originalY;
|
||||
|
||||
stopObserving();
|
||||
(event.target as Element).setPointerCapture(event.pointerId);
|
||||
};
|
||||
|
||||
function resize(event: PointerEvent): void {
|
||||
const element = event.target! as Element;
|
||||
|
@ -120,8 +150,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
return;
|
||||
}
|
||||
|
||||
const dragWidth = event.clientX - containerLeft - left;
|
||||
const dragHeight = event.clientY - containerTop - top;
|
||||
const dragWidth = getDragWidth(event);
|
||||
const dragHeight = getDragHeight(event);
|
||||
|
||||
const widthIncrease = dragWidth / naturalWidth!;
|
||||
const heightIncrease = dragHeight / naturalHeight!;
|
||||
|
@ -137,11 +167,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
showDimensions = height >= 50;
|
||||
showFloat = width >= 100;
|
||||
|
||||
image!.style.width = `${width}px`;
|
||||
image!.style.removeProperty("height");
|
||||
activeImage!.width = width;
|
||||
}
|
||||
|
||||
let sizeSelect: any;
|
||||
let active = false;
|
||||
|
||||
function onDblclick() {
|
||||
sizeSelect.toggleActualSize();
|
||||
|
@ -149,35 +179,26 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
const nightMode = getContext(nightModeKey);
|
||||
|
||||
onDestroy(() => observer.disconnect());
|
||||
onDestroy(() => resizeObserver.disconnect());
|
||||
</script>
|
||||
|
||||
{#if image && imageRule}
|
||||
<div
|
||||
style="--top: {top}px; --left: {left}px; --width: {width}px; --height: {height}px;"
|
||||
class="image-handle-selection"
|
||||
>
|
||||
<div
|
||||
style="--top: {top}px; --left: {left}px; --width: {width}px; --height: {height}px;"
|
||||
class="image-handle-selection"
|
||||
>
|
||||
{#if activeImage}
|
||||
<div
|
||||
class="image-handle-bg"
|
||||
on:mousedown|preventDefault
|
||||
on:dblclick={onDblclick}
|
||||
/>
|
||||
|
||||
{#if showFloat}
|
||||
<div class="image-handle-float" class:is-rtl={isRtl}>
|
||||
<ImageHandleFloat {isRtl} bind:float={image.style.float} />
|
||||
<div class="image-handle-float" class:is-rtl={isRtl} on:click={updateSizes}>
|
||||
<ImageHandleFloat {activeImage} {isRtl} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="image-handle-size-select" class:is-rtl={isRtl}>
|
||||
<ImageHandleSizeSelect
|
||||
bind:this={sizeSelect}
|
||||
bind:active
|
||||
{image}
|
||||
{imageRule}
|
||||
{selector}
|
||||
{isRtl}
|
||||
on:update={updateSizes}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if showDimensions}
|
||||
<div class="image-handle-dimensions" class:is-rtl={isRtl}>
|
||||
<span>{actualWidth}×{actualHeight}</span>
|
||||
|
@ -186,22 +207,47 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
>{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if sheet}
|
||||
<div class="image-handle-size-select" class:is-rtl={isRtl}>
|
||||
<ImageHandleSizeSelect
|
||||
bind:this={sizeSelect}
|
||||
bind:active
|
||||
{container}
|
||||
{sheet}
|
||||
{activeImage}
|
||||
{isRtl}
|
||||
on:update={updateSizes}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if activeImage}
|
||||
<div
|
||||
class:nightMode
|
||||
class:active
|
||||
class="image-handle-control image-handle-control-nw"
|
||||
on:mousedown|preventDefault
|
||||
on:pointerdown={setPointerCapture(true, true)}
|
||||
on:pointerup={startObserving}
|
||||
on:pointermove={resize}
|
||||
/>
|
||||
<div
|
||||
class:nightMode
|
||||
class:active
|
||||
class="image-handle-control image-handle-control-ne"
|
||||
on:mousedown|preventDefault
|
||||
on:pointerdown={setPointerCapture(true, false)}
|
||||
on:pointerup={startObserving}
|
||||
on:pointermove={resize}
|
||||
/>
|
||||
<div
|
||||
class:nightMode
|
||||
class:active
|
||||
class="image-handle-control image-handle-control-sw"
|
||||
on:mousedown|preventDefault
|
||||
on:pointerdown={setPointerCapture}
|
||||
on:pointerdown={setPointerCapture(false, true)}
|
||||
on:pointerup={startObserving}
|
||||
on:pointermove={resize}
|
||||
/>
|
||||
|
@ -210,12 +256,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
class:active
|
||||
class="image-handle-control image-handle-control-se"
|
||||
on:mousedown|preventDefault
|
||||
on:pointerdown={setPointerCapture}
|
||||
on:pointerdown={setPointerCapture(false, false)}
|
||||
on:pointerup={startObserving}
|
||||
on:pointermove={resize}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div {
|
||||
|
@ -301,6 +347,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
left: -5px;
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
|
||||
&.active {
|
||||
cursor: nw-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.image-handle-control-ne {
|
||||
|
@ -308,6 +358,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
right: -5px;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
|
||||
&.active {
|
||||
cursor: ne-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.image-handle-control-sw {
|
||||
|
|
|
@ -11,7 +11,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
import { floatNoneIcon, floatLeftIcon, floatRightIcon } from "./icons";
|
||||
|
||||
export let float: string;
|
||||
export let activeImage: HTMLImageElement;
|
||||
export let isRtl: boolean;
|
||||
|
||||
const leftValues = {
|
||||
|
@ -33,18 +33,20 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
<ButtonGroupItem>
|
||||
<IconButton
|
||||
tooltip={tr.editingFloatNone()}
|
||||
active={float === "" || float === "none"}
|
||||
active={activeImage.style.float === "" ||
|
||||
activeImage.style.float === "none"}
|
||||
flipX={isRtl}
|
||||
on:click={() => (float = "")}>{@html floatNoneIcon}</IconButton
|
||||
on:click={() => (activeImage.style.float = "")}
|
||||
>{@html floatNoneIcon}</IconButton
|
||||
>
|
||||
</ButtonGroupItem>
|
||||
|
||||
<ButtonGroupItem>
|
||||
<IconButton
|
||||
tooltip={inlineStart.label}
|
||||
active={float === inlineStart.position}
|
||||
active={activeImage.style.float === inlineStart.position}
|
||||
flipX={isRtl}
|
||||
on:click={() => (float = inlineStart.position)}
|
||||
on:click={() => (activeImage.style.float = inlineStart.position)}
|
||||
>{@html floatLeftIcon}</IconButton
|
||||
>
|
||||
</ButtonGroupItem>
|
||||
|
@ -52,9 +54,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
<ButtonGroupItem>
|
||||
<IconButton
|
||||
tooltip={inlineEnd.label}
|
||||
active={float === inlineEnd.position}
|
||||
active={activeImage.style.float === inlineEnd.position}
|
||||
flipX={isRtl}
|
||||
on:click={() => (float = inlineEnd.position)}
|
||||
on:click={() => (activeImage.style.float = inlineEnd.position)}
|
||||
>{@html floatRightIcon}</IconButton
|
||||
>
|
||||
</ButtonGroupItem>
|
||||
|
|
|
@ -9,43 +9,184 @@ 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 { createEventDispatcher } from "svelte";
|
||||
import { createEventDispatcher, onDestroy } from "svelte";
|
||||
import { sizeActual, sizeMinimized } from "./icons";
|
||||
import { nodeIsElement } from "./helpers";
|
||||
|
||||
export let container: HTMLElement;
|
||||
export let sheet: CSSStyleSheet;
|
||||
|
||||
export let activeImage: HTMLImageElement | null;
|
||||
export let active: boolean = false;
|
||||
|
||||
export let image: HTMLImageElement;
|
||||
export let imageRule: CSSStyleRule;
|
||||
export let selector: string;
|
||||
export let active: boolean;
|
||||
export let isRtl: boolean;
|
||||
export let maxWidth = 250;
|
||||
export let maxHeight = 125;
|
||||
|
||||
$: icon = active ? sizeActual : sizeMinimized;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export function toggleActualSize() {
|
||||
if (!image.hasAttribute("src")) {
|
||||
function createPathRecursive(tokens: string[], element: Element): string[] {
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
|
||||
if (!element.parentElement) {
|
||||
const nth =
|
||||
Array.prototype.indexOf.call(
|
||||
(element.parentNode! as Document | ShadowRoot).children,
|
||||
element
|
||||
) + 1;
|
||||
return [`${tagName}:nth-child(${nth})`, ...tokens];
|
||||
} else {
|
||||
const nth =
|
||||
Array.prototype.indexOf.call(element.parentElement.children, element) +
|
||||
1;
|
||||
return createPathRecursive(
|
||||
[`${tagName}:nth-child(${nth})`, ...tokens],
|
||||
element.parentElement
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function createPath(element: Element): string {
|
||||
return createPathRecursive([], element).join(" > ");
|
||||
}
|
||||
|
||||
let images: HTMLImageElement[] = [];
|
||||
|
||||
$: for (const [index, image] of images.entries()) {
|
||||
const rule = sheet.cssRules[index] as CSSStyleRule;
|
||||
rule.selectorText = createPath(image);
|
||||
}
|
||||
|
||||
function filterImages(nodes: HTMLCollection | Node[]): HTMLImageElement[] {
|
||||
const result: HTMLImageElement[] = [];
|
||||
|
||||
for (const node of nodes) {
|
||||
if (!nodeIsElement(node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.tagName === "IMG") {
|
||||
result.push(node as HTMLImageElement);
|
||||
} else {
|
||||
result.push(...filterImages(node.children));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function setImageRule(image: HTMLImageElement, rule: CSSStyleRule): void {
|
||||
const width = Number(image.getAttribute("width")) | image.width;
|
||||
|
||||
rule.style.setProperty(
|
||||
"width",
|
||||
width < maxWidth ? `${width}px` : "auto",
|
||||
"important"
|
||||
);
|
||||
rule.style.setProperty("height", "auto", "important");
|
||||
rule.style.setProperty("max-width", `min(${maxWidth}px, 100%)`, "important");
|
||||
rule.style.setProperty("max-height", `${maxHeight}px`, "important");
|
||||
}
|
||||
|
||||
function resetImageRule(rule: CSSStyleRule): void {
|
||||
rule.style.removeProperty("width");
|
||||
rule.style.removeProperty("height");
|
||||
rule.style.removeProperty("max-width");
|
||||
rule.style.removeProperty("max-height");
|
||||
}
|
||||
|
||||
function addImage(image: HTMLImageElement): void {
|
||||
if (!container.contains(image)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
imageRule.selectorText = imageRule.selectorText.replace(selector, "");
|
||||
active = false;
|
||||
images.push(image);
|
||||
const index = sheet.insertRule(
|
||||
`${createPath(image)} {}`,
|
||||
sheet.cssRules.length
|
||||
);
|
||||
const rule = sheet.cssRules[index] as CSSStyleRule;
|
||||
setImageRule(image, rule);
|
||||
|
||||
images = images;
|
||||
}
|
||||
|
||||
function addImageOnLoad(image: HTMLImageElement): void {
|
||||
if (image.complete && image.naturalWidth !== 0 && image.naturalHeight !== 0) {
|
||||
addImage(image);
|
||||
} else {
|
||||
imageRule.selectorText += selector;
|
||||
active = true;
|
||||
image.addEventListener("load", () => {
|
||||
addImage(image);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function removeImage(image: HTMLImageElement): void {
|
||||
const index = images.indexOf(image);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
images.splice(index, 1);
|
||||
sheet.deleteRule(index);
|
||||
}
|
||||
|
||||
const mutationObserver = new MutationObserver((mutations) => {
|
||||
const addedImages = mutations.flatMap((mutation) =>
|
||||
filterImages([...mutation.addedNodes])
|
||||
);
|
||||
|
||||
for (const image of addedImages) {
|
||||
addImageOnLoad(image);
|
||||
}
|
||||
|
||||
const removedImages = mutations.flatMap((mutation) =>
|
||||
filterImages([...mutation.removedNodes])
|
||||
);
|
||||
|
||||
for (const image of removedImages) {
|
||||
removeImage(image);
|
||||
}
|
||||
|
||||
images = images;
|
||||
});
|
||||
|
||||
$: if (container) {
|
||||
mutationObserver.observe(container, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
export function toggleActualSize() {
|
||||
const index = images.indexOf(activeImage!);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rule = sheet.cssRules[index] as CSSStyleRule;
|
||||
active = !active;
|
||||
|
||||
if (active) {
|
||||
resetImageRule(rule);
|
||||
} else {
|
||||
setImageRule(activeImage!, rule);
|
||||
}
|
||||
|
||||
dispatch("update", active);
|
||||
}
|
||||
|
||||
onDestroy(() => mutationObserver.disconnect());
|
||||
</script>
|
||||
|
||||
<ButtonGroup size={1.6}>
|
||||
<ButtonGroupItem>
|
||||
<IconButton
|
||||
{active}
|
||||
flipX={isRtl}
|
||||
tooltip={tr.editingActualSize()}
|
||||
on:click={toggleActualSize}>{@html icon}</IconButton
|
||||
>
|
||||
</ButtonGroupItem>
|
||||
</ButtonGroup>
|
||||
{#if activeImage}
|
||||
<ButtonGroup size={1.6}>
|
||||
<ButtonGroupItem>
|
||||
<IconButton
|
||||
{active}
|
||||
flipX={isRtl}
|
||||
tooltip={tr.editingActualSize()}
|
||||
on:click={toggleActualSize}>{@html icon}</IconButton
|
||||
>
|
||||
</ButtonGroupItem>
|
||||
</ButtonGroup>
|
||||
{/if}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
export class EditableContainer extends HTMLDivElement {
|
||||
baseStyle: HTMLStyleElement;
|
||||
baseRule?: CSSStyleRule;
|
||||
imageRule?: CSSStyleRule;
|
||||
imageStyle?: HTMLStyleElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -25,6 +25,7 @@ export class EditableContainer extends HTMLDivElement {
|
|||
|
||||
this.baseStyle = document.createElement("style");
|
||||
this.baseStyle.setAttribute("rel", "stylesheet");
|
||||
this.baseStyle.id = "baseStyle";
|
||||
shadow.appendChild(this.baseStyle);
|
||||
}
|
||||
|
||||
|
@ -32,11 +33,6 @@ export class EditableContainer extends HTMLDivElement {
|
|||
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("max-width", "min(250px, 100%)", "important");
|
||||
this.imageRule.style.setProperty("max-height", "200px", "important");
|
||||
}
|
||||
|
||||
initialize(color: string): void {
|
||||
|
|
|
@ -23,7 +23,7 @@ function onCutOrCopy(): void {
|
|||
}
|
||||
|
||||
export class EditingArea extends HTMLDivElement {
|
||||
imageHandle: ImageHandle;
|
||||
imageHandle: Promise<ImageHandle>;
|
||||
editableContainer: EditableContainer;
|
||||
editable: Editable;
|
||||
codable: Codable;
|
||||
|
@ -35,9 +35,12 @@ export class EditingArea extends HTMLDivElement {
|
|||
this.editableContainer = document.createElement("div", {
|
||||
is: "anki-editable-container",
|
||||
}) as EditableContainer;
|
||||
|
||||
const imageStyle = document.createElement("style");
|
||||
imageStyle.setAttribute("rel", "stylesheet");
|
||||
imageStyle.id = "imageHandleStyle";
|
||||
|
||||
this.editable = document.createElement("anki-editable") as Editable;
|
||||
this.editableContainer.shadowRoot!.appendChild(this.editable);
|
||||
this.appendChild(this.editableContainer);
|
||||
|
||||
const context = new Map();
|
||||
context.set(
|
||||
|
@ -45,14 +48,28 @@ export class EditingArea extends HTMLDivElement {
|
|||
document.documentElement.classList.contains("night-mode")
|
||||
);
|
||||
|
||||
this.imageHandle = new ImageHandle({
|
||||
target: this,
|
||||
anchor: this.editableContainer,
|
||||
props: {
|
||||
container: this.editable,
|
||||
},
|
||||
context,
|
||||
} as any);
|
||||
let imageHandleResolve: (value: ImageHandle) => void;
|
||||
this.imageHandle = new Promise<ImageHandle>((resolve) => {
|
||||
imageHandleResolve = resolve;
|
||||
});
|
||||
|
||||
imageStyle.addEventListener("load", () =>
|
||||
imageHandleResolve(
|
||||
new ImageHandle({
|
||||
target: this,
|
||||
anchor: this.editableContainer,
|
||||
props: {
|
||||
container: this.editable,
|
||||
sheet: imageStyle.sheet,
|
||||
},
|
||||
context,
|
||||
} as any)
|
||||
)
|
||||
);
|
||||
|
||||
this.editableContainer.shadowRoot!.appendChild(imageStyle);
|
||||
this.editableContainer.shadowRoot!.appendChild(this.editable);
|
||||
this.appendChild(this.editableContainer);
|
||||
|
||||
this.codable = document.createElement("textarea", {
|
||||
is: "anki-codable",
|
||||
|
@ -75,7 +92,7 @@ export class EditingArea extends HTMLDivElement {
|
|||
}
|
||||
|
||||
set fieldHTML(content: string) {
|
||||
this.activeInput.fieldHTML = content;
|
||||
this.imageHandle.then(() => (this.activeInput.fieldHTML = content));
|
||||
}
|
||||
|
||||
get fieldHTML(): string {
|
||||
|
@ -186,19 +203,21 @@ export class EditingArea extends HTMLDivElement {
|
|||
}
|
||||
|
||||
resetImageHandle(): void {
|
||||
(this.imageHandle as any).$set({
|
||||
image: null,
|
||||
imageRule: null,
|
||||
});
|
||||
this.imageHandle.then((imageHandle) =>
|
||||
(imageHandle as any).$set({
|
||||
activeImage: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
showImageHandle(event: MouseEvent): void {
|
||||
if (event.target instanceof HTMLImageElement) {
|
||||
(this.imageHandle as any).$set({
|
||||
image: event.target,
|
||||
imageRule: this.editableContainer.imageRule,
|
||||
isRtl: this.isRightToLeft(),
|
||||
});
|
||||
this.imageHandle.then((imageHandle) =>
|
||||
(imageHandle as any).$set({
|
||||
activeImage: event.target,
|
||||
isRtl: this.isRightToLeft(),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.resetImageHandle();
|
||||
}
|
||||
|
@ -211,6 +230,7 @@ export class EditingArea extends HTMLDivElement {
|
|||
this.fieldHTML = this.codable.teardown();
|
||||
this.editable.hidden = false;
|
||||
} else {
|
||||
this.resetImageHandle();
|
||||
this.editable.hidden = true;
|
||||
this.codable.setup(this.editable.fieldHTML);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue