Bladeren bron

:art: selected https://github.com/siyuan-note/siyuan/issues/7536

Vanessa 2 jaren geleden
bovenliggende
commit
24c57bc647

+ 4 - 0
app/appearance/icons/ant/icon.js

@@ -1,5 +1,9 @@
 document.body.insertAdjacentHTML('afterbegin', `<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
 <defs>
+  <symbol id="iconIndeterminateCheck" viewBox="0 0 32 32">
+    <path d="M9.1 17.2h13.8c0.165 0 0.3-0.135 0.3-0.3v-1.8c0-0.165-0.135-0.3-0.3-0.3h-13.8c-0.165 0-0.3 0.135-0.3 0.3v1.8c0 0.165 0.135 0.3 0.3 0.3z"></path>
+    <path d="M29.8 1h-27.6c-0.664 0-1.2 0.536-1.2 1.2v27.6c0 0.664 0.536 1.2 1.2 1.2h27.6c0.664 0 1.2-0.536 1.2-1.2v-27.6c0-0.664-0.536-1.2-1.2-1.2zM28.3 28.3h-24.6v-24.6h24.6v24.6z"></path>
+  </symbol>
   <symbol id="iconPlugin" viewBox="0 0 32 32">
     <path d="M14.077 4.077c0.431 0 0.769 0.338 0.769 0.769v3.077h9.231v9.231h3.077c0.431 0 0.769 0.338 0.769 0.769s-0.338 0.769-0.769 0.769h-3.077v9.231h-3.262c-1.046-2.692-3.677-4.615-6.738-4.615s-5.692 1.923-6.738 4.615h-3.262v-3.262c2.692-1.046 4.615-3.677 4.615-6.738s-1.908-5.692-4.6-6.738l-0.015-3.262h9.231v-3.077c0-0.431 0.338-0.769 0.769-0.769zM14.077 1c-2.123 0-3.846 1.723-3.846 3.846h-6.154c-1.692 0-3.062 1.385-3.062 3.077v5.846h0.446c2.292 0 4.154 1.862 4.154 4.154s-1.862 4.154-4.154 4.154h-0.462v5.846c0 1.692 1.385 3.077 3.077 3.077h5.846v-0.462c0-2.292 1.862-4.154 4.154-4.154s4.154 1.862 4.154 4.154v0.462h5.846c1.692 0 3.077-1.385 3.077-3.077v-6.154c2.123 0 3.846-1.723 3.846-3.846s-1.723-3.846-3.846-3.846v-6.154c0-1.692-1.385-3.077-3.077-3.077h-6.154c0-2.123-1.723-3.846-3.846-3.846z"></path>
   </symbol>

+ 1 - 1
app/appearance/icons/ant/icon.json

@@ -2,5 +2,5 @@
   "name": "ant",
   "author": "Vanessa",
   "url": "https://github.com/Vanessa219",
-  "version": "1.20.0"
+  "version": "1.21.0"
 }

+ 6 - 0
app/appearance/icons/index.html

@@ -28,6 +28,12 @@
 <body>
 <h2>SiYuan</h2>
 <div class="fn__clear">
+    <div>
+        <svg>
+            <use xlink:href="#iconIndeterminateCheck"></use>
+        </svg>
+        iconIndeterminateCheck
+    </div>
     <div>
         <svg>
             <use xlink:href="#iconPlugin"></use>

+ 3 - 0
app/appearance/icons/material/icon.js

@@ -1,5 +1,8 @@
 document.body.insertAdjacentHTML('afterbegin', `<svg style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
 <defs>
+  <symbol id="iconIndeterminateCheck" viewBox="0 0 32 32">
+    <path d="M27.667 1h-23.333c-1.833 0-3.333 1.5-3.333 3.333v23.333c0 1.833 1.5 3.333 3.333 3.333h23.333c1.833 0 3.333-1.5 3.333-3.333v-23.333c0-1.833-1.5-3.333-3.333-3.333zM27.667 27.667h-23.333v-23.333h23.333v23.333zM7.667 14.333h16.667v3.333h-16.667z"></path>
+  </symbol>
   <symbol id="iconPlugin" viewBox="0 0 32 32">
     <path d="M14.077 4.077c0.431 0 0.769 0.338 0.769 0.769v3.077h9.231v9.231h3.077c0.431 0 0.769 0.338 0.769 0.769s-0.338 0.769-0.769 0.769h-3.077v9.231h-3.262c-1.046-2.692-3.677-4.615-6.738-4.615s-5.692 1.923-6.738 4.615h-3.262v-3.262c2.692-1.046 4.615-3.677 4.615-6.738s-1.908-5.692-4.6-6.738l-0.015-3.262h9.231v-3.077c0-0.431 0.338-0.769 0.769-0.769zM14.077 1c-2.123 0-3.846 1.723-3.846 3.846h-6.154c-1.692 0-3.062 1.385-3.062 3.077v5.846h0.446c2.292 0 4.154 1.862 4.154 4.154s-1.862 4.154-4.154 4.154h-0.462v5.846c0 1.692 1.385 3.077 3.077 3.077h5.846v-0.462c0-2.292 1.862-4.154 4.154-4.154s4.154 1.862 4.154 4.154v0.462h5.846c1.692 0 3.077-1.385 3.077-3.077v-6.154c2.123 0 3.846-1.723 3.846-3.846s-1.723-3.846-3.846-3.846v-6.154c0-1.692-1.385-3.077-3.077-3.077h-6.154c0-2.123-1.723-3.846-3.846-3.846z"></path>
   </symbol>

+ 1 - 1
app/appearance/icons/material/icon.json

@@ -2,5 +2,5 @@
   "name": "material",
   "author": "Vanessa",
   "url": "https://github.com/Vanessa219",
-  "version": "1.20.0"
+  "version": "1.21.0"
 }

+ 16 - 0
app/src/assets/scss/business/_av.scss

@@ -2,6 +2,22 @@
   user-select: none;
   box-sizing: border-box;
 
+  &__header {
+    top: -43px;
+    z-index: 2;
+  }
+
+  &__title {
+    height: 30px;
+  }
+
+  &__counter {
+    position: absolute;
+    bottom: 0;
+    height: 30px;
+    background-color: var(--b3-theme-background);
+  }
+
   &__gutters {
     @extend .protyle-gutters;
     position: absolute;

+ 74 - 26
app/src/protyle/render/av/action.ts

@@ -4,7 +4,7 @@ import {transaction} from "../../wysiwyg/transaction";
 import {openEditorTab} from "../../../menus/util";
 import {copySubMenu} from "../../../menus/commonMenuItem";
 import {popTextCell, showHeaderCellMenu} from "./cell";
-import {getColIconByType} from "./col";
+import {getColIconByType, updateHeader} from "./col";
 import {emitOpenMenu} from "../../../plugin/EventBus";
 
 export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLElement }) => {
@@ -55,7 +55,31 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
 
     const checkElement = hasClosestByClassName(event.target, "av__firstcol");
     if (checkElement) {
-        // TODO
+        window.siyuan.menus.menu.remove();
+        const rowElement = checkElement.parentElement;
+        const useElement = checkElement.querySelector("use")
+        if (rowElement.classList.contains("av__row--header")) {
+            if ("#iconCheck" === useElement.getAttribute("xlink:href")) {
+                rowElement.parentElement.querySelectorAll(".av__firstcol").forEach(item => {
+                    item.querySelector("use").setAttribute("xlink:href", "#iconUncheck");
+                    item.parentElement.classList.remove("av__row--select");
+                })
+            } else {
+                rowElement.parentElement.querySelectorAll(".av__firstcol").forEach(item => {
+                    item.querySelector("use").setAttribute("xlink:href", "#iconCheck");
+                    item.parentElement.classList.add("av__row--select");
+                })
+            }
+        } else {
+            if (useElement.getAttribute("xlink:href") === "#iconUncheck") {
+                checkElement.parentElement.classList.add("av__row--select");
+                useElement.setAttribute("xlink:href", "#iconCheck");
+            } else {
+                checkElement.parentElement.classList.remove("av__row--select");
+                useElement.setAttribute("xlink:href", "#iconUncheck");
+            }
+        }
+        updateHeader(rowElement);
         event.preventDefault();
         event.stopPropagation();
         return true;
@@ -81,7 +105,7 @@ export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: a
         return false;
     }
     if (rowElement.classList.contains("av__row--header")) {
-        return  false
+        return false
     }
     const blockElement = hasClosestBlock(rowElement);
     if (!blockElement) {
@@ -90,55 +114,72 @@ export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: a
     event.preventDefault();
     event.stopPropagation();
 
-    blockElement.querySelectorAll(".av__row--select").forEach(item => {
-        item.classList.remove("av__row--select");
-    });
-    const rowId = rowElement.getAttribute("data-id");
-    const menu = new Menu();
+    if (!rowElement.classList.contains("av__row--select")) {
+        blockElement.querySelectorAll(".av__row--select").forEach(item => {
+            item.classList.remove("av__row--select");
+        });
+        blockElement.querySelectorAll(".av__firstcol use").forEach(item => {
+            item.setAttribute("xlink:href", "#iconUncheck");
+        });
+    }
+
+    const menu = new Menu("av-row-menu");
     if (menu.isOpen) {
         return true;
     }
     rowElement.classList.add("av__row--select");
-    menu.addItem({
-        icon: "iconCopy",
-        label: window.siyuan.languages.duplicate,
-        click() {
-
-        }
+    rowElement.querySelector(".av__firstcol use").setAttribute("xlink:href", "#iconCheck");
+    const rowIds: string[] = [];
+    const blockIds: string[] = [];
+    blockElement.querySelectorAll(".av__row--select:not(.av__row--header )").forEach(item => {
+        rowIds.push(item.getAttribute("data-id"));
+        blockIds.push(item.querySelector(".av__cell").getAttribute("data-block-id"));
     });
+    updateHeader(rowElement);
     menu.addItem({
         icon: "iconTrashcan",
         label: window.siyuan.languages.delete,
         click() {
             transaction(protyle, [{
                 action: "removeAttrViewBlock",
-                srcIDs: [rowElement.querySelector(".av__cell").getAttribute("data-block-id")],
+                srcIDs: blockIds,
                 parentID: blockElement.getAttribute("data-av-id"),
             }], [{
                 action: "insertAttrViewBlock",
                 parentID: blockElement.getAttribute("data-av-id"),
                 previousID: rowElement.previousElementSibling?.getAttribute("data-id") || "",
-                srcIDs: [rowId],
+                srcIDs: rowIds,
             }]);
             rowElement.remove();
         }
     });
-    menu.addSeparator();
-    openEditorTab(protyle.app, rowId);
     menu.addItem({
-        label: window.siyuan.languages.copy,
         icon: "iconCopy",
-        type: "submenu",
-        submenu: copySubMenu(rowId)
-    });
-    menu.addSeparator();
-    menu.addItem({
-        icon: "iconEdit",
-        label: window.siyuan.languages.edit,
+        label: window.siyuan.languages.duplicate,
         click() {
 
         }
     });
+    if (rowIds.length === 1) {
+        menu.addSeparator();
+        openEditorTab(protyle.app, rowIds[0]);
+        menu.addItem({
+            label: window.siyuan.languages.copy,
+            icon: "iconCopy",
+            type: "submenu",
+            submenu: copySubMenu(rowIds[0])
+        });
+    }
+    menu.addSeparator();
+    if (rowIds.length === 1) {
+        menu.addItem({
+            icon: "iconEdit",
+            label: window.siyuan.languages.edit,
+            click() {
+
+            }
+        });
+    }
     const editAttrSubmenu: IMenu[] = [];
     rowElement.parentElement.querySelectorAll(".av__row--header .av__cell").forEach((cellElement) => {
         editAttrSubmenu.push({
@@ -154,6 +195,13 @@ export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: a
         type: "submenu",
         submenu: editAttrSubmenu
     });
+    menu.addItem({
+        icon: "iconMove",
+        label: window.siyuan.languages.move,
+        click() {
+
+        }
+    });
     if (protyle?.app?.plugins) {
         emitOpenMenu({
             plugins: protyle.app.plugins,

+ 31 - 0
app/src/protyle/render/av/col.ts

@@ -1,3 +1,5 @@
+import {hasClosestBlock} from "../../util/hasClosest";
+
 export const getColIconByType = (type: TAVCol) => {
     switch (type) {
         case "text":
@@ -6,3 +8,32 @@ export const getColIconByType = (type: TAVCol) => {
             return "iconParagraph";
     }
 };
+
+export const updateHeader = (rowElement: HTMLElement) => {
+    const blockElement = hasClosestBlock(rowElement);
+    if (!blockElement) {
+        return;
+    }
+    const selectCount = rowElement.parentElement.querySelectorAll(".av__row--select:not(.av__row--header)").length
+    const diffCount = rowElement.parentElement.childElementCount - 3 - selectCount
+    const headElement = rowElement.parentElement.firstElementChild
+    const headUseElement = headElement.querySelector("use")
+    const counterElement = blockElement.querySelector(".av__counter")
+    const avHeadElement = blockElement.querySelector(".av__header") as HTMLElement
+    if (diffCount === 0) {
+        headElement.classList.add("av__row--select");
+        headUseElement.setAttribute("xlink:href", "#iconCheck");
+    } else if (diffCount === rowElement.parentElement.childElementCount - 3) {
+        headElement.classList.remove("av__row--select");
+        headUseElement.setAttribute("xlink:href", "#iconUncheck");
+        counterElement.classList.add("fn__none")
+        avHeadElement.style.position = ""
+        return;
+    } else if (diffCount > 0) {
+        headElement.classList.add("av__row--select");
+        headUseElement.setAttribute("xlink:href", "#iconIndeterminateCheck");
+    }
+    counterElement.classList.remove("fn__none")
+    counterElement.innerHTML = `${selectCount} selected`
+    avHeadElement.style.position = "sticky"
+}

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

@@ -69,13 +69,14 @@ export const avRender = (element: Element, cb?: () => void) => {
                 e.style.width = e.parentElement.clientWidth + "px";
                 e.style.alignSelf = "center";
                 e.firstElementChild.outerHTML = `<div>
-    <div style="padding-left: ${paddingLeft};padding-right: ${paddingRight};">
-        <div>
-            <div>tab1</div>
+    <div class="av__header" style="padding-left: ${paddingLeft};padding-right: ${paddingRight};">
+        <div class="layout-tab-bar fn__flex">
+            <div class="item item--focus">tab1</div>
         </div>
-        <div contenteditable="true">
+        <div contenteditable="true" class="av__title">
             ${data.title}
         </div>
+        <div class="av__counter fn__none"></div>
     </div>
     <div class="av__scroll">
         <div style="padding-left: ${paddingLeft};padding-right: ${paddingRight};float: left;">

+ 1 - 1
app/src/protyle/scroll/event.ts

@@ -26,7 +26,7 @@ export const scrollEvent = (protyle: IProtyle, element: HTMLElement) => {
 
         protyle.wysiwyg.element.querySelectorAll(".av").forEach((item: HTMLElement) => {
             if (item.parentElement.classList.contains("protyle-wysiwyg")) {
-                const headerTop = item.offsetTop - 30 + 56; // 30 - 面包屑, 56 - tab+title
+                const headerTop = item.offsetTop + 48;
                 const headerElement = item.querySelector(".av__row--header") as HTMLElement;
                 if (headerElement) {
                     if (headerTop < element.scrollTop && headerTop + headerElement.parentElement.clientHeight > element.scrollTop) {