🎨 rollup cell menu

This commit is contained in:
Vanessa 2024-01-01 17:20:22 +08:00
parent 68a720db83
commit 5cd21dd855
4 changed files with 149 additions and 55 deletions

View file

@ -276,6 +276,8 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type
updateCellValueByInput(protyle, type, cellElements); updateCellValueByInput(protyle, type, cellElements);
} else if (type === "relation") { } else if (type === "relation") {
openMenuPanel({protyle, blockElement, type: "relation", cellElements}); openMenuPanel({protyle, blockElement, type: "relation", cellElements});
} else if (type === "rollup") {
openMenuPanel({protyle, blockElement, type: "rollup", cellElements, colId: cellElements[0].dataset.colId});
} }
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");
@ -644,8 +646,8 @@ 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 === "rollup") { } else if (cellValue.type === "rollup") {
cellValue?.rollup?.contents?.forEach((item) => { cellValue?.rollup?.contents?.forEach((item, index) => {
text += renderCell(item, wrap); text += renderRollup(item, wrap) + (index === cellValue.rollup.contents.length - 1 ? "" : " ,");
}); });
} else if (cellValue.type === "relation") { } else if (cellValue.type === "relation") {
cellValue?.relation?.contents?.forEach((item, index) => { cellValue?.relation?.contents?.forEach((item, index) => {
@ -659,6 +661,71 @@ export const renderCell = (cellValue: IAVCellValue, wrap: boolean) => {
return text; return text;
}; };
const renderRollup = (cellValue: IAVCellValue, wrap: boolean) => {
let text = ""
if (["text", "template"].includes(cellValue.type)) {
text = `<span class="av__celltext">${cellValue ? (cellValue[cellValue.type as "text"].content || "") : ""}</span>`;
} else if (["url", "email", "phone"].includes(cellValue.type)) {
const urlContent = cellValue ? cellValue[cellValue.type as "url"].content : "";
// https://github.com/siyuan-note/siyuan/issues/9291
let urlAttr = "";
if (cellValue.type === "url") {
urlAttr = ` data-href="${urlContent}"`;
}
text = `<span class="av__celltext av__celltext--url" data-type="${cellValue.type}"${urlAttr}>${urlContent}</span>`;
} else if (cellValue.type === "block") {
if (cellValue?.isDetached) {
text = `<span class="av__celltext">${cellValue.block.content || ""}</span>
<span class="b3-chip b3-chip--info b3-chip--small" data-type="block-more">${window.siyuan.languages.more}</span>`;
} else {
text = `<span data-type="block-ref" data-id="${cellValue.block.id}" data-subtype="s" class="av__celltext av__celltext--ref">${cellValue.block.content || ""}</span>
<span class="b3-chip b3-chip--info b3-chip--small popover__block" data-id="${cellValue.block.id}" data-type="block-more">${window.siyuan.languages.update}</span>`;
}
} else if (cellValue.type === "number") {
text = `<span style="float: right;${wrap ? "word-break: break-word;" : ""}" class="av__celltext">${cellValue?.number.formattedContent || cellValue?.number.content || ""}</span>`;
} else if (cellValue.type === "mSelect" || cellValue.type === "select") {
cellValue?.mSelect?.forEach((item) => {
text += `<span class="b3-chip" style="background-color:var(--b3-font-background${item.color});color:var(--b3-font-color${item.color})">${item.content}</span>`;
});
} else if (cellValue.type === "date") {
const dataValue = cellValue ? cellValue.date : null;
text = `<span class="av__celltext" data-value='${JSON.stringify(dataValue)}'>`;
if (dataValue && dataValue.isNotEmpty) {
text += dayjs(dataValue.content).format(dataValue.isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm");
}
if (dataValue && dataValue.hasEndDate && dataValue.isNotEmpty && dataValue.isNotEmpty2) {
text += `<svg class="av__cellicon"><use xlink:href="#iconForward"></use></svg>${dayjs(dataValue.content2).format(dataValue.isNotTime ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm")}`;
}
text += "</span>";
} else if (["created", "updated"].includes(cellValue.type)) {
const dataValue = cellValue ? cellValue[cellValue.type as "date"] : null;
text = `<span class="av__celltext" data-value='${JSON.stringify(dataValue)}'>`;
if (dataValue && dataValue.isNotEmpty) {
text += dayjs(dataValue.content).format("YYYY-MM-DD HH:mm");
}
text += "</span>";
} else if (cellValue.type === "mAsset") {
cellValue?.mAsset?.forEach((item) => {
if (item.type === "image") {
text += `<img class="av__cellassetimg" src="${item.content}">`;
} else {
text += `<span class="b3-chip av__celltext--url" data-url="${item.content}">${item.name}</span>`;
}
});
} else if (cellValue.type === "checkbox") {
text += `<svg class="av__checkbox"><use xlink:href="#icon${cellValue?.checkbox?.checked ? "Check" : "Uncheck"}"></use></svg>`;
} else if (cellValue.type === "rollup") {
cellValue?.rollup?.contents?.forEach((item) => {
text += renderCell(item, wrap) + '<span class="fn__space"></span>';
});
} else if (cellValue.type === "relation") {
cellValue?.relation?.contents?.forEach((item, index) => {
text += `<span class="av__celltext--ref" style="margin-right: 8px" data-id="${cellValue?.relation?.blockIDs[index]}">${item}</span>`;
});
}
return text;
}
export const updateHeaderCell = (cellElement: HTMLElement, headerValue: { export const updateHeaderCell = (cellElement: HTMLElement, headerValue: {
icon?: string, icon?: string,
name?: string, name?: string,

View file

@ -9,7 +9,7 @@ import {removeAttrViewColAnimation, updateAttrViewCellAnimation} from "./action"
import {openEmojiPanel, unicode2Emoji} from "../../../emoji"; import {openEmojiPanel, unicode2Emoji} from "../../../emoji";
import {focusBlock} from "../../util/selection"; import {focusBlock} from "../../util/selection";
import {toggleUpdateRelationBtn} from "./relation"; import {toggleUpdateRelationBtn} from "./relation";
import {getNameByOperator} from "./calc"; import {bindRollupEvent, getRollupHTML} from "./rollup";
export const duplicateCol = (options: { export const duplicateCol = (options: {
protyle: IProtyle, protyle: IProtyle,
@ -160,21 +160,7 @@ export const getEditHTML = (options: {
<button style="margin: 4px 0 8px;" class="b3-button fn__block" data-type="updateRelation">${window.siyuan.languages.confirm}</button> <button style="margin: 4px 0 8px;" class="b3-button fn__block" data-type="updateRelation">${window.siyuan.languages.confirm}</button>
</div>`; </div>`;
} else if (colData.type === "rollup") { } else if (colData.type === "rollup") {
html += `<button class="b3-menu__item" data-type="goSearchRollupCol" data-old-value='${JSON.stringify(colData.rollup || {})}'> html += getRollupHTML({colData});
<span class="b3-menu__label">${window.siyuan.languages.relation}</span>
<span class="b3-menu__accelerator"></span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item" data-type="goSearchRollupTarget">
<span class="b3-menu__label">${window.siyuan.languages.attr}</span>
<span class="b3-menu__accelerator"></span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item" data-type="goSearchRollupCalc">
<span class="b3-menu__label">${window.siyuan.languages.calc}</span>
<span class="b3-menu__accelerator">${getNameByOperator(colData.rollup?.calc?.operator)}</span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>`;
} }
return `<div class="b3-menu__items"> return `<div class="b3-menu__items">
${html} ${html}
@ -364,36 +350,7 @@ export const bindEditEvent = (options: {
toggleUpdateRelationBtn(options.menuElement, avID); toggleUpdateRelationBtn(options.menuElement, avID);
} }
} }
bindRollupEvent(options);
const goSearchRollupColElement = options.menuElement.querySelector('[data-type="goSearchRollupCol"]') as HTMLElement;
if (goSearchRollupColElement) {
const oldValue = JSON.parse(goSearchRollupColElement.dataset.oldValue) as IAVCellRollupValue;
const goSearchRollupTargetElement = options.menuElement.querySelector('[data-type="goSearchRollupTarget"]') as HTMLElement;
let targetKeyAVId = ""
if (oldValue.relationKeyID) {
options.data.view.columns.find((item) => {
if (item.id === oldValue.relationKeyID) {
goSearchRollupColElement.querySelector(".b3-menu__accelerator").textContent = item.name;
targetKeyAVId = item.relation.avID;
goSearchRollupTargetElement.dataset.avId = targetKeyAVId;
return true;
}
})
}
if (oldValue.keyID && targetKeyAVId) {
fetchPost("/api/av/getAttributeView", {id: targetKeyAVId}, (response) => {
response.data.av.keyValues.find((item: { key: { id: string, name: string, type: TAVCol } }) => {
if (item.key.id === oldValue.keyID) {
goSearchRollupTargetElement.querySelector('.b3-menu__accelerator').textContent = item.key.name;
const goSearchRollupCalcElement = options.menuElement.querySelector('[data-type="goSearchRollupCalc"]') as HTMLElement;
goSearchRollupCalcElement.dataset.colType = item.key.type;
goSearchRollupCalcElement.dataset.calc = oldValue.calc.operator;
return true;
}
});
});
}
}
}; };
export const getColNameByType = (type: TAVCol) => { export const getColNameByType = (type: TAVCol) => {

View file

@ -27,15 +27,15 @@ import {focusBlock, getEditorRange} from "../../util/selection";
import {avRender} from "./render"; import {avRender} from "./render";
import {setPageSize} from "./row"; import {setPageSize} from "./row";
import {bindRelationEvent, getRelationHTML, openSearchAV, setRelationCell, updateRelation} from "./relation"; import {bindRelationEvent, getRelationHTML, openSearchAV, setRelationCell, updateRelation} from "./relation";
import {goSearchRollupCol} from "./rollup"; import {bindRollupEvent, getRollupHTML, goSearchRollupCol} from "./rollup";
import {updateCellsValue} from "./cell"; import {updateCellsValue} from "./cell";
import {openCalcMenu} from "./calc"; import {openCalcMenu} from "./calc";
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" | "relation", type: "select" | "properties" | "config" | "sorts" | "filters" | "edit" | "date" | "asset" | "switcher" | "relation" | "rollup",
colId?: string, // for edit colId?: string, // for edit, rollup
cellElements?: HTMLElement[], // for select & date & relation & asset cellElements?: HTMLElement[], // for select & date & relation & asset
cb?: (avPanelElement: Element) => void cb?: (avPanelElement: Element) => void
}) => { }) => {
@ -70,6 +70,8 @@ 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 === "rollup") {
html = `<div class="b3-menu__items">${getRollupHTML({data, cellElements: options.cellElements})}</div>`;
} else if (options.type === "relation") { } else if (options.type === "relation") {
html = getRelationHTML(data, options.cellElements); html = getRelationHTML(data, options.cellElements);
if (!html) { if (!html) {
@ -90,7 +92,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", "relation"].includes(options.type)) { if (["select", "date", "asset", "relation", "rollup"].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, options.blockElement); bindSelectEvent(options.protyle, data, menuElement, options.cellElements, options.blockElement);
@ -115,8 +117,10 @@ export const openMenuPanel = (options: {
}, Constants.TIMEOUT_LOAD); // 等待加载 }, Constants.TIMEOUT_LOAD); // 等待加载
} else if (options.type === "relation") { } else if (options.type === "relation") {
bindRelationEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements}); bindRelationEvent({protyle: options.protyle, data, menuElement, cellElements: options.cellElements});
} else if (options.type === "rollup") {
bindRollupEvent({protyle: options.protyle, data, menuElement});
} }
if (["select", "date", "relation"].includes(options.type)) { if (["select", "date", "relation", "rollup"].includes(options.type)) {
const inputElement = menuElement.querySelector("input"); const inputElement = menuElement.querySelector("input");
if (inputElement) { if (inputElement) {
inputElement.select(); inputElement.select();
@ -787,7 +791,7 @@ export const openMenuPanel = (options: {
data, data,
isRelation: true, isRelation: true,
protyle: options.protyle, protyle: options.protyle,
colId: menuElement.querySelector(".b3-menu__item").getAttribute("data-col-id") colId: options.colId || menuElement.querySelector(".b3-menu__item").getAttribute("data-col-id")
}); });
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -798,7 +802,7 @@ export const openMenuPanel = (options: {
data, data,
isRelation: false, isRelation: false,
protyle: options.protyle, protyle: options.protyle,
colId: menuElement.querySelector(".b3-menu__item").getAttribute("data-col-id") colId: options.colId || menuElement.querySelector(".b3-menu__item").getAttribute("data-col-id")
}); });
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();

View file

@ -8,6 +8,7 @@ import {genIconHTML} from "../util";
import {unicode2Emoji} from "../../../emoji"; import {unicode2Emoji} from "../../../emoji";
import {getColIconByType} from "./col"; import {getColIconByType} from "./col";
import {showMessage} from "../../../dialog/message"; import {showMessage} from "../../../dialog/message";
import {getNameByOperator} from "./calc";
const updateCol = (options: { const updateCol = (options: {
target: HTMLElement, target: HTMLElement,
@ -144,3 +145,68 @@ export const goSearchRollupCol = (options: {
}); });
menu.element.querySelector(".b3-menu__items").setAttribute("style", "overflow: initial"); menu.element.querySelector(".b3-menu__items").setAttribute("style", "overflow: initial");
}; };
export const getRollupHTML = (options: { data?: IAV, cellElements?: HTMLElement[], colData?: IAVColumn }) => {
let colData: IAVColumn;
if (options.colData) {
colData = options.colData;
} else {
options.data.view.columns.find((item) => {
if (item.id === options.cellElements[0].dataset.colId) {
colData = item;
return true;
}
});
}
return `<button class="b3-menu__item" data-type="goSearchRollupCol" data-old-value='${JSON.stringify(colData.rollup || {})}'>
<span class="b3-menu__label">${window.siyuan.languages.relation}</span>
<span class="b3-menu__accelerator"></span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item" data-type="goSearchRollupTarget">
<span class="b3-menu__label">${window.siyuan.languages.attr}</span>
<span class="b3-menu__accelerator"></span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item" data-type="goSearchRollupCalc">
<span class="b3-menu__label">${window.siyuan.languages.calc}</span>
<span class="b3-menu__accelerator">${getNameByOperator(colData.rollup?.calc?.operator)}</span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>`
}
export const bindRollupEvent = (options: {
protyle: IProtyle,
data: IAV,
menuElement: HTMLElement
}) => {
const goSearchRollupColElement = options.menuElement.querySelector('[data-type="goSearchRollupCol"]') as HTMLElement;
if (goSearchRollupColElement) {
const oldValue = JSON.parse(goSearchRollupColElement.dataset.oldValue) as IAVCellRollupValue;
const goSearchRollupTargetElement = options.menuElement.querySelector('[data-type="goSearchRollupTarget"]') as HTMLElement;
let targetKeyAVId = ""
if (oldValue.relationKeyID) {
options.data.view.columns.find((item) => {
if (item.id === oldValue.relationKeyID) {
goSearchRollupColElement.querySelector(".b3-menu__accelerator").textContent = item.name;
targetKeyAVId = item.relation.avID;
goSearchRollupTargetElement.dataset.avId = targetKeyAVId;
return true;
}
})
}
if (oldValue.keyID && targetKeyAVId) {
fetchPost("/api/av/getAttributeView", {id: targetKeyAVId}, (response) => {
response.data.av.keyValues.find((item: { key: { id: string, name: string, type: TAVCol } }) => {
if (item.key.id === oldValue.keyID) {
goSearchRollupTargetElement.querySelector('.b3-menu__accelerator').textContent = item.key.name;
const goSearchRollupCalcElement = options.menuElement.querySelector('[data-type="goSearchRollupCalc"]') as HTMLElement;
goSearchRollupCalcElement.dataset.colType = item.key.type;
goSearchRollupCalcElement.dataset.calc = oldValue.calc.operator;
return true;
}
});
});
}
}
}