Vanessa 2023-12-24 16:54:15 +08:00
parent 504d3f6155
commit f42873c893
4 changed files with 164 additions and 15 deletions

View file

@ -70,6 +70,13 @@ const genCellValueByElement = (colType: TAVCol, cellElement: HTMLElement) => {
checked: cellElement.querySelector("use").getAttribute("xlink:href") === "#iconCheck" ? true : false checked: cellElement.querySelector("use").getAttribute("xlink:href") === "#iconCheck" ? true : false
} }
}; };
}else if (colType === "relation") {
cellValue = {
type: colType,
relation: {
blockIDs: Array.from(cellElement.querySelectorAll("span")).map((item: HTMLElement) => item.getAttribute("data-id")),
}
};
} }
if (colType === "block") { if (colType === "block") {
cellValue.isDetached = cellElement.dataset.detached === "true"; cellValue.isDetached = cellElement.dataset.detached === "true";
@ -156,6 +163,13 @@ export const genCellValue = (colType: TAVCol, value: string | any) => {
checked: value ? true : false checked: value ? true : false
} }
}; };
}else if (colType === "relation") {
cellValue = {
type: colType,
relation: {
blockIDs: value as string[],
}
};
} }
} }
if (colType === "block") { if (colType === "block") {
@ -258,6 +272,8 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type
openMenuPanel({protyle, blockElement, type: "date", cellElements}); openMenuPanel({protyle, blockElement, type: "date", cellElements});
} else if (type === "checkbox") { } else if (type === "checkbox") {
updateCellValueByInput(protyle, type, cellElements); updateCellValueByInput(protyle, type, cellElements);
} else if (type === "relation") {
openMenuPanel({protyle, blockElement, type: "relation", cellElements});
} }
if (!hasClosestByClassName(cellElements[0], "custom-attr")) { if (!hasClosestByClassName(cellElements[0], "custom-attr")) {
cellElements[0].classList.add("av__cell--select"); cellElements[0].classList.add("av__cell--select");
@ -464,7 +480,7 @@ const updateCellValueByInput = (protyle: IProtyle, type: TAVCol, cellElements: H
}); });
}; };
export const updateCellsValue = (protyle: IProtyle, nodeElement: HTMLElement, value = "") => { export const updateCellsValue = (protyle: IProtyle, nodeElement: HTMLElement, value: string | any = "") => {
const doOperations: IOperation[] = []; const doOperations: IOperation[] = [];
const undoOperations: IOperation[] = []; const undoOperations: IOperation[] = [];
@ -583,6 +599,10 @@ export const renderCell = (cellValue: IAVCellValue, wrap: boolean) => {
}); });
} else if (cellValue.type === "checkbox") { } else if (cellValue.type === "checkbox") {
text += `<svg class="av__checkbox"><use xlink:href="#icon${cellValue?.checkbox?.checked ? "Check" : "Uncheck"}"></use></svg>`; text += `<svg class="av__checkbox"><use xlink:href="#icon${cellValue?.checkbox?.checked ? "Check" : "Uncheck"}"></use></svg>`;
} else if (cellValue.type === "relation") {
cellValue?.relation?.contents?.forEach((item, index) => {
text += `<span data-id="${cellValue?.relation?.blockIDs[index]}">${item}</span>`;
})
} }
if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated"].includes(cellValue.type) && if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated"].includes(cellValue.type) &&
cellValue && cellValue[cellValue.type as "url"].content) { cellValue && cellValue[cellValue.type as "url"].content) {

View file

@ -26,14 +26,14 @@ import {removeBlock} from "../../wysiwyg/remove";
import {getEditorRange} from "../../util/selection"; import {getEditorRange} from "../../util/selection";
import {avRender} from "./render"; import {avRender} from "./render";
import {setPageSize} from "./row"; import {setPageSize} from "./row";
import {openSearchAV, updateRelation} from "./relation"; import {bindRelationEvent, getRelationHTML, openSearchAV, setRelationCell, updateRelation} from "./relation";
export const openMenuPanel = (options: { export const openMenuPanel = (options: {
protyle: IProtyle, protyle: IProtyle,
blockElement: Element, blockElement: Element,
type: "select" | "properties" | "config" | "sorts" | "filters" | "edit" | "date" | "asset" | "switcher", type: "select" | "properties" | "config" | "sorts" | "filters" | "edit" | "date" | "asset" | "switcher" | "relation",
colId?: string, // for edit colId?: string, // for edit
cellElements?: HTMLElement[], // for select & date cellElements?: HTMLElement[], // for select & date & relation & asset
cb?: (avPanelElement: Element) => void cb?: (avPanelElement: Element) => void
}) => { }) => {
let avPanelElement = document.querySelector(".av__panel"); let avPanelElement = document.querySelector(".av__panel");
@ -67,6 +67,17 @@ export const openMenuPanel = (options: {
html = getEditHTML({protyle: options.protyle, data, colId: options.colId}); html = getEditHTML({protyle: options.protyle, data, colId: options.colId});
} else if (options.type === "date") { } else if (options.type === "date") {
html = getDateHTML(data.view, options.cellElements); html = getDateHTML(data.view, options.cellElements);
} else if (options.type === "relation") {
html = getRelationHTML(data, options.cellElements);
if (!html) {
openMenuPanel({
protyle: options.protyle,
blockElement: options.blockElement,
type: "edit",
colId: options.cellElements[0].dataset.colId
});
return;
}
} }
document.body.insertAdjacentHTML("beforeend", `<div class="av__panel" style="z-index: ${++window.siyuan.zIndex}"> document.body.insertAdjacentHTML("beforeend", `<div class="av__panel" style="z-index: ${++window.siyuan.zIndex}">
@ -76,7 +87,7 @@ export const openMenuPanel = (options: {
avPanelElement = document.querySelector(".av__panel"); avPanelElement = document.querySelector(".av__panel");
const menuElement = avPanelElement.lastElementChild as HTMLElement; const menuElement = avPanelElement.lastElementChild as HTMLElement;
const tabRect = options.blockElement.querySelector(".av__views")?.getBoundingClientRect(); const tabRect = options.blockElement.querySelector(".av__views")?.getBoundingClientRect();
if (["select", "date", "asset"].includes(options.type)) { if (["select", "date", "asset", "relation"].includes(options.type)) {
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect(); const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
if (options.type === "select") { if (options.type === "select") {
bindSelectEvent(options.protyle, data, menuElement, options.cellElements); bindSelectEvent(options.protyle, data, menuElement, options.cellElements);
@ -86,12 +97,16 @@ export const openMenuPanel = (options: {
bindAssetEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements}); bindAssetEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
setTimeout(() => { setTimeout(() => {
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height); setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
}, Constants.TIMEOUT_LOAD); // 等待图片加载 }, Constants.TIMEOUT_LOAD); // 等待加载
} else if (options.type === "relation") {
bindRelationEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
} }
if (["select", "date"].includes(options.type)) { if (["select", "date", "relation"].includes(options.type)) {
const inputElement = menuElement.querySelector("input"); const inputElement = menuElement.querySelector("input");
inputElement.select(); if (inputElement) {
inputElement.focus(); inputElement.select();
inputElement.focus();
}
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height); setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
} }
} else { } else {
@ -861,6 +876,11 @@ export const openMenuPanel = (options: {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
break; break;
} else if (type === "setRelationCell") {
setRelationCell(options.protyle, data, options.blockElement as HTMLElement, target);
event.preventDefault();
event.stopPropagation();
break;
} else if (type === "addColOptionOrCell") { } else if (type === "addColOptionOrCell") {
addColOptionOrCell(options.protyle, data, options.cellElements, target, menuElement); addColOptionOrCell(options.protyle, data, options.cellElements, target, menuElement);
window.siyuan.menus.menu.remove(); window.siyuan.menus.menu.remove();

View file

@ -4,6 +4,7 @@ import {upDownHint} from "../../../util/upDownHint";
import {fetchPost} from "../../../util/fetch"; import {fetchPost} from "../../../util/fetch";
import {escapeHtml} from "../../../util/escape"; import {escapeHtml} from "../../../util/escape";
import {transaction} from "../../wysiwyg/transaction"; import {transaction} from "../../wysiwyg/transaction";
import {updateCellsValue} from "./cell";
const genSearchList = (element: Element, keyword: string, avId: string, cb?: () => void) => { const genSearchList = (element: Element, keyword: string, avId: string, cb?: () => void) => {
fetchPost("/api/av/searchAttributeView", {keyword}, (response) => { fetchPost("/api/av/searchAttributeView", {keyword}, (response) => {
@ -135,7 +136,7 @@ export const toggleUpdateRelationBtn = (menuItemsElement: HTMLElement, avId: str
inputItemElement.classList.add("fn__none"); inputItemElement.classList.add("fn__none");
} }
const inputElement = inputItemElement.querySelector("input") as HTMLInputElement; const inputElement = inputItemElement.querySelector("input") as HTMLInputElement;
if ((searchElement.dataset.avId && oldValue.avID !== searchElement.dataset.avId)|| oldValue.isTwoWay !== switchElement.checked || inputElement.dataset.oldValue !== inputElement.value) { if ((searchElement.dataset.avId && oldValue.avID !== searchElement.dataset.avId) || oldValue.isTwoWay !== switchElement.checked || inputElement.dataset.oldValue !== inputElement.value) {
btnElement.classList.remove("fn__none"); btnElement.classList.remove("fn__none");
} else { } else {
btnElement.classList.add("fn__none"); btnElement.classList.add("fn__none");
@ -150,3 +151,105 @@ export const toggleUpdateRelationBtn = (menuItemsElement: HTMLElement, avId: str
btnElement.classList.remove("fn__none"); btnElement.classList.remove("fn__none");
} }
} }
export const bindRelationEvent = (options: {
protyle: IProtyle,
data: IAV,
menuElement: HTMLElement,
cellElements: HTMLElement[]
}) => {
const hasIds = options.menuElement.textContent.split(",");
fetchPost("/api/av/renderAttributeView", {
id: options.menuElement.firstElementChild.getAttribute("data-av-id"),
}, response => {
const avData = response.data as IAV;
let cellIndex = 0
avData.view.columns.find((item, index) => {
if (item.type === "block") {
cellIndex = index
return;
}
})
let html = ""
let selectHTML = ""
avData.view.rows.forEach((item) => {
const text = item.cells[cellIndex].value.block.content || item.cells[cellIndex].value.block.id;
if (hasIds.includes(item.id)) {
selectHTML += `<button data-id="${item.id}" data-type="setRelationCell" data-type="setRelationCell" class="b3-menu__item" draggable="true">
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
<span class="b3-menu__label">${text}</span>
</button>`
} else {
html += `<button data-id="${item.id}" class="b3-menu__item" data-type="setRelationCell">
<span class="b3-menu__label">${text}</span>
</button>`
}
})
const empty = `<button class="b3-menu__item">
<span class="b3-menu__label">${window.siyuan.languages.emptyContent}</span>
</button>`
options.menuElement.innerHTML = `<div class="b3-menu__items">${selectHTML || empty}
<button class="b3-menu__separator"></button>
${html || empty}</div>`
})
}
export const getRelationHTML = (data: IAV, cellElements?: HTMLElement[]) => {
let colRelationData: IAVCellRelationValue
data.view.columns.find(item => {
if (item.id === cellElements[0].dataset.colId) {
colRelationData = item.relation
return true;
}
})
if (colRelationData && colRelationData.avID) {
let ids = ""
cellElements[0].querySelectorAll("span").forEach((item) => {
ids += `${item.getAttribute("data-id")},`;
});
return `<span data-av-id="${colRelationData.avID}">${ids}</span>`
} else {
return ""
}
}
export const setRelationCell = (protyle: IProtyle, data: IAV, nodeElement: HTMLElement, target: HTMLElement) => {
const menuElement = hasClosestByClassName(target, "b3-menu__items");
if (!menuElement) {
return
}
const ids: string[] = [];
Array.from(menuElement.children).forEach((item) => {
const id = item.getAttribute("data-id")
if (item.getAttribute("draggable") && id) {
ids.push(id);
}
})
const empty = `<button class="b3-menu__item">
<span class="b3-menu__label">${window.siyuan.languages.emptyContent}</span>
</button>`
const targetId = target.getAttribute("data-id")
const separatorElement = menuElement.querySelector(".b3-menu__separator");
if (target.getAttribute("draggable")) {
if (!separatorElement.nextElementSibling.getAttribute("data-id")) {
separatorElement.nextElementSibling.remove();
}
ids.splice(ids.indexOf(targetId), 1);
separatorElement.after(target);
// TODO
if (!separatorElement.previousElementSibling) {
separatorElement.insertAdjacentHTML("beforebegin", empty);
}
} else {
if (!separatorElement.previousElementSibling.getAttribute("data-id")) {
separatorElement.previousElementSibling.remove();
}
ids.push(targetId);
separatorElement.before(target);
// TODO
if (!separatorElement.nextElementSibling) {
separatorElement.insertAdjacentHTML("afterend", empty);
}
}
updateCellsValue(protyle, nodeElement, ids);
};

View file

@ -1084,11 +1084,7 @@ interface IAVColumn {
name: string, name: string,
color: string, color: string,
}[], }[],
relation?: { relation?: IAVCellRelationValue
avID: string
backKeyID: string
isTwoWay: boolean
}
} }
interface IAVRow { interface IAVRow {
@ -1138,6 +1134,10 @@ interface IAVCellValue {
checkbox?: { checkbox?: {
checked: boolean checked: boolean
} }
relation?: {
blockIDs: string[]
contents?: string[]
}
date?: IAVCellDateValue date?: IAVCellDateValue
created?: IAVCellDateValue created?: IAVCellDateValue
updated?: IAVCellDateValue updated?: IAVCellDateValue
@ -1162,3 +1162,9 @@ interface IAVCellAssetValue {
name: string, name: string,
type: "file" | "image" type: "file" | "image"
} }
interface IAVCellRelationValue {
avID: string
backKeyID: string
isTwoWay: boolean
}