Add dropdown menu to mathjax button

This commit is contained in:
Henrik Giesel 2021-03-31 03:34:08 +02:00
parent 391f64f648
commit fa900e1565
11 changed files with 124 additions and 73 deletions

View file

@ -60,6 +60,6 @@
} }
</script> </script>
<SquareButton {id} {className} {props} {active} {onClick}> <SquareButton {id} {className} {props} {active} {onClick} on:mount>
{@html icon} {@html icon}
</SquareButton> </SquareButton>

View file

@ -1,14 +0,0 @@
<script lang="typescript">
export let onClick: (event: ClickEvent) => void;
</script>
<li>
<button class="dropdown-item" on:click={onClick} on:mousedown|preventDefault>
{#if $$slots.default}
<slot />
{:else}
<span class="float-start"><slot name="start" /></span>
<span class="float-end"><slot name="end" /></span>
{/if}
</button>
</li>

View file

@ -1,40 +1,26 @@
<script lang="typescript"> <script lang="typescript">
import { onMount } from "svelte"; interface DropdownItem {
label: string;
import type { ButtonDefinition } from "./types"; endLabel: string;
onClick: (event: ClickEvent) => void;
export let button: ButtonDefinition;
export let menuId: string;
function extend({ className, props, ...rest }: ButtonDefinition): ButtonDefinition {
return {
className: `${className} dropdown-toggle`,
props: {
"data-bs-toggle": "dropdown",
"aria-expanded": "false",
...props,
},
...rest,
};
} }
function createDropdown({ detail }: CustomEvent): void { export let id: string;
const button: HTMLButtonElement = detail.button; export let menuItems: DropdownItem[];
/* Prevent focus on menu activation */
const noop = () => {};
Object.defineProperty(button, "focus", { value: noop });
/* Set custom menu without using .dropdown
* Rendering the menu here would cause the menu to
* be displayed outside of the visible area
*/
const dropdown = new bootstrap.Dropdown(button);
dropdown._menu = button.getRootNode().getElementById(menuId);
}
</script> </script>
<svelte:component <ul class="dropdown-menu" {id}>
this={button.component} {#each menuItems as menuItem}
{...extend(button)} <li>
on:mount={createDropdown} /> <button
class="dropdown-item"
on:click={menuItem.onClick}
on:mousedown|preventDefault>
<span class="float-start">{menuItem.label}</span>
{#if menuItem.endLabel}
<span class="float-end">{menuItem.endLabel}</span>
{/if}
</button>
</li>
{/each}
</ul>

View file

@ -1,5 +1,4 @@
<script lang="typescript"> <script lang="typescript">
import DropdownItem from "./DropdownItem.svelte";
import type { Readable } from "svelte/store"; import type { Readable } from "svelte/store";
import { setContext } from "svelte"; import { setContext } from "svelte";
import { disabledKey, nightModeKey } from "./contextKeys"; import { disabledKey, nightModeKey } from "./contextKeys";
@ -8,11 +7,13 @@
import type { Buttons } from "./types"; import type { Buttons } from "./types";
export let buttons: Buttons = []; export let buttons: Buttons = [];
export let menus: SvelteComponent[];
export let nightMode: boolean; export let nightMode: boolean;
export let disabled: Readable<boolean> = false; export let disabled: Readable<boolean> = false;
setContext(disabledKey, disabled);
setContext(nightModeKey, nightMode); setContext(nightModeKey, nightMode);
setContext(disabledKey, disabled);
</script> </script>
<style lang="scss"> <style lang="scss">
@ -38,14 +39,11 @@
} }
</style> </style>
<ul class="dropdown-menu" id="dropdownMenuButton123" aria-labelledby="dropdownMenuButton123"> <div>
<DropdownItem> {#each menus as menu}
<svelte:fragment slot="start">Action</svelte:fragment> <svelte:component this={menu.component} {...menu} />
<svelte:fragment slot="end">Shortcut</svelte:fragment> {/each}
</DropdownItem> </div>
<DropdownItem>Action 1</DropdownItem>
<DropdownItem>Action 2</DropdownItem>
</ul>
<nav> <nav>
<ButtonGroup {buttons} /> <ButtonGroup {buttons} />

View file

@ -9,6 +9,6 @@
export let onClick: (event: ClickEvent) => void; export let onClick: (event: ClickEvent) => void;
</script> </script>
<SquareButton {id} {className} {props} {onClick}> <SquareButton {id} {className} {props} {onClick} on:mount>
{@html icon} {@html icon}
</SquareButton> </SquareButton>

View file

@ -9,6 +9,7 @@
export let label: string; export let label: string;
export let onClick: (event: ClickEvent) => void; export let onClick: (event: ClickEvent) => void;
export let disables = true;
let buttonRef: HTMLButtonElement; let buttonRef: HTMLButtonElement;
@ -21,7 +22,7 @@
onMount(() => dispatch("mount", { button: buttonRef })); onMount(() => dispatch("mount", { button: buttonRef }));
const disabledStore = getContext(disabledKey); const disabledStore = getContext(disabledKey);
$: disabled = $disabledStore; $: disabled = disables && $disabledStore;
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -1,5 +1,5 @@
<script lang="typescript"> <script lang="typescript">
import { getContext } from "svelte"; import { getContext, onMount, createEventDispatcher } from "svelte";
import type { Readable } from "svelte/store"; import type { Readable } from "svelte/store";
import { disabledKey } from "./contextKeys"; import { disabledKey } from "./contextKeys";
@ -10,8 +10,13 @@
export let onClick: (event: ClickEvent) => void; export let onClick: (event: ClickEvent) => void;
export let active = false; export let active = false;
let buttonRef: HTMLButtonElement;
const disabledStore = getContext(disabledKey); const disabledStore = getContext(disabledKey);
$: disabled = $disabledStore; $: disabled = $disabledStore;
const dispatch = createEventDispatcher();
onMount(() => dispatch("mount", { button: buttonRef }));
</script> </script>
<style lang="scss"> <style lang="scss">
@ -75,6 +80,7 @@
</style> </style>
<button <button
bind:this={buttonRef}
{id} {id}
class={className} class={className}
{...props} {...props}

View file

@ -0,0 +1,46 @@
<script lang="typescript">
import { onMount } from "svelte";
import type { ButtonDefinition } from "./types";
export let button: ButtonDefinition;
export let menuId: string;
function extend({ className, props, ...rest }: ButtonDefinition): ButtonDefinition {
return {
className: `${className} dropdown-toggle`,
props: {
"data-bs-toggle": "dropdown",
"aria-expanded": "false",
...props,
},
...rest,
};
}
function createDropdown({ detail }: CustomEvent): void {
const button: HTMLButtonElement = detail.button;
/* Prevent focus on menu activation */
const noop = () => {};
Object.defineProperty(button, "focus", { value: noop });
/* Set custom menu without using .dropdown
* Rendering the menu here would cause the menu to
* be displayed outside of the visible area
*/
const dropdown = new bootstrap.Dropdown(button);
const menu = button.getRootNode().getElementById(menuId);
if (!menu) {
console.log(`Could not find menu "${menuId}" for dropdown menu.`);
}
dropdown._menu = menu;
}
</script>
<svelte:component
this={button.component}
{...extend(button)}
on:mount={createDropdown} />

View file

@ -20,8 +20,8 @@ function onCloze(): void {
bridgeCommand("cloze"); bridgeCommand("cloze");
} }
function onMore(): void { function onHtmlEdit(): void {
bridgeCommand("more"); bridgeCommand("htmlEdit");
} }
export const attachmentButton = { export const attachmentButton = {
@ -41,11 +41,10 @@ export const clozeButton = {
export const mathjaxButton = { export const mathjaxButton = {
component: IconButton, component: IconButton,
icon: functionIcon, icon: functionIcon,
onClick: onMore,
}; };
export const htmlButton = { export const htmlButton = {
component: IconButton, component: IconButton,
icon: xmlIcon, icon: xmlIcon,
onClick: onMore, onClick: onHtmlEdit,
}; };

View file

@ -5,13 +5,15 @@ import { setupI18n, ModuleName } from "anki/i18n";
import EditorToolbarSvelte from "./EditorToolbar.svelte"; import EditorToolbarSvelte from "./EditorToolbar.svelte";
import LabelButton from "./LabelButton.svelte";
import DropdownMenu from "./DropdownMenu.svelte"; import DropdownMenu from "./DropdownMenu.svelte";
import WithDropdownMenu from "./WithDropdownMenu.svelte";
// @ts-ignore // @ts-ignore
export { updateActiveButtons, clearActiveButtons } from "./CommandIconButton.svelte"; export { updateActiveButtons, clearActiveButtons } from "./CommandIconButton.svelte";
import { Writable, writable } from "svelte/store"; import { Writable, writable } from "svelte/store";
import { fieldsButton, cardsButton } from "./notetype";
import { import {
boldButton, boldButton,
italicButton, italicButton,
@ -31,11 +33,16 @@ import {
htmlButton, htmlButton,
} from "./extra"; } from "./extra";
const defaultMenus = [
{
component: DropdownMenu,
id: "mathjaxMenu",
menuItems: [{ label: "Foo", onClick: () => console.log("foo") }],
},
];
const defaultButtons = [ const defaultButtons = [
[ [fieldsButton, cardsButton],
{ component: LabelButton, label: "Fields..." },
{ component: LabelButton, label: "Cards..." },
],
[ [
boldButton, boldButton,
italicButton, italicButton,
@ -45,7 +52,13 @@ const defaultButtons = [
eraserButton, eraserButton,
], ],
[forecolorButton, colorpickerButton], [forecolorButton, colorpickerButton],
[attachmentButton, recordButton, clozeButton, mathjaxButton, htmlButton], [
attachmentButton,
recordButton,
clozeButton,
{ component: WithDropdownMenu, menuId: "mathjaxMenu", button: mathjaxButton },
htmlButton,
],
]; ];
class EditorToolbar extends HTMLElement { class EditorToolbar extends HTMLElement {
@ -60,6 +73,7 @@ class EditorToolbar extends HTMLElement {
this.component = new EditorToolbarSvelte({ this.component = new EditorToolbarSvelte({
target: this, target: this,
props: { props: {
menus: defaultMenus,
buttons: defaultButtons, buttons: defaultButtons,
nightMode: checkNightMode(), nightMode: checkNightMode(),
disabled: this.disabled, disabled: this.disabled,

View file

@ -0,0 +1,15 @@
import { bridgeCommand } from "anki/bridgecommand";
import LabelButton from "./LabelButton.svelte";
export const fieldsButton = {
component: LabelButton,
label: "Fields...",
onClick: () => bridgeCommand("fields"),
disables: false,
};
export const cardsButton = {
component: LabelButton,
label: "Cards...",
onClick: () => bridgeCommand("cards"),
disables: false,
};