Merge pull request #957 from hgiesel/fieldsshadow
Implement editor as Web Component
This commit is contained in:
commit
b66bedbc9f
19
qt/aqt/data/web/css/editable.scss
Normal file
19
qt/aqt/data/web/css/editable.scss
Normal file
@ -0,0 +1,19 @@
|
||||
anki-editable {
|
||||
display: block;
|
||||
overflow-wrap: break-word;
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
|
||||
&:empty::after {
|
||||
content: "\a";
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
img.drawing {
|
||||
zoom: 50%;
|
||||
|
||||
.nightMode & {
|
||||
filter: unquote("invert() hue-rotate(180deg)");
|
||||
}
|
||||
}
|
@ -1,12 +1,16 @@
|
||||
/* Copyright: Ankitects Pty Ltd and contributors
|
||||
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
|
||||
|
||||
html {
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
#fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 5px;
|
||||
|
||||
& > * {
|
||||
& > *, & > * > * {
|
||||
margin: 1px 0;
|
||||
|
||||
&:first-child {
|
||||
@ -22,13 +26,9 @@
|
||||
.field {
|
||||
border: 1px solid var(--border);
|
||||
background: var(--frame-bg);
|
||||
padding: 5px;
|
||||
overflow-wrap: break-word;
|
||||
overflow: auto;
|
||||
|
||||
&:empty::after {
|
||||
content: "\A";
|
||||
white-space: pre;
|
||||
&.dupe {
|
||||
background: var(--flag1-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,10 +37,6 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
@ -55,6 +51,8 @@ body {
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
padding: 2px;
|
||||
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.topbuts > * {
|
||||
@ -118,21 +116,26 @@ button.highlighted {
|
||||
}
|
||||
|
||||
#topbutsright & {
|
||||
border-bottom: 3px solid #000;
|
||||
border-bottom: 3px solid black;
|
||||
}
|
||||
|
||||
.nightMode #topbutsright & {
|
||||
border-bottom: 3px solid white;
|
||||
}
|
||||
}
|
||||
|
||||
.dupe {
|
||||
background: var(--flag1-bg);
|
||||
}
|
||||
#dupes {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
|
||||
#dupes a {
|
||||
color: var(--link);
|
||||
}
|
||||
text-align: center;
|
||||
background-color: var(--bg-color);
|
||||
|
||||
.drawing {
|
||||
zoom: 50%;
|
||||
}
|
||||
.nightMode img.drawing {
|
||||
filter: unquote("invert() hue-rotate(180deg)");
|
||||
&.is-inactive {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
/* Copyright: Ankitects Pty Ltd and contributors
|
||||
* License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */
|
||||
|
||||
let currentField = null;
|
||||
let changeTimer = null;
|
||||
let currentNoteId = null;
|
||||
let currentField: EditingArea | null = null;
|
||||
let changeTimer: number | null = null;
|
||||
let currentNoteId: number | null = null;
|
||||
|
||||
declare interface String {
|
||||
format(...args: string[]): string;
|
||||
@ -19,7 +19,7 @@ String.prototype.format = function (...args: string[]): string {
|
||||
};
|
||||
|
||||
function setFGButton(col: string): void {
|
||||
$("#forecolor")[0].style.backgroundColor = col;
|
||||
document.getElementById("forecolor").style.backgroundColor = col;
|
||||
}
|
||||
|
||||
function saveNow(keepFocus: boolean): void {
|
||||
@ -33,7 +33,7 @@ function saveNow(keepFocus: boolean): void {
|
||||
saveField("key");
|
||||
} else {
|
||||
// triggers onBlur, which saves
|
||||
currentField.blur();
|
||||
currentField.blurEditable();
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ interface Selection {
|
||||
function onKey(evt: KeyboardEvent): void {
|
||||
// esc clears focus, allowing dialog to close
|
||||
if (evt.code === "Escape") {
|
||||
currentField.blur();
|
||||
currentField.blurEditable();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -60,28 +60,23 @@ function onKey(evt: KeyboardEvent): void {
|
||||
if (evt.code === "Enter" && !inListItem()) {
|
||||
evt.preventDefault();
|
||||
document.execCommand("insertLineBreak");
|
||||
return;
|
||||
}
|
||||
|
||||
// fix Ctrl+right/left handling in RTL fields
|
||||
if (currentField.dir === "rtl") {
|
||||
const selection = window.getSelection();
|
||||
let granularity = "character";
|
||||
let alter = "move";
|
||||
if (evt.ctrlKey) {
|
||||
granularity = "word";
|
||||
}
|
||||
if (evt.shiftKey) {
|
||||
alter = "extend";
|
||||
}
|
||||
if (evt.code === "ArrowRight") {
|
||||
selection.modify(alter, "right", granularity);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
} else if (evt.code === "ArrowLeft") {
|
||||
selection.modify(alter, "left", granularity);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
// // fix Ctrl+right/left handling in RTL fields
|
||||
if (currentField.isRightToLeft()) {
|
||||
const selection = currentField.getSelection();
|
||||
const granularity = evt.ctrlKey ? "word" : "character";
|
||||
const alter = evt.shiftKey ? "extend" : "move";
|
||||
|
||||
switch (evt.code) {
|
||||
case "ArrowRight":
|
||||
selection.modify(alter, "right", granularity);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
case "ArrowLeft":
|
||||
selection.modify(alter, "left", granularity);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,12 +86,12 @@ function onKey(evt: KeyboardEvent): void {
|
||||
function onKeyUp(evt: KeyboardEvent): void {
|
||||
// Avoid div element on remove
|
||||
if (evt.code === "Enter" || evt.code === "Backspace") {
|
||||
const anchor = window.getSelection().anchorNode;
|
||||
const anchor = currentField.getSelection().anchorNode;
|
||||
|
||||
if (
|
||||
nodeIsElement(anchor) &&
|
||||
anchor.tagName === "DIV" &&
|
||||
!anchor.classList.contains("field") &&
|
||||
!(anchor instanceof EditingArea) &&
|
||||
anchor.childElementCount === 1 &&
|
||||
anchor.children[0].tagName === "BR"
|
||||
) {
|
||||
@ -172,7 +167,7 @@ function nodeIsInline(node: Node): boolean {
|
||||
}
|
||||
|
||||
function inListItem(): boolean {
|
||||
const anchor = window.getSelection().anchorNode;
|
||||
const anchor = currentField.getSelection().anchorNode;
|
||||
|
||||
let inList = false;
|
||||
let n = nodeIsElement(anchor) ? anchor : anchor.parentElement;
|
||||
@ -195,7 +190,7 @@ function insertNewline(): void {
|
||||
// differently. so in such cases we note the height has not
|
||||
// changed and insert an extra newline.
|
||||
|
||||
const r = window.getSelection().getRangeAt(0);
|
||||
const r = currentField.getSelection().getRangeAt(0);
|
||||
if (!r.collapsed) {
|
||||
// delete any currently selected text first, making
|
||||
// sure the delete is undoable
|
||||
@ -211,7 +206,7 @@ function insertNewline(): void {
|
||||
|
||||
// is the cursor in an environment that respects whitespace?
|
||||
function inPreEnvironment(): boolean {
|
||||
const anchor = window.getSelection().anchorNode;
|
||||
const anchor = currentField.getSelection().anchorNode;
|
||||
const n = nodeIsElement(anchor) ? anchor : anchor.parentElement;
|
||||
|
||||
return window.getComputedStyle(n).whiteSpace.startsWith("pre");
|
||||
@ -254,13 +249,14 @@ function clearChangeTimer(): void {
|
||||
}
|
||||
|
||||
function onFocus(evt: FocusEvent): void {
|
||||
const elem = evt.currentTarget as HTMLElement;
|
||||
const elem = evt.currentTarget as EditingArea;
|
||||
if (currentField === elem) {
|
||||
// anki window refocused; current element unchanged
|
||||
return;
|
||||
}
|
||||
elem.focusEditable();
|
||||
currentField = elem;
|
||||
pycmd(`focus:${currentFieldOrdinal()}`);
|
||||
pycmd(`focus:${currentField.ord}`);
|
||||
enableButtons();
|
||||
// do this twice so that there's no flicker on newer versions
|
||||
caretToEnd();
|
||||
@ -284,18 +280,19 @@ function onFocus(evt: FocusEvent): void {
|
||||
}
|
||||
|
||||
function focusField(n: number): void {
|
||||
if (n === null) {
|
||||
return;
|
||||
const field = document.getElementById(`f${n}`) as EditingArea;
|
||||
|
||||
if (field) {
|
||||
field.focusEditable();
|
||||
}
|
||||
$(`#f${n}`).focus();
|
||||
}
|
||||
|
||||
function focusIfField(x: number, y: number): boolean {
|
||||
const elements = document.elementsFromPoint(x, y);
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let elem = elements[i] as HTMLElement;
|
||||
if (elem.classList.contains("field")) {
|
||||
elem.focus();
|
||||
let elem = elements[i] as EditingArea;
|
||||
if (elem instanceof EditingArea) {
|
||||
elem.focusEditable();
|
||||
// the focus event may not fire if the window is not active, so make sure
|
||||
// the current field is set
|
||||
currentField = elem;
|
||||
@ -311,12 +308,12 @@ function onPaste(): void {
|
||||
}
|
||||
|
||||
function caretToEnd(): void {
|
||||
const r = document.createRange();
|
||||
r.selectNodeContents(currentField);
|
||||
r.collapse(false);
|
||||
const s = document.getSelection();
|
||||
s.removeAllRanges();
|
||||
s.addRange(r);
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(currentField.editable);
|
||||
range.collapse(false);
|
||||
const selection = currentField.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
|
||||
function onBlur(): void {
|
||||
@ -334,7 +331,7 @@ function onBlur(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function fieldContainsInlineContent(field: HTMLDivElement): boolean {
|
||||
function containsInlineContent(field: Element): boolean {
|
||||
if (field.childNodes.length === 0) {
|
||||
// for now, for all practical purposes, empty fields are in block mode
|
||||
return false;
|
||||
@ -356,18 +353,7 @@ function saveField(type: "blur" | "key"): void {
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldText =
|
||||
fieldContainsInlineContent(currentField) &&
|
||||
currentField.innerHTML.endsWith("<br>")
|
||||
? // trim trailing <br>
|
||||
currentField.innerHTML.slice(0, -4)
|
||||
: currentField.innerHTML;
|
||||
|
||||
pycmd(`${type}:${currentFieldOrdinal()}:${currentNoteId}:${fieldText}`);
|
||||
}
|
||||
|
||||
function currentFieldOrdinal(): string {
|
||||
return currentField.id.substring(1);
|
||||
pycmd(`${type}:${currentField.ord}:${currentNoteId}:${currentField.fieldHTML}`);
|
||||
}
|
||||
|
||||
function wrappedExceptForWhitespace(text: string, front: string, back: string): string {
|
||||
@ -393,10 +379,10 @@ function enableButtons(): void {
|
||||
|
||||
// disable the buttons if a field is not currently focused
|
||||
function maybeDisableButtons(): void {
|
||||
if (!document.activeElement || document.activeElement.className !== "field") {
|
||||
disableButtons();
|
||||
} else {
|
||||
if (document.activeElement instanceof EditingArea) {
|
||||
enableButtons();
|
||||
} else {
|
||||
disableButtons();
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,7 +396,7 @@ function wrapIntoText(front: string, back: string): void {
|
||||
}
|
||||
|
||||
function wrapInternal(front: string, back: string, plainText: boolean): void {
|
||||
const s = window.getSelection();
|
||||
const s = currentField.getSelection();
|
||||
let r = s.getRangeAt(0);
|
||||
const content = r.cloneContents();
|
||||
const span = document.createElement("span");
|
||||
@ -437,41 +423,198 @@ function onCutOrCopy(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
function createField(
|
||||
index: number,
|
||||
label: string,
|
||||
color: string,
|
||||
content: string
|
||||
): [HTMLDivElement, HTMLDivElement] {
|
||||
const name = document.createElement("div");
|
||||
name.id = `name${index}`;
|
||||
name.className = "fname";
|
||||
class Editable extends HTMLElement {
|
||||
set fieldHTML(content: string) {
|
||||
this.innerHTML = content;
|
||||
|
||||
const fieldname = document.createElement("span");
|
||||
fieldname.className = "fieldname";
|
||||
fieldname.innerText = label;
|
||||
name.appendChild(fieldname);
|
||||
|
||||
const field = document.createElement("div");
|
||||
field.id = `f${index}`;
|
||||
field.className = "field";
|
||||
field.setAttribute("contenteditable", "true");
|
||||
field.style.color = color;
|
||||
field.addEventListener("keydown", onKey);
|
||||
field.addEventListener("keyup", onKeyUp);
|
||||
field.addEventListener("input", onInput);
|
||||
field.addEventListener("focus", onFocus);
|
||||
field.addEventListener("blur", onBlur);
|
||||
field.addEventListener("paste", onPaste);
|
||||
field.addEventListener("copy", onCutOrCopy);
|
||||
field.addEventListener("oncut", onCutOrCopy);
|
||||
field.innerHTML = content;
|
||||
|
||||
if (fieldContainsInlineContent(field)) {
|
||||
field.appendChild(document.createElement("br"));
|
||||
if (containsInlineContent(this)) {
|
||||
this.appendChild(document.createElement("br"));
|
||||
}
|
||||
}
|
||||
|
||||
return [name, field];
|
||||
get fieldHTML(): string {
|
||||
return containsInlineContent(this) && this.innerHTML.endsWith("<br>")
|
||||
? this.innerHTML.slice(0, -4) // trim trailing <br>
|
||||
: this.innerHTML;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.setAttribute("contenteditable", "");
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("anki-editable", Editable);
|
||||
|
||||
class EditingArea extends HTMLDivElement {
|
||||
editable: Editable;
|
||||
baseStyle: HTMLStyleElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: "open" });
|
||||
this.className = "field";
|
||||
|
||||
const rootStyle = document.createElement("link");
|
||||
rootStyle.setAttribute("rel", "stylesheet");
|
||||
rootStyle.setAttribute("href", "./_anki/css/editable.css");
|
||||
this.shadowRoot.appendChild(rootStyle);
|
||||
|
||||
this.baseStyle = document.createElement("style");
|
||||
this.baseStyle.setAttribute("rel", "stylesheet");
|
||||
this.shadowRoot.appendChild(this.baseStyle);
|
||||
|
||||
this.editable = document.createElement("anki-editable") as Editable;
|
||||
this.shadowRoot.appendChild(this.editable);
|
||||
}
|
||||
|
||||
get ord(): number {
|
||||
return Number(this.getAttribute("ord"));
|
||||
}
|
||||
|
||||
set fieldHTML(content: string) {
|
||||
this.editable.fieldHTML = content;
|
||||
}
|
||||
|
||||
get fieldHTML(): string {
|
||||
return this.editable.fieldHTML;
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
this.addEventListener("keydown", onKey);
|
||||
this.addEventListener("keyup", onKeyUp);
|
||||
this.addEventListener("input", onInput);
|
||||
this.addEventListener("focus", onFocus);
|
||||
this.addEventListener("blur", onBlur);
|
||||
this.addEventListener("paste", onPaste);
|
||||
this.addEventListener("copy", onCutOrCopy);
|
||||
this.addEventListener("oncut", onCutOrCopy);
|
||||
|
||||
const baseStyleSheet = this.baseStyle.sheet as CSSStyleSheet;
|
||||
baseStyleSheet.insertRule("anki-editable {}", 0);
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
this.removeEventListener("keydown", onKey);
|
||||
this.removeEventListener("keyup", onKeyUp);
|
||||
this.removeEventListener("input", onInput);
|
||||
this.removeEventListener("focus", onFocus);
|
||||
this.removeEventListener("blur", onBlur);
|
||||
this.removeEventListener("paste", onPaste);
|
||||
this.removeEventListener("copy", onCutOrCopy);
|
||||
this.removeEventListener("oncut", onCutOrCopy);
|
||||
}
|
||||
|
||||
initialize(color: string, content: string): void {
|
||||
this.setBaseColor(color);
|
||||
this.editable.fieldHTML = content;
|
||||
}
|
||||
|
||||
setBaseColor(color: string): void {
|
||||
const styleSheet = this.baseStyle.sheet as CSSStyleSheet;
|
||||
const firstRule = styleSheet.cssRules[0] as CSSStyleRule;
|
||||
firstRule.style.color = color;
|
||||
}
|
||||
|
||||
setBaseStyling(fontFamily: string, fontSize: string, direction: string): void {
|
||||
const styleSheet = this.baseStyle.sheet as CSSStyleSheet;
|
||||
const firstRule = styleSheet.cssRules[0] as CSSStyleRule;
|
||||
firstRule.style.fontFamily = fontFamily;
|
||||
firstRule.style.fontSize = fontSize;
|
||||
firstRule.style.direction = direction;
|
||||
}
|
||||
|
||||
isRightToLeft(): boolean {
|
||||
return this.editable.style.direction === "rtl";
|
||||
}
|
||||
|
||||
getSelection(): Selection {
|
||||
return this.shadowRoot.getSelection();
|
||||
}
|
||||
|
||||
focusEditable(): void {
|
||||
this.editable.focus();
|
||||
}
|
||||
|
||||
blurEditable(): void {
|
||||
this.editable.blur();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("anki-editing-area", EditingArea, { extends: "div" });
|
||||
|
||||
class EditorField extends HTMLDivElement {
|
||||
labelContainer: HTMLDivElement;
|
||||
label: HTMLSpanElement;
|
||||
editingArea: EditingArea;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.labelContainer = document.createElement("div");
|
||||
this.labelContainer.className = "fname";
|
||||
this.appendChild(this.labelContainer);
|
||||
|
||||
this.label = document.createElement("span");
|
||||
this.label.className = "fieldname";
|
||||
this.labelContainer.appendChild(this.label);
|
||||
|
||||
this.editingArea = document.createElement("div", {
|
||||
is: "anki-editing-area",
|
||||
}) as EditingArea;
|
||||
this.appendChild(this.editingArea);
|
||||
}
|
||||
|
||||
static get observedAttributes(): string[] {
|
||||
return ["ord"];
|
||||
}
|
||||
|
||||
set ord(n: number) {
|
||||
this.setAttribute("ord", String(n));
|
||||
}
|
||||
|
||||
attributeChangedCallback(name: string, _oldValue: string, newValue: string): void {
|
||||
switch (name) {
|
||||
case "ord":
|
||||
this.editingArea.setAttribute("ord", newValue);
|
||||
}
|
||||
}
|
||||
|
||||
initialize(label: string, color: string, content: string): void {
|
||||
this.label.innerText = label;
|
||||
this.editingArea.initialize(color, content);
|
||||
}
|
||||
|
||||
setBaseStyling(fontFamily: string, fontSize: string, direction: string): void {
|
||||
this.editingArea.setBaseStyling(fontFamily, fontSize, direction);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("anki-editor-field", EditorField, { extends: "div" });
|
||||
|
||||
function adjustFieldAmount(amount: number): void {
|
||||
const fieldsContainer = document.getElementById("fields");
|
||||
|
||||
while (fieldsContainer.childElementCount < amount) {
|
||||
const newField = document.createElement("div", {
|
||||
is: "anki-editor-field",
|
||||
}) as EditorField;
|
||||
newField.ord = fieldsContainer.childElementCount;
|
||||
fieldsContainer.appendChild(newField);
|
||||
}
|
||||
|
||||
while (fieldsContainer.childElementCount > amount) {
|
||||
fieldsContainer.removeChild(fieldsContainer.lastElementChild);
|
||||
}
|
||||
}
|
||||
|
||||
function forEditorField<T>(
|
||||
values: T[],
|
||||
func: (field: EditorField, value: T) => void
|
||||
): void {
|
||||
const fields = document.getElementById("fields").children;
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const field = fields[i] as EditorField;
|
||||
func(field, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function setFields(fields: [string, string][]): void {
|
||||
@ -481,49 +624,33 @@ function setFields(fields: [string, string][]): void {
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--text-fg");
|
||||
|
||||
const elements = fields.flatMap(([name, fieldcontent], index: number) =>
|
||||
createField(index, name, color, fieldcontent)
|
||||
adjustFieldAmount(fields.length);
|
||||
forEditorField(fields, (field, [name, fieldContent]) =>
|
||||
field.initialize(name, color, fieldContent)
|
||||
);
|
||||
|
||||
const fieldsContainer = document.getElementById("fields");
|
||||
// can be replaced with ParentNode.replaceChildren in Chrome 86+
|
||||
while (fieldsContainer.firstChild) {
|
||||
fieldsContainer.removeChild(fieldsContainer.firstChild);
|
||||
}
|
||||
for (const element of elements) {
|
||||
fieldsContainer.appendChild(element);
|
||||
}
|
||||
|
||||
maybeDisableButtons();
|
||||
}
|
||||
|
||||
function setBackgrounds(cols: "dupe"[]) {
|
||||
for (let i = 0; i < cols.length; i++) {
|
||||
const element = document.querySelector(`#f${i}`);
|
||||
element.classList.toggle("dupe", cols[i] === "dupe");
|
||||
}
|
||||
function setBackgrounds(cols: ("dupe" | "")[]) {
|
||||
forEditorField(cols, (field, value) =>
|
||||
field.editingArea.classList.toggle("dupe", value === "dupe")
|
||||
);
|
||||
document
|
||||
.querySelector("#dupes")
|
||||
.classList.toggle("is-inactive", !cols.includes("dupe"));
|
||||
}
|
||||
|
||||
function setFonts(fonts: [string, number, boolean][]): void {
|
||||
for (let i = 0; i < fonts.length; i++) {
|
||||
const n = $(`#f${i}`);
|
||||
n.css("font-family", fonts[i][0]).css("font-size", fonts[i][1]);
|
||||
n[0].dir = fonts[i][2] ? "rtl" : "ltr";
|
||||
}
|
||||
forEditorField(fonts, (field, [fontFamily, fontSize, isRtl]) => {
|
||||
field.setBaseStyling(fontFamily, `${fontSize}px`, isRtl ? "rtl" : "ltr");
|
||||
});
|
||||
}
|
||||
|
||||
function setNoteId(id: number): void {
|
||||
currentNoteId = id;
|
||||
}
|
||||
|
||||
function showDupes(): void {
|
||||
$("#dupes").show();
|
||||
}
|
||||
|
||||
function hideDupes(): void {
|
||||
$("#dupes").hide();
|
||||
}
|
||||
|
||||
let pasteHTML = function (
|
||||
html: string,
|
||||
internal: boolean,
|
||||
|
@ -73,8 +73,9 @@ audio = (
|
||||
|
||||
_html = """
|
||||
<style>
|
||||
html { background: %s; }
|
||||
#topbutsOuter { background: %s; }
|
||||
:root {
|
||||
--bg-color: %s;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<div id="topbutsOuter">
|
||||
@ -82,11 +83,9 @@ html { background: %s; }
|
||||
</div>
|
||||
<div id="fields">
|
||||
</div>
|
||||
</div>
|
||||
<div id="dupes" style="display:none;">
|
||||
<a href="#" onclick="pycmd('dupes');return false;">
|
||||
%s
|
||||
</a>
|
||||
<div id="dupes" class="is-inactive">
|
||||
<a href="#" onclick="pycmd('dupes');return false;">%s</a>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
@ -219,7 +218,7 @@ class Editor:
|
||||
bgcol = self.mw.app.palette().window().color().name() # type: ignore
|
||||
# then load page
|
||||
self.web.stdHtml(
|
||||
_html % (bgcol, bgcol, topbuts, tr(TR.EDITING_SHOW_DUPLICATES)),
|
||||
_html % (bgcol, topbuts, tr(TR.EDITING_SHOW_DUPLICATES)),
|
||||
css=["css/editor.css"],
|
||||
js=["js/vendor/jquery.min.js", "js/editor.js"],
|
||||
context=self,
|
||||
@ -505,7 +504,7 @@ class Editor:
|
||||
self.web.setFocus()
|
||||
gui_hooks.editor_did_load_note(self)
|
||||
|
||||
js = "setFields(%s); setFonts(%s); focusField(%s); setNoteId(%s)" % (
|
||||
js = "setFields(%s); setFonts(%s); focusField(%s); setNoteId(%s);" % (
|
||||
json.dumps(data),
|
||||
json.dumps(self.fonts()),
|
||||
json.dumps(focusTo),
|
||||
@ -534,9 +533,7 @@ class Editor:
|
||||
err = self.note.dupeOrEmpty()
|
||||
if err == 2:
|
||||
cols[0] = "dupe"
|
||||
self.web.eval("showDupes();")
|
||||
else:
|
||||
self.web.eval("hideDupes();")
|
||||
|
||||
self.web.eval("setBackgrounds(%s);" % json.dumps(cols))
|
||||
|
||||
def showDupes(self):
|
||||
|
Loading…
Reference in New Issue
Block a user