import {Menu} from "../../../plugin/Menu"; import {hasClosestByClassName, hasTopClosestByClassName} from "../../util/hasClosest"; import {UDLRHint, upDownHint} from "../../../util/upDownHint"; import {fetchPost} from "../../../util/fetch"; import {escapeGreat, escapeHtml} from "../../../util/escape"; import {transaction} from "../../wysiwyg/transaction"; import {updateCellsValue} from "./cell"; import {updateAttrViewCellAnimation} from "./action"; import {focusBlock} from "../../util/selection"; import {setPosition} from "../../../util/setPosition"; import * as dayjs from "dayjs"; import {getFieldsByData, getViewName} from "./view"; import {getColId} from "./col"; import {getFieldIdByCellElement} from "./row"; import {isMobile} from "../../../util/functions"; import {showMessage} from "../../../dialog/message"; import {writeText} from "../../util/compatibility"; interface IAVItem { avID: string; avName: string; blockID: string; hPath: string; viewName: string; viewID: string; viewLayout: string; } const genSearchList = (element: Element, keyword: string, avId?: string, excludes = true, cb?: () => void) => { fetchPost("/api/av/searchAttributeView", { keyword, excludes: (excludes && avId) ? [avId] : undefined }, (response) => { let html = ""; response.data.results.forEach((item: IAVItem & { children: IAVItem[] }, index: number) => { const hasChildren = item.children && item.children.length > 0 && excludes; html += `
${hasChildren ? '' : ""}
${escapeHtml(item.avName || window.siyuan.languages._kernel[267])}
${escapeGreat(item.hPath)}
`; if (hasChildren) { html += '
'; item.children.forEach((subItem) => { const viewDefaultName = getViewName(subItem.viewLayout); html += `
${escapeHtml(subItem.viewName)} ${viewDefaultName}
`; }); html += "
"; } }); element.innerHTML = html; if (cb) { cb(); } }); }; const setDatabase = (avId: string, element: HTMLElement, item: HTMLElement) => { element.dataset.avId = item.dataset.avId; element.dataset.blockId = item.dataset.blockId; element.querySelector(".b3-menu__accelerator").textContent = item.querySelector(".b3-list-item__hinticon").classList.contains("fn__none") ? item.querySelector(".b3-list-item__text").textContent : window.siyuan.languages.thisDatabase; const menuElement = hasClosestByClassName(element, "b3-menu__items"); if (menuElement) { toggleUpdateRelationBtn(menuElement, avId, true); } }; export const openSearchAV = (avId: string, target: HTMLElement, cb?: (element: HTMLElement) => void, excludes = true) => { window.siyuan.menus.menu.remove(); const menu = new Menu(); menu.addItem({ iconHTML: "", type: "empty", label: `
`, bind(element) { const listElement = element.querySelector(".b3-list"); const inputElement = element.querySelector("input"); inputElement.addEventListener("keydown", (event: KeyboardEvent) => { if (event.isComposing) { return; } UDLRHint(listElement, event); if (event.key === "Enter") { event.preventDefault(); event.stopPropagation(); const listItemElement = listElement.querySelector(".b3-list-item--focus") as HTMLElement; if (cb) { cb(listItemElement); } else { setDatabase(avId, target, listItemElement); } window.siyuan.menus.menu.remove(); } }); inputElement.addEventListener("input", (event: InputEvent) => { event.stopPropagation(); if (event.isComposing) { return; } genSearchList(listElement, inputElement.value, avId, excludes); }); inputElement.addEventListener("compositionend", () => { genSearchList(listElement, inputElement.value, avId, excludes); }); element.lastElementChild.addEventListener("click", (event) => { let clickTarget = event.target as HTMLElement; while (clickTarget && !clickTarget.classList.contains("b3-list")) { if (clickTarget.classList.contains("b3-list-item__toggle")) { if (clickTarget.firstElementChild.classList.contains("b3-list-item__arrow--open")) { clickTarget.firstElementChild.classList.remove("b3-list-item__arrow--open"); clickTarget.parentElement.nextElementSibling.classList.add("fn__none"); } else { clickTarget.firstElementChild.classList.add("b3-list-item__arrow--open"); clickTarget.parentElement.nextElementSibling.classList.remove("fn__none"); } event.preventDefault(); event.stopPropagation(); break; } else if (clickTarget.classList.contains("b3-list-item")) { event.preventDefault(); event.stopPropagation(); if (cb) { cb(clickTarget); } else { setDatabase(avId, target, clickTarget); } window.siyuan.menus.menu.remove(); break; } clickTarget = clickTarget.parentElement; } }); genSearchList(listElement, "", avId, excludes, () => { const rect = 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"); const popoverElement = hasTopClosestByClassName(target, "block__popover", true); menu.element.setAttribute("data-from", popoverElement ? popoverElement.dataset.level + "popover" : "app"); }; export const updateRelation = (options: { protyle: IProtyle, avID: string, avElement: Element, colsData: IAVColumn[], blockElement: Element, }) => { const inputElement = options.avElement.querySelector('input[data-type="colName"]') as HTMLInputElement; const goSearchAVElement = options.avElement.querySelector('.b3-menu__item[data-type="goSearchAV"]') as HTMLElement; const newAVId = goSearchAVElement.getAttribute("data-av-id"); const colId = options.avElement.querySelector(".b3-menu__item").getAttribute("data-col-id"); let colData: IAVColumn; options.colsData.find(item => { if (item.id === colId) { if (!item.relation) { item.relation = {}; } colData = item; return true; } }); const colNewName = (options.avElement.querySelector('[data-type="name"]') as HTMLInputElement).value; transaction(options.protyle, [{ action: "updateAttrViewColRelation", avID: options.avID, keyID: colId, id: newAVId || colData.relation.avID, backRelationKeyID: colData.relation.avID === newAVId ? (colData.relation.backKeyID || Lute.NewNodeID()) : Lute.NewNodeID(), isTwoWay: (options.avElement.querySelector(".b3-switch") as HTMLInputElement).checked, name: inputElement.value, format: colNewName }, { action: "doUpdateUpdated", id: options.blockElement.getAttribute("data-node-id"), data: dayjs().format("YYYYMMDDHHmmss"), }], [{ action: "updateAttrViewColRelation", avID: options.avID, keyID: colId, id: colData.relation.avID || newAVId, backRelationKeyID: colData.relation.backKeyID, isTwoWay: colData.relation.isTwoWay, name: inputElement.dataset.oldValue, format: colData.name }]); options.avElement.remove(); updateAttrViewCellAnimation(options.blockElement.querySelector(`.av__row--header .av__cell[data-col-id="${colId}"]`), undefined, {name: colNewName}); focusBlock(options.blockElement); }; export const toggleUpdateRelationBtn = (menuItemsElement: HTMLElement, avId: string, resetData = false) => { const searchElement = menuItemsElement.querySelector('.b3-menu__item[data-type="goSearchAV"]') as HTMLElement; const switchItemElement = searchElement.nextElementSibling; const switchElement = switchItemElement.querySelector(".b3-switch") as HTMLInputElement; const inputItemElement = switchItemElement.nextElementSibling; const btnElement = inputItemElement.nextElementSibling; const oldValue = JSON.parse(searchElement.dataset.oldValue) as IAVColumnRelation; if (oldValue.avID) { const inputElement = inputItemElement.querySelector("input") as HTMLInputElement; if (resetData) { if (searchElement.dataset.avId !== oldValue.avID) { inputElement.value = ""; switchElement.checked = false; } else { inputElement.value = inputElement.dataset.oldValue; switchElement.checked = oldValue.isTwoWay; } } if (switchElement.checked) { inputItemElement.classList.remove("fn__none"); } else { inputItemElement.classList.add("fn__none"); } if ((searchElement.dataset.avId && oldValue.avID !== searchElement.dataset.avId) || oldValue.isTwoWay !== switchElement.checked || inputElement.dataset.oldValue !== inputElement.value) { btnElement.classList.remove("fn__none"); } else { btnElement.classList.add("fn__none"); } } else if (searchElement.dataset.avId) { if (switchElement.checked) { inputItemElement.classList.remove("fn__none"); } else { inputItemElement.classList.add("fn__none"); } btnElement.classList.remove("fn__none"); } }; const updateCopyRelatedItems = (menuElement: Element) => { const inputElement = menuElement.querySelector(".b3-form__icona .b3-text-field") as HTMLInputElement; if (menuElement.querySelector(".b3-menu__icon.fn__grab")) { inputElement.nextElementSibling.classList.remove("fn__none"); inputElement.style.paddingRight = "26px"; } else { inputElement.nextElementSibling.classList.add("fn__none"); inputElement.style.paddingRight = ""; } }; const genSelectItemHTML = (options: { type: "selected" | "empty" | "unselect", id?: string, isDetached?: boolean, text?: string, className?: string, rowId?: string, newName?: string }) => { if (options.type === "selected") { return ` ${options.text} `; } if (options.type === "empty") { if (options.newName) { return ``; } return ``; } if (options.type == "unselect") { return ``; } }; const filterItem = (menuElement: Element, cellElement: HTMLElement, keyword: string) => { fetchPost("/api/av/getAttributeViewPrimaryKeyValues", { id: menuElement.firstElementChild.getAttribute("data-av-id"), keyword, }, response => { const cells = response.data.rows.values as IAVCellValue[] || []; let html = ""; let selectHTML = ""; const hasIds: string[] = []; cellElement.querySelectorAll(".av__cell--relation").forEach((relationItem: HTMLElement) => { const item = relationItem.querySelector(".av__celltext") as HTMLElement; hasIds.push(relationItem.dataset.rowId); selectHTML += ``; }); cells.forEach((item) => { if (!hasIds.includes(item.blockID)) { html += genSelectItemHTML({ type: "unselect", rowId: item.blockID, id: item.block.id, isDetached: item.isDetached, text: Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled) }); } }); const refElement = menuElement.querySelector(".popover__block"); menuElement.querySelector(".b3-menu__items").innerHTML = `${selectHTML} ${html} ${keyword ? genSelectItemHTML({ type: "empty", newName: Lute.EscapeHTMLStr(keyword), text: `${refElement.textContent}`, }) : (html ? "" : genSelectItemHTML({type: "empty"}))}`; menuElement.querySelector(".b3-menu__items .b3-menu__item:not(.fn__none)").classList.add("b3-menu__item--current"); updateCopyRelatedItems(menuElement); }); }; export const bindRelationEvent = (options: { menuElement: HTMLElement, protyle: IProtyle, blockElement: Element, cellElements: HTMLElement[] }) => { fetchPost("/api/av/getAttributeViewPrimaryKeyValues", { id: options.menuElement.firstElementChild.getAttribute("data-av-id"), keyword: "", }, response => { const cells = response.data.rows.values as IAVCellValue[] || []; let html = ""; let selectHTML = ""; const hasIds: string[] = []; options.cellElements[0].querySelectorAll(".av__cell--relation").forEach((relationItem: HTMLElement) => { const item = relationItem.querySelector(".av__celltext") as HTMLElement; hasIds.push(relationItem.dataset.rowId); selectHTML += ``; }); cells.forEach((item) => { if (!hasIds.includes(item.blockID)) { html += genSelectItemHTML({ type: "unselect", rowId: item.blockID, id: item.block.id, isDetached: item.isDetached, text: Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled) }); } }); options.menuElement.querySelector(".b3-menu__items").innerHTML = `${selectHTML} ${html || genSelectItemHTML({type: "empty"})}`; const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect(); setPosition(options.menuElement, cellRect.left, cellRect.bottom, cellRect.height); options.menuElement.querySelector(".b3-menu__items .b3-menu__item:not(.fn__none)").classList.add("b3-menu__item--current"); const inputElement = options.menuElement.querySelector("input"); inputElement.focus(); const databaseName = inputElement.parentElement.parentElement.querySelector(".popover__block"); databaseName.innerHTML = Lute.EscapeHTMLStr(response.data.name); databaseName.setAttribute("data-id", response.data.blockIDs[0]); const listElement = options.menuElement.querySelector(".b3-menu__items"); inputElement.addEventListener("keydown", (event) => { if (event.isComposing) { return; } upDownHint(listElement, event, "b3-menu__item--current"); const currentElement = options.menuElement.querySelector(".b3-menu__item--current") as HTMLElement; if (event.key === "Enter" && currentElement && currentElement.getAttribute("data-type") === "setRelationCell") { setRelationCell(options.protyle, options.blockElement as HTMLElement, currentElement, options.cellElements); event.preventDefault(); event.stopPropagation(); } }); inputElement.addEventListener("input", (event: InputEvent) => { if (event.isComposing) { return; } filterItem(options.menuElement, options.cellElements[0], inputElement.value); event.stopPropagation(); }); inputElement.addEventListener("compositionend", (event) => { event.stopPropagation(); filterItem(options.menuElement, options.cellElements[0], inputElement.value); }); updateCopyRelatedItems(options.menuElement); options.menuElement.querySelector('[data-type="copyRelatedItems"]').addEventListener("click", () => { let copyText = ""; const selectedElements = options.menuElement.querySelectorAll('.b3-menu__item[draggable="true"]'); selectedElements.forEach((item: HTMLElement) => { if (selectedElements.length > 1) { copyText += "- "; } const textElement = item.querySelector(".b3-menu__label") as HTMLElement; if (!textElement.dataset.id || textElement.dataset.id === "undefined") { copyText += textElement.textContent + "\n"; } else { copyText += `((${textElement.dataset.id} "${textElement.textContent}"))\n`; } }); if (copyText) { writeText(copyText.trimEnd()); showMessage(window.siyuan.languages.copied); } }); }); }; export const getRelationHTML = (data: IAV, cellElements?: HTMLElement[]) => { let colRelationData: IAVColumnRelation; getFieldsByData(data).find(item => { if (item.id === getColId(cellElements[0], data.viewType)) { colRelationData = item.relation; return true; } }); if (colRelationData && colRelationData.avID) { return `
`; } else { return ""; } }; export const setRelationCell = (protyle: IProtyle, nodeElement: HTMLElement, target: HTMLElement, cellElements: HTMLElement[]) => { const menuElement = hasClosestByClassName(target, "b3-menu"); if (!menuElement) { return; } if (menuElement.querySelector(".dragover__bottom, .dragover__top")) { return; } if (!nodeElement.contains(cellElements[0])) { const viewType = nodeElement.getAttribute("data-av-type") as TAVView; const rowID = getFieldIdByCellElement(cellElements[0], viewType); if (viewType === "table") { cellElements[0] = (nodeElement.querySelector(`.av__row[data-id="${rowID}"] .av__cell[data-col-id="${cellElements[0].dataset.colId}"]`) || nodeElement.querySelector(`.fn__flex-1[data-col-id="${cellElements[0].dataset.colId}"]`)) as HTMLElement; } else { cellElements[0] = (nodeElement.querySelector(`.av__gallery-item[data-id="${rowID}"] .av__cell[data-field-id="${cellElements[0].dataset.fieldId}"]`)) as HTMLElement; } } const newValue: IAVCellRelationValue = {blockIDs: [], contents: []}; menuElement.querySelectorAll('[draggable="true"]').forEach(item => { const rowId = item.getAttribute("data-row-id"); const blockPopElement = item.querySelector(".b3-menu__label"); newValue.blockIDs.push(rowId); newValue.contents.push({ type: "block", block: { id: blockPopElement.getAttribute("data-id"), content: blockPopElement.textContent }, isDetached: !blockPopElement.classList.contains("popover__block") }); }); if (target.classList.contains("b3-menu__item")) { const rowId = target.getAttribute("data-row-id"); const id = target.querySelector(".b3-menu__label").getAttribute("data-id"); const separatorElement = menuElement.querySelector(".b3-menu__separator"); const searchValue = menuElement.querySelector("input").value; if (target.getAttribute("draggable")) { if (!separatorElement.nextElementSibling.getAttribute("data-row-id") && !searchValue) { separatorElement.nextElementSibling.remove(); } const removeIndex = newValue.blockIDs.indexOf(rowId); newValue.blockIDs.splice(removeIndex, 1); newValue.contents.splice(removeIndex, 1); separatorElement.after(target); target.outerHTML = genSelectItemHTML({ type: "unselect", rowId, id, isDetached: !target.querySelector(".popover__block"), text: Lute.EscapeHTMLStr(target.querySelector(".b3-menu__label").textContent), className: target.className }); } else if (rowId) { newValue.blockIDs.push(rowId); newValue.contents.push({ type: "block", block: { id, content: target.firstElementChild.textContent }, isDetached: !target.firstElementChild.getAttribute("style") }); separatorElement.before(target); target.outerHTML = ``; if (!separatorElement.nextElementSibling) { separatorElement.insertAdjacentHTML("afterend", genSelectItemHTML({type: "empty"})); } } else { const blockID = target.querySelector(".popover__block").getAttribute("data-id"); const content = target.querySelector("b").textContent; const rowId = Lute.NewNodeID(); const bodyElement = hasClosestByClassName(cellElements[0], "av__body"); transaction(protyle, [{ action: "insertAttrViewBlock", avID: menuElement.firstElementChild.getAttribute("data-av-id"), srcs: [{ itemID: rowId, id: Lute.NewNodeID(), isDetached: true, content }], blockID, groupID: bodyElement ? bodyElement.getAttribute("data-group-id") : "", }, { action: "doUpdateUpdated", id: blockID, data: dayjs().format("YYYYMMDDHHmmss"), }]); newValue.blockIDs.push(rowId); newValue.contents.push({ type: "block", block: { content }, isDetached: true }); separatorElement.insertAdjacentHTML("beforebegin", ``); } } updateCellsValue(protyle, nodeElement, newValue, cellElements); updateCopyRelatedItems(menuElement); };