mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-23 10:00:13 +01:00
❤️ 完整开源界面和内核 https://github.com/siyuan-note/siyuan/issues/5013
This commit is contained in:
parent
e650b8100c
commit
f40ed985e1
1214 changed files with 345766 additions and 9 deletions
432
app/src/protyle/breadcrumb/index.ts
Normal file
432
app/src/protyle/breadcrumb/index.ts
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
import {getIconByType} from "../../editor/getIcon";
|
||||
import {fetchPost} from "../../util/fetch";
|
||||
import {Constants} from "../../constants";
|
||||
import {MenuItem} from "../../menus/Menu";
|
||||
import {fullscreen} from "./action";
|
||||
import {exportMd} from "../../menus/commonMenuItem";
|
||||
import {setEditMode} from "../util/setEditMode";
|
||||
import {RecordMedia} from "../util/RecordMedia";
|
||||
import {hideMessage, showMessage} from "../../dialog/message";
|
||||
import {uploadFiles} from "../upload";
|
||||
import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName} from "../util/hasClosest";
|
||||
import {needSubscribe} from "../../util/needSubscribe";
|
||||
import {isMobile} from "../../util/functions";
|
||||
import {zoomOut} from "../../menus/protyle";
|
||||
import {getEditorRange} from "../util/selection";
|
||||
import {setPadding} from "../ui/initUI";
|
||||
import {onGet} from "../util/onGet";
|
||||
/// #if !BROWSER
|
||||
import {getCurrentWindow} from "@electron/remote";
|
||||
/// #endif
|
||||
import {openFileById} from "../../editor/util";
|
||||
import {getAllModels} from "../../layout/getAll";
|
||||
|
||||
export class Breadcrumb {
|
||||
public element: HTMLElement;
|
||||
private mediaRecorder: RecordMedia;
|
||||
private id: string;
|
||||
|
||||
constructor(protyle: IProtyle) {
|
||||
const element = document.createElement("div");
|
||||
element.className = "protyle-breadcrumb";
|
||||
let html = `<div class="protyle-breadcrumb__bar"></div>
|
||||
<span class="fn__flex-1 fn__space fn__flex-shrink"></span>
|
||||
<button class="b3-tooltips b3-tooltips__w block__icon fn__flex-center" style="opacity: 1;" data-menu="true" aria-label="${window.siyuan.languages.more}"><svg><use xlink:href="#iconMore"></use></svg></button>`;
|
||||
if (protyle.options.render.breadcrumbContext) {
|
||||
html += `<span class="fn__space"></span>
|
||||
<div class="b3-tooltips b3-tooltips__w block__icon fn__flex-center" style="opacity: 1;" data-type="context" aria-label="${window.siyuan.languages.context}"><svg><use xlink:href="#iconAlignCenter"></use></svg></div>`;
|
||||
}
|
||||
element.innerHTML = html;
|
||||
this.element = element.firstElementChild as HTMLElement;
|
||||
element.addEventListener("click", (event) => {
|
||||
let target = event.target as HTMLElement;
|
||||
while (target && !target.isEqualNode(element)) {
|
||||
const id = target.getAttribute("data-node-id");
|
||||
if (id) {
|
||||
if (protyle.options.render.breadcrumbDocName && window.siyuan.ctrlIsPressed) {
|
||||
if (!isMobile()) {
|
||||
openFileById({id, action: [Constants.CB_GET_FOCUS]});
|
||||
}
|
||||
} else {
|
||||
// const activeElement = this.element.querySelector(".protyle-breadcrumb__item--active")
|
||||
// if (activeElement) {
|
||||
// activeElement.classList.remove("protyle-breadcrumb__item--active");
|
||||
// }
|
||||
zoomOut(protyle, id);
|
||||
}
|
||||
event.preventDefault();
|
||||
break;
|
||||
} else if (target.getAttribute("data-menu") === "true") {
|
||||
this.showMenu(protyle, {
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
});
|
||||
event.preventDefault();
|
||||
break;
|
||||
} else if (target.getAttribute("data-type") === "context") {
|
||||
if (target.classList.contains("block__icon--active")) {
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: protyle.options.blockId,
|
||||
mode: 0,
|
||||
size: Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, protyle);
|
||||
});
|
||||
target.classList.remove("block__icon--active");
|
||||
} else {
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: protyle.options.blockId,
|
||||
mode: 3,
|
||||
size: Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, protyle, [Constants.CB_GET_HL]);
|
||||
});
|
||||
target.classList.add("block__icon--active");
|
||||
}
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
});
|
||||
element.addEventListener("mouseleave", () => {
|
||||
protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--hl").forEach(item => {
|
||||
item.classList.remove("protyle-wysiwyg--hl");
|
||||
});
|
||||
});
|
||||
this.element.addEventListener("mouseover", (event) => {
|
||||
if (!protyle.selectElement.classList.contains("fn__none")) {
|
||||
return;
|
||||
}
|
||||
const target = event.target as HTMLElement;
|
||||
const svgElement = hasClosestByAttribute(target, "data-node-id", null);
|
||||
if (svgElement) {
|
||||
protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--hl").forEach(item => {
|
||||
item.classList.remove("protyle-wysiwyg--hl");
|
||||
});
|
||||
const nodeElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${svgElement.getAttribute("data-node-id")}"]`);
|
||||
if (nodeElement) {
|
||||
nodeElement.classList.add("protyle-wysiwyg--hl");
|
||||
}
|
||||
}
|
||||
});
|
||||
/// #if !BROWSER
|
||||
if ("windows" !== window.siyuan.config.system.os && "linux" !== window.siyuan.config.system.os) {
|
||||
const currentWindow = getCurrentWindow();
|
||||
element.querySelector(".fn__flex-shrink").addEventListener("dblclick", (event) => {
|
||||
if (hasClosestByClassName(event.target as HTMLElement, "fullscreen")) {
|
||||
if (currentWindow.isMaximized()) {
|
||||
currentWindow.unmaximize();
|
||||
} else {
|
||||
currentWindow.maximize();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/// #endif
|
||||
}
|
||||
|
||||
private showMenu(protyle: IProtyle, position: { x: number, y: number }) {
|
||||
let id;
|
||||
const cursorNodeElement = hasClosestBlock(getEditorRange(protyle.element).startContainer);
|
||||
if (cursorNodeElement) {
|
||||
id = cursorNodeElement.getAttribute("data-node-id");
|
||||
}
|
||||
fetchPost("/api/block/getBlockWordCount", {id: id || protyle.block.id}, (response) => {
|
||||
window.siyuan.menus.menu.remove();
|
||||
|
||||
if (!protyle.contentElement.classList.contains("fn__none")) {
|
||||
let uploadHTML = "";
|
||||
if (!protyle.disabled) {
|
||||
uploadHTML = '<input class="b3-form__upload" type="file" multiple="multiple"';
|
||||
if (protyle.options.upload.accept) {
|
||||
uploadHTML += ` accept="${protyle.options.upload.accept}">`;
|
||||
} else {
|
||||
uploadHTML += ">";
|
||||
}
|
||||
}
|
||||
const uploadMenu = new MenuItem({
|
||||
disabled: protyle.disabled,
|
||||
icon: "iconDownload",
|
||||
label: `${window.siyuan.languages.insertAsset}${uploadHTML}`,
|
||||
}).element;
|
||||
if (!protyle.disabled) {
|
||||
uploadMenu.querySelector("input").addEventListener("change", (event: InputEvent & { target: HTMLInputElement }) => {
|
||||
if (event.target.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
uploadFiles(protyle, event.target.files, event.target);
|
||||
window.siyuan.menus.menu.remove();
|
||||
});
|
||||
}
|
||||
window.siyuan.menus.menu.append(uploadMenu);
|
||||
if (window.siyuan.config.system.container !== "android" || !window.JSAndroid) {
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
disabled: protyle.disabled && (!this.mediaRecorder || (this.mediaRecorder && !this.mediaRecorder.isRecording)),
|
||||
current: this.mediaRecorder && this.mediaRecorder.isRecording,
|
||||
icon: "iconRecord",
|
||||
label: this.mediaRecorder?.isRecording ? window.siyuan.languages.endRecord : window.siyuan.languages.startRecord,
|
||||
click: () => {
|
||||
if (!this.mediaRecorder) {
|
||||
navigator.mediaDevices.getUserMedia({audio: true}).then((mediaStream: MediaStream) => {
|
||||
this.mediaRecorder = new RecordMedia(mediaStream);
|
||||
this.mediaRecorder.recorder.onaudioprocess = (e: AudioProcessingEvent) => {
|
||||
// Do nothing if not recording:
|
||||
if (!this.mediaRecorder.isRecording) {
|
||||
return;
|
||||
}
|
||||
// Copy the data from the input buffers;
|
||||
const left = e.inputBuffer.getChannelData(0);
|
||||
const right = e.inputBuffer.getChannelData(1);
|
||||
this.mediaRecorder.cloneChannelData(left, right);
|
||||
};
|
||||
this.mediaRecorder.startRecordingNewWavFile();
|
||||
showMessage(window.siyuan.languages.recording, 86400000);
|
||||
}).catch(() => {
|
||||
showMessage(window.siyuan.languages["record-tip"]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mediaRecorder.isRecording) {
|
||||
this.mediaRecorder.stopRecording();
|
||||
hideMessage();
|
||||
const file: File = new File([this.mediaRecorder.buildWavFileBlob()],
|
||||
`record${(new Date()).getTime()}.wav`, {type: "video/webm"});
|
||||
uploadFiles(protyle, [file]);
|
||||
} else {
|
||||
showMessage(window.siyuan.languages.recording, 86400000);
|
||||
this.mediaRecorder.startRecordingNewWavFile();
|
||||
}
|
||||
}
|
||||
}).element);
|
||||
}
|
||||
}
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
label: window.siyuan.languages.uploadAssets2CDN,
|
||||
icon: "iconCloud",
|
||||
click() {
|
||||
if (!needSubscribe()) {
|
||||
fetchPost("/api/asset/uploadCloud", {id: protyle.block.parentID});
|
||||
}
|
||||
}
|
||||
}).element);
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
label: window.siyuan.languages.spaceInZE,
|
||||
click: () => {
|
||||
fetchPost("/api/format/autoSpace", {
|
||||
id: protyle.block.rootID
|
||||
}, () => {
|
||||
if (isMobile()) {
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: protyle.block.id,
|
||||
mode: 0,
|
||||
size: Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, protyle, [Constants.CB_GET_FOCUS]);
|
||||
});
|
||||
} else {
|
||||
getAllModels().editor.forEach(item => {
|
||||
if (item.editor.protyle.block.rootID === protyle.block.rootID) {
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: item.editor.protyle.block.rootID,
|
||||
mode: 0,
|
||||
size: Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, item.editor.protyle, [Constants.CB_GET_FOCUS]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}).element);
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
label: window.siyuan.languages.netImg2LocalAsset,
|
||||
click: () => {
|
||||
fetchPost("/api/format/netImg2LocalAssets", {
|
||||
id: protyle.block.rootID
|
||||
}, () => {
|
||||
if (isMobile()) {
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: protyle.block.id,
|
||||
mode: 0,
|
||||
size: Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, protyle, [Constants.CB_GET_FOCUS]);
|
||||
});
|
||||
} else {
|
||||
getAllModels().editor.forEach(item => {
|
||||
if (item.editor.protyle.block.rootID === protyle.block.rootID) {
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: item.editor.protyle.block.rootID,
|
||||
mode: 0,
|
||||
size: Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, item.editor.protyle, [Constants.CB_GET_FOCUS]);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}).element);
|
||||
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
icon: "iconRefresh",
|
||||
accelerator: window.siyuan.config.keymap.editor.general.refresh.custom,
|
||||
label: window.siyuan.languages.refresh,
|
||||
click: () => {
|
||||
protyle.title?.render(protyle, true);
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: protyle.block.showAll ? protyle.block.id : protyle.block.rootID,
|
||||
mode: 0,
|
||||
size: protyle.block.showAll ? Constants.SIZE_GET_MAX : Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, protyle, protyle.block.showAll ? [Constants.CB_GET_ALL, Constants.CB_GET_FOCUS] : [Constants.CB_GET_FOCUS]);
|
||||
});
|
||||
}
|
||||
}).element);
|
||||
if (!isMobile()) {
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
icon: "iconFullscreen",
|
||||
accelerator: window.siyuan.config.keymap.editor.general.fullscreen.custom,
|
||||
label: window.siyuan.languages.fullscreen,
|
||||
click: () => {
|
||||
fullscreen(protyle.element);
|
||||
setPadding(protyle);
|
||||
}
|
||||
}).element);
|
||||
}
|
||||
const editSubmenu: IMenu[] = [{
|
||||
current: !protyle.contentElement.classList.contains("fn__none"),
|
||||
label: window.siyuan.languages.wysiwyg,
|
||||
accelerator: window.siyuan.config.keymap.editor.general.wysiwyg.custom,
|
||||
click: () => {
|
||||
setEditMode(protyle, "wysiwyg");
|
||||
protyle.scroll.lastScrollTop = 0;
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: protyle.block.rootID,
|
||||
size: Constants.SIZE_GET,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, protyle);
|
||||
window.siyuan.menus.menu.remove();
|
||||
});
|
||||
}
|
||||
}];
|
||||
editSubmenu.push({
|
||||
current: !protyle.preview.element.classList.contains("fn__none"),
|
||||
icon: "iconPreview",
|
||||
label: window.siyuan.languages.preview,
|
||||
accelerator: window.siyuan.config.keymap.editor.general.preview.custom,
|
||||
click: () => {
|
||||
setEditMode(protyle, "preview");
|
||||
window.siyuan.menus.menu.remove();
|
||||
}
|
||||
});
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
icon: "iconEdit",
|
||||
label: window.siyuan.languages["edit-mode"],
|
||||
type: "submenu",
|
||||
submenu: editSubmenu
|
||||
}).element);
|
||||
/// #if !BROWSER
|
||||
window.siyuan.menus.menu.append(exportMd(protyle.block.parentID));
|
||||
/// #endif
|
||||
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
|
||||
|
||||
window.siyuan.menus.menu.append(new MenuItem({
|
||||
type: "readonly",
|
||||
label: `<div class="fn__flex">${window.siyuan.languages.docRuneCount}<span class="fn__space fn__flex-1"></span>${response.data.rootBlockRuneCount}</div>
|
||||
<div class="fn__flex">${window.siyuan.languages.docWordCount}<span class="fn__space fn__flex-1"></span>${response.data.rootBlockWordCount}</div>
|
||||
<div class="fn__flex">${window.siyuan.languages.blockRuneCount}<span class="fn__space fn__flex-1"></span>${response.data.blockRuneCount}</div>
|
||||
<div class="fn__flex">${window.siyuan.languages.blockWordCount}<span class="fn__space fn__flex-1"></span>${response.data.blockWordCount}</div>`,
|
||||
}).element);
|
||||
window.siyuan.menus.menu.popup(position);
|
||||
});
|
||||
}
|
||||
|
||||
public render(protyle: IProtyle, update = false) {
|
||||
let range: Range;
|
||||
let blockElement: Element;
|
||||
if (getSelection().rangeCount > 0) {
|
||||
range = getSelection().getRangeAt(0);
|
||||
if (!protyle.wysiwyg.element.isEqualNode(range.startContainer) && !protyle.wysiwyg.element.contains(range.startContainer)) {
|
||||
blockElement = protyle.wysiwyg.element.firstElementChild;
|
||||
} else {
|
||||
blockElement = hasClosestBlock(range.startContainer) as Element;
|
||||
}
|
||||
}
|
||||
if (!blockElement) {
|
||||
blockElement = protyle.wysiwyg.element.firstElementChild;
|
||||
}
|
||||
const id = blockElement.getAttribute("data-node-id");
|
||||
if (id === this.id && !update) {
|
||||
protyle.breadcrumb.element.querySelectorAll(".protyle-breadcrumb__item--active").forEach(item => {
|
||||
item.classList.remove("protyle-breadcrumb__item--active");
|
||||
});
|
||||
const currentElement = protyle.breadcrumb.element.querySelector(`[data-node-id="${protyle.block.showAll ? protyle.block.id : protyle.block.parentID}"]`);
|
||||
if (currentElement) {
|
||||
currentElement.classList.add("protyle-breadcrumb__item--active");
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.id = id;
|
||||
fetchPost("/api/block/getBlockBreadcrumb", {id}, (response) => {
|
||||
let html = "";
|
||||
response.data.forEach((item: { id: string, name: string, type: string, subType: string, children: [] }, index: number) => {
|
||||
let isCurrent = false;
|
||||
if (!protyle.block.showAll && item.id === protyle.block.parentID) {
|
||||
isCurrent = true;
|
||||
} else if (protyle.block.showAll && item.id === protyle.block.id) {
|
||||
isCurrent = true;
|
||||
}
|
||||
if (index === 0 && !protyle.options.render.breadcrumbDocName) {
|
||||
html += `<span class="protyle-breadcrumb__item${isCurrent ? " protyle-breadcrumb__item--active" : ""}" data-node-id="${item.id}">
|
||||
<svg class="popover__block" data-id="${item.id}"><use xlink:href="#${getIconByType(item.type, item.subType)}"></use></svg>
|
||||
</span>`;
|
||||
} else {
|
||||
html += `<span class="protyle-breadcrumb__item${isCurrent ? " protyle-breadcrumb__item--active" : ""}" data-node-id="${item.id}">
|
||||
<svg class="popover__block" data-id="${item.id}"><use xlink:href="#${getIconByType(item.type, item.subType)}"></use></svg>
|
||||
<span class="protyle-breadcrumb__text" title="${item.name}">${item.name}</span>
|
||||
</span>`;
|
||||
}
|
||||
if (index !== response.data.length - 1) {
|
||||
html += '<svg class="protyle-breadcrumb__arrow"><use xlink:href="#iconRight"></use></svg>';
|
||||
}
|
||||
});
|
||||
this.element.innerHTML = html;
|
||||
const itemElements = Array.from(this.element.querySelectorAll(".protyle-breadcrumb__text"));
|
||||
if (itemElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
let jump = false;
|
||||
while (this.element.scrollHeight > 60 && !jump) {
|
||||
itemElements.find((item, index) => {
|
||||
if (itemElements.length === 1) {
|
||||
item.classList.add("protyle-breadcrumb__text--ellipsis");
|
||||
jump = true;
|
||||
return true;
|
||||
}
|
||||
if (index !== 0 && !item.classList.contains("protyle-breadcrumb__text--ellipsis")) {
|
||||
item.classList.add("protyle-breadcrumb__text--ellipsis");
|
||||
return true;
|
||||
}
|
||||
if (index === itemElements.length - 1 && item.classList.contains("protyle-breadcrumb__text--ellipsis")) {
|
||||
jump = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this.element.classList.add("protyle-breadcrumb__bar--hide");
|
||||
window.siyuan.hideBreadcrumb = true;
|
||||
}
|
||||
|
||||
public show() {
|
||||
this.element.classList.remove("protyle-breadcrumb__bar--hide");
|
||||
window.siyuan.hideBreadcrumb = false;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue