diff --git a/app/appearance/icons/ant/icon.js b/app/appearance/icons/ant/icon.js index 07a9f5ff9..c2679beeb 100644 --- a/app/appearance/icons/ant/icon.js +++ b/app/appearance/icons/ant/icon.js @@ -1,5 +1,9 @@ document.body.insertAdjacentHTML('afterbegin', ` + + + + diff --git a/app/appearance/icons/ant/icon.json b/app/appearance/icons/ant/icon.json index 4708ab846..380e4d5c5 100644 --- a/app/appearance/icons/ant/icon.json +++ b/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" } diff --git a/app/appearance/icons/index.html b/app/appearance/icons/index.html index f70f05344..469ae5811 100644 --- a/app/appearance/icons/index.html +++ b/app/appearance/icons/index.html @@ -28,6 +28,12 @@

SiYuan

+
+ + + + iconIndeterminateCheck +
diff --git a/app/appearance/icons/material/icon.js b/app/appearance/icons/material/icon.js index ec87636e3..206a6a3cb 100644 --- a/app/appearance/icons/material/icon.js +++ b/app/appearance/icons/material/icon.js @@ -1,5 +1,8 @@ document.body.insertAdjacentHTML('afterbegin', ` + + + diff --git a/app/appearance/icons/material/icon.json b/app/appearance/icons/material/icon.json index 6cb88b830..2fa9d974c 100644 --- a/app/appearance/icons/material/icon.json +++ b/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" } diff --git a/app/src/assets/scss/business/_av.scss b/app/src/assets/scss/business/_av.scss index b22ec1af6..6019bb195 100644 --- a/app/src/assets/scss/business/_av.scss +++ b/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; diff --git a/app/src/protyle/render/av/action.ts b/app/src/protyle/render/av/action.ts index 273402047..d97812b5d 100644 --- a/app/src/protyle/render/av/action.ts +++ b/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,15 +114,45 @@ 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"); + 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: blockIds, + parentID: blockElement.getAttribute("data-av-id"), + }], [{ + action: "insertAttrViewBlock", + parentID: blockElement.getAttribute("data-av-id"), + previousID: rowElement.previousElementSibling?.getAttribute("data-id") || "", + srcIDs: rowIds, + }]); + rowElement.remove(); + } + }); menu.addItem({ icon: "iconCopy", label: window.siyuan.languages.duplicate, @@ -106,39 +160,26 @@ export const avContextmenu = (protyle: IProtyle, event: MouseEvent & { detail: a } }); - menu.addItem({ - icon: "iconTrashcan", - label: window.siyuan.languages.delete, - click() { - transaction(protyle, [{ - action: "removeAttrViewBlock", - srcIDs: [rowElement.querySelector(".av__cell").getAttribute("data-block-id")], - parentID: blockElement.getAttribute("data-av-id"), - }], [{ - action: "insertAttrViewBlock", - parentID: blockElement.getAttribute("data-av-id"), - previousID: rowElement.previousElementSibling?.getAttribute("data-id") || "", - srcIDs: [rowId], - }]); - rowElement.remove(); - } - }); + 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(); - 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, - click() { + 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, diff --git a/app/src/protyle/render/av/col.ts b/app/src/protyle/render/av/col.ts index 425ccc437..5a0e1c094 100644 --- a/app/src/protyle/render/av/col.ts +++ b/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" +} diff --git a/app/src/protyle/render/av/render.ts b/app/src/protyle/render/av/render.ts index d4b197015..83dcd15a7 100644 --- a/app/src/protyle/render/av/render.ts +++ b/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 = `
-
-
-
tab1
+
+
+
tab1
-
+
${data.title}
+
diff --git a/app/src/protyle/scroll/event.ts b/app/src/protyle/scroll/event.ts index 3c7bdc1d6..c3ea57d71 100644 --- a/app/src/protyle/scroll/event.ts +++ b/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) {