siyuan/app/src/protyle/render/av/rollup.ts

230 lines
10 KiB
TypeScript

import {Menu} from "../../../plugin/Menu";
import {hasClosestByClassName} from "../../util/hasClosest";
import {upDownHint} from "../../../util/upDownHint";
import {fetchPost} from "../../../util/fetch";
import {escapeHtml} from "../../../util/escape";
import {transaction} from "../../wysiwyg/transaction";
import {unicode2Emoji} from "../../../emoji";
import {getColIconByType, getColId} from "./col";
import {showMessage} from "../../../dialog/message";
import {getNameByOperator} from "./calc";
import {getFieldsByData} from "./view";
const updateCol = (options: {
target: HTMLElement,
data: IAV,
protyle: IProtyle,
colId: string,
isRelation: boolean,
}, itemElement: HTMLElement) => {
if (itemElement.classList.contains("b3-list--empty")) {
return;
}
options.target.querySelector(".b3-menu__accelerator").textContent = itemElement.querySelector(".b3-list-item__text").textContent;
const colData = getFieldsByData(options.data).find((item) => {
if (item.id === options.colId) {
if (!item.rollup) {
item.rollup = {};
}
return true;
}
});
if (options.isRelation) {
if (itemElement.dataset.colId === colData.rollup?.relationKeyID) {
return;
}
colData.rollup = {
relationKeyID: itemElement.dataset.colId
};
const goSearchRollupTargetElement = options.target.nextElementSibling as HTMLElement;
goSearchRollupTargetElement.querySelector(".b3-menu__accelerator").textContent = "";
goSearchRollupTargetElement.setAttribute("data-av-id", itemElement.dataset.targetAvId);
const goSearchRollupCalcElement = goSearchRollupTargetElement.nextElementSibling as HTMLElement;
goSearchRollupCalcElement.removeAttribute("data-col-type");
goSearchRollupCalcElement.removeAttribute("data-calc");
goSearchRollupCalcElement.querySelector(".b3-menu__accelerator").textContent = window.siyuan.languages.original;
} else {
if (itemElement.dataset.colId === colData.rollup?.keyID) {
return;
}
colData.rollup.keyID = itemElement.dataset.colId;
delete colData.rollup.calc;
const goSearchRollupCalcElement = options.target.nextElementSibling as HTMLElement;
goSearchRollupCalcElement.removeAttribute("data-calc");
goSearchRollupCalcElement.setAttribute("data-col-type", itemElement.dataset.colType);
goSearchRollupCalcElement.querySelector(".b3-menu__accelerator").textContent = window.siyuan.languages.original;
}
const oldColValue = JSON.parse(JSON.stringify(colData.rollup));
transaction(options.protyle, [{
action: "updateAttrViewColRollup",
id: options.colId,
avID: options.data.id,
parentID: colData.rollup.relationKeyID,
keyID: colData.rollup.keyID,
data: {
calc: colData.rollup.calc,
},
}], [{
action: "updateAttrViewColRollup",
id: options.colId,
avID: options.data.id,
parentID: oldColValue.relationKeyID,
keyID: oldColValue.keyID,
data: {
calc: oldColValue.calc,
}
}]);
};
const genSearchList = (element: Element, keyword: string, avId: string, isRelation: boolean, cb?: () => void) => {
if (!isRelation && !avId) {
showMessage(window.siyuan.languages.selectRelation);
return;
}
fetchPost(isRelation ? "/api/av/searchAttributeViewRelationKey" : "/api/av/searchAttributeViewRollupDestKeys", {
avID: avId,
keyword
}, (response) => {
let html = "";
response.data.keys.forEach((item: IAVColumn, index: number) => {
html += `<div class="b3-list-item b3-list-item--narrow${index === 0 ? " b3-list-item--focus" : ""}" data-col-id="${item.id}" ${isRelation ? `data-target-av-id="${item.relation.avID}"` : `data-col-type="${item.type}"`}>
${item.icon ? unicode2Emoji(item.icon, "b3-list-item__graphic", true) : `<svg class="b3-list-item__graphic"><use xlink:href="#${getColIconByType(item.type)}"></use></svg>`}
<span class="b3-list-item__text">${escapeHtml(item.name || window.siyuan.languages.title)}</span>
</div>`;
});
element.innerHTML = html || `<div class="b3-list--empty">${window.siyuan.languages.emptyContent}</div>`;
if (cb) {
cb();
}
});
};
export const goSearchRollupCol = (options: {
target: HTMLElement,
data: IAV,
protyle: IProtyle,
colId: string,
isRelation: boolean,
}) => {
window.siyuan.menus.menu.remove();
const menu = new Menu();
menu.addItem({
iconHTML: "",
type: "empty",
label: `<div class="fn__flex-column b3-menu__filter">
<input class="b3-text-field fn__flex-shrink" placeholder="${window.siyuan.languages[options.isRelation ? "searchRelation" : "searchRollupProperty"]}"/>
<div class="fn__hr"></div>
<div class="b3-list fn__flex-1 b3-list--background">
<img style="margin: 0 auto;display: block;width: 64px;height: 64px" src="/stage/loading-pure.svg">
</div>
</div>`,
bind(element) {
const listElement = element.querySelector(".b3-list");
const inputElement = element.querySelector("input");
inputElement.addEventListener("keydown", (event: KeyboardEvent) => {
if (event.isComposing) {
return;
}
const currentElement = upDownHint(listElement, event);
if (currentElement) {
event.stopPropagation();
}
if (event.key === "Enter") {
event.preventDefault();
event.stopPropagation();
updateCol(options, listElement.querySelector(".b3-list-item--focus") as HTMLElement);
window.siyuan.menus.menu.remove();
}
});
inputElement.addEventListener("input", (event) => {
event.stopPropagation();
genSearchList(listElement, inputElement.value, options.isRelation ? options.data.id : options.target.dataset.avId, options.isRelation);
});
element.lastElementChild.addEventListener("click", (event) => {
const listItemElement = hasClosestByClassName(event.target as HTMLElement, "b3-list-item");
if (listItemElement) {
event.stopPropagation();
updateCol(options, listItemElement);
window.siyuan.menus.menu.remove();
}
});
genSearchList(listElement, "", options.isRelation ? options.data.id : options.target.dataset.avId, options.isRelation, () => {
const rect = options.target.getBoundingClientRect();
menu.open({
x: rect.left,
y: rect.bottom,
h: rect.height,
});
element.querySelector("input").focus();
});
}
});
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 {
getFieldsByData(options.data).find((item) => {
if (item.id === getColId(options.cellElements[0], options.data.viewType)) {
colData = item;
return true;
}
});
}
return `<button class="b3-menu__item b3-menu__item--current" 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.rollupProperty}</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.rollupCalc}</span>
<span class="b3-menu__accelerator">${getNameByOperator(colData.rollup?.calc?.operator, true)}</span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>`;
};
export const bindRollupData = (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) {
getFieldsByData(options.data).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;
if (oldValue.calc) {
goSearchRollupCalcElement.dataset.calc = oldValue.calc.operator;
}
return true;
}
});
});
}
}
};