Vanessa 2023-11-20 11:12:02 +08:00
parent ec4da7cde7
commit c8ea33c5ce
13 changed files with 372 additions and 245 deletions

View file

@ -1,4 +1,9 @@
{
"checked": "Checked",
"unchecked": "Unchecked",
"percentChecked": "Percent checked",
"percentUnchecked": "Percent unchecked",
"checkbox": "Checkbox",
"copyInline": "Copy inline element",
"unsplit": "Unsplit",
"unsplitAll": "Unsplit All",

View file

@ -1,4 +1,9 @@
{
"checked": "marcado",
"unchecked": "desmarcado",
"percentChecked": "Porcentaje comprobado",
"percentUnchecked": "Porcentaje no marcado",
"checkbox": "Casilla de verificación",
"copyInline": "Copiar elemento en línea",
"unsplit": "Desdividir",
"unsplitAll": "Desdividir Todo",

View file

@ -1,4 +1,9 @@
{
"checked": "Coché",
"unchecked": "Décoché",
"percentChecked": "Pourcentage vérifié",
"percentUnchecked": "Pourcentage non coché",
"checkbox": "case à cocher",
"copyInline": "Copier l'élément en ligne",
"unsplit": "Unsplit",
"unsplitAll": "Tout dédiviser",

View file

@ -1,4 +1,9 @@
{
"checked": "已完成",
"unchecked": "未完成",
"percentChecked": "已完成佔比",
"percentUnchecked": "未完成佔比",
"checkbox": "勾選方塊",
"copyInline": "複製行級元素",
"unsplit": "取消分割畫面",
"unsplitAll": "取消全部分螢幕",

View file

@ -1,4 +1,9 @@
{
"checked": "已完成",
"unchecked": "未完成",
"percentChecked": "已完成占比",
"percentUnchecked": "未完成占比",
"checkbox": "勾选框",
"copyInline": "复制行级元素",
"unsplit": "取消分屏",
"unsplitAll": "取消全部分屏",

View file

@ -113,7 +113,8 @@
}
&--select {
&:not(.av__row--header) .av__cell {
&:not(.av__row--header) .av__cell,
&:not(.av__row--header) .av__firstcol {
background-color: var(--b3-av-background-hl);
}
@ -231,7 +232,7 @@
position: absolute;
right: 5px;
font-size: 85%;
top: 8px;
top: 5.5px;
}
&.dragover__right {
@ -267,15 +268,15 @@
align-items: center;
flex: 1;
overflow: hidden;
}
& > .av__cellicon {
height: 1em;
width: 1em;
color: var(--b3-theme-on-surface);
margin: 0 5px 0 0;
flex-shrink: 0;
line-height: 1em;
}
&__cellheadericon {
height: 1em;
width: 1em;
color: var(--b3-theme-on-surface);
margin: 0 5px 0 0;
flex-shrink: 0;
line-height: 1em;
}
&__celltext {
@ -292,15 +293,23 @@
}
}
&__checkbox {
color: var(--b3-theme-on-surface);
height: 14px;
width: 14px;
float: left;
padding: 4.5px 0;
&:hover {
color: var(--b3-theme-on-background);
}
}
&__firstcol {
svg {
color: var(--b3-theme-on-surface);
height: 33px;
width: 24px;
@extend .av__checkbox;
opacity: 0;
padding: 5px;
box-sizing: border-box;
float: left;
padding: 9.5px 5px;
}
&:hover svg {

View file

@ -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 {getTypeByCellElement, openCalcMenu, popTextCell} from "./cell";
import {getTypeByCellElement, popTextCell} from "./cell";
import {getColIconByType, showColMenu} from "./col";
import {insertAttrViewBlockAnimation, updateHeader} from "./row";
import {emitOpenMenu} from "../../../plugin/EventBus";
@ -23,6 +23,7 @@ import {getSearch} from "../../../util/functions";
import {unicode2Emoji} from "../../../emoji";
import {selectRow} from "./row";
import * as dayjs from "dayjs";
import {openCalcMenu} from "./calc";
export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLElement }) => {
const blockElement = hasClosestBlock(event.target);

View file

@ -0,0 +1,256 @@
import {Menu} from "../../../plugin/Menu";
import {transaction} from "../../wysiwyg/transaction";
import {hasClosestBlock, hasClosestByClassName} from "../../util/hasClosest";
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;
}
const rowElement = hasClosestByClassName(calcElement, "av__row--footer");
if (!rowElement) {
return;
}
rowElement.classList.add("av__row--show");
const menu = new Menu("av-calc", () => {
rowElement.classList.remove("av__row--show");
});
if (menu.isOpen) {
return;
}
const type = calcElement.dataset.dtype as TAVCol;
const colId = calcElement.dataset.colId;
const avId = blockElement.dataset.avId;
const oldOperator = calcElement.dataset.operator;
if (type !== "checkbox") {
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
});
if (type !== "checkbox") {
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
});
} else {
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Checked",
label: window.siyuan.languages.checked
});
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Unchecked",
label: window.siyuan.languages.unchecked
});
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Percent checked",
label: window.siyuan.languages.percentChecked
});
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Percent unchecked",
label: window.siyuan.languages.percentUnchecked
});
}
if (["number", "template"].includes(type)) {
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
});
} else if (["date", "created", "updated"].includes(type)) {
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Earliest",
label: window.siyuan.languages.calcOperatorEarliest
});
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Latest",
label: window.siyuan.languages.calcOperatorLatest
});
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});
};

View file

@ -133,223 +133,18 @@ export const genCellValue = (colType: TAVCol, value: string | any) => {
type: colType,
mAsset: value as IAVCellAssetValue[]
};
} else if (colType === "checkbox") {
cellValue = {
type: colType,
checkbox: {
checked: value ? true : false
}
};
}
}
return cellValue;
};
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;
}
const rowElement = hasClosestByClassName(calcElement, "av__row--footer");
if (!rowElement) {
return;
}
rowElement.classList.add("av__row--show");
const menu = new Menu("av-calc", () => {
rowElement.classList.remove("av__row--show");
});
if (menu.isOpen) {
return;
}
const type = calcElement.dataset.dtype as TAVCol;
const colId = calcElement.dataset.colId;
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 (["number", "template"].includes(type)) {
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
});
} else if (["date", "created", "updated"].includes(type)) {
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Earliest",
label: window.siyuan.languages.calcOperatorEarliest
});
calcItem({
menu,
protyle,
colId,
avId,
oldOperator,
operator: "Latest",
label: window.siyuan.languages.calcOperatorLatest
});
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 cellScrollIntoView = (blockElement: HTMLElement, cellElement: Element, onlyHeight = true) => {
const cellRect = cellElement.getBoundingClientRect();
if (!onlyHeight) {
@ -427,7 +222,7 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type
}
cellRect = cellElements[0].getBoundingClientRect();
let html = "";
const style = `style="position:absolute;left: ${cellRect.left}px;top: ${cellRect.top}px;width:${Math.max(cellRect.width, 58)}px;height: ${cellRect.height}px"`;
const style = `style="position:absolute;left: ${cellRect.left}px;top: ${cellRect.top}px;width:${Math.max(cellRect.width, 25)}px;height: ${cellRect.height}px"`;
if (["text", "url", "email", "phone", "block", "template"].includes(type)) {
html = `<textarea ${style} class="b3-text-field">${cellElements[0].firstElementChild.textContent}</textarea>`;
} else if (type === "number") {
@ -439,6 +234,8 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type
openMenuPanel({protyle, blockElement, type: "asset", cellElements});
} else if (type === "date") {
openMenuPanel({protyle, blockElement, type: "date", cellElements});
} else if (type === "checkbox") {
updateCellValue(protyle, type, cellElements);
}
if (!hasClosestByClassName(cellElements[0], "custom-attr")) {
cellElements[0].classList.add("av__cell--select");
@ -548,22 +345,28 @@ const updateCellValue = (protyle: IProtyle, type: TAVCol, cellElements: HTMLElem
const cellId = item.getAttribute("data-id");
const colId = item.getAttribute("data-col-id");
const inputValue: {
content: string | number,
isNotEmpty?: boolean
} = {
content: (avMaskElement.querySelector(".b3-text-field") as HTMLInputElement).value
};
content?: string | number,
isNotEmpty?: boolean,
checked?: boolean,
} = {};
const oldValue: {
content: string | number,
isNotEmpty?: boolean
} = {
content: type === "block" ? item.firstElementChild.textContent.trim() : item.textContent.trim()
};
content?: string | number,
isNotEmpty?: boolean,
checked?: boolean,
} = {};
if (type === "number") {
oldValue.content = parseFloat(oldValue.content as string);
oldValue.isNotEmpty = typeof oldValue.content === "number" && !isNaN(oldValue.content);
inputValue.content = parseFloat(inputValue.content as string);
inputValue.isNotEmpty = typeof inputValue.content === "number" && !isNaN(inputValue.content);
} else if (type === "checkbox") {
const useElement = item.querySelector("use")
inputValue.checked = useElement.getAttribute("xlink:href") === "#iconUncheck"
oldValue.checked = !inputValue.checked
useElement.setAttribute("xlink:href", inputValue.checked ? "#iconCheck" : "#iconUncheck")
} else {
inputValue.content = (avMaskElement.querySelector(".b3-text-field") as HTMLInputElement).value
oldValue.content = type === "block" ? item.firstElementChild.textContent.trim() : item.textContent.trim()
}
if (objEquals(inputValue, oldValue)) {
return;
@ -607,6 +410,6 @@ const updateCellValue = (protyle: IProtyle, type: TAVCol, cellElements: HTMLElem
focusBlock(blockElement);
}
setTimeout(() => {
avMaskElement.remove();
avMaskElement?.remove();
});
};

View file

@ -353,6 +353,8 @@ export const getColIconByType = (type: TAVCol) => {
return "iconPhone";
case "template":
return "iconMath";
case "checkbox":
return "iconCheck";
}
};
@ -379,7 +381,7 @@ export const addAttrViewColAnimation = (options: {
if (index === 0) {
html = `<div class="av__cell" data-icon="${options.icon || ""}" data-col-id="${options.id}" data-dtype="${options.type}" style="width: 200px;white-space: nowrap;">
<div draggable="true" class="av__cellheader">
${options.icon ? unicode2Emoji(options.icon, "av__cellicon", true) : `<svg class="av__cellicon"><use xlink:href="#${getColIconByType(options.type)}"></use></svg>`}
${options.icon ? unicode2Emoji(options.icon, "av__cellheadericon", true) : `<svg class="av__cellheadericon"><use xlink:href="#${getColIconByType(options.type)}"></use></svg>`}
<span class="av__celltext">${options.name}</span>
</div>
<div class="av__widthdrag"></div>
@ -825,6 +827,31 @@ export const addCol = (protyle: IProtyle, blockElement: Element) => {
});
}
});
menu.addItem({
icon: "iconCheck",
label: window.siyuan.languages.checkbox,
click() {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
name: window.siyuan.languages.checkbox,
avID,
type: "checkbox",
id
}], [{
action: "removeAttrViewCol",
id,
avID,
}]);
addAttrViewColAnimation({
blockElement: blockElement,
protyle: protyle,
type: "checkbox",
name: window.siyuan.languages.checkbox,
id
});
}
});
menu.addItem({
icon: "iconLink",
label: window.siyuan.languages.link,

View file

@ -85,9 +85,9 @@ data-icon="${column.icon}" data-dtype="${column.type}" data-pin="${column.pin}"
style="width: ${column.width || "200px"};
${column.wrap ? "" : "white-space: nowrap;"}">
<div draggable="true" class="av__cellheader">
${column.icon ? unicode2Emoji(column.icon, "av__cellicon", true) : `<svg class="av__cellicon"><use xlink:href="#${getColIconByType(column.type)}"></use></svg>`}
${column.icon ? unicode2Emoji(column.icon, "av__cellheadericon", true) : `<svg class="av__cellheadericon"><use xlink:href="#${getColIconByType(column.type)}"></use></svg>`}
<span class="av__celltext">${column.name}</span>
${column.pin ? '<div class="fn__flex-1"></div><svg class="av__cellicon"><use xlink:href="#iconPin"></use></svg>' : ""}
${column.pin ? '<div class="fn__flex-1"></div><svg class="av__cellheadericon"><use xlink:href="#iconPin"></use></svg>' : ""}
</div>
<div class="av__widthdrag"></div>
</div>`;
@ -172,6 +172,8 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '<svg><use x
text += `<span class="b3-chip av__celltext--url" data-url="${item.content}">${item.name}</span>`;
}
});
} else if (cell.valueType === "checkbox") {
text += `<svg class="av__checkbox"><use xlink:href="#icon${cell.value?.checkbox?.checked ? "Check" : "Uncheck"}"></use></svg>`;
}
if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated"].includes(cell.valueType) &&
cell.value && cell.value[cell.valueType as "url"].content) {

View file

@ -385,7 +385,7 @@ export class WYSIWYG {
const scrollElement = nodeElement.querySelector(".av__scroll");
const contentRect = protyle.contentElement.getBoundingClientRect();
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
newWidth = Math.max(oldWidth + (moveEvent.clientX - event.clientX), 58) + "px";
newWidth = Math.max(oldWidth + (moveEvent.clientX - event.clientX), 25) + "px";
scrollElement.querySelectorAll(".av__row, .av__row--footer").forEach(item => {
(item.querySelector(`[data-col-id="${dragColId}"]`) as HTMLElement).style.width = newWidth;
});

View file

@ -71,6 +71,7 @@ type TAVCol =
| "template"
| "created"
| "updated"
| "checkbox"
type THintSource = "search" | "av" | "hint";
type TAVFilterOperator =
"="
@ -1094,6 +1095,9 @@ interface IAVCellValue {
}
template?: {
content: string
},
checkbox?: {
checked: boolean
}
date?: IAVCellDateValue
created?: IAVCellDateValue