anki/ts/components/HorizontalResizer.svelte
Matthias Metelka f72570c604
Make tags editor resizable using Henrik's components (#2046)
* Make tags editor resizable using Henrik's components

All credit for the components goes to Henrik. I just tweaked the design a bit and implemented them in NoteEditor.

Co-Authored-By: Henrik Giesel <hengiesel@gmail.com>

* Remove PaneContent padding

Co-Authored-By: Henrik Giesel <hengiesel@gmail.com>

* Add responsive box-shadows on scroll/resize

only shown when content overflows in the respective direction.

* Remove comment

* Fix overflow calculations and shadow mix-up

This happened when I switched from using scrolledToX to overflowX booleans.

* Simplify overflow calculations

* Make drag handles 0 height/width

The remaining height requirement comes from a margin set on NoteEditor.

* Run eslint on components

* Split editor into three panes: Toolbar, Fields, Tags

* Remove upper split for now

to unblock 2.1.55 beta

* Move panes.scss to sass folder

* Use single type for resizable panes

* Implement collapsed state toggled with click on resizer

* Add button to uncollapse tags pane and focus input

* Add indicator for # of tags

* Use dbclick to prevent interference with resize state

* Add utility functions for expand/collapse

* Meddle around with types and formatting

* Fix collapsed state being forgotten on second browser open (dae)

* Fix typecheck (dae)

Our tooling generates .d.ts files from the Svelte files, but it doesn't
expect variables to be exported. By changing them into functions, they
get included in .bazel/bin/ts/components/Pane.svelte.d.ts

* Remove an unnecessary bridgeCommand (dae)

* Fix the bottom of tags getting cut off (dae)

Not sure why offsetHeight is inaccurate in this case.

* Add missing header (dae)

Co-authored-by: Henrik Giesel <hengiesel@gmail.com>
2022-09-28 14:02:32 +10:00

118 lines
3.5 KiB
Svelte

<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { on } from "../lib/events";
import { Callback, singleCallback } from "../lib/typing";
import IconConstrain from "./IconConstrain.svelte";
import { horizontalHandle } from "./icons";
import type { ResizablePane } from "./types";
export let panes: ResizablePane[];
export let index = 0;
export let tip = "";
export let clientHeight: number;
let destroy: Callback;
let before: ResizablePane;
let after: ResizablePane;
$: resizerAmount = panes.length - 1;
$: componentsHeight = clientHeight - resizerHeight * resizerAmount;
export function move(targets: ResizablePane[], targetHeight: number): void {
const [resizeTarget, resizePartner] = targets;
if (targetHeight <= resizeTarget.maxHeight) {
resizeTarget.resizable.getHeightResizer().setSize(targetHeight);
resizePartner.resizable
.getHeightResizer()
.setSize(componentsHeight - targetHeight);
}
}
function onMove(this: Window, { movementY }: PointerEvent): void {
if (movementY < 0) {
if (after.height - movementY <= after.maxHeight) {
const resized = before.resizable.getHeightResizer().resize(movementY);
after.resizable.getHeightResizer().resize(-resized);
} else {
const resized = before.resizable
.getHeightResizer()
.resize(after.height - after.maxHeight);
after.resizable.getHeightResizer().resize(-resized);
}
} else if (before.height + movementY <= before.maxHeight) {
const resized = after.resizable.getHeightResizer().resize(-movementY);
before.resizable.getHeightResizer().resize(-resized);
} else {
const resized = after.resizable
.getHeightResizer()
.resize(before.height - before.maxHeight);
before.resizable.getHeightResizer().resize(-resized);
}
}
let resizerHeight: number;
function releasePointer(this: Window): void {
destroy();
document.exitPointerLock();
for (const pane of panes) {
pane.resizable.getHeightResizer().stop(componentsHeight, panes.length);
}
}
function lockPointer(this: HTMLDivElement) {
this.requestPointerLock();
before = panes[index];
after = panes[index + 1];
for (const pane of panes) {
pane.resizable.getHeightResizer().start();
}
destroy = singleCallback(
on(window, "pointermove", onMove),
on(window, "pointerup", releasePointer),
);
}
</script>
<div
class="horizontal-resizer"
title={tip}
bind:clientHeight={resizerHeight}
on:pointerdown|preventDefault={lockPointer}
on:dblclick
>
<div class="drag-handle">
<IconConstrain iconSize={80}>{@html horizontalHandle}</IconConstrain>
</div>
</div>
<style lang="scss">
.horizontal-resizer {
width: 100%;
cursor: row-resize;
position: relative;
height: 10px;
border-top: 1px solid var(--border);
z-index: 20;
.drag-handle {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
opacity: 0.4;
}
&:hover .drag-handle {
opacity: 0.8;
}
}
</style>