This commit is contained in:
Vanessa 2023-09-24 17:53:28 +08:00
parent 17055347db
commit 435ad45cb5
14 changed files with 258 additions and 186 deletions

View file

@ -10,6 +10,7 @@ import {MenuItem} from "../../../menus/Menu";
import {exportAsset} from "../../../menus/util";
import {setPosition} from "../../../util/setPosition";
import {previewImage} from "../../preview/image";
import {genAVValueHTML} from "./blockAttr";
export const bindAssetEvent = (options: {
protyle: IProtyle,
@ -104,14 +105,13 @@ export const updateAssetCell = (options: {
addUpdateValue?: IAVCellAssetValue[],
removeContent?: string
}) => {
let cellIndex = 0;
let cellIndex: number;
Array.from(options.cellElements[0].parentElement.querySelectorAll(".av__cell")).find((item: HTMLElement, index) => {
if (item.dataset.id === options.cellElements[0].dataset.id) {
cellIndex = index;
return true;
}
});
const colId = options.cellElements[0].dataset.colId;
const cellDoOperations: IOperation[] = [];
const cellUndoOperations: IOperation[] = [];
@ -121,11 +121,19 @@ export const updateAssetCell = (options: {
const rowID = item.parentElement.dataset.id;
options.data.view.rows.find(row => {
if (row.id === rowID) {
cellData = row.cells[cellIndex];
// 为空时 cellId 每次请求都不一致
cellData.id = item.dataset.id;
if (!cellData.value || !cellData.value.mAsset) {
cellData.value = {mAsset: []} as IAVCellValue;
if (typeof cellIndex === "number") {
cellData = row.cells[cellIndex];
// 为空时 cellId 每次请求都不一致
cellData.id = item.dataset.id;
if (!cellData.value || !cellData.value.mAsset) {
cellData.value = {mAsset: []} as IAVCellValue;
}
} else {
cellData = row.cells.find(cellItem => {
if (cellItem.id === item.dataset.id) {
return true;
}
});
}
return true;
}
@ -188,14 +196,18 @@ export const updateAssetCell = (options: {
mAsset: oldValue
}
});
updateAttrViewCellAnimation(item);
if (item.classList.contains("custom-attr__avvalue")) {
item.innerHTML = genAVValueHTML(cellData.value)
} else {
updateAttrViewCellAnimation(item);
}
});
transaction(options.protyle, cellDoOperations, cellUndoOperations);
const menuElement = document.querySelector(".av__panel > .b3-menu") as HTMLElement;
if (menuElement) {
menuElement.innerHTML = getAssetHTML(options.data.view, options.cellElements);
bindAssetEvent({protyle: options.protyle, data: options.data, menuElement, cellElements: options.cellElements});
const cellRect = options.protyle.wysiwyg.element.querySelector(`.av__cell[data-id="${options.cellElements[0].dataset.id}"]`).getBoundingClientRect();
const cellRect = (options.cellElements[0].classList.contains("custom-attr__avvalue") ? options.cellElements[0] : options.protyle.wysiwyg.element.querySelector(`.av__cell[data-id="${options.cellElements[0].dataset.id}"]`)).getBoundingClientRect();
setTimeout(() => {
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
}, Constants.TIMEOUT_LOAD); // 等待图片加载
@ -251,7 +263,7 @@ export const editAssetItem = (protyle: IProtyle, data: IAV, cellElements: HTMLEl
});
}
});
openMenu(protyle.app, linkAddress, false, true);
openMenu(protyle ? protyle.app : window.siyuan.ws.app, linkAddress, false, true);
/// #if !BROWSER
if (linkAddress?.startsWith("assets/")) {
window.siyuan.menus.menu.append(new MenuItem(exportAsset(linkAddress)).element);
@ -302,7 +314,7 @@ export const addAssetLink = (protyle: IProtyle, data: IAV, cellElements: HTMLEle
const rect = target.getBoundingClientRect();
menu.open({
x: rect.right,
y: rect.bottom,
y: rect.top,
w: rect.width,
h: rect.height,
});

View file

@ -5,8 +5,9 @@ import {hasClosestByAttribute} from "../../util/hasClosest";
import {Menu} from "../../../plugin/Menu";
import {Constants} from "../../../constants";
import * as dayjs from "dayjs";
import {popTextCell} from "./cell";
const genAVValueHTML = (value: IAVCellValue) => {
export const genAVValueHTML = (value: IAVCellValue) => {
let html = "";
switch (value.type) {
case "text":
@ -57,7 +58,156 @@ const genAVValueHTML = (value: IAVCellValue) => {
return html;
};
export const renderAVAttribute = (element: HTMLElement, id: string) => {
const popDateMenu = (dateElement: HTMLElement) => {
const dateMenu = new Menu("custom-attr-av-date", () => {
const textElements = window.siyuan.menus.menu.element.querySelectorAll(".b3-text-field") as NodeListOf<HTMLInputElement>;
const hasEndDate = (window.siyuan.menus.menu.element.querySelector(".b3-switch") as HTMLInputElement).checked;
fetchPost("/api/av/setAttributeViewBlockAttr", {
avID: dateElement.dataset.avId,
keyID: dateElement.dataset.colId,
rowID: dateElement.dataset.blockId,
cellID: dateElement.dataset.id,
value: {
date: {
isNotEmpty: textElements[0].value !== "",
isNotEmpty2: textElements[1].value !== "",
content: new Date(textElements[0].value).getTime(),
content2: new Date(textElements[1].value).getTime(),
hasEndDate
}
}
});
let dataHTML = "";
if (textElements[0].value !== "") {
dataHTML = `<span data-content="${new Date(textElements[0].value).getTime()}">${dayjs(textElements[0].value).format("YYYY-MM-DD HH:mm")}</span>`;
}
if (hasEndDate && textElements[0].value !== "" && textElements[1].value !== "") {
dataHTML += `<svg class="custom-attr__avarrow"><use xlink:href="#iconForward"></use></svg><span data-content="${new Date(textElements[1].value).getTime()}">${dayjs(textElements[1].value).format("YYYY-MM-DD HH:mm")}</span>`;
}
dateElement.innerHTML = dataHTML;
});
if (dateMenu.isOpen) {
return;
}
const hasEndDate = dateElement.querySelector("svg");
const timeElements = dateElement.querySelectorAll("span");
dateMenu.addItem({
iconHTML: "",
label: `<input value="${timeElements[0] ? dayjs(parseInt(timeElements[0].dataset.content)).format("YYYY-MM-DDTHH:mm") : ""}" type="datetime-local" class="b3-text-field fn__size200" style="margin: 4px 0">`
});
dateMenu.addItem({
iconHTML: "",
label: `<input value="${timeElements[1] ? dayjs(parseInt(timeElements[1].dataset.content)).format("YYYY-MM-DDTHH:mm") : ""}" type="datetime-local" class="b3-text-field fn__size200${hasEndDate ? "" : " fn__none"}" style="margin: 4px 0">`
});
dateMenu.addSeparator();
dateMenu.addItem({
iconHTML: "",
label: `<label class="fn__flex">
<span>${window.siyuan.languages.endDate}</span>
<span class="fn__space fn__flex-1"></span>
<input type="checkbox" class="b3-switch fn__flex-center"${hasEndDate ? " checked" : ""}>
</label>`,
click(element, event) {
const switchElement = element.querySelector(".b3-switch") as HTMLInputElement;
if ((event.target as HTMLElement).tagName !== "INPUT") {
switchElement.checked = !switchElement.checked;
} else {
switchElement.outerHTML = `<input type="checkbox" class="b3-switch fn__flex-center"${switchElement.checked ? " checked" : ""}>`;
}
window.siyuan.menus.menu.element.querySelectorAll('[type="datetime-local"]')[1].classList.toggle("fn__none");
return true;
}
});
dateMenu.addSeparator();
dateMenu.addItem({
icon: "iconTrashcan",
label: window.siyuan.languages.clear,
click() {
const textElements = window.siyuan.menus.menu.element.querySelectorAll(".b3-text-field") as NodeListOf<HTMLInputElement>;
textElements[0].value = "";
textElements[1].value = "";
(window.siyuan.menus.menu.element.querySelector(".b3-switch") as HTMLInputElement).checked = false;
}
});
const datetRect = dateElement.getBoundingClientRect();
dateMenu.open({
x: datetRect.left,
y: datetRect.bottom
});
}
const popSelectMenu = (mSelectElement: HTMLElement) => {
const mSelectMenu = new Menu("custom-attr-av-select", () => {
const mSelect: { content: string, color: string }[] = [];
let mSelectHTML = "";
window.siyuan.menus.menu.element.querySelectorAll(".svg").forEach(item => {
const chipElement = item.parentElement.previousElementSibling.firstElementChild as HTMLElement;
const content = chipElement.textContent.trim();
const color = chipElement.dataset.color;
mSelect.push({
content,
color
});
mSelectHTML += `<span class="b3-chip b3-chip--middle" style="background-color:var(--b3-font-background${color});color:var(--b3-font-color${color})">${content}</span>`;
});
fetchPost("/api/av/setAttributeViewBlockAttr", {
avID: mSelectElement.dataset.avId,
keyID: mSelectElement.dataset.colId,
rowID: mSelectElement.dataset.blockId,
cellID: mSelectElement.dataset.id,
value: {
mSelect
}
});
mSelectElement.innerHTML = mSelectHTML;
});
if (mSelectMenu.isOpen) {
return;
}
const names: string[] = [];
mSelectElement.querySelectorAll(".b3-chip").forEach(item => {
names.push(item.textContent.trim());
});
JSON.parse(mSelectElement.dataset.options || "").forEach((item: { name: string, color: string }) => {
mSelectMenu.addItem({
iconHTML: "",
label: `<span class="b3-chip" data-color="${item.color}" style="height:24px;background-color:var(--b3-font-background${item.color});color:var(--b3-font-color${item.color})">
<span class="fn__ellipsis">${item.name}</span>
</span>`,
accelerator: names.includes(item.name) ? '<svg class="svg" style="height: 30px; float: left;"><use xlink:href="#iconSelect"></use></svg>' : Constants.ZWSP,
click(element) {
const acceleratorElement = element.querySelector(".b3-menu__accelerator");
if (mSelectElement.dataset.type === "select") {
window.siyuan.menus.menu.element.querySelectorAll(".b3-menu__accelerator").forEach(itemElement => {
if (itemElement.isSameNode(acceleratorElement)) {
if (acceleratorElement.querySelector("svg")) {
acceleratorElement.innerHTML = "";
} else {
acceleratorElement.innerHTML = '<svg class="svg" style="height: 30px; float: left;"><use xlink:href="#iconSelect"></use></svg>';
}
} else {
itemElement.innerHTML = "";
}
});
return false;
}
if (acceleratorElement.querySelector("svg")) {
acceleratorElement.innerHTML = "";
} else {
acceleratorElement.innerHTML = '<svg class="svg" style="height: 30px; float: left;"><use xlink:href="#iconSelect"></use></svg>';
}
return true;
}
});
});
const mSelecttRect = mSelectElement.getBoundingClientRect();
mSelectMenu.open({
x: mSelecttRect.left,
y: mSelecttRect.bottom
});
}
export const renderAVAttribute = (element: HTMLElement, id: string, protyle?: IProtyle) => {
fetchPost("/api/av/getAttributeViewKeys", {id}, (response) => {
let html = "";
response.data.forEach((table: {
@ -77,12 +227,12 @@ export const renderAVAttribute = (element: HTMLElement, id: string) => {
<span>${table.avName || window.siyuan.languages.database}</span>
</div>`;
table.keyValues?.forEach(item => {
html += `<div class="block__icons">
html += `<div class="block__icons" data-id="${id}">
<div class="block__logo">
<svg><use xlink:href="#${getColIconByType(item.key.type)}"></use></svg>
<span>${item.key.name}</span>
</div>
<div data-av-id="${table.avID}" data-key-id="${item.values[0].keyID}" data-block-id="${item.values[0].blockID}" data-id="${item.values[0].id}" data-type="${item.values[0].type}"
<div data-av-id="${table.avID}" data-col-id="${item.values[0].keyID}" data-block-id="${item.values[0].blockID}" data-id="${item.values[0].id}" data-type="${item.values[0].type}"
data-options="${item.key?.options ? escapeAttr(JSON.stringify(item.key.options)) : "[]"}"
class="fn__flex-1 fn__flex${["url", "text", "number", "email", "phone"].includes(item.values[0].type) ? "" : " custom-attr__avvalue"}">
${genAVValueHTML(item.values[0])}
@ -95,155 +245,21 @@ class="fn__flex-1 fn__flex${["url", "text", "number", "email", "phone"].includes
const target = event.target as HTMLElement;
const dateElement = hasClosestByAttribute(target, "data-type", "date");
if (dateElement) {
const dateMenu = new Menu("custom-attr-av-date", () => {
const textElements = window.siyuan.menus.menu.element.querySelectorAll(".b3-text-field") as NodeListOf<HTMLInputElement>;
const hasEndDate = (window.siyuan.menus.menu.element.querySelector(".b3-switch") as HTMLInputElement).checked;
fetchPost("/api/av/setAttributeViewBlockAttr", {
avID: dateElement.dataset.avId,
keyID: dateElement.dataset.keyId,
rowID: dateElement.dataset.blockId,
cellID: dateElement.dataset.id,
value: {
date: {
isNotEmpty: textElements[0].value !== "",
isNotEmpty2: textElements[1].value !== "",
content: new Date(textElements[0].value).getTime(),
content2: new Date(textElements[1].value).getTime(),
hasEndDate
}
}
});
let dataHTML = "";
if (textElements[0].value !== "") {
dataHTML = `<span data-content="${new Date(textElements[0].value).getTime()}">${dayjs(textElements[0].value).format("YYYY-MM-DD HH:mm")}</span>`;
}
if (hasEndDate && textElements[0].value !== "" && textElements[1].value !== "") {
dataHTML += `<svg class="custom-attr__avarrow"><use xlink:href="#iconForward"></use></svg><span data-content="${new Date(textElements[1].value).getTime()}">${dayjs(textElements[1].value).format("YYYY-MM-DD HH:mm")}</span>`;
}
dateElement.innerHTML = dataHTML;
});
if (dateMenu.isOpen) {
return;
}
const hasEndDate = dateElement.querySelector("svg");
const timeElements = dateElement.querySelectorAll("span");
dateMenu.addItem({
iconHTML: "",
label: `<input value="${timeElements[0] ? dayjs(parseInt(timeElements[0].dataset.content)).format("YYYY-MM-DDTHH:mm") : ""}" type="datetime-local" class="b3-text-field fn__size200" style="margin: 4px 0">`
});
dateMenu.addItem({
iconHTML: "",
label: `<input value="${timeElements[1] ? dayjs(parseInt(timeElements[1].dataset.content)).format("YYYY-MM-DDTHH:mm") : ""}" type="datetime-local" class="b3-text-field fn__size200${hasEndDate ? "" : " fn__none"}" style="margin: 4px 0">`
});
dateMenu.addSeparator();
dateMenu.addItem({
iconHTML: "",
label: `<label class="fn__flex">
<span>${window.siyuan.languages.endDate}</span>
<span class="fn__space fn__flex-1"></span>
<input type="checkbox" class="b3-switch fn__flex-center"${hasEndDate ? " checked" : ""}>
</label>`,
click(element, event) {
const switchElement = element.querySelector(".b3-switch") as HTMLInputElement;
if ((event.target as HTMLElement).tagName !== "INPUT") {
switchElement.checked = !switchElement.checked;
} else {
switchElement.outerHTML = `<input type="checkbox" class="b3-switch fn__flex-center"${switchElement.checked ? " checked" : ""}>`;
}
window.siyuan.menus.menu.element.querySelectorAll('[type="datetime-local"]')[1].classList.toggle("fn__none");
return true;
}
});
dateMenu.addSeparator();
dateMenu.addItem({
icon: "iconTrashcan",
label: window.siyuan.languages.clear,
click() {
const textElements = window.siyuan.menus.menu.element.querySelectorAll(".b3-text-field") as NodeListOf<HTMLInputElement>;
textElements[0].value = "";
textElements[1].value = "";
(window.siyuan.menus.menu.element.querySelector(".b3-switch") as HTMLInputElement).checked = false;
}
});
const datetRect = dateElement.getBoundingClientRect();
dateMenu.open({
x: datetRect.left,
y: datetRect.bottom
});
popDateMenu(dateElement);
event.stopPropagation();
event.preventDefault();
return;
}
const mSelectElement = hasClosestByAttribute(target, "data-type", "select") || hasClosestByAttribute(target, "data-type", "mSelect");
if (mSelectElement) {
const mSelectMenu = new Menu("custom-attr-av-select", () => {
const mSelect: { content: string, color: string }[] = [];
let mSelectHTML = "";
window.siyuan.menus.menu.element.querySelectorAll(".svg").forEach(item => {
const chipElement = item.parentElement.previousElementSibling.firstElementChild as HTMLElement;
const content = chipElement.textContent.trim();
const color = chipElement.dataset.color;
mSelect.push({
content,
color
});
mSelectHTML += `<span class="b3-chip b3-chip--middle" style="background-color:var(--b3-font-background${color});color:var(--b3-font-color${color})">${content}</span>`;
});
fetchPost("/api/av/setAttributeViewBlockAttr", {
avID: mSelectElement.dataset.avId,
keyID: mSelectElement.dataset.keyId,
rowID: mSelectElement.dataset.blockId,
cellID: mSelectElement.dataset.id,
value: {
mSelect
}
});
mSelectElement.innerHTML = mSelectHTML;
});
if (mSelectMenu.isOpen) {
return;
}
const names: string[] = [];
mSelectElement.querySelectorAll(".b3-chip").forEach(item => {
names.push(item.textContent.trim());
});
JSON.parse(mSelectElement.dataset.options || "").forEach((item: { name: string, color: string }) => {
mSelectMenu.addItem({
iconHTML: "",
label: `<span class="b3-chip" data-color="${item.color}" style="height:24px;background-color:var(--b3-font-background${item.color});color:var(--b3-font-color${item.color})">
<span class="fn__ellipsis">${item.name}</span>
</span>`,
accelerator: names.includes(item.name) ? '<svg class="svg" style="height: 30px; float: left;"><use xlink:href="#iconSelect"></use></svg>' : Constants.ZWSP,
click(element) {
const acceleratorElement = element.querySelector(".b3-menu__accelerator");
if (mSelectElement.dataset.type === "select") {
window.siyuan.menus.menu.element.querySelectorAll(".b3-menu__accelerator").forEach(itemElement => {
if (itemElement.isSameNode(acceleratorElement)) {
if (acceleratorElement.querySelector("svg")) {
acceleratorElement.innerHTML = "";
} else {
acceleratorElement.innerHTML = '<svg class="svg" style="height: 30px; float: left;"><use xlink:href="#iconSelect"></use></svg>';
}
} else {
itemElement.innerHTML = "";
}
});
return false;
}
if (acceleratorElement.querySelector("svg")) {
acceleratorElement.innerHTML = "";
} else {
acceleratorElement.innerHTML = '<svg class="svg" style="height: 30px; float: left;"><use xlink:href="#iconSelect"></use></svg>';
}
return true;
}
});
});
const mSelecttRect = mSelectElement.getBoundingClientRect();
mSelectMenu.open({
x: mSelecttRect.left,
y: mSelecttRect.bottom
});
popSelectMenu(mSelectElement);
event.stopPropagation();
event.preventDefault();
return;
}
const mAssetElement = hasClosestByAttribute(target, "data-type", "mAsset");
if (mAssetElement) {
popTextCell(protyle, [mAssetElement], "mAsset");
event.stopPropagation();
event.preventDefault();
return;
@ -267,7 +283,7 @@ class="fn__flex-1 fn__flex${["url", "text", "number", "email", "phone"].includes
}
fetchPost("/api/av/setAttributeViewBlockAttr", {
avID: item.parentElement.dataset.avId,
keyID: item.parentElement.dataset.keyId,
keyID: item.parentElement.dataset.colId,
rowID: item.parentElement.dataset.blockId,
cellID: item.parentElement.dataset.id,
value

View file

@ -339,8 +339,10 @@ export const openCalcMenu = (protyle: IProtyle, calcElement: HTMLElement) => {
menu.open({x: calcRect.left, y: calcRect.bottom, h: calcRect.height});
};
export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[]) => {
const type = cellElements[0].parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElements[0].getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol;
export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type?: TAVCol) => {
if (!type) {
type = cellElements[0].parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElements[0].getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol;
}
if (type === "block") {
return;
}

View file

@ -58,7 +58,7 @@ export const openMenuPanel = (options: {
</div>`);
avPanelElement = document.querySelector(".av__panel");
const menuElement = avPanelElement.lastElementChild as HTMLElement;
const tabRect = options.blockElement.querySelector(".layout-tab-bar").getBoundingClientRect();
const tabRect = options.blockElement.querySelector(".layout-tab-bar")?.getBoundingClientRect();
if (["select", "date", "asset"].includes(options.type)) {
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
if (options.type === "select") {
@ -94,7 +94,12 @@ export const openMenuPanel = (options: {
window.siyuan.dragElement.style.opacity = "";
const sourceElement = window.siyuan.dragElement;
window.siyuan.dragElement = undefined;
if (options.protyle.disabled) {
if (options.protyle && options.protyle.disabled) {
event.preventDefault();
event.stopPropagation();
return;
}
if (!options.protyle && window.siyuan.config.readonly) {
event.preventDefault();
event.stopPropagation();
return;