import {hasClosestBlock} from "../../util/hasClosest"; import {Menu} from "../../../plugin/Menu"; import {transaction} from "../../wysiwyg/transaction"; import {fetchPost} from "../../../util/fetch"; import {getDefaultOperatorByType, setFilter} from "./filter"; import {genCellValue} from "./cell"; import {openMenuPanel} from "./openMenuPanel"; export const duplicateCol = (protyle: IProtyle, type: TAVCol, avID: string, colId: string, newValue: string) => { const id = Lute.NewNodeID(); const nameMatch = newValue.match(/^(.*) \((\d+)\)$/); if (nameMatch) { newValue = `${nameMatch[1]} (${parseInt(nameMatch[2]) + 1})`; } else { newValue = `${newValue} (1)`; } if (["select", "mSelect"].includes(type)) { fetchPost("/api/av/renderAttributeView", {id: avID}, (response) => { const data = response.data as IAV; let colOptions; data.view.columns.find((item) => { if (item.id === colId) { colOptions = item.options; return true; } }); transaction(protyle, [{ action: "addAttrViewCol", name: newValue, avID, type, id }, { action: "sortAttrViewCol", avID, previousID: colId, id }, { action: "updateAttrViewColOptions", id, avID, data: colOptions }], [{ action: "removeAttrViewCol", id, avID, }]); }); } else { transaction(protyle, [{ action: "addAttrViewCol", name: newValue, avID, type, id }, { action: "sortAttrViewCol", avID, previousID: colId, id }], [{ action: "removeAttrViewCol", id, avID, }]); } } export const getEditHTML = (options: { protyle: IProtyle, colId: string, data: IAV }) => { let colData: IAVColumn options.data.view.columns.find((item) => { if (item.id === options.colId) { colData = item; return true; } }) let html = ` `; if (colData.options && colData.options.length > 0) { html += ` ` colData.options.forEach(item => { html += ``; }); } return `${html} `; }; export const bindEditEvent = (options: { protyle: IProtyle, data: IAV, menuElement: HTMLElement }) => { const avID = options.data.id; const colId = options.menuElement.firstElementChild.getAttribute("data-col-id"); const colData = options.data.view.columns.find((item: IAVColumn) => item.id === colId); const nameElement = options.menuElement.querySelector('[data-type="name"]') as HTMLInputElement; nameElement.addEventListener("blur", (event) => { const newValue = nameElement.value; if (newValue === colData.name) { return; } transaction(options.protyle, [{ action: "updateAttrViewCol", id: colId, avID, name: newValue, type: colData.type, }], [{ action: "updateAttrViewCol", id: colId, avID, name: colData.name, type: colData.type, }]); colData.name = newValue; }); nameElement.addEventListener("keydown", (event: KeyboardEvent) => { if (event.isComposing) { return } if (event.key === "Escape") { options.menuElement.parentElement.remove(); } }); const addOptionElement = options.menuElement.querySelector('[data-type="addOption"]') as HTMLInputElement; addOptionElement.addEventListener("keydown", (event: KeyboardEvent) => { if (event.isComposing) { return; } if (event.key === "Escape") { options.menuElement.parentElement.remove(); } if (event.key === "Enter") { let hasSelected = false; colData.options.find((item) => { if (addOptionElement.value === item.name) { hasSelected = true; return true; } }); if (hasSelected) { return; } colData.options.push({ color: (colData.options.length + 1).toString(), name: addOptionElement.value }); transaction(options.protyle, [{ action: "updateAttrViewColOptions", id: colId, avID, data: colData.options }], [{ action: "removeAttrViewColOption", id: colId, avID, data: addOptionElement.value }]); options.menuElement.innerHTML = getEditHTML({protyle: options.protyle, colId, data: options.data}); bindEditEvent({protyle: options.protyle, menuElement: options.menuElement, data: options.data}); (options.menuElement.querySelector('[data-type="addOption"]') as HTMLInputElement).focus(); } }); } export const getColIconByType = (type: TAVCol) => { switch (type) { case "text": return "iconAlignLeft"; case "block": return "iconParagraph"; case "number": return "iconNumber"; case "select": return "iconListItem"; case "mSelect": return "iconList"; case "date": return "iconCalendar"; } }; export const updateHeader = (rowElement: HTMLElement) => { const blockElement = hasClosestBlock(rowElement); if (!blockElement) { return; } const selectCount = rowElement.parentElement.querySelectorAll(".av__row--select:not(.av__row--header)").length; const diffCount = rowElement.parentElement.childElementCount - 3 - selectCount; const headElement = rowElement.parentElement.firstElementChild; const headUseElement = headElement.querySelector("use"); const counterElement = blockElement.querySelector(".av__counter"); const avHeadElement = blockElement.querySelector(".av__header") as HTMLElement; if (diffCount === 0) { headElement.classList.add("av__row--select"); headUseElement.setAttribute("xlink:href", "#iconCheck"); } else if (diffCount === rowElement.parentElement.childElementCount - 3) { headElement.classList.remove("av__row--select"); headUseElement.setAttribute("xlink:href", "#iconUncheck"); counterElement.classList.add("fn__none"); avHeadElement.style.position = ""; return; } else if (diffCount > 0) { headElement.classList.add("av__row--select"); headUseElement.setAttribute("xlink:href", "#iconIndeterminateCheck"); } counterElement.classList.remove("fn__none"); counterElement.innerHTML = `${selectCount} selected`; avHeadElement.style.position = "sticky"; }; export const showColMenu = (protyle: IProtyle, blockElement: HTMLElement, cellElement: HTMLElement) => { const type = cellElement.getAttribute("data-dtype") as TAVCol; const colId = cellElement.getAttribute("data-col-id"); const avID = blockElement.getAttribute("data-av-id"); const menu = new Menu("av-header-cell", () => { const newValue = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value; if (newValue === cellElement.textContent.trim()) { return; } transaction(protyle, [{ action: "updateAttrViewCol", id: colId, avID, name: newValue, type, }], [{ action: "updateAttrViewCol", id: colId, avID, name: cellElement.textContent.trim(), type, }]); }); menu.addItem({ icon: getColIconByType(type), label: ``, }); if (type !== "block") { menu.addItem({ icon: "iconEdit", label: window.siyuan.languages.edit, click() { openMenuPanel({protyle, blockElement, type: "edit", colId}); } }); } menu.addSeparator(); menu.addItem({ icon: "iconUp", label: window.siyuan.languages.asc, click() { fetchPost("/api/av/renderAttributeView", {id: avID}, (response) => { transaction(protyle, [{ action: "setAttrViewSorts", avID: response.data.id, data: [{ column: colId, order: "ASC" }] }], [{ action: "setAttrViewSorts", avID: response.data.id, data: response.data.view.sorts }]); }); } }); menu.addItem({ icon: "iconDown", label: window.siyuan.languages.desc, click() { fetchPost("/api/av/renderAttributeView", {id: avID}, (response) => { transaction(protyle, [{ action: "setAttrViewSorts", avID: response.data.id, data: [{ column: colId, order: "DESC" }] }], [{ action: "setAttrViewSorts", avID: response.data.id, data: response.data.view.sorts }]); }); } }); menu.addItem({ icon: "iconFilter", label: window.siyuan.languages.filter, click() { fetchPost("/api/av/renderAttributeView", {id: avID}, (response) => { const avData = response.data as IAV; let filter: IAVFilter; avData.view.filters.find((item) => { if (item.column === colId) { filter = item; return true; } }); if (!filter) { filter = { column: colId, operator: getDefaultOperatorByType(type), value: genCellValue(type, "") }; avData.view.filters.push(filter); transaction(protyle, [{ action: "setAttrViewFilters", avID, data: [filter] }], [{ action: "setAttrViewFilters", avID, data: [] }]); } setFilter({ filter, protyle, data: avData, target: cellElement, }); }); } }); menu.addSeparator(); if (type !== "block") { menu.addItem({ icon: "iconEyeoff", label: window.siyuan.languages.hide, click() { transaction(protyle, [{ action: "setAttrViewColHidden", id: colId, avID, data: true }], [{ action: "setAttrViewColHidden", id: colId, avID, data: false }]); } }); menu.addItem({ icon: "iconCopy", label: window.siyuan.languages.duplicate, click() { duplicateCol(protyle, type, avID, colId, (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value); } }); menu.addItem({ icon: "iconTrashcan", label: window.siyuan.languages.delete, click() { transaction(protyle, [{ action: "removeAttrViewCol", id: colId, avID, }], [{ action: "addAttrViewCol", name: cellElement.textContent.trim(), avID, type: type, id: colId }]); } }); menu.addSeparator(); } menu.addItem({ label: `