406 lines
12 KiB
TypeScript
406 lines
12 KiB
TypeScript
import { config } from "../../../package.json";
|
|
import { ICONS } from "../../utils/config";
|
|
import { showHint } from "../../utils/hint";
|
|
import { getString } from "../../utils/locale";
|
|
import { getPref, setPref } from "../../utils/prefs";
|
|
import { waitUtilAsync } from "../../utils/wait";
|
|
// TODO: uncouple these imports
|
|
import {} from "./content";
|
|
import { messageHandler } from "./message";
|
|
|
|
export const TAB_TYPE = "betternotes";
|
|
|
|
export function registerWorkspaceTab() {
|
|
const tabContainer = document.querySelector("#tab-bar-container");
|
|
if (!tabContainer) {
|
|
return;
|
|
}
|
|
tabContainer.removeAttribute("hidden");
|
|
const mut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
|
tabContainer.removeAttribute("hidden");
|
|
});
|
|
mut.observe(tabContainer, {
|
|
attributes: true,
|
|
attributeFilter: ["hidden"],
|
|
});
|
|
waitUtilAsync(() =>
|
|
Boolean(ztoolkit.getGlobal("ZoteroContextPane")._notifierID),
|
|
).then(() => {
|
|
addWorkspaceTab();
|
|
});
|
|
window.addEventListener("message", (e) => messageHandler(e), false);
|
|
}
|
|
|
|
export function unregisterWorkspaceTab() {
|
|
addon.data.workspace.tab.id && Zotero_Tabs.close(addon.data.workspace.tab.id);
|
|
}
|
|
|
|
async function addWorkspaceTab() {
|
|
const { id, container } = Zotero_Tabs.add({
|
|
type: TAB_TYPE,
|
|
title: getString("tab.name"),
|
|
index: 1,
|
|
data: {
|
|
itemID: addon.data.workspace.mainId,
|
|
},
|
|
select: false,
|
|
onClose: () => {
|
|
setWorkspaceTabStatus(false);
|
|
if (addon.data.alive) {
|
|
addWorkspaceTab();
|
|
}
|
|
},
|
|
});
|
|
await waitUtilAsync(() =>
|
|
Boolean(document.querySelector(`.tabs-wrapper .tab[data-id=${id}]`)),
|
|
);
|
|
const tabElem = document.querySelector(
|
|
`.tabs-wrapper .tab[data-id=${id}]`,
|
|
) as HTMLDivElement;
|
|
tabElem.style.width = "30px";
|
|
tabElem.style.minWidth = "30px";
|
|
tabElem.style.maxWidth = "30px";
|
|
tabElem.style.padding = "0px";
|
|
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
|
|
const close = tabElem.querySelector(".tab-close") as HTMLDivElement;
|
|
content.style.verticalAlign = "middle";
|
|
content.style.width = "20px";
|
|
content.style.height = "20px";
|
|
content.style.display = "inline";
|
|
content.innerHTML = "";
|
|
ztoolkit.UI.appendElement(
|
|
{
|
|
tag: "span",
|
|
classList: ["icon-bg"],
|
|
styles: {
|
|
backgroundImage: `url("chrome://${config.addonRef}/content/icons/favicon.png")`,
|
|
},
|
|
},
|
|
content,
|
|
);
|
|
close.style.visibility = "hidden";
|
|
addon.data.workspace.tab.id = id;
|
|
container.setAttribute("workspace-type", "tab");
|
|
addon.data.workspace.tab.container = container;
|
|
}
|
|
|
|
function hoverWorkspaceTab(hovered: boolean) {
|
|
Array.from(document.querySelectorAll(".tab-toggle")).forEach((elem) => {
|
|
(elem as HTMLDivElement).style.visibility = hovered ? "visible" : "hidden";
|
|
});
|
|
const tabElem = document.querySelector(
|
|
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`,
|
|
) as HTMLDivElement;
|
|
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
|
|
content.removeAttribute("style");
|
|
if (hovered) {
|
|
if (ztoolkit.isZotero7()) {
|
|
content.style["-moz-box-pack" as any] = "start";
|
|
} else {
|
|
content.style.position = "absolute";
|
|
content.style.left = "22px";
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateWorkspaceTabToggleButton(
|
|
type: "outline" | "preview" | "notes",
|
|
state: "open" | "collapsed",
|
|
) {
|
|
const elem = document.querySelector(
|
|
`#betternotes-tab-toggle-${type}`,
|
|
) as HTMLDivElement;
|
|
if (!elem) {
|
|
return;
|
|
}
|
|
if (state !== "collapsed") {
|
|
state = "open";
|
|
}
|
|
elem.innerHTML = ICONS[`workspace_${type}_${state}`];
|
|
}
|
|
|
|
function registerWorkspaceTabPaneObserver() {
|
|
const outlineSplitter = document.querySelector(
|
|
"#betternotes-workspace-outline-splitter",
|
|
);
|
|
const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
|
updateWorkspaceTabToggleButton(
|
|
"outline",
|
|
outlineSplitter!.getAttribute("state")! as "open" | "collapsed",
|
|
);
|
|
});
|
|
outlineMut.observe(outlineSplitter!, {
|
|
attributes: true,
|
|
attributeFilter: ["state"],
|
|
});
|
|
const previewSplitter = document.querySelector(
|
|
"#betternotes-workspace-preview-splitter",
|
|
);
|
|
const previeweMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
|
updateWorkspaceTabToggleButton(
|
|
"preview",
|
|
previewSplitter!.getAttribute("state")! as "open" | "collapsed",
|
|
);
|
|
});
|
|
previeweMut.observe(previewSplitter!, {
|
|
attributes: true,
|
|
attributeFilter: ["state"],
|
|
});
|
|
const notesSplitter = document.querySelector("#zotero-context-splitter");
|
|
const notesMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => {
|
|
updateWorkspaceTabToggleButton(
|
|
"notes",
|
|
notesSplitter!.getAttribute("state")! as "open" | "collapsed",
|
|
);
|
|
});
|
|
notesMut.observe(notesSplitter!, {
|
|
attributes: true,
|
|
attributeFilter: ["state"],
|
|
});
|
|
}
|
|
|
|
function isContextPaneInitialized() {
|
|
return (
|
|
(document.querySelector(".notes-pane-deck")?.childElementCount || 0) > 0
|
|
);
|
|
}
|
|
|
|
export async function activateWorkspaceTab() {
|
|
if (Zotero_Tabs.selectedType === TAB_TYPE && isContextPaneInitialized()) {
|
|
(
|
|
document.querySelector("#zotero-tab-toolbar") as XUL.Box
|
|
).style.visibility = "collapse";
|
|
const toolbar = document.querySelector(
|
|
"#zotero-context-toolbar-extension",
|
|
) as XUL.Box;
|
|
toolbar.style.visibility = "collapse";
|
|
toolbar.nextElementSibling?.setAttribute("selectedIndex", "1");
|
|
}
|
|
|
|
if (addon.data.workspace.tab.active) {
|
|
ztoolkit.log("workspace tab is already active");
|
|
return;
|
|
}
|
|
setWorkspaceTabStatus(true);
|
|
// reset tab style
|
|
await waitUtilAsync(() =>
|
|
Boolean(
|
|
document.querySelector(
|
|
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`,
|
|
),
|
|
),
|
|
);
|
|
const tabElem = document.querySelector(
|
|
`.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`,
|
|
) as HTMLDivElement;
|
|
tabElem.removeAttribute("style");
|
|
const content = tabElem.querySelector(".tab-name") as HTMLDivElement;
|
|
const close = tabElem.querySelector(".tab-close") as HTMLDivElement;
|
|
content.removeAttribute("style");
|
|
content.append(document.createTextNode(getString("tab.name")));
|
|
close.style.removeProperty("visibility");
|
|
ztoolkit.UI.insertElementBefore(
|
|
{
|
|
tag: "fragment",
|
|
children: [
|
|
{
|
|
tag: "div",
|
|
id: "betternotes-tab-toggle-outline",
|
|
classList: ["tab-close", "tab-toggle"],
|
|
styles: {
|
|
right: "56px",
|
|
},
|
|
properties: {
|
|
innerHTML: ICONS.workspace_outline_open,
|
|
},
|
|
listeners: [
|
|
{
|
|
type: "click",
|
|
listener: (ev) => {
|
|
addon.hooks.onToggleWorkspacePane(
|
|
"outline",
|
|
undefined,
|
|
addon.data.workspace.tab.container,
|
|
);
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
tag: "div",
|
|
id: "betternotes-tab-toggle-preview",
|
|
classList: ["tab-close", "tab-toggle"],
|
|
styles: {
|
|
right: "40px",
|
|
},
|
|
properties: {
|
|
innerHTML: ICONS.workspace_preview_collapsed,
|
|
},
|
|
listeners: [
|
|
{
|
|
type: "click",
|
|
listener: (ev) => {
|
|
addon.hooks.onToggleWorkspacePane(
|
|
"preview",
|
|
undefined,
|
|
addon.data.workspace.tab.container,
|
|
);
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
tag: "div",
|
|
id: "betternotes-tab-toggle-notes",
|
|
classList: ["tab-close", "tab-toggle"],
|
|
styles: {
|
|
right: "24px",
|
|
},
|
|
properties: {
|
|
innerHTML:
|
|
document
|
|
.querySelector("#zotero-context-splitter")
|
|
?.getAttribute("state") === "open"
|
|
? ICONS.workspace_notes_open
|
|
: ICONS.workspace_notes_collapsed,
|
|
},
|
|
listeners: [
|
|
{
|
|
type: "click",
|
|
listener: (ev) => {
|
|
if (isContextPaneInitialized()) {
|
|
addon.hooks.onToggleWorkspacePane("notes");
|
|
return;
|
|
}
|
|
showHint(getString("workspace.notesPane.hint"));
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
close,
|
|
);
|
|
hoverWorkspaceTab(false);
|
|
tabElem.addEventListener("mouseenter", () => {
|
|
if (Zotero_Tabs.selectedType !== "betternotes") {
|
|
return;
|
|
}
|
|
hoverWorkspaceTab(true);
|
|
});
|
|
tabElem.addEventListener("mousedown", () => hoverWorkspaceTab(true));
|
|
tabElem.addEventListener("mouseleave", () => hoverWorkspaceTab(false));
|
|
tabElem.addEventListener("mousedown", async (ev) => {
|
|
if (ev.button !== 2) {
|
|
return;
|
|
}
|
|
await Zotero.Promise.delay(300);
|
|
const menu = document
|
|
.querySelector("#zotero-itemmenu")
|
|
?.parentElement?.lastElementChild?.querySelector("menu")
|
|
?.querySelector("menupopup")?.lastElementChild;
|
|
menu?.addEventListener("click", () => {
|
|
addon.hooks.onOpenWorkspace("window");
|
|
});
|
|
});
|
|
// load workspace content
|
|
const container = addon.data.workspace.tab.container;
|
|
initWorkspaceTabDragDrop(container, tabElem);
|
|
addon.hooks.onInitWorkspace(container);
|
|
registerWorkspaceTabPaneObserver();
|
|
}
|
|
|
|
export function deActivateWorkspaceTab() {
|
|
if (!isContextPaneInitialized()) {
|
|
return;
|
|
}
|
|
(
|
|
document.querySelector("#zotero-tab-toolbar") as XUL.Box
|
|
).style.removeProperty("visibility");
|
|
const toolbar = document.querySelector(
|
|
"#zotero-context-toolbar-extension",
|
|
) as XUL.Box;
|
|
toolbar.style.removeProperty("visibility");
|
|
}
|
|
|
|
function setWorkspaceTabStatus(status: boolean) {
|
|
addon.data.workspace.tab.active = status;
|
|
setPref("workspace.tab.active", status);
|
|
}
|
|
|
|
function initWorkspaceTabDragDrop(
|
|
container?: XUL.Box,
|
|
tabElem?: HTMLDivElement,
|
|
) {
|
|
if (!container) {
|
|
return;
|
|
}
|
|
const rect = tabElem?.getBoundingClientRect();
|
|
ztoolkit.UI.appendElement(
|
|
{
|
|
tag: "div",
|
|
id: "bn-workspace-tab-drop",
|
|
styles: {
|
|
background: "#252526",
|
|
opacity: "0.6",
|
|
width: "100%",
|
|
height: "100px",
|
|
position: "fixed",
|
|
left: "0px",
|
|
top: `${rect?.bottom}px`,
|
|
textAlign: "center",
|
|
display: "flex",
|
|
visibility: "hidden",
|
|
zIndex: "65535",
|
|
},
|
|
properties: {
|
|
hidden: true,
|
|
ondrop: (ev: DragEvent) => {
|
|
addon.hooks.onOpenWorkspace("window");
|
|
},
|
|
ondragenter: (ev: DragEvent) => {
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
dropElem.style.opacity = "0.9";
|
|
if (ev.dataTransfer) {
|
|
ev.dataTransfer.dropEffect = "move";
|
|
}
|
|
},
|
|
ondragover: (ev: DragEvent) => {
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
},
|
|
ondragleave: (ev: DragEvent) => {
|
|
ev.preventDefault();
|
|
ev.stopPropagation();
|
|
dropElem.style.opacity = "0.6";
|
|
},
|
|
},
|
|
children: [
|
|
{
|
|
tag: "div",
|
|
styles: {
|
|
margin: "auto",
|
|
textAlign: "center",
|
|
color: "#fff",
|
|
},
|
|
properties: {
|
|
innerHTML: getString("tab.openInWindow"),
|
|
},
|
|
},
|
|
],
|
|
enableElementRecord: false,
|
|
},
|
|
container,
|
|
);
|
|
const dropElem = container.querySelector(
|
|
"#bn-workspace-tab-drop",
|
|
) as HTMLDivElement;
|
|
tabElem?.addEventListener("dragstart", (ev) => {
|
|
dropElem.style.visibility = "visible";
|
|
});
|
|
tabElem?.addEventListener("dragend", (ev) => {
|
|
dropElem.style.visibility = "hidden";
|
|
});
|
|
}
|