import {transaction} from "../../wysiwyg/transaction";
import {fetchPost} from "../../../util/fetch";
import {addCol} from "./addCol";
import {getColIconByType} from "./col";
import {setPosition} from "../../../util/setPosition";
import {Menu} from "../../../plugin/Menu";
import {hasClosestByClassName} from "../../util/hasClosest";
export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type: "properties" | "config" | "sorts" | "filters" = "config") => {
let avPanelElement = document.querySelector(".av__panel");
if (avPanelElement) {
avPanelElement.remove();
return;
}
window.siyuan.menus.menu.remove();
const avId = blockElement.getAttribute("data-av-id");
fetchPost("/api/av/renderAttributeView", {id: avId}, (response) => {
const data = response.data.av as IAV;
let html;
if (type === "config") {
html = getConfigHTML(data);
} else if (type === "properties") {
html = getPropertiesHTML(data);
} else if (type === "sorts") {
html = getSortsHTML(data);
} else if (type === "filters") {
html = getFiltersHTML(data);
}
document.body.insertAdjacentHTML("beforeend", `
`);
avPanelElement = document.querySelector(".av__panel");
const menuElement = avPanelElement.lastElementChild as HTMLElement;
const tabRect = blockElement.querySelector(".layout-tab-bar").getBoundingClientRect();
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
bindSortsEvent(protyle, menuElement, data);
avPanelElement.addEventListener("click", (event) => {
event.preventDefault();
let target = event.target as HTMLElement;
while (target && !target.isSameNode(avPanelElement)) {
const type = target.dataset.type;
if (type === "close") {
avPanelElement.remove();
window.siyuan.menus.menu.remove();
event.stopPropagation();
break;
} else if (type === "goConfig") {
menuElement.innerHTML = getConfigHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "goProperties") {
menuElement.innerHTML = getPropertiesHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "goSorts") {
menuElement.innerHTML = getSortsHTML(data);
bindSortsEvent(protyle, menuElement, data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "removeSorts") {
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
sorts: []
}
}], [{
action: "setAttrView",
id: avId,
data: {
sorts: data.sorts
}
}]);
data.sorts = [];
menuElement.innerHTML = getSortsHTML(data);
bindSortsEvent(protyle, menuElement, data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "addSort") {
addSort({data, rect: target.getBoundingClientRect(), menuElement, tabRect, avId, protyle});
event.stopPropagation();
break;
} else if (type === "removeSort") {
const oldSorts = Object.assign([], data.sorts);
data.sorts.find((item: IAVSort, index: number) => {
if (item.column === target.parentElement.dataset.id) {
data.sorts.splice(index, 1);
return true;
}
});
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
sorts: data.sorts
}
}], [{
action: "setAttrView",
id: avId,
data: {
sorts: oldSorts
}
}]);
menuElement.innerHTML = getSortsHTML(data);
bindSortsEvent(protyle, menuElement, data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "goFilters") {
menuElement.innerHTML = getFiltersHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "removeFilters") {
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
filters: []
}
}], [{
action: "setAttrView",
id: avId,
data: {
filters: data.filters
}
}]);
data.filters = [];
menuElement.innerHTML = getFiltersHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "addFilter") {
addFilter({data, rect: target.getBoundingClientRect(), menuElement, tabRect, avId, protyle});
event.stopPropagation();
break;
} else if (type === "removeFilter") {
window.siyuan.menus.menu.remove();
const oldFilters = Object.assign([], data.filters);
data.filters.find((item: IAVFilter, index: number) => {
if (item.column === target.parentElement.dataset.id) {
data.filters.splice(index, 1);
return true;
}
});
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
filters: data.filters
}
}], [{
action: "setAttrView",
id: avId,
data: {
filters: oldFilters
}
}]);
menuElement.innerHTML = getFiltersHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "setFilter") {
const colType = target.getAttribute("data-coltype") as TAVCol;
setFilter({
filter: {
operator: target.dataset.op as TAVFilterOperator,
column: target.parentElement.parentElement.dataset.id,
value: {
[colType]: {content: target.dataset.value}
}
},
protyle,
data,
target
});
event.stopPropagation();
break;
} else if (type === "newCol") {
avPanelElement.remove();
const addMenu = addCol(protyle, blockElement);
addMenu.open({
x: tabRect.right,
y: tabRect.bottom,
h: tabRect.height,
isLeft: true
});
event.stopPropagation();
break;
} else if (type === "showAllCol") {
const doOperations: IOperation[] = [];
const undoOperations: IOperation[] = [];
data.columns.forEach((item: IAVColumn) => {
if (item.hidden) {
doOperations.push({
action: "setAttrViewColHidden",
id: item.id,
parentID: avId,
data: false
});
undoOperations.push({
action: "setAttrViewColHidden",
id: item.id,
parentID: avId,
data: true
});
item.hidden = false;
}
});
if (doOperations.length > 0) {
transaction(protyle, doOperations, undoOperations);
menuElement.innerHTML = getPropertiesHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
}
event.stopPropagation();
break;
} else if (type === "hideAllCol") {
const doOperations: IOperation[] = [];
const undoOperations: IOperation[] = [];
data.columns.forEach((item: IAVColumn) => {
if (!item.hidden && item.type !== "block") {
doOperations.push({
action: "setAttrViewColHidden",
id: item.id,
parentID: avId,
data: true
});
undoOperations.push({
action: "setAttrViewColHidden",
id: item.id,
parentID: avId,
data: false
});
item.hidden = true;
}
});
if (doOperations.length > 0) {
transaction(protyle, doOperations, undoOperations);
menuElement.innerHTML = getPropertiesHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
}
event.stopPropagation();
break;
} else if (type === "hideCol") {
const colId = target.getAttribute("data-id");
transaction(protyle, [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: true
}], [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: false
}]);
data.columns.find((item: IAVColumn) => item.id === colId).hidden = true;
menuElement.innerHTML = getPropertiesHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
} else if (type === "showCol") {
const colId = target.getAttribute("data-id");
transaction(protyle, [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: false
}], [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: true
}]);
data.columns.find((item: IAVColumn) => item.id === colId).hidden = false;
menuElement.innerHTML = getPropertiesHTML(data);
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
event.stopPropagation();
break;
}
target = target.parentElement;
}
});
});
};
const addSort = (options: {
data: IAV,
rect: DOMRect,
menuElement: HTMLElement,
tabRect: DOMRect,
avId: string,
protyle: IProtyle
}) => {
const menu = new Menu("av-add-sort");
options.data.columns.forEach((column) => {
let hasSort = false;
options.data.sorts.find((sort) => {
if (sort.column === column.id) {
hasSort = true;
return true;
}
});
if (!hasSort) {
menu.addItem({
label: column.name,
icon: getColIconByType(column.type),
click: () => {
const oldSorts = Object.assign([], options.data.sorts);
options.data.sorts.push({
column: column.id,
order: "ASC",
});
transaction(options.protyle, [{
action: "setAttrView",
id: options.avId,
data: {
sorts: options.data.sorts
}
}], [{
action: "setAttrView",
id: options.avId,
data: {
sorts: oldSorts
}
}]);
options.menuElement.innerHTML = getSortsHTML(options.data);
bindSortsEvent(options.protyle, options.menuElement, options.data);
setPosition(options.menuElement, options.tabRect.right - options.menuElement.clientWidth, options.tabRect.bottom, options.tabRect.height);
}
});
}
});
menu.open({
x: options.rect.left,
y: options.rect.bottom,
h: options.rect.height,
});
};
const bindSortsEvent = (protyle: IProtyle, menuElement: HTMLElement, data: IAV) => {
menuElement.querySelectorAll("select").forEach((item: HTMLSelectElement) => {
item.addEventListener("change", () => {
const colId = item.parentElement.getAttribute("data-id");
const oldSort = JSON.parse(JSON.stringify(data.sorts));
if (item.previousElementSibling.classList.contains("b3-menu__icon")) {
data.sorts.find((sort: IAVSort) => {
if (sort.column === colId) {
sort.column = item.value;
item.parentElement.setAttribute("data-id", item.value);
return true;
}
});
} else {
data.sorts.find((sort: IAVSort) => sort.column === colId).order = item.value as "ASC" | "DESC";
}
transaction(protyle, [{
action: "setAttrView",
id: data.id,
data: {
sorts: data.sorts
}
}], [{
action: "setAttrView",
id: data.id,
data: {
sorts: oldSort
}
}]);
});
});
};
const getSortsHTML = (data: IAV) => {
let html = "";
const genSortItem = (id: string) => {
let sortHTML = "";
data.columns.forEach((item) => {
sortHTML += ``;
});
return sortHTML;
};
data.sorts.forEach((item: IAVSort) => {
html += ``;
});
return `
${html}
`;
};
export const setFilter = (options: {
filter: IAVFilter,
protyle: IProtyle,
data: IAV,
target: HTMLElement,
}) => {
const colType = Object.keys(options.filter.value)[0] as TAVCol;
const rectTarget = options.target.getBoundingClientRect();
const menu = new Menu("set-filter-" + options.filter.column, () => {
const oldFilters = JSON.parse(JSON.stringify(options.data.filters));
options.data.filters.find((filter) => {
if (filter.column === options.filter.column) {
filter.value[colType] = {
content: textElement.value
};
filter.operator = (window.siyuan.menus.menu.element.querySelector(".b3-select") as HTMLSelectElement).value as TAVFilterOperator;
return true;
}
});
transaction(options.protyle, [{
action: "setAttrView",
id: options.data.id,
data: {
filters: options.data.filters
}
}], [{
action: "setAttrView",
id: options.data.id,
data: {
filters: oldFilters
}
}]);
const menuElement = hasClosestByClassName(options.target, "b3-menu");
if (menuElement) {
menuElement.innerHTML = getFiltersHTML(options.data);
}
});
if (menu.isOpen) {
return;
}
let selectHTML = "";
switch (colType) {
case "text":
selectHTML = `
`;
break;
case "number":
selectHTML = `
`;
break;
}
menu.addItem({
iconHTML: "",
label: ``
});
menu.addItem({
iconHTML: "",
label: ``
});
const textElement = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement);
textElement.addEventListener("keydown", (event) => {
if (event.isComposing) {
event.preventDefault();
return;
}
if (event.key === "Enter") {
menu.close();
event.preventDefault();
}
});
menu.open({x: rectTarget.left, y: rectTarget.bottom});
textElement.select();
};
const addFilter = (options: {
data: IAV,
rect: DOMRect,
menuElement: HTMLElement,
tabRect: DOMRect,
avId: string,
protyle: IProtyle
}) => {
const menu = new Menu("av-add-filter");
options.data.columns.forEach((column) => {
let hasFilter = false;
options.data.filters.find((filter) => {
if (filter.column === column.id) {
hasFilter = true;
return true;
}
});
if (!hasFilter) {
menu.addItem({
label: column.name,
icon: getColIconByType(column.type),
click: () => {
const oldFilters = Object.assign([], options.data.filters);
options.data.filters.push({
column: column.id,
operator: "Contains",
value: {
[column.type]: {
content: ""
}
},
});
transaction(options.protyle, [{
action: "setAttrView",
id: options.avId,
data: {
filters: options.data.filters
}
}], [{
action: "setAttrView",
id: options.avId,
data: {
filters: oldFilters
}
}]);
options.menuElement.innerHTML = getFiltersHTML(options.data);
setPosition(options.menuElement, options.tabRect.right - options.menuElement.clientWidth, options.tabRect.bottom, options.tabRect.height);
const filterElement = options.menuElement.querySelector(`[data-id="${column.id}"] .b3-chip`) as HTMLElement;
const colType = filterElement.getAttribute("data-coltype") as TAVCol;
setFilter({
filter: {
operator: filterElement.dataset.op as TAVFilterOperator,
column: filterElement.parentElement.parentElement.dataset.id,
value: {
[colType]: {content: filterElement.dataset.value}
}
},
protyle: options.protyle,
data: options.data,
target: filterElement
});
}
});
}
});
menu.open({
x: options.rect.left,
y: options.rect.bottom,
h: options.rect.height,
});
};
const getFiltersHTML = (data: IAV) => {
let html = "";
const genFilterItem = (filter: IAVFilter) => {
let filterHTML = "";
data.columns.find((item) => {
if (item.id === filter.column) {
const filterValue = (filter.value && filter.value[item.type] && filter.value[item.type].content) ? filter.value[item.type].content : "";
filterHTML += `
${item.name}${filterValue ? ": " + filterValue : ""}
`;
return true;
}
});
return filterHTML;
};
data.filters.forEach((item: IAVFilter) => {
html += ``;
});
return `
${html}
`;
};
const getPropertiesHTML = (data: IAV) => {
let showHTML = "";
let hideHTML = "";
data.columns.forEach((item: IAVColumn) => {
if (item.hidden) {
hideHTML += ``;
} else {
showHTML += ``;
}
});
if (hideHTML) {
hideHTML = `
${hideHTML}`;
}
return `
${showHTML}
${hideHTML}
`;
};
const getConfigHTML = (data: IAV) => {
return `
`;
};