mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 17:26:36 -04:00
First implementation of ImageHandle
This commit is contained in:
parent
41c4be2f54
commit
0b06891771
5 changed files with 185 additions and 1 deletions
118
ts/editor/ImageHandle.svelte
Normal file
118
ts/editor/ImageHandle.svelte
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
<!--
|
||||||
|
Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
-->
|
||||||
|
<script lang="typescript">
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
import { nightModeKey } from "components/context-keys";
|
||||||
|
|
||||||
|
export let hidden: boolean;
|
||||||
|
|
||||||
|
export let top: number = 0;
|
||||||
|
export let left: number = 0;
|
||||||
|
export let width: number = 0;
|
||||||
|
export let height: number = 0;
|
||||||
|
|
||||||
|
function setPointerCapture(event: PointerEvent): void {
|
||||||
|
if (event.pointerId !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(event.target as Element).setPointerCapture(event.pointerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resize(event: PointerEvent): void {
|
||||||
|
if (!(event.target as Element).hasPointerCapture(event.pointerId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nightMode = getContext(nightModeKey);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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:nightMode class="image-handle-control image-handle-control-nw" />
|
||||||
|
<div class:nightMode class="image-handle-control image-handle-control-ne" />
|
||||||
|
<div class:nightMode class="image-handle-control image-handle-control-sw" />
|
||||||
|
<div
|
||||||
|
class:nightMode
|
||||||
|
class="image-handle-control image-handle-control-se is-active"
|
||||||
|
on:pointerdown={setPointerCapture}
|
||||||
|
on:pointermove={resize}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
div {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-handle-selection {
|
||||||
|
top: var(--top);
|
||||||
|
left: var(--left);
|
||||||
|
width: var(--width);
|
||||||
|
height: var(--height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-handle-bg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: black;
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-handle-control {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border: 1px solid black;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.nightMode {
|
||||||
|
border-color: white;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-handle-control-nw {
|
||||||
|
top: -5px;
|
||||||
|
left: -5px;
|
||||||
|
border-bottom: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-handle-control-ne {
|
||||||
|
top: -5px;
|
||||||
|
right: -5px;
|
||||||
|
border-bottom: none;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-handle-control-sw {
|
||||||
|
bottom: -5px;
|
||||||
|
left: -5px;
|
||||||
|
border-top: none;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-handle-control-se {
|
||||||
|
bottom: -5px;
|
||||||
|
right: -5px;
|
||||||
|
border-top: none;
|
||||||
|
border-left: none;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
cursor: se-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
17
ts/editor/ImageHandleContainer.svelte
Normal file
17
ts/editor/ImageHandleContainer.svelte
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!--
|
||||||
|
Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
-->
|
||||||
|
<script lang="typescript">
|
||||||
|
import ImageHandle from "./ImageHandle.svelte";
|
||||||
|
|
||||||
|
export let hidden: boolean;
|
||||||
|
export let top: number;
|
||||||
|
export let left: number;
|
||||||
|
export let width: number;
|
||||||
|
export let height: number;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ImageHandle {hidden} {top} {left} {width} {height} />
|
||||||
|
</div>
|
|
@ -23,7 +23,9 @@ function clearChangeTimer(): void {
|
||||||
|
|
||||||
export function saveField(currentField: EditingArea, type: "blur" | "key"): void {
|
export function saveField(currentField: EditingArea, type: "blur" | "key"): void {
|
||||||
clearChangeTimer();
|
clearChangeTimer();
|
||||||
const command = `${type}:${currentField.ord}:${getNoteId()}:${currentField.fieldHTML}`
|
const command = `${type}:${currentField.ord}:${getNoteId()}:${
|
||||||
|
currentField.fieldHTML
|
||||||
|
}`;
|
||||||
bridgeCommand(command);
|
bridgeCommand(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
@typescript-eslint/no-non-null-assertion: "off",
|
@typescript-eslint/no-non-null-assertion: "off",
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ImageHandleContainer from "./ImageHandleContainer.svelte";
|
||||||
|
|
||||||
import type { EditableContainer } from "./editable-container";
|
import type { EditableContainer } from "./editable-container";
|
||||||
import type { Editable } from "./editable";
|
import type { Editable } from "./editable";
|
||||||
import type { Codable } from "./codable";
|
import type { Codable } from "./codable";
|
||||||
|
@ -13,12 +15,14 @@ import { updateActiveButtons } from "./toolbar";
|
||||||
import { bridgeCommand } from "./lib";
|
import { bridgeCommand } from "./lib";
|
||||||
import { onInput, onKey, onKeyUp } from "./input-handlers";
|
import { onInput, onKey, onKeyUp } from "./input-handlers";
|
||||||
import { onFocus, onBlur } from "./focus-handlers";
|
import { onFocus, onBlur } from "./focus-handlers";
|
||||||
|
import { nightModeKey } from "components/context-keys";
|
||||||
|
|
||||||
function onCutOrCopy(): void {
|
function onCutOrCopy(): void {
|
||||||
bridgeCommand("cutOrCopy");
|
bridgeCommand("cutOrCopy");
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditingArea extends HTMLDivElement {
|
export class EditingArea extends HTMLDivElement {
|
||||||
|
imageHandle: ImageHandleContainer;
|
||||||
editableContainer: EditableContainer;
|
editableContainer: EditableContainer;
|
||||||
editable: Editable;
|
editable: Editable;
|
||||||
codable: Codable;
|
codable: Codable;
|
||||||
|
@ -34,12 +38,32 @@ export class EditingArea extends HTMLDivElement {
|
||||||
this.editableContainer.shadowRoot!.appendChild(this.editable);
|
this.editableContainer.shadowRoot!.appendChild(this.editable);
|
||||||
this.appendChild(this.editableContainer);
|
this.appendChild(this.editableContainer);
|
||||||
|
|
||||||
|
const context = new Map();
|
||||||
|
context.set(
|
||||||
|
nightModeKey,
|
||||||
|
document.documentElement.classList.contains("night-mode")
|
||||||
|
);
|
||||||
|
|
||||||
|
this.imageHandle = new ImageHandleContainer({
|
||||||
|
target: this,
|
||||||
|
anchor: this.editableContainer,
|
||||||
|
props: {
|
||||||
|
hidden: true,
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
},
|
||||||
|
context,
|
||||||
|
} as any);
|
||||||
|
|
||||||
this.codable = document.createElement("textarea", {
|
this.codable = document.createElement("textarea", {
|
||||||
is: "anki-codable",
|
is: "anki-codable",
|
||||||
}) as Codable;
|
}) as Codable;
|
||||||
this.appendChild(this.codable);
|
this.appendChild(this.codable);
|
||||||
|
|
||||||
this.onPaste = this.onPaste.bind(this);
|
this.onPaste = this.onPaste.bind(this);
|
||||||
|
this.showImageHandle = this.showImageHandle.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeInput(): Editable | Codable {
|
get activeInput(): Editable | Codable {
|
||||||
|
@ -68,6 +92,7 @@ export class EditingArea extends HTMLDivElement {
|
||||||
this.addEventListener("copy", onCutOrCopy);
|
this.addEventListener("copy", onCutOrCopy);
|
||||||
this.addEventListener("oncut", onCutOrCopy);
|
this.addEventListener("oncut", onCutOrCopy);
|
||||||
this.addEventListener("mouseup", updateActiveButtons);
|
this.addEventListener("mouseup", updateActiveButtons);
|
||||||
|
this.editable.addEventListener("click", this.showImageHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback(): void {
|
disconnectedCallback(): void {
|
||||||
|
@ -80,6 +105,7 @@ export class EditingArea extends HTMLDivElement {
|
||||||
this.removeEventListener("copy", onCutOrCopy);
|
this.removeEventListener("copy", onCutOrCopy);
|
||||||
this.removeEventListener("oncut", onCutOrCopy);
|
this.removeEventListener("oncut", onCutOrCopy);
|
||||||
this.removeEventListener("mouseup", updateActiveButtons);
|
this.removeEventListener("mouseup", updateActiveButtons);
|
||||||
|
this.editable.removeEventListener("click", this.showImageHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(color: string, content: string): void {
|
initialize(color: string, content: string): void {
|
||||||
|
@ -144,6 +170,26 @@ export class EditingArea extends HTMLDivElement {
|
||||||
this.activeInput.onPaste(event);
|
this.activeInput.onPaste(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showImageHandle(event: MouseEvent): void {
|
||||||
|
if (event.target instanceof HTMLImageElement) {
|
||||||
|
const image = event.target;
|
||||||
|
const imageRect = image.getBoundingClientRect();
|
||||||
|
const editableRect = this.editable.getBoundingClientRect();
|
||||||
|
|
||||||
|
(this.imageHandle as any).$set({
|
||||||
|
hidden: false,
|
||||||
|
top: imageRect.top - editableRect.top,
|
||||||
|
left: imageRect.left - editableRect.left,
|
||||||
|
width: image.clientWidth,
|
||||||
|
height: image.clientHeight,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
(this.imageHandle as any).$set({
|
||||||
|
hidden: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toggleHtmlEdit(): void {
|
toggleHtmlEdit(): void {
|
||||||
const hadFocus = this.hasFocus();
|
const hadFocus = this.hasFocus();
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
|
position: relative;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
background: var(--frame-bg);
|
background: var(--frame-bg);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue