Vanessa 2023-07-02 20:52:16 +08:00
parent 593959312d
commit 22c46bbc6b
10 changed files with 283 additions and 118 deletions

View file

@ -78,6 +78,8 @@ progressLoading: 400
#windowControls: 502 #windowControls: 502
.b3-snackbar: 503 .b3-snackbar: 503
.av__panel: 504
*/ */
html { html {

View file

@ -12,6 +12,7 @@
outline: none; outline: none;
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
&:empty::after { &:empty::after {
color: var(--b3-theme-on-surface); color: var(--b3-theme-on-surface);
content: attr(data-tip); content: attr(data-tip);
@ -109,6 +110,15 @@
} }
} }
&__panel {
z-index: 504;
position: relative;
.b3-menu__item:not([data-type="title"]):hover {
background-color: var(--b3-list-hover);
}
}
&.protyle-wysiwyg--select { &.protyle-wysiwyg--select {
.layout-tab-bar, .layout-tab-bar,
.av__row--header, .av__row--header,

View file

@ -231,6 +231,7 @@
&:hover { &:hover {
opacity: 1; opacity: 1;
color: var(--b3-theme-on-background);
} }
} }

View file

@ -874,7 +874,8 @@ export const globalShortcut = (app: App) => {
window.addEventListener("click", (event: MouseEvent & { target: HTMLElement }) => { window.addEventListener("click", (event: MouseEvent & { target: HTMLElement }) => {
if (!window.siyuan.menus.menu.element.contains(event.target) && !hasClosestByAttribute(event.target, "data-menu", "true")) { if (!window.siyuan.menus.menu.element.contains(event.target) && !hasClosestByAttribute(event.target, "data-menu", "true")) {
if (getSelection().rangeCount > 0 && window.siyuan.menus.menu.element.contains(getSelection().getRangeAt(0).startContainer)) { if (getSelection().rangeCount > 0 && window.siyuan.menus.menu.element.contains(getSelection().getRangeAt(0).startContainer) &&
window.siyuan.menus.menu.element.contains(document.activeElement)) {
// https://ld246.com/article/1654567749834/comment/1654589171218#comments // https://ld246.com/article/1654567749834/comment/1654589171218#comments
} else { } else {
window.siyuan.menus.menu.remove(); window.siyuan.menus.menu.remove();

View file

@ -46,11 +46,11 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
filter: ["ai chat"], filter: ["ai chat"],
value: Constants.ZWSP + 5, value: Constants.ZWSP + 5,
html: '<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSparkles"></use></svg><span class="b3-list-item__text">AI Chat</span></div>', html: '<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSparkles"></use></svg><span class="b3-list-item__text">AI Chat</span></div>',
}, { }, /*{
filter: ["属性视图", "shuxingshitu", "sxst", "attribute view"], filter: ["属性视图", "shuxingshitu", "sxst", "attribute view"],
value: '<div data-type="NodeAttributeView" data-av-type="table"></div>', value: '<div data-type="NodeAttributeView" data-av-type="table"></div>',
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconDatabase"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.attributeView}</span></div>`, html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconDatabase"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.attributeView}</span></div>`,
}, { },*/ {
filter: ["文档", "子文档", "wendang", "wd", "ziwendang", "zwd", "xjwd"], filter: ["文档", "子文档", "wendang", "wd", "ziwendang", "zwd", "xjwd"],
value: Constants.ZWSP + 4, value: Constants.ZWSP + 4,
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconFile"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.newFile}</span><span class="b3-menu__accelerator">${updateHotkeyTip(window.siyuan.config.keymap.general.newFile.custom)}</span></div>`, html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconFile"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.newFile}</span><span class="b3-menu__accelerator">${updateHotkeyTip(window.siyuan.config.keymap.general.newFile.custom)}</span></div>`,

View file

@ -6,6 +6,8 @@ import {copySubMenu} from "../../../menus/commonMenuItem";
import {popTextCell, showHeaderCellMenu} from "./cell"; import {popTextCell, showHeaderCellMenu} from "./cell";
import {getColIconByType, updateHeader} from "./col"; import {getColIconByType, updateHeader} from "./col";
import {emitOpenMenu} from "../../../plugin/EventBus"; import {emitOpenMenu} from "../../../plugin/EventBus";
import {addCol} from "./addCol";
import {openMenuPanel} from "./openMenuPanel";
export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLElement }) => { export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLElement }) => {
const blockElement = hasClosestBlock(event.target); const blockElement = hasClosestBlock(event.target);
@ -14,108 +16,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
} }
const addElement = hasClosestByAttribute(event.target, "data-type", "av-header-add"); const addElement = hasClosestByAttribute(event.target, "data-type", "av-header-add");
if (addElement) { if (addElement) {
const menu = new Menu("av-header-add"); addCol(protyle, blockElement, addElement);
menu.addItem({
icon: "iconAlignLeft",
label: window.siyuan.languages.text,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconNumber",
label: window.siyuan.languages.number,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconListItem",
label: window.siyuan.languages.select,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconList",
label: window.siyuan.languages.multiSelect,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconCalendar",
label: window.siyuan.languages.date,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
const addRect = addElement.getBoundingClientRect();
menu.open({
x: addRect.left,
y: addRect.bottom,
h: addRect.height
});
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
return true; return true;
@ -161,6 +62,21 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
return true; return true;
} }
const headerMoreElement = hasClosestByAttribute(event.target, "data-type","av-header-more");
if (headerMoreElement) {
openMenuPanel(protyle, blockElement, "properties");
event.preventDefault();
event.stopPropagation();
return true;
}
const moreElement = hasClosestByAttribute(event.target, "data-type","av-more");
if (moreElement) {
openMenuPanel(protyle, blockElement, "config");
event.preventDefault();
event.stopPropagation();
return true;
}
const cellElement = hasClosestByClassName(event.target, "av__cell"); const cellElement = hasClosestByClassName(event.target, "av__cell");
if (cellElement) { if (cellElement) {
if (cellElement.parentElement.classList.contains("av__row--header")) { if (cellElement.parentElement.classList.contains("av__row--header")) {

View file

@ -0,0 +1,107 @@
import {Menu} from "../../../plugin/Menu";
import {transaction} from "../../wysiwyg/transaction";
export const addCol = (protyle:IProtyle, blockElement:HTMLElement, addElement:HTMLElement) => {
const menu = new Menu("av-header-add");
menu.addItem({
icon: "iconAlignLeft",
label: window.siyuan.languages.text,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconNumber",
label: window.siyuan.languages.number,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconListItem",
label: window.siyuan.languages.select,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconList",
label: window.siyuan.languages.multiSelect,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
menu.addItem({
icon: "iconCalendar",
label: window.siyuan.languages.date,
click() {
const id = Lute.NewNodeID();
const type = "text";
transaction(protyle, [{
action: "addAttrViewCol",
name: "Text",
parentID: blockElement.getAttribute("data-av-id"),
type,
id
}], [{
action: "removeAttrViewCol",
id,
parentID: blockElement.getAttribute("data-av-id"),
}]);
}
});
const addRect = addElement.getBoundingClientRect();
menu.open({
x: addRect.left,
y: addRect.bottom,
h: addRect.height
});
}

View file

@ -89,6 +89,8 @@ const removeCol = (cellElement: HTMLElement) => {
export const showHeaderCellMenu = (protyle: IProtyle, blockElement: HTMLElement, cellElement: HTMLElement) => { export const showHeaderCellMenu = (protyle: IProtyle, blockElement: HTMLElement, cellElement: HTMLElement) => {
const type = cellElement.getAttribute("data-dtype") as TAVCol; const type = cellElement.getAttribute("data-dtype") as TAVCol;
const colId = cellElement.getAttribute("data-id");
const avId = blockElement.getAttribute("data-av-id");
const menu = new Menu("av-header-cell", () => { const menu = new Menu("av-header-cell", () => {
const newValue = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value; const newValue = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value;
if (newValue === cellElement.textContent.trim()) { if (newValue === cellElement.textContent.trim()) {
@ -96,14 +98,14 @@ export const showHeaderCellMenu = (protyle: IProtyle, blockElement: HTMLElement,
} }
transaction(protyle, [{ transaction(protyle, [{
action: "updateAttrViewCol", action: "updateAttrViewCol",
id: cellElement.getAttribute("data-id"), id: colId,
parentID: blockElement.getAttribute("data-av-id"), parentID: avId,
name: newValue, name: newValue,
type, type,
}], [{ }], [{
action: "updateAttrViewCol", action: "updateAttrViewCol",
id: cellElement.getAttribute("data-id"), id: colId,
parentID: blockElement.getAttribute("data-av-id"), parentID: avId,
name: cellElement.textContent.trim(), name: cellElement.textContent.trim(),
type, type,
}]); }]);
@ -150,10 +152,14 @@ export const showHeaderCellMenu = (protyle: IProtyle, blockElement: HTMLElement,
label: window.siyuan.languages.hide, label: window.siyuan.languages.hide,
click() { click() {
transaction(protyle, [{ transaction(protyle, [{
action:"setAttrViewColHidden", action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: true data: true
}], [{ }], [{
action:"setAttrViewColHidden", action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: false data: false
}]); }]);
} }
@ -169,17 +175,16 @@ export const showHeaderCellMenu = (protyle: IProtyle, blockElement: HTMLElement,
icon: "iconTrashcan", icon: "iconTrashcan",
label: window.siyuan.languages.delete, label: window.siyuan.languages.delete,
click() { click() {
const id = cellElement.getAttribute("data-id");
transaction(protyle, [{ transaction(protyle, [{
action: "removeAttrViewCol", action: "removeAttrViewCol",
id, id: colId,
parentID: blockElement.getAttribute("data-av-id"), parentID: avId,
}], [{ }], [{
action: "addAttrViewCol", action: "addAttrViewCol",
name: cellElement.textContent.trim(), name: cellElement.textContent.trim(),
parentID: blockElement.getAttribute("data-av-id"), parentID: avId,
type: type, type: type,
id id: colId
}]); }]);
removeCol(cellElement); removeCol(cellElement);
} }
@ -199,5 +204,9 @@ export const showHeaderCellMenu = (protyle: IProtyle, blockElement: HTMLElement,
y: cellRect.bottom, y: cellRect.bottom,
h: cellRect.height h: cellRect.height
}); });
(window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement)?.select(); const inputElement = window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement
if (inputElement) {
inputElement.select();
inputElement.focus();
}
}; };

View file

@ -0,0 +1,103 @@
import {Menu} from "../../../plugin/Menu";
import {transaction} from "../../wysiwyg/transaction";
import {fetchPost} from "../../../util/fetch";
import {hideElements} from "../../ui/hideElements";
export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type: "properties" | "config" = "config") => {
let avMenuPanel = document.querySelector(".av__panel");
if (avMenuPanel) {
avMenuPanel.remove();
return;
}
window.siyuan.menus.menu.remove();
fetchPost("/api/av/renderAttributeView", {id: blockElement.getAttribute("data-av-id")}, (response) => {
const data = response.data.av;
const tabRect = blockElement.querySelector(".layout-tab-bar").getBoundingClientRect()
let html
if (type === "config") {
html = `<div class="av__panel">
<div class="b3-dialog__scrim" data-type="close"></div>
<div class="b3-menu" style="width: 300px;right:${window.innerWidth - tabRect.right}px;top:${tabRect.bottom}px">
<button class="b3-menu__item" data-type="title">
<span class="b3-menu__label">${window.siyuan.languages.config}</span>
<svg class="b3-menu__action" data-type="close" style="opacity: 1"><use xlink:href="#iconCloseRound"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"></svg>
<span class="b3-menu__label">${window.siyuan.languages.attr}</span>
<span class="b3-menu__accelerator">${data.columns.filter((item: IAVColumn) => !item.hidden).length}/${data.columns.length}</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"><use xlink:href="#iconFilter"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.filter}</span>
<span class="b3-menu__accelerator">${data.filters.length}</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"><use xlink:href="#iconSort"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.sort}</span>
<span class="b3-menu__accelerator">${data.sorts.length}</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"></svg>
<span class="b3-menu__label">${window.siyuan.languages.pageCount}</span>
<span class="b3-menu__accelerator">50</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
</div>
</div>`
} else if (type === "properties") {
html = `<div class="av__panel">
<div class="b3-dialog__scrim" data-type="close"></div>
<div class="b3-menu" style="width: 300px;right:${window.innerWidth - tabRect.right}px;top:${tabRect.bottom}px">
<button class="b3-menu__item">
<svg class="b3-menu__icon"><use xlink:href="#iconLeft"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.attr}</span>
<svg class="b3-menu__action" data-type="close" style="opacity: 1"><use xlink:href="#iconCloseRound"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"></svg>
<span class="b3-menu__label">${window.siyuan.languages.attr}</span>
<span class="b3-menu__accelerator">${data.columns.filter((item: IAVColumn) => !item.hidden).length}/${data.columns.length}</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"><use xlink:href="#iconFilter"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.filter}</span>
<span class="b3-menu__accelerator">${data.filters.length}</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"><use xlink:href="#iconSort"></use></svg>
<span class="b3-menu__label">${window.siyuan.languages.sort}</span>
<span class="b3-menu__accelerator">${data.sorts.length}</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item">
<svg class="b3-menu__icon"></svg>
<span class="b3-menu__label">${window.siyuan.languages.pageCount}</span>
<span class="b3-menu__accelerator">50</span>
<svg class="b3-menu__icon b3-menu__icon--arrow"><use xlink:href="#iconRight"></use></svg>
</button>
</div>
</div>`
}
document.body.insertAdjacentHTML("beforeend", html);
avMenuPanel = document.querySelector(".av__panel");
avMenuPanel.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
let target = event.target as HTMLElement;
while (target && !target.isSameNode(avMenuPanel)) {
const type = target.dataset.type;
if (type === "close") {
avMenuPanel.remove();
break;
}
target = target.parentElement;
}
});
});
}

View file

@ -49,6 +49,9 @@ export const avRender = (element: Element, cb?: () => void) => {
</div> </div>
<div class="av__firstcol"><svg><use xlink:href="#iconUncheck"></use></svg></div>`; <div class="av__firstcol"><svg><use xlink:href="#iconUncheck"></use></svg></div>`;
row.cells.forEach((cell, index) => { row.cells.forEach((cell, index) => {
if (data.columns[index].hidden) {
return;
}
let text: string; let text: string;
if (cell.valueType === "text") { if (cell.valueType === "text") {
text = cell.value?.text.content || ""; text = cell.value?.text.content || "";
@ -78,8 +81,21 @@ export const avRender = (element: Element, cb?: () => void) => {
<svg class="item__graphic"><use xlink:href="#iconTable"></use></svg> <svg class="item__graphic"><use xlink:href="#iconTable"></use></svg>
<span class="item__text">Table</span> <span class="item__text">Table</span>
</div> </div>
<div class="fn__flex-1"></div>
<span data-type="av-filter" class="block__icon block__icon--show b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.filter}">
<svg><use xlink:href="#iconFilter"></use></svg>
</span>
<div class="fn__space"></div>
<span data-type="av-sort" class="block__icon block__icon--show b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.sort}">
<svg><use xlink:href="#iconSort"></use></svg>
</span>
<div class="fn__space"></div>
<span data-type="av-more" class="block__icon block__icon--show b3-tooltips b3-tooltips__w" aria-label="${window.siyuan.languages.more}">
<svg><use xlink:href="#iconMore"></use></svg>
</span>
<div class="fn__space"></div>
</div> </div>
<div contenteditable="true" class="av__title" data-tip="${window.siyuan.languages.title}">${data.title||""}</div> <div contenteditable="true" class="av__title" data-tip="${window.siyuan.languages.title}">${data.title || ""}</div>
<div class="av__counter fn__none"></div> <div class="av__counter fn__none"></div>
</div> </div>
<div class="av__scroll"> <div class="av__scroll">