Ver Fonte

:art: https://github.com/siyuan-note/siyuan/issues/11053

Vanessa há 7 meses atrás
pai
commit
e9c8cd0e0d

+ 1 - 6
app/src/block/popover.ts

@@ -28,12 +28,7 @@ export const initBlockPopover = (app: App) => {
             let tooltipClass = "";
             let tip = aElement.getAttribute("aria-label");
             if (aElement.classList.contains("av__cell")) {
-                if (aElement.classList.contains("av__cell--header")) {
-                    const textElement = aElement.querySelector(".av__celltext");
-                    if (textElement.scrollWidth > textElement.clientWidth + 2) {
-                        tip = getCellText(aElement);
-                    }
-                } else {
+                if (!aElement.classList.contains("av__cell--header")) {
                     if (aElement.firstElementChild?.getAttribute("data-type") === "url") {
                         if (aElement.firstElementChild.textContent.indexOf("...") > -1) {
                             tip = Lute.EscapeHTMLStr(aElement.firstElementChild.getAttribute("data-href"));

+ 65 - 21
app/src/protyle/render/av/col.ts

@@ -15,6 +15,7 @@ import * as dayjs from "dayjs";
 import {setPosition} from "../../../util/setPosition";
 import {duplicateNameAddOne} from "../../../util/functions";
 import {Dialog} from "../../../dialog";
+import {escapeAttr} from "../../../util/escape";
 
 export const duplicateCol = (options: {
     protyle: IProtyle,
@@ -575,35 +576,60 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
     const avID = blockElement.getAttribute("data-av-id");
     const blockID = blockElement.getAttribute("data-node-id");
     const oldValue = cellElement.querySelector(".av__celltext").textContent.trim();
+    const oldDesc = cellElement.dataset.desc;
     const menu = new Menu("av-header-cell", () => {
         const newValue = (menu.element.querySelector(".b3-text-field") as HTMLInputElement).value;
-        if (newValue === oldValue) {
-            return;
+        if (newValue !== oldValue) {
+            transaction(protyle, [{
+                action: "updateAttrViewCol",
+                id: colId,
+                avID,
+                name: newValue,
+                type,
+            }], [{
+                action: "updateAttrViewCol",
+                id: colId,
+                avID,
+                name: oldValue,
+                type,
+            }]);
+            updateAttrViewCellAnimation(blockElement.querySelector(`.av__row--header .av__cell[data-col-id="${colId}"]`), undefined, {name: newValue});
+        }
+        const newDesc = menu.element.querySelector("textarea").value;
+        if (newDesc !== oldDesc) {
+            transaction(protyle, [{
+                action: "setAttrViewColDesc",
+                id: colId,
+                avID,
+                data: newDesc,
+            }], [{
+                action: "setAttrViewColDesc",
+                id: colId,
+                avID,
+                data: oldDesc,
+            }]);
         }
-        transaction(protyle, [{
-            action: "updateAttrViewCol",
-            id: colId,
-            avID,
-            name: newValue,
-            type,
-        }], [{
-            action: "updateAttrViewCol",
-            id: colId,
-            avID,
-            name: oldValue,
-            type,
-        }]);
-        updateAttrViewCellAnimation(blockElement.querySelector(`.av__row--header .av__cell[data-col-id="${colId}"]`), undefined, {name: newValue});
         // https://github.com/siyuan-note/siyuan/issues/9862
         focusBlock(blockElement);
     });
     menu.addItem({
-        iconHTML: `<span class="b3-menu__avemoji">${cellElement.dataset.icon ? unicode2Emoji(cellElement.dataset.icon) : `<svg style="height: 14px;width: 14px;"><use xlink:href="#${getColIconByType(type)}"></use></svg>`}</span>`,
+        iconHTML: '',
         type: "readonly",
-        label: `<input style="margin: 4px 0" class="b3-text-field fn__block fn__size200" type="text" value="${oldValue}">`,
+        label: `<div>
+    <div class="fn__flex">
+        <span class="b3-menu__avemoji" data-icon="${cellElement.dataset.icon}">${cellElement.dataset.icon ? unicode2Emoji(cellElement.dataset.icon) : `<svg style="height: 14px;width: 14px;"><use xlink:href="#${getColIconByType(type)}"></use></svg>`}</span>
+        <div class="b3-form__icona fn__size200">
+            <input class="b3-text-field b3-form__icona-input" type="text" value="${oldValue}">
+            <svg data-position="top" class="b3-form__icona-icon ariaLabel" aria-label="${oldDesc ? oldDesc : window.siyuan.languages.addDesc}"><use xlink:href="#iconInfo"></use></svg>
+        </div>
+    </div>
+    <div class="fn__non">
+        <div class="fn__hr--small"></div>
+        <textarea style="margin-left: 22px" rows="1" class="b3-text-field fn__size200" type="text" data-value="${escapeAttr(oldDesc)}">${oldDesc}</textarea>
+    </div>
+</div>`,
         bind(element) {
             const iconElement = element.querySelector(".b3-menu__avemoji") as HTMLElement;
-            iconElement.setAttribute("data-icon", cellElement.dataset.icon);
             iconElement.addEventListener("click", (event) => {
                 const rect = iconElement.getBoundingClientRect();
                 openEmojiPanel("", "av", {
@@ -623,14 +649,32 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
                         avID,
                         data: cellElement.dataset.icon,
                     }]);
-                    iconElement.setAttribute("data-icon", unicode);
+                    iconElement.dataset.icon = unicode;
                     iconElement.innerHTML = unicode ? unicode2Emoji(unicode) : `<svg style="height: 14px;width: 14px"><use xlink:href="#${getColIconByType(type)}"></use></svg>`;
                     updateAttrViewCellAnimation(blockElement.querySelector(`.av__row--header .av__cell[data-col-id="${colId}"]`), undefined, {icon: unicode});
                 }, iconElement.querySelector("img"));
                 event.preventDefault();
                 event.stopPropagation();
             });
-            element.querySelector("input").addEventListener("keydown", (event: KeyboardEvent) => {
+            const inputElement = element.querySelector("input");
+            inputElement.addEventListener("keydown", (event: KeyboardEvent) => {
+                if (event.isComposing) {
+                    return;
+                }
+                if (event.key === "Enter") {
+                    menu.close();
+                    event.preventDefault();
+                }
+            });
+            const descElement = element.querySelector('textarea');
+            inputElement.nextElementSibling.addEventListener("click", () => {
+                const descPanelElement = descElement.parentElement
+                descPanelElement.classList.toggle("fn__none");
+                if (!descPanelElement.classList.contains("fn__none")) {
+                    descElement.focus();
+                }
+            })
+            descElement.addEventListener("keydown", (event: KeyboardEvent) => {
                 if (event.isComposing) {
                     return;
                 }

+ 7 - 4
app/src/protyle/render/av/render.ts

@@ -10,6 +10,7 @@ import {getCalcValue} from "./calc";
 import {renderAVAttribute} from "./blockAttr";
 import {showMessage} from "../../../dialog/message";
 import {addClearButton} from "../../../util/addClearButton";
+import {escapeAriaLabel, escapeAttr, escapeHtml} from "../../../util/escape";
 
 export const avRender = (element: Element, protyle: IProtyle, cb?: () => void, viewID?: string) => {
     let avElements: Element[] = [];
@@ -130,11 +131,13 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: () => void, v
                     if (column.hidden) {
                         return;
                     }
-                    tableHTML += `<div class="av__cell av__cell--header" data-col-id="${column.id}"  draggable="true" 
+                    tableHTML += `<div class="av__cell av__cell--header ariaLabel" data-col-id="${column.id}"  draggable="true" 
 data-icon="${column.icon}" data-dtype="${column.type}" data-wrap="${column.wrap}" data-pin="${column.pin}" 
+data-desc="${escapeAttr(column.desc)}" data-position="top"
+aria-label="${escapeAriaLabel(column.name)}<br><div class='ft__on-surface'>${escapeAriaLabel(column.desc || "")}</div>"
 style="width: ${column.width || "200px"};">
     ${column.icon ? unicode2Emoji(column.icon, "av__cellheadericon", true) : `<svg class="av__cellheadericon"><use xlink:href="#${getColIconByType(column.type)}"></use></svg>`}
-    <span class="av__celltext fn__flex-1">${column.name}</span>
+    <span class="av__celltext fn__flex-1">${escapeHtml(column.name)}</span>
     ${column.pin ? '<svg class="av__cellheadericon av__cellheadericon--pin"><use xlink:href="#iconPin"></use></svg>' : ""}
     <div class="av__widthdrag"></div>
 </div>`;
@@ -199,9 +202,9 @@ ${cell.color ? `color:${cell.color};` : ""}">${renderCell(cell.value, rowIndex)}
                 let tabHTML = "";
                 let viewData: IAVView;
                 response.data.views.forEach((item: IAVView) => {
-                    tabHTML += `<div data-id="${item.id}" data-page="${item.pageSize}" aria-label="${item.name}<br><div class='ft__on-surface'>${item.desc || ""}</div>" class="ariaLabel item${item.id === response.data.viewID ? " item--focus" : ""}">
+                    tabHTML += `<div data-position="top" data-id="${item.id}" data-page="${item.pageSize}" aria-label="${escapeAriaLabel(item.name)}<br><div class='ft__on-surface'>${escapeAriaLabel(item.desc || "")}</div>" class="ariaLabel item${item.id === response.data.viewID ? " item--focus" : ""}">
     ${item.icon ? unicode2Emoji(item.icon, "item__graphic", true) : '<svg class="item__graphic"><use xlink:href="#iconTable"></use></svg>'}
-    <span class="item__text">${item.name}</span>
+    <span class="item__text">${escapeHtml(item.name)}</span>
 </div>`;
                     if (item.id === response.data.viewID) {
                         viewData = item;

+ 11 - 6
app/src/protyle/render/av/view.ts

@@ -6,6 +6,7 @@ import {focusBlock} from "../../util/selection";
 import {Constants} from "../../../constants";
 import {upDownHint} from "../../../util/upDownHint";
 import {avRender} from "./render";
+import {escapeAriaLabel, escapeAttr} from "../../../util/escape";
 
 export const openViewMenu = (options: { protyle: IProtyle, blockElement: HTMLElement, element: HTMLElement }) => {
     if (options.protyle.disabled) {
@@ -120,9 +121,13 @@ export const bindViewEvent = (options: {
         }
     });
     inputElement.select();
-    const descElement = options.menuElement.querySelector('.b3-text-field[data-type="desc"]') as HTMLInputElement;
+    const descElement = options.menuElement.querySelector('.b3-text-field[data-type="desc"]') as HTMLTextAreaElement;
     inputElement.nextElementSibling.addEventListener("click", () => {
-        descElement.parentElement.classList.toggle("fn__none");
+        const descPanelElement = descElement.parentElement
+        descPanelElement.classList.toggle("fn__none");
+        if (!descPanelElement.classList.contains("fn__none")) {
+            descElement.focus();
+        }
     })
     descElement.addEventListener("blur", () => {
         if (descElement.value !== descElement.dataset.value) {
@@ -196,14 +201,14 @@ export const getViewHTML = (data: IAV) => {
     <div>
         <div class="fn__flex">
             <span class="b3-menu__avemoji" data-icon="${view.icon}" data-type="update-view-icon">${view.icon ? unicode2Emoji(view.icon) : '<svg style="height: 14px;width: 14px"><use xlink:href="#iconTable"></use></svg>'}</span>
-            <div class="b3-form__icona">
-                <input data-type="name" class="b3-text-field b3-form__icona-input" type="text" value="${view.name}" data-value="${view.name}">
-                <svg data-position="top" class="b3-form__icona-icon ariaLabel" aria-label="${view.desc ? view.desc : window.siyuan.languages.addDesc}"><use xlink:href="#iconInfo"></use></svg>
+            <div class="b3-form__icona fn__size200">
+                <input data-type="name" class="b3-text-field b3-form__icona-input" type="text" value="${escapeAttr(view.name)}" data-value="${escapeAttr(view.name)}">
+                <svg data-position="top" class="b3-form__icona-icon ariaLabel" aria-label="${view.desc ? escapeAriaLabel(view.desc) : window.siyuan.languages.addDesc}"><use xlink:href="#iconInfo"></use></svg>
             </div>
         </div>
         <div class="fn__none">
             <div class="fn__hr--small"></div>
-            <input data-type="desc" class="b3-text-field fn__block" type="text" value="${view.desc}" data-value="${view.desc}">
+            <textarea style="margin-left: 22px" rows="1" data-type="desc" class="b3-text-field fn__size200" type="text" data-value="${escapeAttr(view.desc)}">${view.desc}</textarea>
         </div>
     </div>
 </button>

+ 1 - 1
app/src/protyle/wysiwyg/transaction.ts

@@ -743,7 +743,7 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, isUndo:
         "replaceAttrViewBlock", "updateAttrViewColTemplate", "setAttrViewColPin", "addAttrViewView", "setAttrViewColIcon",
         "removeAttrViewView", "setAttrViewViewName", "setAttrViewViewIcon", "duplicateAttrViewView", "sortAttrViewView",
         "updateAttrViewColRelation", "setAttrViewPageSize", "updateAttrViewColRollup", "sortAttrViewKey",
-        "duplicateAttrViewKey", "setAttrViewViewDesc"].includes(operation.action)) {
+        "duplicateAttrViewKey", "setAttrViewViewDesc", "setAttrViewColDesc"].includes(operation.action)) {
         refreshAV(protyle, operation);
         return;
     }

+ 2 - 0
app/src/types/index.d.ts

@@ -54,6 +54,7 @@ type TOperation =
     | "setAttrViewColDate"
     | "unbindAttrViewBlock"
     | "setAttrViewViewDesc"
+    | "setAttrViewColDesc"
 type TBazaarType = "templates" | "icons" | "widgets" | "themes" | "plugins"
 type TCardType = "doc" | "notebook" | "all"
 type TEventBus = "ws-main" | "sync-start" | "sync-end" | "sync-fail" |
@@ -799,6 +800,7 @@ interface IAVColumn {
     icon: string,
     id: string,
     name: string,
+    desc: string,
     wrap: boolean,
     pin: boolean,
     hidden: boolean,