Add hiding functionality in ButtonGroup

This commit is contained in:
Henrik Giesel 2021-05-05 15:12:02 +02:00
parent 26f85a0f9d
commit e1cc22b9ee
5 changed files with 113 additions and 45 deletions

View file

@ -3,21 +3,21 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import ButtonGroupItem from "./ButtonGroupItem.svelte";
import type { SvelteComponentTyped } from "svelte";
import { setContext } from "svelte";
import type { Writable } from "svelte/store";
import { writable } from "svelte/store";
import { buttonGroupKey } from "./contextKeys";
import type { Identifier } from "./identifier";
import { insert, add, update } from "./identifier";
import { insert, add, update, find } from "./identifier";
export let id: string | undefined = undefined;
let className: string = "";
export { className as class };
export let api = {};
export let buttonGroupRef: HTMLDivElement;
$: root = buttonGroupRef?.getRootNode() as Document;
let buttonGroupRef: HTMLDivElement;
interface ButtonRegistration {
detach: Writable<boolean>;
@ -25,20 +25,28 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const items: ButtonRegistration[] = [];
function registerButton(): ButtonRegistration {
function makeRegistration(): ButtonRegistration {
const detach = writable(false);
const registration = { detach };
items.push(registration);
return { detach };
}
function registerButton(
index = items.length,
registration = makeRegistration()
): ButtonRegistration {
items.splice(index, 0, registration);
return registration;
}
const dynamicItems: ButtonRegistration[] = [];
let dynamic: SvelteComponentTyped[] = [];
function addButton(
button: SvelteComponentTyped,
add: (added: Element, parent: Element) => number
): void {
const registration = makeRegistration();
const callback = (
mutations: MutationRecord[],
observer: MutationObserver
@ -48,6 +56,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
if (addedNode.nodeType === Node.ELEMENT_NODE) {
const index = add(addedNode as Element, buttonGroupRef);
if (index >= 0) {
registerButton(index, registration);
}
}
}
@ -57,6 +69,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const observer = new MutationObserver(callback);
observer.observe(buttonGroupRef, { childList: true });
dynamicItems.push(registration);
dynamic = [...dynamic, button];
}
@ -64,12 +77,26 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
addButton(button, (added, parent) => insert(added, parent, id));
const appendButton = (button: SvelteComponentTyped, id: Identifier = -1) =>
addButton(button, (added, parent) => add(added, parent, id));
function updateRegistration(
f: (registration: ButtonRegistration) => void,
id: Identifier
): void {
const match = find(buttonGroupRef.children, id);
if (match) {
const [index] = match;
const registration = items[index];
f(registration);
}
}
const showButton = (id: Identifier) =>
update((element) => element.removeAttribute("hidden"), buttonGroupRef, id);
updateRegistration(({ detach }) => detach.update(() => false), id);
const hideButton = (id: Identifier) =>
update((element) => element.setAttribute("hidden", ""), buttonGroupRef, id);
updateRegistration(({ detach }) => detach.update(() => true), id);
const toggleButton = (id: Identifier) =>
update((element) => element.toggleAttribute("hidden"), buttonGroupRef, id);
updateRegistration(({ detach }) => detach.update((old) => !old), id);
setContext(
buttonGroupKey,
@ -97,7 +124,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<div bind:this={buttonGroupRef} {id} class={className} dir="ltr">
<slot />
{#each dynamic as component}
<svelte:component this={component} />
{#each dynamic as component, i}
<ButtonGroupItem registration={dynamicItems[i]}>
<svelte:component this={component} />
</ButtonGroupItem>
{/each}
</div>

View file

@ -8,8 +8,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { getContext } from "svelte";
import { buttonGroupKey } from "./contextKeys";
export let registration = undefined;
const { registerButton } = getContext(buttonGroupKey);
const { detach } = registerButton();
const { detach } = registration ?? registerButton();
</script>
<Detachable detach={$detach}>

View file

@ -2,54 +2,86 @@
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export type Identifier = string | number;
function find(collection: HTMLCollection, idOrIndex: Identifier): Element | null {
let element: Element | null = null;
export function find(
collection: HTMLCollection,
idOrIndex: Identifier
): [number, Element] | null {
let result: [number, Element] | null = null;
if (typeof idOrIndex === "string") {
element = collection.namedItem(idOrIndex);
const element = collection.namedItem(idOrIndex);
if (element) {
const index = Array.prototype.indexOf.call(collection, element);
result = [index, element];
}
} else if (idOrIndex < 0) {
const normalizedIndex = collection.length + idOrIndex;
element = collection.item(normalizedIndex);
const index = collection.length + idOrIndex;
const element = collection.item(index);
if (element) {
result = [index, element];
}
} else {
element = collection.item(idOrIndex);
const index = idOrIndex;
const element = collection.item(index);
if (element) {
result = [index, element];
}
}
return element;
return result;
}
export function insert(
element: Element,
collection: Element,
idOrIndex: Identifier
): void {
const reference = find(collection.children, idOrIndex);
): number {
const match = find(collection.children, idOrIndex);
if (reference) {
collection.insertBefore(element, reference);
if (match) {
const [index, reference] = match;
collection.insertBefore(element, reference[0]);
return index;
}
return -1;
}
export function add(
element: Element,
collection: Element,
idOrIndex: Identifier
): void {
const before = find(collection.children, idOrIndex);
): number {
const match = find(collection.children, idOrIndex);
if (before) {
if (match) {
const [index, before] = match;
const reference = before.nextElementSibling ?? null;
collection.insertBefore(element, reference);
return index + 1;
}
return -1;
}
export function update(
f: (element: Element) => void,
collection: Element,
idOrIndex: Identifier
): void {
const element = find(collection.children, idOrIndex);
): number {
const match = find(collection.children, idOrIndex);
if (element) {
f(element);
if (match) {
const [index, element] = match;
f(element[0]);
return index;
}
return -1;
}

View file

@ -5,8 +5,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="typescript">
import * as tr from "lib/i18n";
import IconButton from "components/IconButton.svelte";
import ButtonGroup from "components/ButtonGroup.svelte";
import IconButton from "components/IconButton.svelte";
import WithState from "components/WithState.svelte";
import WithShortcut from "components/WithShortcut.svelte";

View file

@ -7,27 +7,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import * as tr from "lib/i18n";
import ButtonGroup from "components/ButtonGroup.svelte";
import ButtonGroupItem from "components/ButtonGroupItem.svelte";
import LabelButton from "components/LabelButton.svelte";
import WithShortcut from "components/WithShortcut.svelte";
export let api = {};
</script>
<ButtonGroup id="notetype" class="" {api}>
<LabelButton
disables={false}
tooltip={tr.editingCustomizeFields()}
on:click={() => bridgeCommand('fields')}>
{tr.editingFields()}...
</LabelButton>
<WithShortcut shortcut="Control+KeyL" let:createShortcut let:shortcutLabel>
<ButtonGroup id="notetype" {api}>
<ButtonGroupItem>
<LabelButton
disables={false}
tooltip={`${tr.editingCustomizeCardTemplates()} (${shortcutLabel})`}
on:click={() => bridgeCommand('cards')}
on:mount={createShortcut}>
{tr.editingCards()}...
tooltip={tr.editingCustomizeFields()}
on:click={() => bridgeCommand('fields')}>
{tr.editingFields()}...
</LabelButton>
</WithShortcut>
</ButtonGroupItem>
<ButtonGroupItem>
<WithShortcut shortcut="Control+KeyL" let:createShortcut let:shortcutLabel>
<LabelButton
disables={false}
tooltip={`${tr.editingCustomizeCardTemplates()} (${shortcutLabel})`}
on:click={() => bridgeCommand('cards')}
on:mount={createShortcut}>
{tr.editingCards()}...
</LabelButton>
</WithShortcut>
</ButtonGroupItem>
</ButtonGroup>