mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56: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 {
|
||||
clearChangeTimer();
|
||||
const command = `${type}:${currentField.ord}:${getNoteId()}:${currentField.fieldHTML}`
|
||||
const command = `${type}:${currentField.ord}:${getNoteId()}:${
|
||||
currentField.fieldHTML
|
||||
}`;
|
||||
bridgeCommand(command);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
@typescript-eslint/no-non-null-assertion: "off",
|
||||
*/
|
||||
|
||||
import ImageHandleContainer from "./ImageHandleContainer.svelte";
|
||||
|
||||
import type { EditableContainer } from "./editable-container";
|
||||
import type { Editable } from "./editable";
|
||||
import type { Codable } from "./codable";
|
||||
|
@ -13,12 +15,14 @@ import { updateActiveButtons } from "./toolbar";
|
|||
import { bridgeCommand } from "./lib";
|
||||
import { onInput, onKey, onKeyUp } from "./input-handlers";
|
||||
import { onFocus, onBlur } from "./focus-handlers";
|
||||
import { nightModeKey } from "components/context-keys";
|
||||
|
||||
function onCutOrCopy(): void {
|
||||
bridgeCommand("cutOrCopy");
|
||||
}
|
||||
|
||||
export class EditingArea extends HTMLDivElement {
|
||||
imageHandle: ImageHandleContainer;
|
||||
editableContainer: EditableContainer;
|
||||
editable: Editable;
|
||||
codable: Codable;
|
||||
|
@ -34,12 +38,32 @@ export class EditingArea extends HTMLDivElement {
|
|||
this.editableContainer.shadowRoot!.appendChild(this.editable);
|
||||
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", {
|
||||
is: "anki-codable",
|
||||
}) as Codable;
|
||||
this.appendChild(this.codable);
|
||||
|
||||
this.onPaste = this.onPaste.bind(this);
|
||||
this.showImageHandle = this.showImageHandle.bind(this);
|
||||
}
|
||||
|
||||
get activeInput(): Editable | Codable {
|
||||
|
@ -68,6 +92,7 @@ export class EditingArea extends HTMLDivElement {
|
|||
this.addEventListener("copy", onCutOrCopy);
|
||||
this.addEventListener("oncut", onCutOrCopy);
|
||||
this.addEventListener("mouseup", updateActiveButtons);
|
||||
this.editable.addEventListener("click", this.showImageHandle);
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
|
@ -80,6 +105,7 @@ export class EditingArea extends HTMLDivElement {
|
|||
this.removeEventListener("copy", onCutOrCopy);
|
||||
this.removeEventListener("oncut", onCutOrCopy);
|
||||
this.removeEventListener("mouseup", updateActiveButtons);
|
||||
this.editable.removeEventListener("click", this.showImageHandle);
|
||||
}
|
||||
|
||||
initialize(color: string, content: string): void {
|
||||
|
@ -144,6 +170,26 @@ export class EditingArea extends HTMLDivElement {
|
|||
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 {
|
||||
const hadFocus = this.hasFocus();
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
}
|
||||
|
||||
.field {
|
||||
position: relative;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--frame-bg);
|
||||
|
||||
|
|
Loading…
Reference in a new issue