mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
Add hiding functionality in ButtonGroup
This commit is contained in:
parent
26f85a0f9d
commit
e1cc22b9ee
5 changed files with 113 additions and 45 deletions
|
@ -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>
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue