mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-22 17:40:13 +01:00
This commit is contained in:
parent
504d3f6155
commit
f42873c893
4 changed files with 164 additions and 15 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
|
||||||
16
app/src/types/index.d.ts
vendored
16
app/src/types/index.d.ts
vendored
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue