anki/ts/sveltelib/closing-click.ts
Henrik Giesel 88217c5e7d
Replace (some) Bootstrap dropdowns with Floating UI (#1695)
* Implement a first version of WithFloating and Portal

* Add outside slot for Portal

* Execute computePosition from WithFloating

* Set up a first example of new WithFloating with the Latex menu

* Use autoUpdate in WithFloating

* Create sveltelib/position

* Add event-store

* Use event-store in close-on-click

* Implement subscribeToUpdates

* Introduce sass/elevation

* Split close-on-click to closing-click and subscribe-trigger

* Have closing-* stores return a symbol

- This way they act more of an EventEmitter than a store

* Allow passing show store

* Remove styling on float on updatePosition removal

* Implement a nice border for dropdowns

* Apply different border and box-shadow to Popover in dark/light theme

* Fix Ctrl+Shift+T not working

* Satisfy formatters and tests

* Add copyright header

* move copyright header to top (dae)
2022-03-02 14:21:19 +10:00

65 lines
1.8 KiB
TypeScript

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { Readable } from "svelte/store";
import { derived } from "svelte/store";
/**
* Typically the right-sided mouse button.
*/
function isSecondaryButton(event: MouseEvent): boolean {
return event.button === 2;
}
interface ClosingClickArgs {
/**
* Clicking on the reference element should not close.
* The reference should handle this itself.
*/
reference: EventTarget;
floating: EventTarget;
inside: boolean;
outside: boolean;
}
/**
* Returns a derived store, which translates `MouseEvent`s into a boolean
* indicating whether they constitue a click that should close `floating`.
*
* @param: Should be an event store wrapping document.click.
*/
function isClosingClick(
store: Readable<MouseEvent>,
{ reference, floating, inside, outside }: ClosingClickArgs,
): Readable<symbol> {
function isTriggerClick(path: EventTarget[]): boolean {
return (
// Reference element was clicked, e.g. the button.
// The reference element needs to handle opening/closing itself.
!path.includes(reference) &&
((inside && path.includes(floating)) ||
(outside && !path.includes(floating)))
);
}
function shouldClose(event: MouseEvent): boolean {
if (isSecondaryButton(event)) {
return true;
}
if (isTriggerClick(event.composedPath())) {
return true;
}
return false;
}
return derived(store, (event: MouseEvent, set: (value: symbol) => void): void => {
if (shouldClose(event)) {
set(Symbol());
}
});
}
export default isClosingClick;