import {Menu} from "../../../plugin/Menu"; import {transaction} from "../../wysiwyg/transaction"; import {hasClosestByClassName} from "../../util/hasClosest"; import {confirmDialog} from "../../../dialog/confirmDialog"; import {upDownHint} from "../../../util/upDownHint"; import {bindEditEvent, getEditHTML} from "./col"; import {updateAttrViewCellAnimation} from "./action"; import {genAVValueHTML} from "./blockAttr"; const filterSelectHTML = (key: string, options: { name: string, color: string }[]) => { let html = ""; let hasMatch = false; if (options) { options.forEach(item => { if (!key || (key.toLowerCase().indexOf(item.name.toLowerCase()) > -1 || item.name.toLowerCase().indexOf(key.toLowerCase()) > -1)) { html += ``; } if (key === item.name) { hasMatch = true; } }); } if (!hasMatch && key) { const colorIndex = (options?.length || 0) % 13 + 1; html = `${html}`; } return html; }; export const removeCellOption = (protyle: IProtyle, data: IAV, cellElements: HTMLElement[], target: HTMLElement) => { if (!target) { return; } const colId = cellElements[0].dataset.colId; const doOperations: IOperation[] = []; const undoOperations: IOperation[] = []; let newData: IAVCellSelectValue[]; cellElements.forEach((item, elementIndex) => { const rowID = (hasClosestByClassName(item, "av__row") as HTMLElement).dataset.id; const cellId = item.dataset.id; let cellData: IAVCell; data.view.rows.find(row => { if (row.id === rowID) { row.cells.find(cell => { if (cell.id === cellId) { cellData = cell; return true; } }); return true; } }); const oldValue = Object.assign([], cellData.value.mSelect); if (elementIndex === 0) { cellData.value.mSelect?.find((item: { content: string }, index: number) => { if (item.content === target.dataset.content) { cellData.value.mSelect.splice(index, 1); return true; } }); newData = cellData.value.mSelect; } else { cellData.value.mSelect = newData; } doOperations.push({ action: "updateAttrViewCell", id: cellId, keyID: colId, rowID, avID: data.id, data: cellData.value }); undoOperations.push({ action: "updateAttrViewCell", id: cellId, keyID: colId, rowID, avID: data.id, data: { mSelect: oldValue } }); if (item.classList.contains("custom-attr__avvalue")) { item.innerHTML = genAVValueHTML(cellData.value); } else { updateAttrViewCellAnimation(item); } }); transaction(protyle, doOperations, undoOperations); target.remove(); }; export const setColOption = (protyle: IProtyle, data: IAV, target: HTMLElement, cellElements?: HTMLElement[]) => { const menuElement = hasClosestByClassName(target, "b3-menu"); if (!menuElement) { return; } const colId = cellElements ? cellElements[0].dataset.colId : menuElement.querySelector(".b3-menu__item").getAttribute("data-col-id"); let name = target.parentElement.dataset.name; let color = target.parentElement.dataset.color; const menu = new Menu("av-col-option", () => { if (name === inputElement.value || !inputElement.value) { return; } let hasName = false data.view.columns.find(column => { if (column.id === colId) { column.options.find((item) => { if (item.name === inputElement.value) { hasName = true; return true; } }); return true; } }); if (hasName) { return; } transaction(protyle, [{ action: "updateAttrViewColOption", id: colId, avID: data.id, data: { newColor: color, oldName: name, newName: inputElement.value }, }], [{ action: "updateAttrViewColOption", id: colId, avID: data.id, data: { newColor: color, oldName: inputElement.value, newName: name }, }]); data.view.columns.find(column => { if (column.id === colId) { column.options.find((item) => { if (item.name === name) { item.name = inputElement.value; return true; } }); return true; } }); if (!cellElements) { menuElement.innerHTML = getEditHTML({protyle, data, colId}); bindEditEvent({protyle, data, menuElement}); } else { cellElements.forEach((cellElement: HTMLMediaElement) => { data.view.rows.find(row => { if (row.id === (hasClosestByClassName(cellElement, "av__row") as HTMLElement).dataset.id) { row.cells.find(cell => { if (cell.id === cellElement.dataset.id) { cell.value.mSelect.find((item) => { if (item.content === name) { item.content = inputElement.value; return true; } }); if (cellElement.classList.contains("custom-attr__avvalue")) { cellElement.innerHTML = genAVValueHTML(cell.value); } else { updateAttrViewCellAnimation(cellElement); } return true; } }); return true; } }); }); menuElement.innerHTML = getSelectHTML(data.view, cellElements); bindSelectEvent(protyle, data, menuElement, cellElements); } }); if (menu.isOpen) { return; } menu.addItem({ iconHTML: "", label: ``, bind(element) { element.querySelector("input").addEventListener("keydown", (event: KeyboardEvent) => { if (event.isComposing) { return; } if (event.key === "Enter") { menu.close(); } }); } }); menu.addItem({ label: window.siyuan.languages.delete, icon: "iconTrashcan", click() { confirmDialog(window.siyuan.languages.deleteOpConfirm, window.siyuan.languages.confirmDelete, () => { let colOptions: { name: string, color: string }[] = []; data.view.columns.find(column => { if (column.id === colId) { colOptions = column.options; return true; } }); const newName = target.parentElement.dataset.name; transaction(protyle, [{ action: "removeAttrViewColOption", id: colId, avID: data.id, data: newName, }], [{ action: "updateAttrViewColOptions", id: colId, avID: data.id, data: colOptions }]); colOptions.find((item, index) => { if (item.name === newName) { colOptions.splice(index, 1); return true; } }); if (!cellElements) { menuElement.innerHTML = getEditHTML({protyle, data, colId}); bindEditEvent({protyle, data, menuElement}); } else { cellElements.forEach((cellElement: HTMLElement) => { data.view.rows.find(row => { if (row.id === (hasClosestByClassName(cellElement, "av__row") as HTMLElement).dataset.id) { row.cells.find(cell => { if (cell.id === cellElement.dataset.id) { cell.value.mSelect.find((item, index) => { if (item.content === newName) { cell.value.mSelect.splice(index, 1); return true; } }); if (cellElement.classList.contains("custom-attr__avvalue")) { cellElement.innerHTML = genAVValueHTML(cell.value); } else { updateAttrViewCellAnimation(cellElement); } return true; } }); return true; } }); }); menuElement.innerHTML = getSelectHTML(data.view, cellElements); bindSelectEvent(protyle, data, menuElement, cellElements); } }); } }); menu.addSeparator(); Array.from(Array(13).keys()).forEach(index => { menu.addItem({ accelerator: parseInt(color) === index + 1 ? '' : undefined, iconHTML: "", label: `A`, click(element) { if (element.lastElementChild.classList.contains("b3-menu__accelerator")) { return; } element.parentElement.querySelector(".b3-menu__accelerator")?.remove(); element.insertAdjacentHTML("beforeend", ''); transaction(protyle, [{ action: "updateAttrViewColOption", id: colId, avID: data.id, data: { oldName: name, newName: inputElement.value, oldColor: color, newColor: (index + 1).toString() }, }], [{ action: "updateAttrViewColOption", id: colId, avID: data.id, data: { oldName: inputElement.value, newName: name, oldColor: (index + 1).toString(), newColor: color }, }]); data.view.columns.find(column => { if (column.id === colId) { column.options.find((item) => { if (item.name === name) { item.name = inputElement.value; item.color = (index + 1).toString(); return true; } }); return true; } }); if (!cellElements) { menuElement.innerHTML = getEditHTML({protyle, data, colId}); bindEditEvent({protyle, data, menuElement}); } else { cellElements.forEach((cellElement: HTMLElement) => { data.view.rows.find(row => { if (row.id === (hasClosestByClassName(cellElement, "av__row") as HTMLElement).dataset.id) { row.cells.find(cell => { if (cell.id === cellElement.dataset.id) { cell.value.mSelect.find((item) => { if (item.content === name) { item.content = inputElement.value; item.color = (index + 1).toString(); return true; } }); if (cellElement.classList.contains("custom-attr__avvalue")) { cellElement.innerHTML = genAVValueHTML(cell.value); } else { updateAttrViewCellAnimation(cellElement); } return true; } }); return true; } }); }); menuElement.innerHTML = getSelectHTML(data.view, cellElements); bindSelectEvent(protyle, data, menuElement, cellElements); } name = inputElement.value; color = (index + 1).toString(); return true; } }); }); const rect = target.getBoundingClientRect(); menu.open({ x: rect.right, y: rect.bottom, w: rect.width, h: rect.height, }); const inputElement = window.siyuan.menus.menu.element.querySelector("input"); inputElement.select(); }; export const bindSelectEvent = (protyle: IProtyle, data: IAV, menuElement: HTMLElement, cellElements: HTMLElement[]) => { const inputElement = menuElement.querySelector("input"); const colId = cellElements[0].dataset.colId; let colData: IAVColumn; data.view.columns.find((item: IAVColumn) => { if (item.id === colId) { colData = item; return; } }); if (!colData.options) { colData.options = []; } const listElement = menuElement.lastElementChild.lastElementChild as HTMLElement; inputElement.addEventListener("input", (event: InputEvent) => { if (event.isComposing) { return; } listElement.innerHTML = filterSelectHTML(inputElement.value, colData.options); }); inputElement.addEventListener("compositionend", (event: InputEvent) => { if (event.isComposing) { return; } listElement.innerHTML = filterSelectHTML(inputElement.value, colData.options); }); inputElement.addEventListener("keydown", (event: KeyboardEvent) => { if (event.isComposing) { return; } let currentElement = upDownHint(listElement, event, "b3-menu__item--current"); if (event.key === "Enter") { if (!currentElement) { currentElement = menuElement.querySelector(".b3-menu__item--current"); } addColOptionOrCell(protyle, data, cellElements, currentElement, menuElement); } else if (event.key === "Backspace" && inputElement.value === "") { removeCellOption(protyle, data, cellElements, inputElement.previousElementSibling as HTMLElement); } }); }; export const addColOptionOrCell = (protyle: IProtyle, data: IAV, cellElements: HTMLElement[], currentElement: HTMLElement, menuElement: HTMLElement) => { let hasSelected = false; Array.from(menuElement.querySelectorAll(".b3-chips .b3-chip")).find((item: HTMLElement) => { if (item.dataset.content === currentElement.dataset.name) { hasSelected = true; return true; } }); if (hasSelected) { menuElement.querySelector("input").focus(); return; } const rowElement = hasClosestByClassName(cellElements[0], "av__row"); if (!rowElement) { return; } let cellIndex: number; Array.from(rowElement.querySelectorAll(".av__cell")).find((item: HTMLElement, index) => { if (item.dataset.id === cellElements[0].dataset.id) { cellIndex = index; return true; } }); const colId = cellElements[0].dataset.colId; let colData: IAVColumn; data.view.columns.find((item: IAVColumn) => { if (item.id === colId) { colData = item; if (!colData.options) { colData.options = []; } return; } }); const cellDoOperations: IOperation[] = []; const cellUndoOperations: IOperation[] = []; let newValue: IAVCellSelectValue[]; cellElements.forEach((item, index) => { const itemRowElement = hasClosestByClassName(item, "av__row"); if (!itemRowElement) { return; } let cellData: IAVCell; const rowID = itemRowElement.dataset.id; data.view.rows.find(row => { if (row.id === rowID) { if (typeof cellIndex === "number") { cellData = row.cells[cellIndex]; // 为空时 cellId 每次请求都不一致 cellData.id = item.dataset.id; if (!cellData.value || !cellData.value.mSelect) { cellData.value = {mSelect: []} as IAVCellValue; } } else { cellData = row.cells.find(cellItem => { if (cellItem.id === item.dataset.id) { return true; } }); } return true; } }); const oldValue = Object.assign([], cellData.value.mSelect); if (colData.type === "mSelect") { if (index === 0) { let hasOption = false; cellData.value.mSelect.find((item) => { if (item.content === currentElement.dataset.name) { hasOption = true; return true; } }); if (!hasOption) { cellData.value.mSelect.push({ color: currentElement.dataset.color, content: currentElement.dataset.name }); } newValue = cellData.value.mSelect; } else { cellData.value.mSelect = newValue; } } else { if (index === 0) { cellData.value.mSelect = [{ color: currentElement.dataset.color, content: currentElement.dataset.name }]; newValue = cellData.value.mSelect; } else { cellData.value.mSelect = newValue; } } cellDoOperations.push({ action: "updateAttrViewCell", id: cellData.id, keyID: colId, rowID, avID: data.id, data: cellData.value }); cellUndoOperations.push({ action: "updateAttrViewCell", id: cellData.id, keyID: colId, rowID, avID: data.id, data: { [colData.type]: oldValue } }); if (item.classList.contains("custom-attr__avvalue")) { item.innerHTML = genAVValueHTML(cellData.value); } else { updateAttrViewCellAnimation(item); } }); if (currentElement.querySelector(".b3-menu__accelerator")) { colData.options.push({ color: currentElement.dataset.color, name: currentElement.dataset.name }); cellDoOperations.splice(0, 0, { action: "updateAttrViewColOptions", id: colId, avID: data.id, data: colData.options }); transaction(protyle, cellDoOperations, [{ action: "removeAttrViewColOption", id: colId, avID: data.id, data: currentElement.dataset.name, }]); } else { transaction(protyle, cellDoOperations, cellUndoOperations); } if (colData.type === "select") { menuElement.parentElement.remove(); } else { menuElement.innerHTML = getSelectHTML(data.view, cellElements); bindSelectEvent(protyle, data, menuElement, cellElements); menuElement.querySelector("input").focus(); } }; export const getSelectHTML = (data: IAVTable, cellElements: HTMLElement[]) => { const colId = cellElements[0].dataset["colId"]; const colData = data.columns.find(item => { if (item.id === colId) { return item; } }); let allUniqueOptions: IAVCellSelectValue[] = []; data.rows.find(row => { const rowElement = hasClosestByClassName(cellElements[0], "av__row"); if (rowElement && rowElement.dataset.id === row.id) { row.cells.find(cell => { if (cell.id === cellElements[0].dataset.id) { if (cell.value && cell.value.mSelect) { allUniqueOptions = cell.value.mSelect; } return true; } }); return true; } }); let selectedHTML = ""; allUniqueOptions.forEach((unique) => { selectedHTML += `
${unique.content}
`; }); return `
${selectedHTML}
${filterSelectHTML("", colData.options)}
`; };