mirror of
https://github.com/ankitects/anki.git
synced 2025-11-12 23:57:13 -05:00
Add dropdown menu to mathjax button
This commit is contained in:
parent
391f64f648
commit
fa900e1565
11 changed files with 124 additions and 73 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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} />
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
46
ts/editor-toolbar/WithDropdownMenu.svelte
Normal file
46
ts/editor-toolbar/WithDropdownMenu.svelte
Normal 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} />
|
||||||
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
15
ts/editor-toolbar/notetype.ts
Normal file
15
ts/editor-toolbar/notetype.ts
Normal 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,
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue