From 9ea5b148c76ce7f5df4d1332672f831854ad1c4c Mon Sep 17 00:00:00 2001 From: Vanessa Date: Sat, 26 Jul 2025 11:30:57 +0800 Subject: [PATCH] :art: https://github.com/siyuan-note/siyuan/issues/10964 --- app/src/protyle/render/av/gallery/render.ts | 63 +--- app/src/protyle/render/av/render.ts | 382 +++++++++++--------- app/src/types/index.d.ts | 1 + 3 files changed, 219 insertions(+), 227 deletions(-) diff --git a/app/src/protyle/render/av/gallery/render.ts b/app/src/protyle/render/av/gallery/render.ts index bbcf7651c..9255e8766 100644 --- a/app/src/protyle/render/av/gallery/render.ts +++ b/app/src/protyle/render/av/gallery/render.ts @@ -1,14 +1,13 @@ import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName} from "../../../util/hasClosest"; import {Constants} from "../../../../constants"; import {fetchPost} from "../../../../util/fetch"; -import {escapeAriaLabel, escapeAttr, escapeHtml} from "../../../../util/escape"; +import {escapeAttr} from "../../../../util/escape"; import {unicode2Emoji} from "../../../../emoji"; import {cellValueIsEmpty, renderCell} from "../cell"; import {focusBlock} from "../../../util/selection"; import {electronUndo} from "../../../undo"; import {addClearButton} from "../../../../util/addClearButton"; -import {avRender, updateSearch} from "../render"; -import {getViewIcon} from "../view"; +import {avRender, genTabHeaderHTML, updateSearch} from "../render"; import {processRender} from "../../../util/processCode"; import {getColIconByType, getColNameByType} from "../col"; import {getCompressURL} from "../../../../util/image"; @@ -117,65 +116,9 @@ ${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex, `; }); galleryHTML += ``; - let tabHTML = ""; - let viewData: IAVView; - response.data.views.forEach((item: IAVView) => { - tabHTML += `
- ${item.icon ? unicode2Emoji(item.icon, "item__graphic", true) : ``} - ${escapeHtml(item.name)} -
`; - if (item.id === response.data.viewID) { - viewData = item; - } - }); if (options.renderAll) { options.blockElement.firstElementChild.outerHTML = `
-
-
-
- ${tabHTML} -
-
- - - -
-
- - - - ${response.data.views.length} - -
- - - -
- - - -
- -
- -
-
- - - -
- - - -
- ${response.data.isMirror ? ` -
` : ""} -
-
${response.data.name || ""}
-
-
+ ${genTabHeaderHTML(response.data, isSearching || !!query, options.protyle.disabled || !!hasClosestByAttribute(options.blockElement, "data-type", "NodeBlockQueryEmbed"))}
${galleryHTML} diff --git a/app/src/protyle/render/av/render.ts b/app/src/protyle/render/av/render.ts index e90af8763..cad05511e 100644 --- a/app/src/protyle/render/av/render.ts +++ b/app/src/protyle/render/av/render.ts @@ -15,9 +15,214 @@ import {electronUndo} from "../../undo"; import {isInAndroid, isInHarmony, isInIOS} from "../../util/compatibility"; import {isMobile} from "../../../util/functions"; import {renderGallery} from "./gallery/render"; -import {getViewIcon} from "./view"; +import {getFieldsByData, getViewIcon} from "./view"; import {openMenuPanel} from "./openMenuPanel"; +export const genTabHeaderHTML = (data: IAV, showSearch: boolean, editable: boolean) => { + let tabHTML = ""; + let viewData: IAVView; + let hasFilter = false; + getFieldsByData(data).forEach((item) => { + if (!hasFilter) { + data.view.filters.find(filterItem => { + if (filterItem.value.type === item.type && item.id === filterItem.column) { + hasFilter = true; + return true; + } + }); + } + }); + data.views.forEach((item: IAVView) => { + tabHTML += `
+ ${item.icon ? unicode2Emoji(item.icon, "item__graphic", true) : ``} + ${escapeHtml(item.name)} +
`; + if (item.id === data.viewID) { + viewData = item; + } + }); + return `
+
+
+ ${tabHTML} +
+
+ + + +
+
+ + + + ${data.views.length} + +
+ + + +
+ + + +
+ +
+ +
+
+ + + +
+ + + +
+ ${data.isMirror ? ` +
` : ""} +
+
${data.name || ""}
+
+
`; +}; + +const getTableHTMLs = (data: IAVTable, e: HTMLElement) => { + let calcHTML = ""; + let contentHTML = '
'; + let pinIndex = -1; + let pinMaxIndex = -1; + let indexWidth = 0; + const eWidth = e.clientWidth; + data.columns.forEach((item, index) => { + if (!item.hidden) { + if (item.pin) { + pinIndex = index; + } + if (indexWidth < eWidth - 200) { + indexWidth += parseInt(item.width) || 200; + pinMaxIndex = index; + } + } + }); + if (eWidth === 0) { + pinMaxIndex = pinIndex; + } + pinIndex = Math.min(pinIndex, pinMaxIndex); + if (pinIndex > -1) { + contentHTML = '
'; + calcHTML = '
'; + } + let hasCalc = false; + data.columns.forEach((column: IAVColumn, index: number) => { + if (column.hidden) { + return; + } + contentHTML += `
+ ${column.icon ? unicode2Emoji(column.icon, "av__cellheadericon", true) : ``} + ${escapeHtml(column.name)} + ${column.pin ? '' : ""} +
+
`; + if (pinIndex === index) { + contentHTML += "
"; + } + if (column.type === "lineNumber") { + // lineNumber type 不参与计算操作 + calcHTML += `
 
`; + } else { + calcHTML += `
${getCalcValue(column) || `${window.siyuan.languages.calc}`}
`; + } + if (column.calc && column.calc.operator !== "") { + hasCalc = true; + } + + if (pinIndex === index) { + calcHTML += "
"; + } + }); + contentHTML += `
+
+
+
+
+
`; + // body + data.rows.forEach((row: IAVRow, rowIndex: number) => { + contentHTML += `
`; + if (pinIndex > -1) { + contentHTML += '
'; + } else { + contentHTML += '
'; + } + + row.cells.forEach((cell, index) => { + if (data.columns[index].hidden) { + return; + } + // https://github.com/siyuan-note/siyuan/issues/10262 + let checkClass = ""; + if (cell.valueType === "checkbox") { + checkClass = cell.value?.checkbox?.checked ? " av__cell-check" : " av__cell-uncheck"; + } + contentHTML += `
${renderCell(cell.value, rowIndex, data.showIcon)}
`; + + if (pinIndex === index) { + contentHTML += "
"; + } + }); + contentHTML += "
"; + }); + return { + contentHTML, + footerHTML: `
${calcHTML}
` + }; +}; + +const renderGroupTable = (options: { + blockElement: HTMLElement, + protyle: IProtyle, + cb?: (data: IAV) => void, + renderAll: boolean + data: IAV +}) => { + const searchInputElement = options.blockElement.querySelector('[data-type="av-search"]') as HTMLInputElement; + const isSearching = searchInputElement && document.activeElement === searchInputElement; + const query = searchInputElement?.value || ""; + + let avBodyHTML = ""; + options.data.view.groups.forEach((group: IAVTable) => { + if (group.groupHidden === 0) { + group.columns = (options.data.view as IAVTable).columns; + avBodyHTML += `
${group.name}
${getTableHTMLs(group, options.blockElement).contentHTML}`; + } + }); + if (options.renderAll) { + options.blockElement.firstElementChild.outerHTML = `
+ ${genTabHeaderHTML(options.data, isSearching || !!query, options.protyle.disabled || !!hasClosestByAttribute(options.blockElement, "data-type", "NodeBlockQueryEmbed"))} +
+ ${avBodyHTML} +
+
${Constants.ZWSP}
+
`; + } else { + options.blockElement.firstElementChild.querySelector(".av__scroll").innerHTML = avBodyHTML; + } +}; + export const avRender = (element: Element, protyle: IProtyle, cb?: (data: IAV) => void, renderAll = true) => { let avElements: Element[] = []; if (element.getAttribute("data-type") === "NodeAttributeView") { @@ -102,129 +307,16 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: (data: IAV) = renderGallery({blockElement: e, protyle, cb, renderAll}); return; } + if (data.groups?.length > 0) { + renderGroupTable({blockElement: e, protyle, cb, renderAll, data: response.data}); + return; + } if (!e.dataset.pageSize) { e.dataset.pageSize = data.pageSize.toString(); } - // header - let tableHTML = '
'; - let calcHTML = ""; - let pinIndex = -1; - let pinMaxIndex = -1; - let indexWidth = 0; - const eWidth = e.clientWidth; - let hasFilter = false; - data.columns.forEach((item, index) => { - if (!hasFilter) { - data.filters.find(filterItem => { - if (filterItem.value.type === item.type && item.id === filterItem.column) { - hasFilter = true; - return true; - } - }); - } - if (!item.hidden) { - if (item.pin) { - pinIndex = index; - } - if (indexWidth < eWidth - 200) { - indexWidth += parseInt(item.width) || 200; - pinMaxIndex = index; - } - } - }); - if (eWidth === 0) { - pinMaxIndex = pinIndex; - } - pinIndex = Math.min(pinIndex, pinMaxIndex); - if (pinIndex > -1) { - tableHTML = '
'; - calcHTML = '
'; - } - let hasCalc = false; - data.columns.forEach((column: IAVColumn, index: number) => { - if (column.hidden) { - return; - } - tableHTML += `
- ${column.icon ? unicode2Emoji(column.icon, "av__cellheadericon", true) : ``} - ${escapeHtml(column.name)} - ${column.pin ? '' : ""} -
-
`; - if (pinIndex === index) { - tableHTML += "
"; - } - - if (column.type === "lineNumber") { - // lineNumber type 不参与计算操作 - calcHTML += `
 
`; - } else { - calcHTML += `
${getCalcValue(column) || `${window.siyuan.languages.calc}`}
`; - } - if (column.calc && column.calc.operator !== "") { - hasCalc = true; - } - - if (pinIndex === index) { - calcHTML += "
"; - } - }); - tableHTML += `
-
-
-
-
-
`; - // body - data.rows.forEach((row: IAVRow, rowIndex: number) => { - tableHTML += `
`; - if (pinIndex > -1) { - tableHTML += '
'; - } else { - tableHTML += '
'; - } - - row.cells.forEach((cell, index) => { - if (data.columns[index].hidden) { - return; - } - // https://github.com/siyuan-note/siyuan/issues/10262 - let checkClass = ""; - if (cell.valueType === "checkbox") { - checkClass = cell.value?.checkbox?.checked ? " av__cell-check" : " av__cell-uncheck"; - } - tableHTML += `
${renderCell(cell.value, rowIndex, data.showIcon)}
`; - - if (pinIndex === index) { - tableHTML += "
"; - } - }); - tableHTML += "
"; - }); - let tabHTML = ""; - let viewData: IAVView; - response.data.views.forEach((item: IAVView) => { - tabHTML += `
- ${item.icon ? unicode2Emoji(item.icon, "item__graphic", true) : ``} - ${escapeHtml(item.name)} -
`; - if (item.id === response.data.viewID) { - viewData = item; - } - }); + const tableHTMLs = getTableHTMLs(data, e); const avBodyHTML = `
- ${tableHTML} + ${tableHTMLs.contentHTML}
-
${calcHTML}
+ ${tableHTMLs.footerHTML}
`; if (renderAll) { e.firstElementChild.outerHTML = `
-
-
-
- ${tabHTML} -
-
- - - -
-
- - - - ${response.data.views.length} - -
- - - -
- - - -
- -
- -
-
- - - -
- - - -
- ${response.data.isMirror ? ` -
` : ""} -
-
${response.data.name || ""}
-
-
+ ${genTabHeaderHTML(response.data, isSearching || !!query, protyle.disabled || !!hasClosestByAttribute(e, "data-type", "NodeBlockQueryEmbed"))}
${avBodyHTML}
@@ -451,6 +498,7 @@ export const updateSearch = (e: HTMLElement, protyle: IProtyle) => { const refreshTimeouts: { [key: string]: number; } = {}; + export const refreshAV = (protyle: IProtyle, operation: IOperation) => { if (operation.action === "setAttrViewName") { Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-av-id="${operation.id}"]`)).forEach((item: HTMLElement) => { diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index 175290deb..5a532f324 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -842,6 +842,7 @@ interface IAV { viewID: string; viewType: TAVView; views: IAVView[]; + isMirror?: boolean; } interface IAVView {