Merge pull request #1153 from hgiesel/goodbyewebcomp

Make EditorToolbar more similar to DeckOptions
This commit is contained in:
Damien Elmes 2021-04-26 09:20:06 +10:00 committed by GitHub
commit ab666612f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 154 deletions

View file

@ -387,12 +387,12 @@ class Browser(QMainWindow):
editor._links["preview"] = lambda _editor: self.onTogglePreview() editor._links["preview"] = lambda _editor: self.onTogglePreview()
editor.web.eval( editor.web.eval(
f""" f"""
$editorToolbar.addButton(editorToolbar.labelButton({{ $editorToolbar.then(({{ addButton }}) => addButton(editorToolbar.labelButton({{
label: `{tr.actions_preview()}`, label: `{tr.actions_preview()}`,
tooltip: `{tr.browsing_preview_selected_card(val=shortcut(preview_shortcut))}`, tooltip: `{tr.browsing_preview_selected_card(val=shortcut(preview_shortcut))}`,
onClick: () => bridgeCommand("preview"), onClick: () => bridgeCommand("preview"),
disables: false, disables: false,
}}), "notetype", -1); }}), "notetype", -1));
""" """
) )

View file

@ -82,7 +82,7 @@ _html = """
} }
</style> </style>
<div> <div>
<anki-editor-toolbar id="editorToolbar"></anki-editor-toolbar> <div id="editorToolbar"></div>
<div id="fields"> <div id="fields">
</div> </div>
<div id="dupes" class="is-inactive"> <div id="dupes" class="is-inactive">
@ -155,7 +155,7 @@ class Editor:
gui_hooks.editor_did_init_left_buttons(lefttopbtns, self) gui_hooks.editor_did_init_left_buttons(lefttopbtns, self)
lefttopbtns_defs = [ lefttopbtns_defs = [
f"$editorToolbar.addButton(editorToolbar.rawButton({{ html: `{button}` }}), 'notetype', -1);" f"$editorToolbar.then(({{ addButton }}) => addButton(editorToolbar.rawButton({{ html: `{button}` }}), 'notetype', -1));"
for button in lefttopbtns for button in lefttopbtns
] ]
lefttopbtns_js = "\n".join(lefttopbtns_defs) lefttopbtns_js = "\n".join(lefttopbtns_defs)
@ -173,10 +173,10 @@ class Editor:
) )
righttopbtns_js = ( righttopbtns_js = (
f""" f"""
$editorToolbar.addButton(editorToolbar.buttonGroup({{ $editorToolbar.then(({{ addButton }}) => addButton(editorToolbar.buttonGroup({{
id: "addons", id: "addons",
items: [ {righttopbtns_defs} ] items: [ {righttopbtns_defs} ]
}}), -1); }}), -1));
""" """
if righttopbtns_defs if righttopbtns_defs
else "" else ""
@ -1277,9 +1277,13 @@ gui_hooks.editor_will_munge_html.append(reverse_url_quoting)
def set_cloze_button(editor: Editor) -> None: def set_cloze_button(editor: Editor) -> None:
if editor.note.model()["type"] == MODEL_CLOZE: if editor.note.model()["type"] == MODEL_CLOZE:
editor.web.eval('$editorToolbar.showButton("template", "cloze"); ') editor.web.eval(
'$editorToolbar.then(({ showButton }) => showButton("template", "cloze")); '
)
else: else:
editor.web.eval('$editorToolbar.hideButton("template", "cloze"); ') editor.web.eval(
'$editorToolbar.then(({ hideButton }) => hideButton("template", "cloze")); '
)
gui_hooks.editor_did_load_note.append(set_cloze_button) gui_hooks.editor_did_load_note.append(set_cloze_button)

View file

@ -18,19 +18,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<script lang="typescript"> <script lang="typescript">
import type { Readable } from "svelte/store"; import type { Identifier } from "./identifiable";
import type { ToolbarItem, IterableToolbarItem } from "./types"; import type { ToolbarItem, IterableToolbarItem } from "./types";
import { setContext } from "svelte"; import { setContext } from "svelte";
import { disabledKey, nightModeKey } from "./contextKeys"; import { disabledKey, nightModeKey } from "./contextKeys";
import { add, insert, updateRecursive } from "./identifiable";
import { showComponent, hideComponent, toggleComponent } from "./hideable";
import ButtonGroup from "./ButtonGroup.svelte"; import ButtonGroup from "./ButtonGroup.svelte";
export let buttons: Readable<IterableToolbarItem[]>; export let buttons: IterableToolbarItem[];
export let menus: Readable<ToolbarItem[]>; export let menus: ToolbarItem[];
$: _buttons = $buttons;
$: _menus = $menus;
export let nightMode: boolean; export let nightMode: boolean;
setContext(nightModeKey, nightMode); setContext(nightModeKey, nightMode);
@ -42,6 +41,77 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: style = `--toolbar-size: ${size}px; --toolbar-wrap: ${ $: style = `--toolbar-size: ${size}px; --toolbar-wrap: ${
wraps ? "wrap" : "nowrap" wraps ? "wrap" : "nowrap"
}`; }`;
export function updateButton(
update: (component: ToolbarItem) => ToolbarItem,
...identifiers: Identifier[]
): void {
buttons = updateRecursive(
update,
({ items: buttons } as unknown) as ToolbarItem,
...identifiers
).items as IterableToolbarItem[];
}
export function showButton(...identifiers: Identifier[]): void {
updateButton(showComponent, ...identifiers);
}
export function hideButton(...identifiers: Identifier[]): void {
updateButton(hideComponent, ...identifiers);
}
export function toggleButton(...identifiers: Identifier[]): void {
updateButton(toggleComponent, ...identifiers);
}
export function insertButton(
newButton: ToolbarItem,
...identifiers: Identifier[]
): void {
const initIdentifiers = identifiers.slice(0, -1);
const lastIdentifier = identifiers[identifiers.length - 1];
updateButton(
(component: ToolbarItem) =>
insert(component as IterableToolbarItem, newButton, lastIdentifier),
...initIdentifiers
);
}
export function addButton(
newButton: ToolbarItem,
...identifiers: Identifier[]
): void {
const initIdentifiers = identifiers.slice(0, -1);
const lastIdentifier = identifiers[identifiers.length - 1];
updateButton(
(component: ToolbarItem) =>
add(component as IterableToolbarItem, newButton, lastIdentifier),
...initIdentifiers
);
}
export function updateMenu(
update: (component: ToolbarItem) => ToolbarItem,
...identifiers: Identifier[]
): void {
menus = updateRecursive(
update,
({ items: menus } as unknown) as ToolbarItem,
...identifiers
).items as ToolbarItem[];
}
export function addMenu(newMenu: ToolbarItem, ...identifiers: Identifier[]): void {
const initIdentifiers = identifiers.slice(0, -1);
const lastIdentifier = identifiers[identifiers.length - 1];
updateMenu(
(component: ToolbarItem) =>
add(component as IterableToolbarItem, newMenu, lastIdentifier),
...initIdentifiers
);
}
</script> </script>
<style lang="scss"> <style lang="scss">
@ -57,11 +127,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</style> </style>
<div {style}> <div {style}>
{#each _menus as menu} {#each menus as menu}
<svelte:component this={menu.component} {...menu} /> <svelte:component this={menu.component} {...menu} />
{/each} {/each}
</div> </div>
<nav {style}> <nav {style}>
<ButtonGroup items={_buttons} className="p-0 mb-1" /> <ButtonGroup items={buttons} className="p-0 mb-1" />
</nav> </nav>

View file

@ -1,5 +0,0 @@
import type { EditorToolbar } from ".";
declare namespace globalThis {
const $editorToolbar: EditorToolbar;
}

View file

@ -1,139 +1,27 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { ToolbarItem, IterableToolbarItem } from "./types"; import type { ToolbarItem, IterableToolbarItem } from "./types";
import type { Identifier } from "./identifiable";
import { Writable, writable } from "svelte/store"; import EditorToolbar from "./EditorToolbar.svelte";
export { default as EditorToolbar } from "./EditorToolbar.svelte";
import EditorToolbarSvelte from "./EditorToolbar.svelte";
import "./bootstrap.css"; import "./bootstrap.css";
import { add, insert, updateRecursive } from "./identifiable"; export function editorToolbar(
import { showComponent, hideComponent, toggleComponent } from "./hideable"; target: HTMLElement,
buttons: IterableToolbarItem[] = [],
let buttonsResolve: (value: Writable<IterableToolbarItem[]>) => void; menus: ToolbarItem[] = []
let menusResolve: (value: Writable<ToolbarItem[]>) => void; ): EditorToolbar {
return new EditorToolbar({
export class EditorToolbar extends HTMLElement { target,
private buttonsPromise: Promise<Writable<IterableToolbarItem[]>> = new Promise( props: {
(resolve) => { buttons,
buttonsResolve = resolve; menus,
} nightMode: document.documentElement.classList.contains("night-mode"),
); },
private menusPromise: Promise<Writable<ToolbarItem[]>> = new Promise( });
(resolve): void => {
menusResolve = resolve;
}
);
connectedCallback(): void {
globalThis.$editorToolbar = this;
const buttons = writable([]);
const menus = writable([]);
new EditorToolbarSvelte({
target: this,
props: {
buttons,
menus,
nightMode: document.documentElement.classList.contains("night-mode"),
},
});
buttonsResolve(buttons);
menusResolve(menus);
}
updateButton(
update: (component: ToolbarItem) => ToolbarItem,
...identifiers: Identifier[]
): void {
this.buttonsPromise.then(
(
buttons: Writable<IterableToolbarItem[]>
): Writable<IterableToolbarItem[]> => {
buttons.update(
(items: IterableToolbarItem[]): IterableToolbarItem[] =>
updateRecursive(
update,
({ items } as unknown) as ToolbarItem,
...identifiers
).items as IterableToolbarItem[]
);
return buttons;
}
);
}
showButton(...identifiers: Identifier[]): void {
this.updateButton(showComponent, ...identifiers);
}
hideButton(...identifiers: Identifier[]): void {
this.updateButton(hideComponent, ...identifiers);
}
toggleButton(...identifiers: Identifier[]): void {
this.updateButton(toggleComponent, ...identifiers);
}
insertButton(newButton: ToolbarItem, ...identifiers: Identifier[]): void {
const initIdentifiers = identifiers.slice(0, -1);
const lastIdentifier = identifiers[identifiers.length - 1];
this.updateButton(
(component: ToolbarItem) =>
insert(component as IterableToolbarItem, newButton, lastIdentifier),
...initIdentifiers
);
}
addButton(newButton: ToolbarItem, ...identifiers: Identifier[]): void {
const initIdentifiers = identifiers.slice(0, -1);
const lastIdentifier = identifiers[identifiers.length - 1];
this.updateButton(
(component: ToolbarItem) =>
add(component as IterableToolbarItem, newButton, lastIdentifier),
...initIdentifiers
);
}
updateMenu(
update: (component: ToolbarItem) => ToolbarItem,
...identifiers: Identifier[]
): void {
this.menusPromise.then(
(menus: Writable<ToolbarItem[]>): Writable<ToolbarItem[]> => {
menus.update(
(items: ToolbarItem[]): ToolbarItem[] =>
updateRecursive(
update,
({ items } as unknown) as ToolbarItem,
...identifiers
).items as ToolbarItem[]
);
return menus;
}
);
}
addMenu(newMenu: ToolbarItem, ...identifiers: Identifier[]): void {
const initIdentifiers = identifiers.slice(0, -1);
const lastIdentifier = identifiers[identifiers.length - 1];
this.updateMenu(
(component: ToolbarItem) =>
add(component as IterableToolbarItem, newMenu, lastIdentifier),
...initIdentifiers
);
}
} }
customElements.define("anki-editor-toolbar", EditorToolbar);
/* Exports for editor */ /* Exports for editor */
// @ts-expect-error insufficient typing of svelte modules // @ts-expect-error insufficient typing of svelte modules
export { updateActiveButtons, clearActiveButtons } from "./CommandIconButton.svelte"; export { updateActiveButtons, clearActiveButtons } from "./CommandIconButton.svelte";

View file

@ -31,7 +31,6 @@ declare global {
} }
} }
import "editor-toolbar";
customElements.define("anki-editable", Editable); customElements.define("anki-editable", Editable);
customElements.define("anki-editing-area", EditingArea, { extends: "div" }); customElements.define("anki-editing-area", EditingArea, { extends: "div" });
customElements.define("anki-label-container", LabelContainer, { extends: "div" }); customElements.define("anki-label-container", LabelContainer, { extends: "div" });
@ -170,4 +169,4 @@ export function setFormat(cmd: string, arg?: any, nosave: boolean = false): void
const i18n = setupI18n({ modules: [ModuleName.EDITING] }); const i18n = setupI18n({ modules: [ModuleName.EDITING] });
initToolbar(i18n); export const $editorToolbar = initToolbar(i18n);

View file

@ -1,14 +1,23 @@
// Copyright: Ankitects Pty Ltd and contributors // Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { editorToolbar, EditorToolbar } from "editor-toolbar";
import { getNotetypeGroup } from "./notetype"; import { getNotetypeGroup } from "./notetype";
import { getFormatInlineGroup } from "./formatInline"; import { getFormatInlineGroup } from "./formatInline";
import { getFormatBlockGroup, getFormatBlockMenus } from "./formatBlock"; import { getFormatBlockGroup, getFormatBlockMenus } from "./formatBlock";
import { getColorGroup } from "./color"; import { getColorGroup } from "./color";
import { getTemplateGroup, getTemplateMenus } from "./template"; import { getTemplateGroup, getTemplateMenus } from "./template";
export function initToolbar(i18n: Promise<void>): void { export function initToolbar(i18n: Promise<void>) {
let toolbarResolve: (value: EditorToolbar) => void;
const toolbarPromise = new Promise<EditorToolbar>((resolve) => {
toolbarResolve = resolve;
});
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
i18n.then(() => { i18n.then(() => {
const target = document.getElementById("editorToolbar")!;
const buttons = [ const buttons = [
getNotetypeGroup(), getNotetypeGroup(),
getFormatInlineGroup(), getFormatInlineGroup(),
@ -19,8 +28,9 @@ export function initToolbar(i18n: Promise<void>): void {
const menus = [...getFormatBlockMenus(), ...getTemplateMenus()]; const menus = [...getFormatBlockMenus(), ...getTemplateMenus()];
globalThis.$editorToolbar.updateButton(() => ({ items: buttons })); toolbarResolve(editorToolbar(target, buttons, menus));
globalThis.$editorToolbar.updateMenu(() => ({ items: menus }));
}); });
}); });
return toolbarPromise;
} }