Utilize enableButtons disableButtons from Svelte component

This commit is contained in:
Henrik Giesel 2021-03-30 06:14:00 +02:00
parent 9340d20c20
commit a820059b8f
13 changed files with 71 additions and 95 deletions

View file

@ -82,7 +82,7 @@ _html = """
} }
</style> </style>
<div> <div>
<anki-editor-toolbar></anki-editor-toolbar> <anki-editor-toolbar id="editorToolbar"></anki-editor-toolbar>
<div id="fields"> <div id="fields">
</div> </div>
<div id="dupes" class="is-inactive"> <div id="dupes" class="is-inactive">
@ -1113,6 +1113,7 @@ class Editor:
dupes=showDupes, dupes=showDupes,
paste=onPaste, paste=onPaste,
cutOrCopy=onCutOrCopy, cutOrCopy=onCutOrCopy,
htmlEdit=onHtmlEdit,
) )

View file

@ -77,7 +77,7 @@ esbuild(
"//ts:protobuf-shim.js", "//ts:protobuf-shim.js",
], ],
args = [ args = [
"--global-name=anki", "--global-name=editorToolbar",
"--inject:ts/protobuf-shim.js", "--inject:ts/protobuf-shim.js",
"--loader:.svg=text", "--loader:.svg=text",
], ],

View file

@ -3,7 +3,7 @@
export const commandMap = writable(new Map<string, boolean>()); export const commandMap = writable(new Map<string, boolean>());
function updateButton(key: string): void { function initializeButton(key: string): void {
commandMap.update( commandMap.update(
(map: Map<string, boolean>): Map<string, boolean> => (map: Map<string, boolean>): Map<string, boolean> =>
new Map([...map, [key, document.queryCommandState(key)]]) new Map([...map, [key, document.queryCommandState(key)]])
@ -24,11 +24,11 @@
); );
} }
export function updateButtonActive() { export function updateActiveButtons() {
updateButtons((key: string): boolean => document.queryCommandState(key)); updateButtons((key: string): boolean => document.queryCommandState(key));
} }
export function clearButtonActive() { export function clearActiveButtons() {
updateButtons((): boolean => false); updateButtons((): boolean => false);
} }
</script> </script>
@ -44,7 +44,7 @@
let active = false; let active = false;
if (activatable) { if (activatable) {
updateButton(command); initializeButton(command);
commandMap.subscribe((map: Record<string, boolean>): void => { commandMap.subscribe((map: Record<string, boolean>): void => {
active = map.get(command); active = map.get(command);

View file

@ -1,11 +1,17 @@
<script lang="typescript"> <script lang="typescript">
import type { Readable } from "svelte/store";
import { setContext } from "svelte";
import { disabledKey, nightModeKey } from "./contextKeys";
import ButtonGroup from "./ButtonGroup.svelte"; import ButtonGroup from "./ButtonGroup.svelte";
import type { Buttons } from "./ButtonGroup.svelte"; import type { Buttons } from "./ButtonGroup.svelte";
export let buttons: Buttons = []; export let buttons: Buttons = [];
export let nightMode: boolean; export let nightMode: boolean;
export let disabled: Readable<boolean> = false;
console.log(nightMode); setContext(disabledKey, disabled);
setContext(nightModeKey, nightMode);
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -1,8 +1,14 @@
<script lang="typescript"> <script lang="typescript">
import { getContext } from "svelte";
import type { Readable } from "svelte/store";
import { disabledKey } from "./contextKeys";
export let className: string = ""; export let className: string = "";
export let onClick: (event: ClickEvent) => void; export let onClick: (event: ClickEvent) => void;
export let active = false; export let active = false;
export let disabled = false;
const disabledStore = getContext(disabledKey)
$: disabled = $disabledStore;
</script> </script>
<style lang="scss"> <style lang="scss">
@ -16,6 +22,7 @@
& > :global(svg), & > :global(svg),
& > :global(img) { & > :global(img) {
fill: currentColor;
vertical-align: unset; vertical-align: unset;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -38,6 +45,7 @@
&[disabled] { &[disabled] {
opacity: 0.4; opacity: 0.4;
cursor: not-allowed;
&:hover { &:hover {
background-color: white; background-color: white;

View file

@ -0,0 +1,2 @@
export let nightModeKey = Symbol("nightMode");
export let disabledKey = Symbol("disabled");

View file

@ -1,5 +1,5 @@
// @ts-ignore // @ts-ignore
import CommandIconButton, { updateButtonActive } from "./CommandIconButton.svelte"; import CommandIconButton from "./CommandIconButton.svelte";
import boldIcon from "./type-bold.svg"; import boldIcon from "./type-bold.svg";
import italicIcon from "./type-italic.svg"; import italicIcon from "./type-italic.svg";
import underlineIcon from "./type-underline.svg"; import underlineIcon from "./type-underline.svg";
@ -43,6 +43,3 @@ export const eraserButton = {
command: "removeFormat", command: "removeFormat",
highlightable: false, highlightable: false,
}; };
// TODO
setInterval(updateButtonActive, 2000);

View file

@ -1,9 +1,16 @@
import type { SvelteComponent } from "svelte"; import type { SvelteComponent } from "svelte";
import { checkNightMode } from "anki/nightmode"; import { checkNightMode } from "anki/nightmode";
import { setupI18n, ModuleName } from "anki/i18n";
import EditorToolbarSvelte from "./EditorToolbar.svelte"; import EditorToolbarSvelte from "./EditorToolbar.svelte";
import LabelButton from "./LabelButton.svelte"; import LabelButton from "./LabelButton.svelte";
// @ts-ignore
export { updateActiveButtons, clearActiveButtons } from "./CommandIconButton.svelte";
import { Writable, writable } from "svelte/store";
import { import {
boldButton, boldButton,
italicButton, italicButton,
@ -42,17 +49,29 @@ const defaultButtons = [
class EditorToolbar extends HTMLElement { class EditorToolbar extends HTMLElement {
component?: SvelteComponent; component?: SvelteComponent;
disabled?: Writable<boolean>;
connectedCallback(): void { connectedCallback(): void {
const nightMode = checkNightMode(); this.disabled = writable(false);
setupI18n({ modules: [ModuleName.STATISTICS, ModuleName.SCHEDULING] }).then(() => {
this.component = new EditorToolbarSvelte({ this.component = new EditorToolbarSvelte({
target: this, target: this,
props: { props: {
nightMode,
buttons: defaultButtons, buttons: defaultButtons,
nightMode: checkNightMode(),
disabled: this.disabled,
}, },
}); });
})
}
enableButtons(): void {
this.disabled?.set(false);
}
disableButtons(): void {
this.disabled?.set(true);
} }
} }

View file

@ -6,7 +6,6 @@ import type { Editable } from "./editable";
import { bridgeCommand } from "./lib"; import { bridgeCommand } from "./lib";
import { onInput, onKey, onKeyUp } from "./inputHandlers"; import { onInput, onKey, onKeyUp } from "./inputHandlers";
import { onFocus, onBlur } from "./focusHandlers"; import { onFocus, onBlur } from "./focusHandlers";
import { updateButtonState } from "./toolbar";
function onPaste(evt: ClipboardEvent): void { function onPaste(evt: ClipboardEvent): void {
bridgeCommand("paste"); bridgeCommand("paste");
@ -60,7 +59,8 @@ export class EditingArea extends HTMLDivElement {
this.addEventListener("paste", onPaste); this.addEventListener("paste", onPaste);
this.addEventListener("copy", onCutOrCopy); this.addEventListener("copy", onCutOrCopy);
this.addEventListener("oncut", onCutOrCopy); this.addEventListener("oncut", onCutOrCopy);
this.addEventListener("mouseup", updateButtonState); // @ts-ignore
this.addEventListener("mouseup", editorToolbar.updateActiveButtons);
const baseStyleSheet = this.baseStyle.sheet as CSSStyleSheet; const baseStyleSheet = this.baseStyle.sheet as CSSStyleSheet;
baseStyleSheet.insertRule("anki-editable {}", 0); baseStyleSheet.insertRule("anki-editable {}", 0);
@ -75,7 +75,8 @@ export class EditingArea extends HTMLDivElement {
this.removeEventListener("paste", onPaste); this.removeEventListener("paste", onPaste);
this.removeEventListener("copy", onCutOrCopy); this.removeEventListener("copy", onCutOrCopy);
this.removeEventListener("oncut", onCutOrCopy); this.removeEventListener("oncut", onCutOrCopy);
this.removeEventListener("mouseup", updateButtonState); // @ts-ignore
this.removeEventListener("mouseup", editorToolbar.updateActiveButtons);
} }
initialize(color: string, content: string): void { initialize(color: string, content: string): void {

View file

@ -5,13 +5,13 @@ import type { EditingArea } from "./editingArea";
import { saveField } from "./changeTimer"; import { saveField } from "./changeTimer";
import { bridgeCommand } from "./lib"; import { bridgeCommand } from "./lib";
import { enableButtons, disableButtons } from "./toolbar";
export function onFocus(evt: FocusEvent): void { export function onFocus(evt: FocusEvent): void {
const currentField = evt.currentTarget as EditingArea; const currentField = evt.currentTarget as EditingArea;
currentField.focusEditable(); currentField.focusEditable();
bridgeCommand(`focus:${currentField.ord}`); bridgeCommand(`focus:${currentField.ord}`);
enableButtons(); // @ts-ignore
document.getElementById("editorToolbar").enableButtons();
} }
export function onBlur(evt: FocusEvent): void { export function onBlur(evt: FocusEvent): void {
@ -19,5 +19,6 @@ export function onBlur(evt: FocusEvent): void {
const currentFieldUnchanged = previousFocus === document.activeElement; const currentFieldUnchanged = previousFocus === document.activeElement;
saveField(previousFocus, currentFieldUnchanged ? "key" : "blur"); saveField(previousFocus, currentFieldUnchanged ? "key" : "blur");
disableButtons(); // @ts-ignore
document.getElementById("editorToolbar").disableButtons();
} }

View file

@ -5,7 +5,6 @@ import { filterHTML } from "html-filter";
import { caretToEnd } from "./helpers"; import { caretToEnd } from "./helpers";
import { saveField } from "./changeTimer"; import { saveField } from "./changeTimer";
import { updateButtonState, disableButtons } from "./toolbar";
import { EditorField } from "./editorField"; import { EditorField } from "./editorField";
import { LabelContainer } from "./labelContainer"; import { LabelContainer } from "./labelContainer";
@ -13,7 +12,6 @@ import { EditingArea } from "./editingArea";
import { Editable } from "./editable"; import { Editable } from "./editable";
export { setNoteId, getNoteId } from "./noteId"; export { setNoteId, getNoteId } from "./noteId";
export { preventButtonFocus, toggleEditorButton, setFGButton } from "./toolbar";
export { saveNow } from "./changeTimer"; export { saveNow } from "./changeTimer";
export { wrap, wrapIntoText } from "./wrap"; export { wrap, wrapIntoText } from "./wrap";
@ -43,7 +41,8 @@ export function focusField(n: number): void {
if (field) { if (field) {
field.editingArea.focusEditable(); field.editingArea.focusEditable();
caretToEnd(field.editingArea); caretToEnd(field.editingArea);
updateButtonState(); // @ts-ignore
editorToolbar.updateActiveButtons();
} }
} }
@ -123,7 +122,8 @@ export function setFields(fields: [string, string][]): void {
if (!getCurrentField()) { if (!getCurrentField()) {
// when initial focus of the window is not on editor (e.g. browser) // when initial focus of the window is not on editor (e.g. browser)
disableButtons(); // @ts-ignore
document.getElementById("editorToolbar").disableButtons();
} }
} }
@ -158,6 +158,7 @@ export function setFormat(cmd: string, arg?: any, nosave: boolean = false): void
document.execCommand(cmd, false, arg); document.execCommand(cmd, false, arg);
if (!nosave) { if (!nosave) {
saveField(getCurrentField() as EditingArea, "key"); saveField(getCurrentField() as EditingArea, "key");
updateButtonState(); // @ts-ignore
editorToolbar.updateActiveButtons();
} }
} }

View file

@ -4,7 +4,6 @@
import { EditingArea } from "./editingArea"; import { EditingArea } from "./editingArea";
import { caretToEnd, nodeIsElement } from "./helpers"; import { caretToEnd, nodeIsElement } from "./helpers";
import { triggerChangeTimer } from "./changeTimer"; import { triggerChangeTimer } from "./changeTimer";
import { updateButtonState } from "./toolbar";
function inListItem(currentField: EditingArea): boolean { function inListItem(currentField: EditingArea): boolean {
const anchor = currentField.getSelection()!.anchorNode!; const anchor = currentField.getSelection()!.anchorNode!;
@ -22,7 +21,8 @@ function inListItem(currentField: EditingArea): boolean {
export function onInput(event: Event): void { export function onInput(event: Event): void {
// make sure IME changes get saved // make sure IME changes get saved
triggerChangeTimer(event.currentTarget as EditingArea); triggerChangeTimer(event.currentTarget as EditingArea);
updateButtonState(); // @ts-ignore
editorToolbar.updateActiveButtons();
} }
export function onKey(evt: KeyboardEvent): void { export function onKey(evt: KeyboardEvent): void {
@ -69,7 +69,8 @@ globalThis.addEventListener("keydown", (evt: KeyboardEvent) => {
const newFocusTarget = evt.target; const newFocusTarget = evt.target;
if (newFocusTarget instanceof EditingArea) { if (newFocusTarget instanceof EditingArea) {
caretToEnd(newFocusTarget); caretToEnd(newFocusTarget);
updateButtonState(); // @ts-ignore
editorToolbar.updateActiveButtons();
} }
}, },
{ once: true } { once: true }

View file

@ -1,61 +0,0 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const highlightButtons = ["bold", "italic", "underline", "superscript", "subscript"];
export function updateButtonState(): void {
for (const name of highlightButtons) {
const elem = document.querySelector(`#${name}`) as HTMLElement;
elem.classList.toggle("highlighted", document.queryCommandState(name));
}
// fixme: forecolor
// 'col': document.queryCommandValue("forecolor")
}
function clearButtonHighlight(): void {
for (const name of highlightButtons) {
const elem = document.querySelector(`#${name}`) as HTMLElement;
elem.classList.remove("highlighted");
}
}
export function preventButtonFocus(): void {
for (const element of document.querySelectorAll("button.linkb")) {
element.addEventListener("mousedown", (evt: Event) => {
evt.preventDefault();
});
}
}
export function enableButtons(): void {
const buttons = document.querySelectorAll(
"button.linkb"
) as NodeListOf<HTMLButtonElement>;
buttons.forEach((elem: HTMLButtonElement): void => {
elem.disabled = false;
});
updateButtonState();
}
export function disableButtons(): void {
const buttons = document.querySelectorAll(
"button.linkb:not(.perm)"
) as NodeListOf<HTMLButtonElement>;
buttons.forEach((elem: HTMLButtonElement): void => {
elem.disabled = true;
});
clearButtonHighlight();
}
export function setFGButton(col: string): void {
document.getElementById("forecolor")!.style.backgroundColor = col;
}
export function toggleEditorButton(buttonOrId: string | HTMLElement): void {
const button =
typeof buttonOrId === "string"
? (document.getElementById(buttonOrId) as HTMLElement)
: buttonOrId;
button.classList.toggle("highlighted");
}