mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-16 14:40:12 +01:00
This commit is contained in:
parent
d4c46ebaa2
commit
cc2b0ffce2
8 changed files with 412 additions and 229 deletions
|
|
@ -188,6 +188,8 @@
|
|||
|
||||
/* 数据库 */
|
||||
--b3-av-gallery-shadow: rgba(0, 0, 0, 0.04) 0px 2px 4px 0px, var(--b3-border-color) 0px 0px 0px 1px;
|
||||
--b3-av-kanban-border: var(--b3-border-color);
|
||||
--b3-av-kanban-content-hover-bg: var(--b3-theme-surface);
|
||||
|
||||
/* 嵌入块 */
|
||||
--b3-embed-background: transparent;
|
||||
|
|
|
|||
|
|
@ -187,6 +187,8 @@
|
|||
|
||||
/* 数据库 */
|
||||
--b3-av-gallery-shadow: rgba(0, 0, 0, 0.04) 0px 2px 4px 0px, var(--b3-border-color) 0px 0px 0px 1px;
|
||||
--b3-av-kanban-border: var(--b3-border-color);
|
||||
--b3-av-kanban-content-hover-bg: var(--b3-theme-surface);
|
||||
|
||||
/* 嵌入块 */
|
||||
--b3-embed-background: transparent;
|
||||
|
|
|
|||
|
|
@ -976,7 +976,7 @@
|
|||
&__kanban {
|
||||
overflow: auto;
|
||||
gap: 16px;
|
||||
padding: 0 1px 16px;
|
||||
padding: 16px 1px;
|
||||
display: flex;
|
||||
|
||||
&-group {
|
||||
|
|
@ -985,6 +985,12 @@
|
|||
border-radius: var(--b3-border-radius);
|
||||
background-color: var(--b3-av-kanban-bg);
|
||||
|
||||
&:hover {
|
||||
.av__group-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&--small {
|
||||
width: 180px;
|
||||
}
|
||||
|
|
@ -993,6 +999,10 @@
|
|||
width: 320px;
|
||||
}
|
||||
|
||||
.av__group-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.av__group-title {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {processRender} from "../../../util/processCode";
|
|||
import {getColIconByType, getColNameByType} from "../col";
|
||||
import {getCompressURL} from "../../../../util/image";
|
||||
import {getPageSize} from "../groups";
|
||||
import {renderKanban} from "../kanban/render";
|
||||
|
||||
interface IIds {
|
||||
groupId: string,
|
||||
|
|
@ -146,7 +147,7 @@ const renderGroupGallery = (options: ITableOptions) => {
|
|||
afterRenderGallery(options);
|
||||
};
|
||||
|
||||
const afterRenderGallery = (options: ITableOptions) => {
|
||||
export const afterRenderGallery = (options: ITableOptions) => {
|
||||
const view = options.data.view as IAVGallery;
|
||||
if (view.coverFrom === 1 || view.coverFrom === 3) {
|
||||
processRender(options.blockElement);
|
||||
|
|
@ -324,8 +325,13 @@ export const renderGallery = async (options: {
|
|||
data = response.data;
|
||||
}
|
||||
if (data.viewType === "table") {
|
||||
options.blockElement.setAttribute("data-av-type", "table");
|
||||
avRender(options.blockElement, options.protyle, options.cb, options.renderAll);
|
||||
options.blockElement.setAttribute("data-av-type", data.viewType);
|
||||
avRender(options.blockElement, options.protyle, options.cb, options.renderAll, data);
|
||||
return;
|
||||
}
|
||||
if (data.viewType === "kanban") {
|
||||
options.blockElement.setAttribute("data-av-type", data.viewType);
|
||||
renderKanban({blockElement: options.blockElement, protyle:options.protyle, cb:options.cb, renderAll:options.renderAll, data});
|
||||
return;
|
||||
}
|
||||
const view: IAVGallery = data.view as IAVGallery;
|
||||
|
|
@ -368,109 +374,3 @@ export const renderGallery = async (options: {
|
|||
options.blockElement.querySelector(".av__gallery").classList.add("av__gallery--top");
|
||||
}
|
||||
};
|
||||
|
||||
export const renderKanban = async (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 editIds: IIds[] = [];
|
||||
options.blockElement.querySelectorAll(".av__gallery-fields--edit").forEach(item => {
|
||||
editIds.push({
|
||||
groupId: (hasClosestByClassName(item, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
fieldId: item.parentElement.getAttribute("data-id"),
|
||||
});
|
||||
});
|
||||
const selectItemIds: IIds[] = [];
|
||||
options.blockElement.querySelectorAll(".av__gallery-item--select").forEach(galleryItem => {
|
||||
const fieldId = galleryItem.getAttribute("data-id");
|
||||
if (fieldId) {
|
||||
selectItemIds.push({
|
||||
groupId: (hasClosestByClassName(galleryItem, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
fieldId
|
||||
});
|
||||
}
|
||||
});
|
||||
const pageSizes: { [key: string]: string } = {};
|
||||
options.blockElement.querySelectorAll(".av__body").forEach((item: HTMLElement) => {
|
||||
pageSizes[item.dataset.groupId || "unGroup"] = item.dataset.pageSize;
|
||||
});
|
||||
const resetData = {
|
||||
isSearching: searchInputElement && document.activeElement === searchInputElement,
|
||||
query: searchInputElement?.value || "",
|
||||
alignSelf: options.blockElement.style.alignSelf,
|
||||
oldOffset: options.protyle.contentElement.scrollTop,
|
||||
editIds,
|
||||
selectItemIds,
|
||||
pageSizes,
|
||||
};
|
||||
if (options.blockElement.firstElementChild.innerHTML === "") {
|
||||
options.blockElement.style.alignSelf = "";
|
||||
options.blockElement.firstElementChild.outerHTML = `<div class="av__kanban fn__flex">
|
||||
<span style="width: 260px;height: 178px;" class="av__pulse"></span>
|
||||
<span style="width: 260px;height: 178px;" class="av__pulse"></span>
|
||||
<span style="width: 260px;height: 178px;" class="av__pulse"></span>
|
||||
</div>`;
|
||||
}
|
||||
const created = options.protyle.options.history?.created;
|
||||
const snapshot = options.protyle.options.history?.snapshot;
|
||||
|
||||
let data: IAV = options.data;
|
||||
if (!data) {
|
||||
const avPageSize = getPageSize(options.blockElement);
|
||||
const response = await fetchSyncPost(created ? "/api/av/renderHistoryAttributeView" : (snapshot ? "/api/av/renderSnapshotAttributeView" : "/api/av/renderAttributeView"), {
|
||||
id: options.blockElement.getAttribute("data-av-id"),
|
||||
created,
|
||||
snapshot,
|
||||
pageSize: avPageSize.unGroupPageSize,
|
||||
groupPaging: avPageSize.groupPageSize,
|
||||
viewID: options.blockElement.getAttribute(Constants.CUSTOM_SY_AV_VIEW) || "",
|
||||
query: resetData.query.trim()
|
||||
});
|
||||
data = response.data;
|
||||
}
|
||||
if (data.viewType === "table") {
|
||||
options.blockElement.setAttribute("data-av-type", "table");
|
||||
avRender(options.blockElement, options.protyle, options.cb, options.renderAll);
|
||||
return;
|
||||
}
|
||||
const view: IAVGallery = data.view as IAVGallery;
|
||||
let bodyHTML = "";
|
||||
view.groups.forEach((group: IAVGallery) => {
|
||||
if (group.groupHidden === 0) {
|
||||
let selectBg;
|
||||
if (["mSelect", "select"].includes(group.groupValue.type)) {
|
||||
selectBg = getComputedStyle(document.documentElement).getPropertyValue(`--b3-font-background${group.groupValue.mSelect[0].color}`);
|
||||
}
|
||||
bodyHTML += `<div class="av__kanban-group" style="--b3-av-kanban-border:${selectBg};--b3-av-kanban-bg:${selectBg}29;--b3-av-kanban-content-bg:${selectBg}47;--b3-av-kanban-content-hover-bg:${selectBg}5c;">
|
||||
${getGroupTitleHTML(group, group.cards.length)}
|
||||
<div data-group-id="${group.id}" data-page-size="${group.pageSize}" data-dtype="${group.groupKey.type}" data-content="${Lute.EscapeHTMLStr(group.groupValue.text?.content)}" class="av__body${group.groupFolded ? " fn__none" : ""}">${getGalleryHTML(group)}</div>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
if (options.renderAll) {
|
||||
options.blockElement.firstElementChild.outerHTML = `<div class="av__container fn__block">
|
||||
${genTabHeaderHTML(data, resetData.isSearching || !!resetData.query, !options.protyle.disabled && !hasClosestByAttribute(options.blockElement, "data-type", "NodeBlockQueryEmbed"))}
|
||||
<div class="av__kanban">
|
||||
${bodyHTML}
|
||||
</div>
|
||||
<div class="av__cursor" contenteditable="true">${Constants.ZWSP}</div>
|
||||
</div>`;
|
||||
} else {
|
||||
options.blockElement.querySelector(".av__kanban").innerHTML = bodyHTML;
|
||||
}
|
||||
afterRenderGallery({
|
||||
resetData,
|
||||
renderAll: options.renderAll,
|
||||
data,
|
||||
cb: options.cb,
|
||||
protyle: options.protyle,
|
||||
blockElement: options.blockElement,
|
||||
});
|
||||
if (view.hideAttrViewName) {
|
||||
options.blockElement.querySelector(".av__gallery").classList.add("av__gallery--top");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
243
app/src/protyle/render/av/kanban/render.ts
Normal file
243
app/src/protyle/render/av/kanban/render.ts
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
import {hasClosestByAttribute, hasClosestByClassName} from "../../../util/hasClosest";
|
||||
import {getPageSize} from "../groups";
|
||||
import {fetchSyncPost} from "../../../../util/fetch";
|
||||
import {Constants} from "../../../../constants";
|
||||
import {avRender, genTabHeaderHTML} from "../render";
|
||||
import {afterRenderGallery, renderGallery} from "../gallery/render";
|
||||
import {escapeAttr, escapeHtml} from "../../../../util/escape";
|
||||
import {getCompressURL} from "../../../../util/image";
|
||||
import {cellValueIsEmpty, renderCell} from "../cell";
|
||||
import {getColIconByType, getColNameByType} from "../col";
|
||||
import {unicode2Emoji} from "../../../../emoji";
|
||||
|
||||
interface IIds {
|
||||
groupId: string,
|
||||
fieldId: string,
|
||||
}
|
||||
|
||||
const getKanbanTitleHTML = (group: IAVView, counter: number) => {
|
||||
let nameHTML = "";
|
||||
if (["mSelect", "select"].includes(group.groupValue.type)) {
|
||||
group.groupValue.mSelect.forEach((item) => {
|
||||
nameHTML += `<span class="b3-chip" style="background-color:var(--b3-font-background${item.color});color:var(--b3-font-color${item.color})">${escapeHtml(item.content)}</span>`;
|
||||
});
|
||||
} else if (group.groupValue.type === "checkbox") {
|
||||
nameHTML = `<svg style="width:calc(1.625em - 12px);height:calc(1.625em - 12px)"><use xlink:href="#icon${group.groupValue.checkbox.checked ? "Check" : "Uncheck"}"></use></svg>`;
|
||||
} else {
|
||||
nameHTML = group.name;
|
||||
}
|
||||
// av__group-name 为第三方需求,本应用内没有使用,但不能移除 https://github.com/siyuan-note/siyuan/issues/15736
|
||||
return `<div class="av__group-title">
|
||||
<span class="av__group-name">${nameHTML}</span>
|
||||
${counter === 0 ? '<span class="fn__space"></span>' : `<span aria-label="${window.siyuan.languages.total}" data-position="north" class="av__group-counter ariaLabel">${counter}</span>`}
|
||||
<span class="fn__flex-1"></span>
|
||||
<span class="av__group-icon ariaLabel" data-type="av-add-top" data-position="north" aria-label="${window.siyuan.languages.newRow}"><svg><use xlink:href="#iconAdd"></use></svg></span>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
const getKanbanHTML = (data: IAVKanban) => {
|
||||
let galleryHTML = "";
|
||||
// body
|
||||
data.cards.forEach((item: IAVGalleryItem, rowIndex: number) => {
|
||||
galleryHTML += `<div data-id="${item.id}" draggable="true" class="av__gallery-item">`;
|
||||
if (data.coverFrom !== 0) {
|
||||
const coverClass = "av__gallery-cover av__gallery-cover--" + data.cardAspectRatio;
|
||||
if (item.coverURL) {
|
||||
if (item.coverURL.startsWith("background")) {
|
||||
galleryHTML += `<div class="${coverClass}"><img class="av__gallery-img" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" style="${item.coverURL}"></div>`;
|
||||
} else {
|
||||
galleryHTML += `<div class="${coverClass}"><img loading="lazy" class="av__gallery-img${data.fitImage ? " av__gallery-img--fit" : ""}" src="${getCompressURL(item.coverURL)}"></div>`;
|
||||
}
|
||||
} else if (item.coverContent) {
|
||||
galleryHTML += `<div class="${coverClass}"><div class="av__gallery-content">${item.coverContent}</div><div></div></div>`;
|
||||
} else {
|
||||
galleryHTML += `<div class="${coverClass}"></div>`;
|
||||
}
|
||||
}
|
||||
galleryHTML += '<div class="av__gallery-fields">';
|
||||
item.values.forEach((cell, fieldsIndex) => {
|
||||
if (data.fields[fieldsIndex].hidden) {
|
||||
return;
|
||||
}
|
||||
let checkClass = "";
|
||||
if (cell.valueType === "checkbox") {
|
||||
checkClass = cell.value?.checkbox?.checked ? " av__cell-check" : " av__cell-uncheck";
|
||||
}
|
||||
const isEmpty = cellValueIsEmpty(cell.value);
|
||||
// NOTE: innerHTML 中不能换行否则 https://github.com/siyuan-note/siyuan/issues/15132
|
||||
let ariaLabel = escapeAttr(data.fields[fieldsIndex].name) || getColNameByType(data.fields[fieldsIndex].type);
|
||||
if (data.fields[fieldsIndex].desc) {
|
||||
ariaLabel += escapeAttr(`<div class="ft__on-surface">${data.fields[fieldsIndex].desc}</div>`);
|
||||
}
|
||||
|
||||
if (cell.valueType === "checkbox" && !data.displayFieldName) {
|
||||
cell.value.checkbox.content = data.fields[fieldsIndex].name || getColNameByType(data.fields[fieldsIndex].type);
|
||||
}
|
||||
const cellHTML = `<div class="av__cell${checkClass}${data.displayFieldName ? "" : " ariaLabel"}"
|
||||
data-wrap="${data.fields[fieldsIndex].wrap}"
|
||||
aria-label="${ariaLabel}"
|
||||
data-position="5west"
|
||||
data-id="${cell.id}"
|
||||
data-field-id="${data.fields[fieldsIndex].id}"
|
||||
data-dtype="${cell.valueType}"
|
||||
${cell.value?.isDetached ? ' data-detached="true"' : ""}
|
||||
style="${cell.bgColor ? `background-color:${cell.bgColor};` : ""}
|
||||
${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex, data.showIcon, "gallery")}</div>`;
|
||||
if (data.displayFieldName) {
|
||||
galleryHTML += `<div class="av__gallery-field av__gallery-field--name" data-empty="${isEmpty}">
|
||||
<div class="av__gallery-name">
|
||||
${data.fields[fieldsIndex].icon ? unicode2Emoji(data.fields[fieldsIndex].icon, undefined, true) : `<svg><use xlink:href="#${getColIconByType(data.fields[fieldsIndex].type)}"></use></svg>`}${Lute.EscapeHTMLStr(data.fields[fieldsIndex].name)}
|
||||
${data.fields[fieldsIndex].desc ? `<svg aria-label="${data.fields[fieldsIndex].desc}" data-position="north" class="ariaLabel"><use xlink:href="#iconInfo"></use></svg>` : ""}
|
||||
</div>
|
||||
${cellHTML}
|
||||
</div>`;
|
||||
} else {
|
||||
galleryHTML += `<div class="av__gallery-field" data-empty="${isEmpty}">
|
||||
<div class="av__gallery-tip">
|
||||
${data.fields[fieldsIndex].icon ? unicode2Emoji(data.fields[fieldsIndex].icon, undefined, true) : `<svg><use xlink:href="#${getColIconByType(data.fields[fieldsIndex].type)}"></use></svg>`}${window.siyuan.languages.edit} ${Lute.EscapeHTMLStr(data.fields[fieldsIndex].name)}
|
||||
</div>
|
||||
${cellHTML}
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
galleryHTML += `</div>
|
||||
<div class="av__gallery-actions">
|
||||
<span class="protyle-icon protyle-icon--first b3-tooltips b3-tooltips__n" aria-label="${window.siyuan.languages.displayEmptyFields}" data-type="av-gallery-edit"><svg><use xlink:href="#iconEdit"></use></svg></span>
|
||||
<span class="protyle-icon protyle-icon--last b3-tooltips b3-tooltips__n" aria-label="${window.siyuan.languages.more}" data-type="av-gallery-more"><svg><use xlink:href="#iconMore"></use></svg></span>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
galleryHTML += `<div class="av__gallery-add" data-type="av-add-bottom"><svg class="svg"><use xlink:href="#iconAdd"></use></svg><span class="fn__space"></span>${window.siyuan.languages.newRow}</div>`;
|
||||
return `<div class="av__gallery${data.cardSize === 0 ? " av__gallery--small" : (data.cardSize === 2 ? " av__gallery--big" : "")}">
|
||||
${galleryHTML}
|
||||
</div>
|
||||
<div class="av__gallery-load${data.cardCount > data.cards.length ? "" : " fn__none"}">
|
||||
<button class="b3-button av__button" data-type="av-load-more">
|
||||
<svg><use xlink:href="#iconArrowDown"></use></svg>
|
||||
<span>${window.siyuan.languages.loadMore}</span>
|
||||
<svg data-type="set-page-size" data-size="${data.pageSize}"><use xlink:href="#iconMore"></use></svg>
|
||||
</button>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
export const renderKanban = async (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 editIds: IIds[] = [];
|
||||
options.blockElement.querySelectorAll(".av__gallery-fields--edit").forEach(item => {
|
||||
editIds.push({
|
||||
groupId: (hasClosestByClassName(item, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
fieldId: item.parentElement.getAttribute("data-id"),
|
||||
});
|
||||
});
|
||||
const selectItemIds: IIds[] = [];
|
||||
options.blockElement.querySelectorAll(".av__gallery-item--select").forEach(galleryItem => {
|
||||
const fieldId = galleryItem.getAttribute("data-id");
|
||||
if (fieldId) {
|
||||
selectItemIds.push({
|
||||
groupId: (hasClosestByClassName(galleryItem, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
fieldId
|
||||
});
|
||||
}
|
||||
});
|
||||
const pageSizes: { [key: string]: string } = {};
|
||||
options.blockElement.querySelectorAll(".av__body").forEach((item: HTMLElement) => {
|
||||
pageSizes[item.dataset.groupId || "unGroup"] = item.dataset.pageSize;
|
||||
});
|
||||
const resetData = {
|
||||
isSearching: searchInputElement && document.activeElement === searchInputElement,
|
||||
query: searchInputElement?.value || "",
|
||||
alignSelf: options.blockElement.style.alignSelf,
|
||||
oldOffset: options.protyle.contentElement.scrollTop,
|
||||
editIds,
|
||||
selectItemIds,
|
||||
pageSizes,
|
||||
};
|
||||
if (options.blockElement.firstElementChild.innerHTML === "") {
|
||||
options.blockElement.style.alignSelf = "";
|
||||
options.blockElement.firstElementChild.outerHTML = `<div class="av__kanban fn__flex">
|
||||
<span style="width: 260px;height: 178px;" class="av__pulse"></span>
|
||||
<span style="width: 260px;height: 178px;" class="av__pulse"></span>
|
||||
<span style="width: 260px;height: 178px;" class="av__pulse"></span>
|
||||
</div>`;
|
||||
}
|
||||
const created = options.protyle.options.history?.created;
|
||||
const snapshot = options.protyle.options.history?.snapshot;
|
||||
|
||||
let data: IAV = options.data;
|
||||
if (!data) {
|
||||
const avPageSize = getPageSize(options.blockElement);
|
||||
const response = await fetchSyncPost(created ? "/api/av/renderHistoryAttributeView" : (snapshot ? "/api/av/renderSnapshotAttributeView" : "/api/av/renderAttributeView"), {
|
||||
id: options.blockElement.getAttribute("data-av-id"),
|
||||
created,
|
||||
snapshot,
|
||||
pageSize: avPageSize.unGroupPageSize,
|
||||
groupPaging: avPageSize.groupPageSize,
|
||||
viewID: options.blockElement.getAttribute(Constants.CUSTOM_SY_AV_VIEW) || "",
|
||||
query: resetData.query.trim()
|
||||
});
|
||||
data = response.data;
|
||||
}
|
||||
if (data.viewType === "table") {
|
||||
options.blockElement.setAttribute("data-av-type", "table");
|
||||
avRender(options.blockElement, options.protyle, options.cb, options.renderAll, data);
|
||||
return;
|
||||
}
|
||||
if (data.viewType === "gallery") {
|
||||
options.blockElement.setAttribute("data-av-type", data.viewType);
|
||||
renderGallery({blockElement: options.blockElement, protyle:options.protyle, cb:options.cb, renderAll:options.renderAll, data});
|
||||
return;
|
||||
}
|
||||
const view: IAVGallery = data.view as IAVKanban;
|
||||
let bodyHTML = "";
|
||||
let isSelectGroup = true;
|
||||
view.groups.forEach((group: IAVKanban) => {
|
||||
if (group.groupHidden === 0) {
|
||||
let selectBg = "";
|
||||
if (group.fillColBackgroundColor) {
|
||||
let color = ""
|
||||
if (["mSelect", "select"].includes(group.groupValue.type)) {
|
||||
isSelectGroup = true;
|
||||
color = getComputedStyle(document.documentElement).getPropertyValue(`--b3-font-background${group.groupValue.mSelect[0].color}`);
|
||||
}
|
||||
if (isSelectGroup) {
|
||||
if (!color) {
|
||||
color = getComputedStyle(document.documentElement).getPropertyValue("--b3-border-color");
|
||||
}
|
||||
selectBg = `style="--b3-av-kanban-border:${color};--b3-av-kanban-bg:${color}29;--b3-av-kanban-content-bg:${color}47;--b3-av-kanban-content-hover-bg:${color}5c;"`;
|
||||
}
|
||||
}
|
||||
bodyHTML += `<div class="av__kanban-group"${selectBg}>
|
||||
${getKanbanTitleHTML(group, group.cards.length)}
|
||||
<div data-group-id="${group.id}" data-page-size="${group.pageSize}" data-dtype="${group.groupKey.type}" data-content="${Lute.EscapeHTMLStr(group.groupValue.text?.content)}" class="av__body${group.groupFolded ? " fn__none" : ""}">${getKanbanHTML(group)}</div>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
if (options.renderAll) {
|
||||
options.blockElement.firstElementChild.outerHTML = `<div class="av__container fn__block">
|
||||
${genTabHeaderHTML(data, resetData.isSearching || !!resetData.query, !options.protyle.disabled && !hasClosestByAttribute(options.blockElement, "data-type", "NodeBlockQueryEmbed"))}
|
||||
<div class="av__kanban">
|
||||
${bodyHTML}
|
||||
</div>
|
||||
<div class="av__cursor" contenteditable="true">${Constants.ZWSP}</div>
|
||||
</div>`;
|
||||
} else {
|
||||
options.blockElement.querySelector(".av__kanban").innerHTML = bodyHTML;
|
||||
}
|
||||
afterRenderGallery({
|
||||
resetData,
|
||||
renderAll: options.renderAll,
|
||||
data,
|
||||
cb: options.cb,
|
||||
protyle: options.protyle,
|
||||
blockElement: options.blockElement,
|
||||
});
|
||||
if (view.hideAttrViewName) {
|
||||
options.blockElement.querySelector(".av__gallery").classList.add("av__gallery--top");
|
||||
}
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import {fetchPost} from "../../../util/fetch";
|
||||
import {fetchSyncPost} from "../../../util/fetch";
|
||||
import {getColIconByType} from "./col";
|
||||
import {Constants} from "../../../constants";
|
||||
import {addDragFill, cellScrollIntoView, popTextCell, renderCell} from "./cell";
|
||||
|
|
@ -13,12 +13,13 @@ import {escapeAriaLabel, escapeAttr, escapeHtml} from "../../../util/escape";
|
|||
import {electronUndo} from "../../undo";
|
||||
import {isInAndroid, isInHarmony, isInIOS} from "../../util/compatibility";
|
||||
import {isMobile} from "../../../util/functions";
|
||||
import {renderKanban, renderGallery} from "./gallery/render";
|
||||
import {renderGallery} from "./gallery/render";
|
||||
import {getFieldsByData, getViewIcon} from "./view";
|
||||
import {openMenuPanel} from "./openMenuPanel";
|
||||
import {getPageSize} from "./groups";
|
||||
import {clearSelect} from "../../util/clearSelect";
|
||||
import {showMessage} from "../../../dialog/message";
|
||||
import {renderKanban} from "./kanban/render";
|
||||
|
||||
interface IIds {
|
||||
groupId: string,
|
||||
|
|
@ -250,7 +251,7 @@ export const getGroupTitleHTML = (group: IAVView, counter: number) => {
|
|||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<span class="av__group-name">${nameHTML}</span>
|
||||
${counter === 0 ? '<span class="fn__space"></span>' : `<span class="av__group-counter">${counter}</span>`}
|
||||
${counter === 0 ? '<span class="fn__space"></span>' : `<span aria-label="${window.siyuan.languages.total}" data-position="north" class="av__group-counter ariaLabel">${counter}</span>`}
|
||||
<span class="av__group-icon av__group-icon--hover ariaLabel" data-type="av-add-top" data-position="north" aria-label="${window.siyuan.languages.newRow}"><svg><use xlink:href="#iconAdd"></use></svg></span>
|
||||
</div>`;
|
||||
};
|
||||
|
|
@ -442,7 +443,7 @@ const afterRenderTable = (options: ITableOptions) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const avRender = (element: Element, protyle: IProtyle, cb?: (data: IAV) => void, renderAll = true) => {
|
||||
export const avRender = async (element: Element, protyle: IProtyle, cb?: (data: IAV) => void, renderAll = true, data?: IAV) => {
|
||||
let avElements: Element[] = [];
|
||||
if (element.getAttribute("data-type") === "NodeAttributeView") {
|
||||
// 编辑器内代码块编辑渲染
|
||||
|
|
@ -453,97 +454,98 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: (data: IAV) =
|
|||
if (avElements.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (avElements.length > 0) {
|
||||
avElements.forEach((e: HTMLElement) => {
|
||||
e.removeAttribute("data-rendering");
|
||||
if (e.getAttribute("data-render") === "true" || hasClosestByClassName(e, "av__gallery-content")) {
|
||||
return;
|
||||
}
|
||||
if (isMobile() || isInIOS() || isInAndroid() || isInHarmony()) {
|
||||
e.classList.add("av--touch");
|
||||
}
|
||||
for (let i = 0; i < avElements.length; i++) {
|
||||
const e = avElements[i] as HTMLElement;
|
||||
e.removeAttribute("data-rendering");
|
||||
if (e.getAttribute("data-render") === "true" || hasClosestByClassName(e, "av__gallery-content")) {
|
||||
return;
|
||||
}
|
||||
if (isMobile() || isInIOS() || isInAndroid() || isInHarmony()) {
|
||||
e.classList.add("av--touch");
|
||||
}
|
||||
|
||||
if (e.getAttribute("data-av-type") === "gallery") {
|
||||
renderGallery({blockElement: e, protyle, cb, renderAll});
|
||||
return;
|
||||
}
|
||||
if (e.getAttribute("data-av-type") === "kanban") {
|
||||
renderKanban({blockElement: e, protyle, cb, renderAll});
|
||||
return;
|
||||
}
|
||||
if (e.getAttribute("data-av-type") === "gallery") {
|
||||
renderGallery({blockElement: e, protyle, cb, renderAll});
|
||||
return;
|
||||
}
|
||||
if (e.getAttribute("data-av-type") === "kanban") {
|
||||
renderKanban({blockElement: e, protyle, cb, renderAll});
|
||||
return;
|
||||
}
|
||||
|
||||
let selectCellId;
|
||||
const selectCellElement = e.querySelector(".av__cell--select") as HTMLElement;
|
||||
if (selectCellElement) {
|
||||
selectCellId = {
|
||||
groupId: (hasClosestByClassName(selectCellElement, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId: (hasClosestByClassName(selectCellElement, "av__row") as HTMLElement).dataset.id,
|
||||
colId: selectCellElement.getAttribute("data-col-id"),
|
||||
};
|
||||
}
|
||||
const selectRowIds: IIds[] = [];
|
||||
e.querySelectorAll(".av__row--select").forEach(rowItem => {
|
||||
const rowId = rowItem.getAttribute("data-id");
|
||||
if (rowId) {
|
||||
selectRowIds.push({
|
||||
groupId: (hasClosestByClassName(rowItem, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId
|
||||
});
|
||||
}
|
||||
});
|
||||
let dragFillId;
|
||||
const dragFillElement = e.querySelector(".av__drag-fill") as HTMLElement;
|
||||
if (dragFillElement) {
|
||||
dragFillId = {
|
||||
groupId: (hasClosestByClassName(dragFillElement, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId: (hasClosestByClassName(dragFillElement, "av__row") as HTMLElement).dataset.id,
|
||||
colId: dragFillElement.parentElement.getAttribute("data-col-id"),
|
||||
};
|
||||
}
|
||||
const activeIds: IIds[] = [];
|
||||
e.querySelectorAll(".av__cell--active").forEach((item: HTMLElement) => {
|
||||
activeIds.push({
|
||||
groupId: (hasClosestByClassName(item, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId: (hasClosestByClassName(item, "av__row") as HTMLElement).dataset.id,
|
||||
colId: item.getAttribute("data-col-id"),
|
||||
});
|
||||
});
|
||||
const searchInputElement = e.querySelector('[data-type="av-search"]') as HTMLInputElement;
|
||||
const pageSizes: { [key: string]: string } = {};
|
||||
e.querySelectorAll(".av__body").forEach((item: HTMLElement) => {
|
||||
pageSizes[item.dataset.groupId || "unGroup"] = item.dataset.pageSize;
|
||||
});
|
||||
const resetData = {
|
||||
selectCellId,
|
||||
alignSelf: e.style.alignSelf,
|
||||
left: e.querySelector(".av__scroll")?.scrollLeft || 0,
|
||||
headerTransform: (e.querySelector('.av__row--header[style^="transform"]') as HTMLElement)?.style.transform,
|
||||
footerTransform: (e.querySelector(".av__row--footer") as HTMLElement)?.style.transform,
|
||||
isSearching: searchInputElement && document.activeElement === searchInputElement,
|
||||
selectRowIds,
|
||||
dragFillId,
|
||||
activeIds,
|
||||
query: searchInputElement?.value || "",
|
||||
pageSizes
|
||||
let selectCellId;
|
||||
const selectCellElement = e.querySelector(".av__cell--select") as HTMLElement;
|
||||
if (selectCellElement) {
|
||||
selectCellId = {
|
||||
groupId: (hasClosestByClassName(selectCellElement, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId: (hasClosestByClassName(selectCellElement, "av__row") as HTMLElement).dataset.id,
|
||||
colId: selectCellElement.getAttribute("data-col-id"),
|
||||
};
|
||||
if (e.firstElementChild.innerHTML === "") {
|
||||
e.style.alignSelf = "";
|
||||
let html = "";
|
||||
[1, 2, 3].forEach(() => {
|
||||
html += `<div class="av__row">
|
||||
}
|
||||
const selectRowIds: IIds[] = [];
|
||||
e.querySelectorAll(".av__row--select").forEach(rowItem => {
|
||||
const rowId = rowItem.getAttribute("data-id");
|
||||
if (rowId) {
|
||||
selectRowIds.push({
|
||||
groupId: (hasClosestByClassName(rowItem, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId
|
||||
});
|
||||
}
|
||||
});
|
||||
let dragFillId;
|
||||
const dragFillElement = e.querySelector(".av__drag-fill") as HTMLElement;
|
||||
if (dragFillElement) {
|
||||
dragFillId = {
|
||||
groupId: (hasClosestByClassName(dragFillElement, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId: (hasClosestByClassName(dragFillElement, "av__row") as HTMLElement).dataset.id,
|
||||
colId: dragFillElement.parentElement.getAttribute("data-col-id"),
|
||||
};
|
||||
}
|
||||
const activeIds: IIds[] = [];
|
||||
e.querySelectorAll(".av__cell--active").forEach((item: HTMLElement) => {
|
||||
activeIds.push({
|
||||
groupId: (hasClosestByClassName(item, "av__body") as HTMLElement).dataset.groupId || "",
|
||||
rowId: (hasClosestByClassName(item, "av__row") as HTMLElement).dataset.id,
|
||||
colId: item.getAttribute("data-col-id"),
|
||||
});
|
||||
});
|
||||
const searchInputElement = e.querySelector('[data-type="av-search"]') as HTMLInputElement;
|
||||
const pageSizes: { [key: string]: string } = {};
|
||||
e.querySelectorAll(".av__body").forEach((item: HTMLElement) => {
|
||||
pageSizes[item.dataset.groupId || "unGroup"] = item.dataset.pageSize;
|
||||
});
|
||||
const resetData = {
|
||||
selectCellId,
|
||||
alignSelf: e.style.alignSelf,
|
||||
left: e.querySelector(".av__scroll")?.scrollLeft || 0,
|
||||
headerTransform: (e.querySelector('.av__row--header[style^="transform"]') as HTMLElement)?.style.transform,
|
||||
footerTransform: (e.querySelector(".av__row--footer") as HTMLElement)?.style.transform,
|
||||
isSearching: searchInputElement && document.activeElement === searchInputElement,
|
||||
selectRowIds,
|
||||
dragFillId,
|
||||
activeIds,
|
||||
query: searchInputElement?.value || "",
|
||||
pageSizes
|
||||
};
|
||||
if (e.firstElementChild.innerHTML === "") {
|
||||
e.style.alignSelf = "";
|
||||
let html = "";
|
||||
[1, 2, 3].forEach(() => {
|
||||
html += `<div class="av__row">
|
||||
<div style="width: 24px;flex-shrink: 0"></div>
|
||||
<div class="av__cell" style="width: 200px"><span class="av__pulse"></span></div>
|
||||
<div class="av__cell" style="width: 200px"><span class="av__pulse"></span></div>
|
||||
<div class="av__cell" style="width: 200px"><span class="av__pulse"></span></div>
|
||||
<div class="av__cell" style="width: 200px"><span class="av__pulse"></span></div>
|
||||
</div>`;
|
||||
});
|
||||
e.firstElementChild.innerHTML = html;
|
||||
}
|
||||
const created = protyle.options.history?.created;
|
||||
const snapshot = protyle.options.history?.snapshot;
|
||||
const avPageSize = getPageSize(e);
|
||||
fetchPost(created ? "/api/av/renderHistoryAttributeView" : (snapshot ? "/api/av/renderSnapshotAttributeView" : "/api/av/renderAttributeView"), {
|
||||
});
|
||||
e.firstElementChild.innerHTML = html;
|
||||
}
|
||||
const created = protyle.options.history?.created;
|
||||
const snapshot = protyle.options.history?.snapshot;
|
||||
const avPageSize = getPageSize(e);
|
||||
if (!data) {
|
||||
const response = await fetchSyncPost(created ? "/api/av/renderHistoryAttributeView" : (snapshot ? "/api/av/renderSnapshotAttributeView" : "/api/av/renderAttributeView"), {
|
||||
id: e.getAttribute("data-av-id"),
|
||||
created,
|
||||
snapshot,
|
||||
|
|
@ -552,43 +554,48 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: (data: IAV) =
|
|||
viewID: e.getAttribute(Constants.CUSTOM_SY_AV_VIEW) || "",
|
||||
query: resetData.query.trim(),
|
||||
blockID: e.getAttribute("data-node-id"),
|
||||
}, (response) => {
|
||||
const data = response.data.view as IAVTable;
|
||||
if (response.data.viewType === "gallery") {
|
||||
e.setAttribute("data-av-type", "table");
|
||||
renderGallery({blockElement: e, protyle, cb, renderAll, data: response.data});
|
||||
return;
|
||||
}
|
||||
if (data.groups?.length > 0) {
|
||||
renderGroupTable({blockElement: e, protyle, cb, renderAll, data: response.data, resetData});
|
||||
return;
|
||||
}
|
||||
const avBodyHTML = `<div class="av__body" data-group-id="" data-page-size="${data.pageSize}" style="float: left">
|
||||
${getTableHTMLs(data, e)}
|
||||
});
|
||||
data = response.data;
|
||||
}
|
||||
if (data.viewType === "gallery") {
|
||||
e.setAttribute("data-av-type", data.viewType);
|
||||
renderGallery({blockElement: e, protyle, cb, renderAll, data});
|
||||
return;
|
||||
}
|
||||
if (data.viewType === "kanban") {
|
||||
e.setAttribute("data-av-type", data.viewType);
|
||||
renderKanban({blockElement: e, protyle, cb, renderAll, data});
|
||||
return;
|
||||
}
|
||||
const view = data.view as IAVTable;
|
||||
if (view.groups?.length > 0) {
|
||||
renderGroupTable({blockElement: e, protyle, cb, renderAll, data, resetData});
|
||||
return;
|
||||
}
|
||||
const avBodyHTML = `<div class="av__body" data-group-id="" data-page-size="${view.pageSize}" style="float: left">
|
||||
${getTableHTMLs(view, e)}
|
||||
</div>`;
|
||||
if (renderAll) {
|
||||
e.firstElementChild.outerHTML = `<div class="av__container">
|
||||
${genTabHeaderHTML(response.data, resetData.isSearching || !!resetData.query, !protyle.disabled && !hasClosestByAttribute(e, "data-type", "NodeBlockQueryEmbed"))}
|
||||
if (renderAll) {
|
||||
e.firstElementChild.outerHTML = `<div class="av__container">
|
||||
${genTabHeaderHTML(data, resetData.isSearching || !!resetData.query, !protyle.disabled && !hasClosestByAttribute(e, "data-type", "NodeBlockQueryEmbed"))}
|
||||
<div class="av__scroll">
|
||||
${avBodyHTML}
|
||||
</div>
|
||||
<div class="av__cursor" contenteditable="true">${Constants.ZWSP}</div>
|
||||
</div>`;
|
||||
} else {
|
||||
e.firstElementChild.querySelector(".av__scroll").innerHTML = avBodyHTML;
|
||||
}
|
||||
afterRenderTable({
|
||||
renderAll,
|
||||
data: response.data,
|
||||
cb,
|
||||
protyle,
|
||||
blockElement: e,
|
||||
resetData
|
||||
});
|
||||
// 历史兼容
|
||||
e.style.margin = "";
|
||||
});
|
||||
} else {
|
||||
e.firstElementChild.querySelector(".av__scroll").innerHTML = avBodyHTML;
|
||||
}
|
||||
afterRenderTable({
|
||||
renderAll,
|
||||
data,
|
||||
cb,
|
||||
protyle,
|
||||
blockElement: e,
|
||||
resetData
|
||||
});
|
||||
// 历史兼容
|
||||
e.style.margin = "";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
14
app/src/types/index.d.ts
vendored
14
app/src/types/index.d.ts
vendored
|
|
@ -909,6 +909,20 @@ interface IAVGallery extends IAVView {
|
|||
cardCount: number,
|
||||
}
|
||||
|
||||
interface IAVKanban extends IAVView {
|
||||
coverFrom: number; // 0:无,1:内容图,2:资源字段,3:内容块
|
||||
coverFromAssetKeyID?: string;
|
||||
cardSize: number; // 0:小卡片,1:中卡片,2:大卡片
|
||||
cardAspectRatio: number;
|
||||
displayFieldName: boolean;
|
||||
fitImage: boolean;
|
||||
cards: IAVGalleryItem[],
|
||||
desc: string
|
||||
fields: IAVColumn[]
|
||||
cardCount: number,
|
||||
fillColBackgroundColor: boolean
|
||||
}
|
||||
|
||||
interface IAVFilter {
|
||||
column: string,
|
||||
operator: TAVFilterOperator,
|
||||
|
|
|
|||
|
|
@ -58,9 +58,14 @@ export const loadAssets = (data: Config.IAppearance) => {
|
|||
/// #if !MOBILE
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll(".av__kanban-group").forEach(item => {
|
||||
const nameElement = item.querySelector(".av__group-title .b3-chip") as HTMLElement;
|
||||
if (nameElement) {
|
||||
const selectBg = getComputedStyle(document.documentElement).getPropertyValue(`--b3-font-background${nameElement.style.backgroundColor.slice(-2, -1)}`);
|
||||
if (item.getAttribute("style")) {
|
||||
let selectBg;
|
||||
const nameElement = item.querySelector(".av__group-title .b3-chip") as HTMLElement;
|
||||
if (nameElement) {
|
||||
selectBg = getComputedStyle(document.documentElement).getPropertyValue(`--b3-font-background${nameElement.style.backgroundColor.slice(-2, -1)}`);
|
||||
} else {
|
||||
selectBg = getComputedStyle(document.documentElement).getPropertyValue("--b3-border-color");
|
||||
}
|
||||
item.setAttribute("style", `--b3-av-kanban-border:${selectBg};--b3-av-kanban-bg:${selectBg}29;--b3-av-kanban-content-bg:${selectBg}47;--b3-av-kanban-content-hover-bg:${selectBg}5c;`);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue