Vanessa 2025-06-10 22:29:50 +08:00
parent 9da8f9b194
commit 3010df69b4
7 changed files with 360 additions and 42 deletions

View file

@ -277,6 +277,10 @@
gap: 16px; gap: 16px;
width: 100%; width: 100%;
&--top {
margin-top: 16px;
}
&--small { &--small {
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
} }
@ -293,11 +297,11 @@
&:hover { &:hover {
.av__gallery-cover { .av__gallery-cover {
background-color: var(--b3-theme-surface-lighter); background-color: var(--b3-list-hover);
} }
.av__gallery-fields { .av__gallery-fields {
background-color: var(--b3-theme-surface-light); background-color: var(--b3-theme-surface);
} }
} }
@ -336,6 +340,7 @@
} }
&-fields { &-fields {
border-radius: 0 0 var(--b3-border-radius) var(--b3-border-radius);
flex: 1; flex: 1;
transition: background 100ms ease-out; transition: background 100ms ease-out;
} }

View file

@ -153,13 +153,20 @@ ${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex)}
<div contenteditable="${options.protyle.disabled || hasClosestByAttribute(options.blockElement, "data-type", "NodeBlockQueryEmbed") ? "false" : "true"}" spellcheck="${window.siyuan.config.editor.spellcheck.toString()}" class="av__title${viewData.hideAttrViewName ? " fn__none" : ""}" data-title="${response.data.name || ""}" data-tip="${window.siyuan.languages.title}">${response.data.name || ""}</div> <div contenteditable="${options.protyle.disabled || hasClosestByAttribute(options.blockElement, "data-type", "NodeBlockQueryEmbed") ? "false" : "true"}" spellcheck="${window.siyuan.config.editor.spellcheck.toString()}" class="av__title${viewData.hideAttrViewName ? " fn__none" : ""}" data-title="${response.data.name || ""}" data-tip="${window.siyuan.languages.title}">${response.data.name || ""}</div>
<div class="av__counter fn__none"></div> <div class="av__counter fn__none"></div>
</div> </div>
<div class="av__gallery${view.cardSize === 0 ? " av__gallery--small" : (view.cardSize === 2 ? " av__gallery--big" : "")}"> <div class="av__gallery${view.cardSize === 0 ? " av__gallery--small" : (view.cardSize === 2 ? " av__gallery--big" : "")}
${view.hideAttrViewName ? " av__gallery--top" : ""}">
${galleryHTML} ${galleryHTML}
</div> </div>
<div class="av__cursor" contenteditable="true">${Constants.ZWSP}</div> <div class="av__cursor" contenteditable="true">${Constants.ZWSP}</div>
</div>`; </div>`;
} else { } else {
options.blockElement.firstElementChild.querySelector(".av__gallery").innerHTML = galleryHTML; const galleryElement = options.blockElement.firstElementChild.querySelector(".av__gallery");
galleryElement.innerHTML = galleryHTML;
if (view.hideAttrViewName) {
galleryElement.classList.add("av__gallery--top");
} else {
galleryElement.classList.remove("av__gallery--top");
}
} }
options.blockElement.setAttribute("data-render", "true"); options.blockElement.setAttribute("data-render", "true");
if (alignSelf) { if (alignSelf) {

View file

@ -0,0 +1,171 @@
import {transaction} from "../../../wysiwyg/transaction";
import {Menu} from "../../../../plugin/Menu";
export const setGalleryCover = (options: {
view: IAVGallery
nodeElement: Element,
protyle: IProtyle,
target: HTMLElement
}) => {
const avID = options.nodeElement.getAttribute("data-av-id");
const blockID = options.nodeElement.getAttribute("data-node-id");
const targetNameElement = options.target.querySelector(".b3-menu__accelerator");
const menu = new Menu();
menu.addItem({
iconHTML: "",
checked: options.view.coverFrom === 0,
label: window.siyuan.languages.calcOperatorNone,
click() {
transaction(options.protyle, [{
action: "setAttrViewCoverFrom",
avID,
blockID,
data: 0
}], [{
action: "setAttrViewCoverFrom",
avID,
blockID,
data: options.view.coverFrom
}]);
options.view.coverFrom = 0;
targetNameElement.textContent = window.siyuan.languages.calcOperatorNone;
}
});
menu.addItem({
iconHTML: "",
checked: options.view.coverFrom === 1,
label: window.siyuan.languages.contentImage,
click() {
transaction(options.protyle, [{
action: "setAttrViewCoverFrom",
avID,
blockID,
data: 1
}], [{
action: "setAttrViewCoverFrom",
avID,
blockID,
data: options.view.coverFrom
}]);
options.view.coverFrom = 1;
targetNameElement.textContent = window.siyuan.languages.contentImage;
}
});
options.view.fields.forEach(item => {
if (item.type === "mAsset") {
menu.addItem({
iconHTML: "",
checked: options.view.coverFromAssetKeyID === item.id,
label: item.name,
click() {
transaction(options.protyle, [{
action: "setAttrViewCoverFrom",
avID,
blockID,
data: 2
}, {
action: "setAttrViewCoverFromAssetKeyID",
avID,
blockID,
keyID: item.id
}], [{
action: "setAttrViewCoverFrom",
avID,
blockID,
data: options.view.coverFrom
}, {
action: "setAttrViewCoverFromAssetKeyID",
avID,
blockID,
keyID: options.view.coverFromAssetKeyID
}]);
options.view.coverFrom = 2;
options.view.coverFromAssetKeyID = item.id;
targetNameElement.textContent = item.name;
}
});
}
});
const rect = options.target.getBoundingClientRect();
menu.open({x: rect.left, y: rect.bottom});
};
export const setGallerySize = (options: {
view: IAVGallery
nodeElement: Element,
protyle: IProtyle,
target: HTMLElement
}) => {
const menu = new Menu();
const avID = options.nodeElement.getAttribute("data-av-id");
const blockID = options.nodeElement.getAttribute("data-node-id");
const galleryElement = options.nodeElement.querySelector(".av__gallery");
const targetNameElement = options.target.querySelector(".b3-menu__accelerator");
menu.addItem({
iconHTML: "",
checked: options.view.cardSize === 0,
label: window.siyuan.languages.small,
click() {
transaction(options.protyle, [{
action: "setAttrViewCardSize",
avID,
blockID,
data: 0
}], [{
action: "setAttrViewCardSize",
avID,
blockID,
data: options.view.cardSize
}]);
options.view.cardSize = 0;
galleryElement.classList.add("av__gallery--small");
galleryElement.classList.remove("av__gallery--big");
targetNameElement.textContent = window.siyuan.languages.small;
}
});
menu.addItem({
iconHTML: "",
checked: options.view.cardSize === 1,
label: window.siyuan.languages.medium,
click() {
transaction(options.protyle, [{
action: "setAttrViewCardSize",
avID,
blockID,
data: 1
}], [{
action: "setAttrViewCardSize",
avID,
blockID,
data: options.view.cardSize
}]);
options.view.cardSize = 1;
galleryElement.classList.remove("av__gallery--big", "av__gallery--small");
targetNameElement.textContent = window.siyuan.languages.medium;
}
});
menu.addItem({
iconHTML: "",
checked: options.view.cardSize === 2,
label: window.siyuan.languages.large,
click() {
transaction(options.protyle, [{
action: "setAttrViewCardSize",
avID,
blockID,
data: 2
}], [{
action: "setAttrViewCardSize",
avID,
blockID,
data: options.view.cardSize
}]);
options.view.cardSize = 2;
galleryElement.classList.remove("av__gallery--small");
galleryElement.classList.add("av__gallery--big");
targetNameElement.textContent = window.siyuan.languages.large;
}
});
const rect = options.target.getBoundingClientRect();
menu.open({x: rect.left, y: rect.bottom});
};

View file

@ -2,13 +2,48 @@ import {transaction} from "../../wysiwyg/transaction";
export const getLayoutHTML = (data: IAV) => { export const getLayoutHTML = (data: IAV) => {
let html = ""; let html = "";
const view = data.view as IAVGallery;
if (data.viewType === "gallery") { if (data.viewType === "gallery") {
html = `<label class="b3-menu__item"> let coverFromTitle = "";
<span class="fn__flex-center">${window.siyuan.languages.showTitle}</span> if (view.coverFrom === 0) {
coverFromTitle = window.siyuan.languages.calcOperatorNone;
} else if (view.coverFrom === 1) {
coverFromTitle = window.siyuan.languages.contentImage;
} else {
view.fields.find(item => {
if (item.type === "mAsset" && item.id === view.coverFromAssetKeyID) {
coverFromTitle = item.name;
return true;
}
});
}
html = `<button class="b3-menu__item" data-type="set-gallery-cover">
<span class="fn__flex-center">${window.siyuan.languages.cardPreview1}</span>
<span class="fn__flex-1"></span>
<span class="b3-menu__accelerator">${coverFromTitle}</span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>
<button class="b3-menu__item" data-type="set-gallery-size">
<span class="fn__flex-center">${window.siyuan.languages.cardSize}</span>
<span class="fn__flex-1"></span>
<span class="b3-menu__accelerator">${view.cardSize === 0 ? window.siyuan.languages.small : (view.cardSize === 1 ? window.siyuan.languages.medium : window.siyuan.languages.large)}</span>
<svg class="b3-menu__icon b3-menu__icon--small"><use xlink:href="#iconRight"></use></svg>
</button>
<label class="b3-menu__item">
<span class="fn__flex-center">${window.siyuan.languages.fitImage}</span>
<span class="fn__space fn__flex-1"></span> <span class="fn__space fn__flex-1"></span>
<input data-type="toggle-view-title" type="checkbox" class="b3-switch b3-switch--menu" ${data.view.hideAttrViewName ? "" : "checked"}> <input data-type="toggle-gallery-fit" type="checkbox" class="b3-switch b3-switch--menu" ${view.fitImage ? "checked" : ""}>
</label>` </label>
// calcOperatorNone <label class="b3-menu__item">
<span class="fn__flex-center">${window.siyuan.languages.showIcon}</span>
<span class="fn__space fn__flex-1"></span>
<input data-type="toggle-gallery-icon" type="checkbox" class="b3-switch b3-switch--menu" ${view.showIcon ? "checked" : ""}>
</label>
<label class="b3-menu__item">
<span class="fn__flex-center">${window.siyuan.languages.wrapAllFields}</span>
<span class="fn__space fn__flex-1"></span>
<input data-type="toggle-gallery-wrap" type="checkbox" class="b3-switch b3-switch--menu" ${view.wrapField ? "checked" : ""}>
</label>`;
} }
return `<div class="b3-menu__items"> return `<div class="b3-menu__items">
<div class="b3-menu__items"> <div class="b3-menu__items">
@ -21,12 +56,12 @@ export const getLayoutHTML = (data: IAV) => {
<button class="b3-menu__separator"></button> <button class="b3-menu__separator"></button>
<button class="b3-menu__item" data-type="nobg"> <button class="b3-menu__item" data-type="nobg">
<div class="av__layout"> <div class="av__layout">
<div class="av__layout-item${data.viewType === "table" ? " av__layout-item--select" : ""}"> <div data-type="set-layoyt" data-view-type="${data.viewType}" class="av__layout-item${data.viewType === "table" ? " av__layout-item--select" : ""}">
<svg><use xlink:href="#iconTable"></use></svg> <svg><use xlink:href="#iconTable"></use></svg>
<div class="fn__hr"></div> <div class="fn__hr"></div>
<div>${window.siyuan.languages.table}</div> <div>${window.siyuan.languages.table}</div>
</div> </div>
<div class="av__layout-item${data.viewType === "gallery" ? " av__layout-item--select" : ""}"> <div data-type="set-layoyt" data-view-type="${data.viewType}" class="av__layout-item${data.viewType === "gallery" ? " av__layout-item--select" : ""}">
<svg><use xlink:href="#iconGallery"></use></svg> <svg><use xlink:href="#iconGallery"></use></svg>
<div class="fn__hr"></div> <div class="fn__hr"></div>
<div>${window.siyuan.languages.gallery}</div> <div>${window.siyuan.languages.gallery}</div>
@ -36,7 +71,7 @@ export const getLayoutHTML = (data: IAV) => {
<label class="b3-menu__item"> <label class="b3-menu__item">
<span class="fn__flex-center">${window.siyuan.languages.showTitle}</span> <span class="fn__flex-center">${window.siyuan.languages.showTitle}</span>
<span class="fn__space fn__flex-1"></span> <span class="fn__space fn__flex-1"></span>
<input data-type="toggle-view-title" type="checkbox" class="b3-switch b3-switch--menu" ${data.view.hideAttrViewName ? "" : "checked"}> <input data-type="toggle-view-title" type="checkbox" class="b3-switch b3-switch--menu" ${view.hideAttrViewName ? "" : "checked"}>
</label> </label>
${html} ${html}
</div>`; </div>`;
@ -52,33 +87,93 @@ export const bindLayoutEvent = (options: {
toggleTitleElement.addEventListener("change", () => { toggleTitleElement.addEventListener("change", () => {
const avID = options.blockElement.getAttribute("data-av-id"); const avID = options.blockElement.getAttribute("data-av-id");
const blockID = options.blockElement.getAttribute("data-node-id"); const blockID = options.blockElement.getAttribute("data-node-id");
if (!toggleTitleElement.checked) { const checked = toggleTitleElement.checked;
// hide
transaction(options.protyle, [{ transaction(options.protyle, [{
action: "hideAttrViewName", action: "hideAttrViewName",
avID, avID,
blockID, blockID,
data: true data: !checked
}], [{ }], [{
action: "hideAttrViewName", action: "hideAttrViewName",
avID, avID,
blockID, blockID,
data: false data: checked
}]);
options.blockElement.querySelector(".av__title").classList.add("fn__none");
} else {
transaction(options.protyle, [{
action: "hideAttrViewName",
avID,
blockID,
data: false
}], [{
action: "hideAttrViewName",
avID,
blockID,
data: true
}]); }]);
if (checked) {
options.blockElement.querySelector(".av__title").classList.remove("fn__none"); options.blockElement.querySelector(".av__title").classList.remove("fn__none");
} else {
// hide
options.blockElement.querySelector(".av__title").classList.add("fn__none");
}
if (options.data.viewType === "gallery") {
const galleryElement = options.blockElement.querySelector(".av__gallery");
if (checked) {
galleryElement.classList.remove("av__gallery--top");
} else {
// hide
galleryElement.classList.add("av__gallery--top");
}
} }
}); });
if (options.data.viewType !== "gallery") {
return;
}
const toggleFitElement = options.menuElement.querySelector('.b3-switch[data-type="toggle-gallery-fit"]') as HTMLInputElement;
toggleFitElement.addEventListener("change", () => {
const avID = options.blockElement.getAttribute("data-av-id");
const blockID = options.blockElement.getAttribute("data-node-id");
const checked = toggleFitElement.checked;
transaction(options.protyle, [{
action: "setAttrViewFitImage",
avID,
blockID,
data: checked
}], [{
action: "setAttrViewFitImage",
avID,
blockID,
data: !checked
}]);
options.blockElement.querySelectorAll(".av__gallery-img").forEach(item => {
if (checked) {
item.classList.add("av__gallery-img--fit");
} else {
item.classList.remove("av__gallery-img--fit");
}
})
});
const toggleIconElement = options.menuElement.querySelector('.b3-switch[data-type="toggle-gallery-icon"]') as HTMLInputElement;
toggleIconElement.addEventListener("change", () => {
const avID = options.blockElement.getAttribute("data-av-id");
const blockID = options.blockElement.getAttribute("data-node-id");
const checked = toggleIconElement.checked;
transaction(options.protyle, [{
action: "setAttrViewShowIcon",
avID,
blockID,
data: checked
}], [{
action: "setAttrViewShowIcon",
avID,
blockID,
data: !checked
}]);
});
const toggleWrapElement = options.menuElement.querySelector('.b3-switch[data-type="toggle-gallery-wrap"]') as HTMLInputElement;
toggleWrapElement.addEventListener("change", () => {
const avID = options.blockElement.getAttribute("data-av-id");
const blockID = options.blockElement.getAttribute("data-node-id");
const checked = toggleWrapElement.checked;
transaction(options.protyle, [{
action: "setAttrViewWrapField",
avID,
blockID,
data: checked
}], [{
action: "setAttrViewWrapField",
avID,
blockID,
data: !checked
}]);
});
}; };

View file

@ -38,6 +38,7 @@ import {openCalcMenu} from "./calc";
import {escapeAttr, escapeHtml} from "../../../util/escape"; import {escapeAttr, escapeHtml} from "../../../util/escape";
import {Dialog} from "../../../dialog"; import {Dialog} from "../../../dialog";
import {bindLayoutEvent, getLayoutHTML} from "./layout"; import {bindLayoutEvent, getLayoutHTML} from "./layout";
import {setGalleryCover, setGallerySize} from "./gallery/util";
export const openMenuPanel = (options: { export const openMenuPanel = (options: {
protyle: IProtyle, protyle: IProtyle,
@ -1360,6 +1361,36 @@ export const openMenuPanel = (options: {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
break; break;
} else if (type === "set-gallery-cover") {
setGalleryCover({
target,
protyle: options.protyle,
nodeElement: options.blockElement,
view: data.view as IAVGallery
});
event.preventDefault();
event.stopPropagation();
break;
} else if (type === "set-gallery-size") {
setGallerySize({
target,
protyle: options.protyle,
nodeElement: options.blockElement,
view: data.view as IAVGallery
});
event.preventDefault();
event.stopPropagation();
break;
} else if (type === "set-layout") {
setPageSize({
target,
protyle: options.protyle,
avID,
nodeElement: options.blockElement
});
event.preventDefault();
event.stopPropagation();
break;
} }
// 有错误日志,没找到重现步骤,需先判断一下 // 有错误日志,没找到重现步骤,需先判断一下
if (!target || !target.parentElement) { if (!target || !target.parentElement) {

View file

@ -850,7 +850,8 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, isUndo:
"replaceAttrViewBlock", "updateAttrViewColTemplate", "setAttrViewColPin", "addAttrViewView", "setAttrViewColIcon", "replaceAttrViewBlock", "updateAttrViewColTemplate", "setAttrViewColPin", "addAttrViewView", "setAttrViewColIcon",
"removeAttrViewView", "setAttrViewViewName", "setAttrViewViewIcon", "duplicateAttrViewView", "sortAttrViewView", "removeAttrViewView", "setAttrViewViewName", "setAttrViewViewIcon", "duplicateAttrViewView", "sortAttrViewView",
"updateAttrViewColRelation", "setAttrViewPageSize", "updateAttrViewColRollup", "sortAttrViewKey", "updateAttrViewColRelation", "setAttrViewPageSize", "updateAttrViewColRollup", "sortAttrViewKey",
"duplicateAttrViewKey", "setAttrViewViewDesc", "setAttrViewColDesc"].includes(operation.action)) { "duplicateAttrViewKey", "setAttrViewViewDesc", "setAttrViewColDesc", "setAttrViewCoverFrom", "setAttrViewShowIcon",
"setAttrViewCoverFromAssetKeyID", "changeAttrViewLayout"].includes(operation.action)) {
if (!isUndo) { if (!isUndo) {
// 撤销 transaction 会进行推送,需使用推送来进行刷新最新数据 https://github.com/siyuan-note/siyuan/issues/13607 // 撤销 transaction 会进行推送,需使用推送来进行刷新最新数据 https://github.com/siyuan-note/siyuan/issues/13607
refreshAV(protyle, operation); refreshAV(protyle, operation);

View file

@ -51,6 +51,13 @@ type TOperation =
| "moveOutlineHeading" | "moveOutlineHeading"
| "updateAttrViewColRollup" | "updateAttrViewColRollup"
| "hideAttrViewName" | "hideAttrViewName"
| "setAttrViewCardSize"
| "setAttrViewCoverFrom"
| "setAttrViewCoverFromAssetKeyID"
| "setAttrViewFitImage"
| "setAttrViewShowIcon"
| "setAttrViewWrapField"
| "changeAttrViewLayout"
| "setAttrViewColDate" | "setAttrViewColDate"
| "unbindAttrViewBlock" | "unbindAttrViewBlock"
| "setAttrViewViewDesc" | "setAttrViewViewDesc"
@ -840,10 +847,11 @@ interface IAVTable extends IAVView {
interface IAVGallery extends IAVView { interface IAVGallery extends IAVView {
coverFrom: number; // 01内容图2资源字段 coverFrom: number; // 01内容图2资源字段
coverFromAssetKeyID?: string;
cardSize: number; // 0小卡片1中卡片2大卡片 cardSize: number; // 0小卡片1中卡片2大卡片
fitImage:boolean; fitImage: boolean;
showIcon:boolean; showIcon: boolean;
wrapField:boolean; wrapField: boolean;
cards: IAVGalleryItem[], cards: IAVGalleryItem[],
desc: string desc: string
fields: IAVColumn[] fields: IAVColumn[]