diff --git a/app/src/assets/scss/business/_av.scss b/app/src/assets/scss/business/_av.scss index 59d250c9b..88dd955e3 100644 --- a/app/src/assets/scss/business/_av.scss +++ b/app/src/assets/scss/business/_av.scss @@ -3,7 +3,7 @@ box-sizing: border-box; font-size: 14px; - &:hover .av__row--footer { + &:hover .av__row--footer > .av__calc--show { opacity: 1; } @@ -76,6 +76,7 @@ .av__cell { padding: 0; + transition: background 20ms ease-in 0s; &:hover { background-color: var(--b3-list-icon-hover); @@ -92,15 +93,21 @@ display: flex; border-top: 1px solid var(--b3-theme-surface-lighter); color: var(--b3-theme-on-surface); - opacity: 0; + + &:hover > .av__calc, + &.av__row--show > .av__calc { + opacity: 1; + } & > .av__calc { + transition: opacity 150ms linear, background 20ms ease-in 0s; display: flex; align-items: center; padding: 5px 5px 5px 7px; border-right: 1px; flex-direction: row-reverse; box-sizing: border-box; + opacity: 0; svg { height: 10px; @@ -119,6 +126,7 @@ padding: 5px 5px 5px 7px; display: flex; align-items: center; + transition: background 20ms ease-in 0s; svg { height: 12px; @@ -191,7 +199,7 @@ width: 5px; height: 100%; right: -2.5px; - transition: background 200ms ease-out 0s; + transition: background 20ms ease-in 0s; z-index: 1; &:hover { diff --git a/app/src/protyle/render/av/action.ts b/app/src/protyle/render/av/action.ts index 588bf3146..6f3db3df3 100644 --- a/app/src/protyle/render/av/action.ts +++ b/app/src/protyle/render/av/action.ts @@ -3,7 +3,7 @@ import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName} from "../ import {transaction} from "../../wysiwyg/transaction"; import {openEditorTab} from "../../../menus/util"; import {copySubMenu} from "../../../menus/commonMenuItem"; -import {popTextCell} from "./cell"; +import {openCalcMenu, popTextCell} from "./cell"; import {getColIconByType, showColMenu, updateHeader} from "./col"; import {emitOpenMenu} from "../../../plugin/EventBus"; import {addCol} from "./addCol"; @@ -115,6 +115,14 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle event.stopPropagation(); return true; } + + const calcElement = hasClosestByClassName(event.target, "av__calc"); + if (calcElement) { + openCalcMenu(protyle, calcElement); + event.preventDefault(); + event.stopPropagation(); + return true; + } return false; }; diff --git a/app/src/protyle/render/av/cell.ts b/app/src/protyle/render/av/cell.ts index fde158587..7aa026240 100644 --- a/app/src/protyle/render/av/cell.ts +++ b/app/src/protyle/render/av/cell.ts @@ -1,6 +1,7 @@ import {transaction} from "../../wysiwyg/transaction"; import {hasClosestBlock, hasClosestByClassName} from "../../util/hasClosest"; import {openMenuPanel} from "./openMenuPanel"; +import {Menu} from "../../../plugin/Menu"; export const genCellValue = (colType: TAVCol, value: string | { content: string, @@ -51,6 +52,185 @@ export const genCellValue = (colType: TAVCol, value: string | { } }; +const calcItem = (options: { + menu: Menu, + protyle: IProtyle, + label: string, + operator: string, + oldOperator: string, + colId: string, + avId: string +}) => { + options.menu.addItem({ + iconHTML: "", + label: options.label, + click() { + transaction(options.protyle, [{ + action: "setAttrViewColCalc", + avID: options.avId, + id: options.colId, + data: { + operator: options.operator + } + }], [{ + action: "setAttrViewColCalc", + avID: options.avId, + id: options.colId, + data: { + operator: options.oldOperator + } + }]); + } + }); +} +export const openCalcMenu = (protyle: IProtyle, calcElement: HTMLElement) => { + const blockElement = hasClosestBlock(calcElement); + if (!blockElement) { + return; + } + calcElement.parentElement.classList.add("av__row--show"); + const menu = new Menu("av-calc", () => { + calcElement.parentElement.classList.remove("av__row--show"); + }); + if (menu.isOpen) { + return; + } + const type = calcElement.dataset.dtype as TAVCol; + const colId = calcElement.dataset.id; + const avId = blockElement.dataset.avId; + const oldOperator = calcElement.dataset.operator; + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "", + label: window.siyuan.languages.calcOperatorNone + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Count all", + label: window.siyuan.languages.calcOperatorCountAll + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Count values", + label: window.siyuan.languages.calcOperatorCountValues + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Count unique values", + label: window.siyuan.languages.calcOperatorCountUniqueValues + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Count empty", + label: window.siyuan.languages.calcOperatorCountEmpty + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Count not empty", + label: window.siyuan.languages.calcOperatorCountNotEmpty + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Percent empty", + label: window.siyuan.languages.calcOperatorPercentEmpty + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Percent not empty", + label: window.siyuan.languages.calcOperatorPercentNotEmpty + }); + if (type === "number") { + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Sum", + label: window.siyuan.languages.calcOperatorSum + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Average", + label: window.siyuan.languages.calcOperatorAverage + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Median", + label: window.siyuan.languages.calcOperatorMedian + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Min", + label: window.siyuan.languages.calcOperatorMin + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Max", + label: window.siyuan.languages.calcOperatorMax + }); + calcItem({ + menu, + protyle, + colId, + avId, + oldOperator, + operator: "Range", + label: window.siyuan.languages.calcOperatorRange + }); + } + const calcRect = calcElement.getBoundingClientRect(); + menu.open({x: calcRect.left, y: calcRect.bottom, h: calcRect.height}); +} + export const popTextCell = (protyle: IProtyle, cellElement: HTMLElement) => { const type = cellElement.parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElement.getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol; const cellRect = cellElement.getBoundingClientRect(); diff --git a/app/src/protyle/render/av/render.ts b/app/src/protyle/render/av/render.ts index f3e69f229..c402bb422 100644 --- a/app/src/protyle/render/av/render.ts +++ b/app/src/protyle/render/av/render.ts @@ -22,7 +22,7 @@ export const avRender = (element: Element, cb?: () => void) => { const data = response.data.view as IAVTable; // header let tableHTML = '
'; - let calcHTML = '
' + let calcHTML = '' data.columns.forEach((column: IAVColumn) => { if (column.hidden) { return; @@ -36,8 +36,8 @@ ${column.wrap ? "" : "white-space: nowrap;"}">
`; - calcHTML += `
${window.siyuan.languages.calc}
` + calcHTML += `
${window.siyuan.languages.calc}
`; }); tableHTML += `
@@ -124,7 +124,7 @@ ${cell.color ? `color:${cell.color};` : ""}">${text}
`; ${window.siyuan.languages.addAttr} - + `; diff --git a/app/src/protyle/wysiwyg/transaction.ts b/app/src/protyle/wysiwyg/transaction.ts index 3de21eeca..23e9005a6 100644 --- a/app/src/protyle/wysiwyg/transaction.ts +++ b/app/src/protyle/wysiwyg/transaction.ts @@ -654,7 +654,7 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, focus: b } else if (["addAttrViewCol", "insertAttrViewBlock", "updateAttrViewCol", "updateAttrViewColOptions", "updateAttrViewColOption", "updateAttrViewCell", "sortAttrViewRow", "sortAttrViewCol", "setAttrViewColHidden", "setAttrViewColWrap", "setAttrViewColWidth", "removeAttrViewColOption", "setAttrViewName", "setAttrViewFilters", - "setAttrViewSorts"].includes(operation.action)) { + "setAttrViewSorts", "setAttrViewColCalc"].includes(operation.action)) { refreshAV(protyle, operation); } }; diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index 8cdebd599..74a7c6e4c 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -35,6 +35,7 @@ type TOperation = | "setAttrViewName" | "setAttrViewFilters" | "setAttrViewSorts" + | "setAttrViewColCalc" type TBazaarType = "templates" | "icons" | "widgets" | "themes" | "plugins" type TCardType = "doc" | "notebook" | "all" type TEventBus = "ws-main" | @@ -853,7 +854,7 @@ interface IAV { view: IAVTable viewID: string viewType: string - views: IAVView[], + views: IAVView[] } interface IAVView { @@ -891,6 +892,10 @@ interface IAVColumn { wrap: boolean, hidden: boolean, type: TAVCol, + calc: { + operator: string, + result: IAVCellValue + }, // 选项列表 options?: { name: string,