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", `
${html}
`); 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; } 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 ` `; };