diff --git a/app/src/protyle/render/av/cell.ts b/app/src/protyle/render/av/cell.ts
index 75c946c99..c0aa3a893 100644
--- a/app/src/protyle/render/av/cell.ts
+++ b/app/src/protyle/render/av/cell.ts
@@ -70,6 +70,13 @@ const genCellValueByElement = (colType: TAVCol, cellElement: HTMLElement) => {
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") {
cellValue.isDetached = cellElement.dataset.detached === "true";
@@ -156,6 +163,13 @@ export const genCellValue = (colType: TAVCol, value: string | any) => {
checked: value ? true : false
}
};
+ }else if (colType === "relation") {
+ cellValue = {
+ type: colType,
+ relation: {
+ blockIDs: value as string[],
+ }
+ };
}
}
if (colType === "block") {
@@ -258,6 +272,8 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type
openMenuPanel({protyle, blockElement, type: "date", cellElements});
} else if (type === "checkbox") {
updateCellValueByInput(protyle, type, cellElements);
+ } else if (type === "relation") {
+ openMenuPanel({protyle, blockElement, type: "relation", cellElements});
}
if (!hasClosestByClassName(cellElements[0], "custom-attr")) {
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 undoOperations: IOperation[] = [];
@@ -583,6 +599,10 @@ export const renderCell = (cellValue: IAVCellValue, wrap: boolean) => {
});
} else if (cellValue.type === "checkbox") {
text += ``;
+ } else if (cellValue.type === "relation") {
+ cellValue?.relation?.contents?.forEach((item, index) => {
+ text += `${item}`;
+ })
}
if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated"].includes(cellValue.type) &&
cellValue && cellValue[cellValue.type as "url"].content) {
diff --git a/app/src/protyle/render/av/openMenuPanel.ts b/app/src/protyle/render/av/openMenuPanel.ts
index 5a4f18450..e497a6365 100644
--- a/app/src/protyle/render/av/openMenuPanel.ts
+++ b/app/src/protyle/render/av/openMenuPanel.ts
@@ -26,14 +26,14 @@ import {removeBlock} from "../../wysiwyg/remove";
import {getEditorRange} from "../../util/selection";
import {avRender} from "./render";
import {setPageSize} from "./row";
-import {openSearchAV, updateRelation} from "./relation";
+import {bindRelationEvent, getRelationHTML, openSearchAV, setRelationCell, updateRelation} from "./relation";
export const openMenuPanel = (options: {
protyle: IProtyle,
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
- cellElements?: HTMLElement[], // for select & date
+ cellElements?: HTMLElement[], // for select & date & relation & asset
cb?: (avPanelElement: Element) => void
}) => {
let avPanelElement = document.querySelector(".av__panel");
@@ -67,6 +67,17 @@ export const openMenuPanel = (options: {
html = getEditHTML({protyle: options.protyle, data, colId: options.colId});
} else if (options.type === "date") {
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", `
@@ -76,7 +87,7 @@ export const openMenuPanel = (options: {
avPanelElement = document.querySelector(".av__panel");
const menuElement = avPanelElement.lastElementChild as HTMLElement;
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();
if (options.type === "select") {
bindSelectEvent(options.protyle, data, menuElement, options.cellElements);
@@ -86,12 +97,16 @@ export const openMenuPanel = (options: {
bindAssetEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
setTimeout(() => {
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");
- inputElement.select();
- inputElement.focus();
+ if (inputElement) {
+ inputElement.select();
+ inputElement.focus();
+ }
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
}
} else {
@@ -861,6 +876,11 @@ export const openMenuPanel = (options: {
event.preventDefault();
event.stopPropagation();
break;
+ } else if (type === "setRelationCell") {
+ setRelationCell(options.protyle, data, options.blockElement as HTMLElement, target);
+ event.preventDefault();
+ event.stopPropagation();
+ break;
} else if (type === "addColOptionOrCell") {
addColOptionOrCell(options.protyle, data, options.cellElements, target, menuElement);
window.siyuan.menus.menu.remove();
diff --git a/app/src/protyle/render/av/relation.ts b/app/src/protyle/render/av/relation.ts
index 9cc6c2bf6..b3312d79b 100644
--- a/app/src/protyle/render/av/relation.ts
+++ b/app/src/protyle/render/av/relation.ts
@@ -4,6 +4,7 @@ import {upDownHint} from "../../../util/upDownHint";
import {fetchPost} from "../../../util/fetch";
import {escapeHtml} from "../../../util/escape";
import {transaction} from "../../wysiwyg/transaction";
+import {updateCellsValue} from "./cell";
const genSearchList = (element: Element, keyword: string, avId: string, cb?: () => void) => {
fetchPost("/api/av/searchAttributeView", {keyword}, (response) => {
@@ -135,7 +136,7 @@ export const toggleUpdateRelationBtn = (menuItemsElement: HTMLElement, avId: str
inputItemElement.classList.add("fn__none");
}
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");
} else {
btnElement.classList.add("fn__none");
@@ -150,3 +151,105 @@ export const toggleUpdateRelationBtn = (menuItemsElement: HTMLElement, avId: str
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 += ``
+ } else {
+ html += ``
+ }
+ })
+ const empty = ``
+ options.menuElement.innerHTML = ``
+ })
+}
+
+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 `${ids}`
+ } 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 = ``
+ 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);
+};
diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts
index 88d1da3c4..e9dd3f4dc 100644
--- a/app/src/types/index.d.ts
+++ b/app/src/types/index.d.ts
@@ -1084,11 +1084,7 @@ interface IAVColumn {
name: string,
color: string,
}[],
- relation?: {
- avID: string
- backKeyID: string
- isTwoWay: boolean
- }
+ relation?: IAVCellRelationValue
}
interface IAVRow {
@@ -1138,6 +1134,10 @@ interface IAVCellValue {
checkbox?: {
checked: boolean
}
+ relation?: {
+ blockIDs: string[]
+ contents?: string[]
+ }
date?: IAVCellDateValue
created?: IAVCellDateValue
updated?: IAVCellDateValue
@@ -1162,3 +1162,9 @@ interface IAVCellAssetValue {
name: string,
type: "file" | "image"
}
+
+interface IAVCellRelationValue {
+ avID: string
+ backKeyID: string
+ isTwoWay: boolean
+}