Vanessa 2023-07-07 11:12:49 +08:00
parent 1abe5dee96
commit 2724692218
6 changed files with 276 additions and 215 deletions

View file

@ -51,7 +51,7 @@
/* 提示 */
--b3-tooltips-background: rgba(0, 0, 0, .9);
--b3-tooltips-color: #bababa;
--b3-tooltips-color: var(--b3-theme-background-light);
--b3-tooltips-shadow: 0 2px 8px rgba(0, 0, 0, .1);
/* 遮罩 */

View file

@ -3,8 +3,8 @@ import {hasClosestBlock, hasClosestByAttribute, hasClosestByClassName} from "../
import {transaction} from "../../wysiwyg/transaction";
import {openEditorTab} from "../../../menus/util";
import {copySubMenu} from "../../../menus/commonMenuItem";
import {popTextCell, showHeaderCellMenu} from "./cell";
import {getColIconByType, updateHeader} from "./col";
import {popTextCell} from "./cell";
import {getColIconByType, showColMenu, updateHeader} from "./col";
import {emitOpenMenu} from "../../../plugin/EventBus";
import {addCol} from "./addCol";
import {openMenuPanel} from "./openMenuPanel";
@ -102,7 +102,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
const cellHeaderElement = hasClosestByClassName(event.target, "av__cellheader");
if (cellHeaderElement) {
showHeaderCellMenu(protyle, blockElement, cellHeaderElement.parentElement);
showColMenu(protyle, blockElement, cellHeaderElement.parentElement);
event.preventDefault();
event.stopPropagation();
return true;

View file

@ -75,185 +75,3 @@ const updateCellValue = (protyle: IProtyle, cellElement: HTMLElement, type: TAVC
avMaskElement.remove();
});
};
const removeCol = (cellElement: HTMLElement) => {
const index = cellElement.getAttribute("data-index");
const blockElement = hasClosestBlock(cellElement);
if (!blockElement) {
return false;
}
blockElement.querySelectorAll(".av__row").forEach((item) => {
item.querySelector(`[data-index="${index}"]`).remove();
});
};
export const showHeaderCellMenu = (protyle: IProtyle, blockElement: HTMLElement, cellElement: HTMLElement) => {
const type = cellElement.getAttribute("data-dtype") as TAVCol;
const colId = cellElement.getAttribute("data-id");
const avId = blockElement.getAttribute("data-av-id");
const menu = new Menu("av-header-cell", () => {
const newValue = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value;
if (newValue === cellElement.textContent.trim()) {
return;
}
transaction(protyle, [{
action: "updateAttrViewCol",
id: colId,
parentID: avId,
name: newValue,
type,
}], [{
action: "updateAttrViewCol",
id: colId,
parentID: avId,
name: cellElement.textContent.trim(),
type,
}]);
});
menu.addItem({
icon: getColIconByType(type),
label: `<input style="margin: 4px 0" class="b3-text-field" type="text" value="${cellElement.innerText.trim()}">`,
});
if (type !== "block") {
menu.addItem({
icon: "iconEdit",
label: window.siyuan.languages.edit,
click() {
}
});
}
menu.addSeparator();
menu.addItem({
icon: "iconUp",
label: window.siyuan.languages.asc,
click() {
fetchPost("/api/av/renderAttributeView", {id: avId}, (response) => {
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
sorts: [{
column: colId,
order: "ASC"
}]
}
}], [{
action: "setAttrView",
id: avId,
data: {
sorts: response.data.av.sorts
}
}]);
});
}
});
menu.addItem({
icon: "iconDown",
label: window.siyuan.languages.desc,
click() {
fetchPost("/api/av/renderAttributeView", {id: avId}, (response) => {
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
sorts: [{
column: colId,
order: "DESC"
}]
}
}], [{
action: "setAttrView",
id: avId,
data: {
sorts: response.data.av.sorts
}
}]);
});
}
});
menu.addItem({
icon: "iconFilter",
label: window.siyuan.languages.filter,
click() {
}
});
menu.addSeparator();
if (type !== "block") {
menu.addItem({
icon: "iconEyeoff",
label: window.siyuan.languages.hide,
click() {
transaction(protyle, [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: true
}], [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: false
}]);
}
});
menu.addItem({
icon: "iconCopy",
label: window.siyuan.languages.duplicate,
click() {
}
});
menu.addItem({
icon: "iconTrashcan",
label: window.siyuan.languages.delete,
click() {
transaction(protyle, [{
action: "removeAttrViewCol",
id: colId,
parentID: avId,
}], [{
action: "addAttrViewCol",
name: cellElement.textContent.trim(),
parentID: avId,
type: type,
id: colId
}]);
removeCol(cellElement);
}
});
menu.addSeparator();
}
menu.addItem({
label: `<div class="fn__flex" style="margin: 4px 0"><span>${window.siyuan.languages.wrap}</span><span class="fn__space fn__flex-1"></span>
<input type="checkbox" class="b3-switch fn__flex-center"${cellElement.style.whiteSpace === "nowrap" ? "" : " checked"}></div>`,
bind(element) {
const inputElement = element.querySelector("input") as HTMLInputElement;
inputElement.addEventListener("change", () => {
transaction(protyle, [{
action: "setAttrViewColWrap",
id: colId,
parentID: avId,
data: inputElement.checked
}], [{
action: "setAttrViewColWrap",
id: colId,
parentID: avId,
data: !inputElement.checked
}]);
});
}
});
const cellRect = cellElement.getBoundingClientRect();
menu.open({
x: cellRect.left,
y: cellRect.bottom,
h: cellRect.height
});
const inputElement = window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement;
if (inputElement) {
inputElement.select();
inputElement.focus();
}
};

View file

@ -1,4 +1,8 @@
import {hasClosestBlock} from "../../util/hasClosest";
import {Menu} from "../../../plugin/Menu";
import {transaction} from "../../wysiwyg/transaction";
import {fetchPost} from "../../../util/fetch";
import {setFilter} from "./openMenuPanel";
export const getColIconByType = (type: TAVCol) => {
switch (type) {
@ -45,3 +49,211 @@ export const updateHeader = (rowElement: HTMLElement) => {
counterElement.innerHTML = `${selectCount} selected`;
avHeadElement.style.position = "sticky";
};
const removeCol = (cellElement: HTMLElement) => {
const index = cellElement.getAttribute("data-index");
const blockElement = hasClosestBlock(cellElement);
if (!blockElement) {
return false;
}
blockElement.querySelectorAll(".av__row").forEach((item) => {
item.querySelector(`[data-index="${index}"]`).remove();
});
};
export const showColMenu = (protyle: IProtyle, blockElement: HTMLElement, cellElement: HTMLElement) => {
const type = cellElement.getAttribute("data-dtype") as TAVCol;
const colId = cellElement.getAttribute("data-id");
const avId = blockElement.getAttribute("data-av-id");
const menu = new Menu("av-header-cell", () => {
const newValue = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value;
if (newValue === cellElement.textContent.trim()) {
return;
}
transaction(protyle, [{
action: "updateAttrViewCol",
id: colId,
parentID: avId,
name: newValue,
type,
}], [{
action: "updateAttrViewCol",
id: colId,
parentID: avId,
name: cellElement.textContent.trim(),
type,
}]);
});
menu.addItem({
icon: getColIconByType(type),
label: `<input style="margin: 4px 0" class="b3-text-field" type="text" value="${cellElement.innerText.trim()}">`,
});
if (type !== "block") {
menu.addItem({
icon: "iconEdit",
label: window.siyuan.languages.edit,
click() {
}
});
}
menu.addSeparator();
menu.addItem({
icon: "iconUp",
label: window.siyuan.languages.asc,
click() {
fetchPost("/api/av/renderAttributeView", {id: avId}, (response) => {
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
sorts: [{
column: colId,
order: "ASC"
}]
}
}], [{
action: "setAttrView",
id: avId,
data: {
sorts: response.data.av.sorts
}
}]);
});
}
});
menu.addItem({
icon: "iconDown",
label: window.siyuan.languages.desc,
click() {
fetchPost("/api/av/renderAttributeView", {id: avId}, (response) => {
transaction(protyle, [{
action: "setAttrView",
id: avId,
data: {
sorts: [{
column: colId,
order: "DESC"
}]
}
}], [{
action: "setAttrView",
id: avId,
data: {
sorts: response.data.av.sorts
}
}]);
});
}
});
menu.addItem({
icon: "iconFilter",
label: window.siyuan.languages.filter,
click() {
fetchPost("/api/av/renderAttributeView", {id: avId}, (response) => {
const avData = response.data.av as IAV
let filter: IAVFilter
avData.filters.find((item) => {
if (item.column === colId) {
filter = item
return true;
}
});
if (!filter) {
filter = {
column: colId,
operator: "Contains",
value: {
[type]: {
content: ""
}
}
}
}
setFilter({
filter,
protyle,
data: avData,
target: cellElement,
});
});
}
});
menu.addSeparator();
if (type !== "block") {
menu.addItem({
icon: "iconEyeoff",
label: window.siyuan.languages.hide,
click() {
transaction(protyle, [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: true
}], [{
action: "setAttrViewColHidden",
id: colId,
parentID: avId,
data: false
}]);
}
});
menu.addItem({
icon: "iconCopy",
label: window.siyuan.languages.duplicate,
click() {
}
});
menu.addItem({
icon: "iconTrashcan",
label: window.siyuan.languages.delete,
click() {
transaction(protyle, [{
action: "removeAttrViewCol",
id: colId,
parentID: avId,
}], [{
action: "addAttrViewCol",
name: cellElement.textContent.trim(),
parentID: avId,
type: type,
id: colId
}]);
removeCol(cellElement);
}
});
menu.addSeparator();
}
menu.addItem({
label: `<div class="fn__flex" style="margin: 4px 0"><span>${window.siyuan.languages.wrap}</span><span class="fn__space fn__flex-1"></span>
<input type="checkbox" class="b3-switch fn__flex-center"${cellElement.style.whiteSpace === "nowrap" ? "" : " checked"}></div>`,
bind(element) {
const inputElement = element.querySelector("input") as HTMLInputElement;
inputElement.addEventListener("change", () => {
transaction(protyle, [{
action: "setAttrViewColWrap",
id: colId,
parentID: avId,
data: inputElement.checked
}], [{
action: "setAttrViewColWrap",
id: colId,
parentID: avId,
data: !inputElement.checked
}]);
});
}
});
const cellRect = cellElement.getBoundingClientRect();
menu.open({
x: cellRect.left,
y: cellRect.bottom,
h: cellRect.height
});
const inputElement = window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement;
if (inputElement) {
inputElement.select();
inputElement.focus();
}
};

View file

@ -142,6 +142,7 @@ export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type
event.stopPropagation();
break;
} else if (type === "removeFilter") {
window.siyuan.menus.menu.remove();
const oldFilters = Object.assign([], data.filters);
data.filters.find((item: IAVFilter, index: number) => {
if (item.column === target.parentElement.dataset.id) {
@ -167,7 +168,19 @@ export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type
event.stopPropagation();
break;
} else if (type === "setFilter") {
setFilter(protyle, data, target);
const colType = target.getAttribute("data-coltype") as TAVCol;
setFilter({
filter: {
operator: target.dataset.op as TAVFilterOperator,
column: target.parentElement.parentElement.dataset.id,
value: {
[colType]: {content: target.dataset.value}
}
},
protyle,
data,
target
});
event.stopPropagation();
break;
} else if (type === "newCol") {
@ -407,13 +420,18 @@ ${html}
</button>`;
};
const setFilter = (protyle: IProtyle, data: IAV, target: HTMLElement) => {
const colType = target.getAttribute("data-coltype") as TAVCol;
const menu = new Menu(undefined, () => {
const colId = target.parentElement.parentElement.getAttribute("data-id");
const oldFilters = JSON.parse(JSON.stringify(data.filters));
data.filters.find((filter) => {
if (filter.column === colId) {
export const setFilter = (options: {
filter: IAVFilter,
protyle: IProtyle,
data: IAV,
target: HTMLElement,
}) => {
const colType = Object.keys(options.filter.value)[0] as TAVCol;
const rectTarget = options.target.getBoundingClientRect();
const menu = new Menu("set-filter-" + options.filter.column, () => {
const oldFilters = JSON.parse(JSON.stringify(options.data.filters));
options.data.filters.find((filter) => {
if (filter.column === options.filter.column) {
filter.value[colType] = {
content: textElement.value
};
@ -421,36 +439,38 @@ const setFilter = (protyle: IProtyle, data: IAV, target: HTMLElement) => {
return true;
}
});
transaction(protyle, [{
transaction(options.protyle, [{
action: "setAttrView",
id: data.id,
id: options.data.id,
data: {
filters: data.filters
filters: options.data.filters
}
}], [{
action: "setAttrView",
id: data.id,
id: options.data.id,
data: {
filters: oldFilters
}
}]);
const menuElement = hasClosestByClassName(target, "b3-menu");
const menuElement = hasClosestByClassName(options.target, "b3-menu");
if (menuElement) {
menuElement.innerHTML = getFiltersHTML(data);
menuElement.innerHTML = getFiltersHTML(options.data);
}
});
if (menu.isOpen) {
return;
}
let selectHTML = "";
const filterOperation = target.getAttribute("data-op");
switch (colType) {
case "text":
selectHTML = `<option ${"=" === filterOperation ? "selected" : ""} value="=">${window.siyuan.languages.filterOperatorIs}</option>
<option ${"!=" === filterOperation ? "selected" : ""} value="!=">${window.siyuan.languages.filterOperatorIsNot}</option>
<option ${"Contains" === filterOperation ? "selected" : ""} value="Contains">${window.siyuan.languages.filterOperatorContains}</option>
<option ${"Does not contains" === filterOperation ? "selected" : ""} value="Does not contains">${window.siyuan.languages.filterOperatorDoesNotContain}</option>
<option ${"Starts with" === filterOperation ? "selected" : ""} value="Starts with">${window.siyuan.languages.filterOperatorStartsWith}</option>
<option ${"Ends with" === filterOperation ? "selected" : ""} value="Ends with">${window.siyuan.languages.filterOperatorEndsWith}</option>
<option ${"Is empty" === filterOperation ? "selected" : ""} value="Is empty">${window.siyuan.languages.filterOperatorIsEmpty}</option>
<option ${"Is not empty" === filterOperation ? "selected" : ""} value="Is not empty">${window.siyuan.languages.filterOperatorIsNotEmpty}</option>
selectHTML = `<option ${"=" === options.filter.operator ? "selected" : ""} value="=">${window.siyuan.languages.filterOperatorIs}</option>
<option ${"!=" === options.filter.operator ? "selected" : ""} value="!=">${window.siyuan.languages.filterOperatorIsNot}</option>
<option ${"Contains" === options.filter.operator ? "selected" : ""} value="Contains">${window.siyuan.languages.filterOperatorContains}</option>
<option ${"Does not contains" === options.filter.operator ? "selected" : ""} value="Does not contains">${window.siyuan.languages.filterOperatorDoesNotContain}</option>
<option ${"Starts with" === options.filter.operator ? "selected" : ""} value="Starts with">${window.siyuan.languages.filterOperatorStartsWith}</option>
<option ${"Ends with" === options.filter.operator ? "selected" : ""} value="Ends with">${window.siyuan.languages.filterOperatorEndsWith}</option>
<option ${"Is empty" === options.filter.operator ? "selected" : ""} value="Is empty">${window.siyuan.languages.filterOperatorIsEmpty}</option>
<option ${"Is not empty" === options.filter.operator ? "selected" : ""} value="Is not empty">${window.siyuan.languages.filterOperatorIsNotEmpty}</option>
`;
break;
}
@ -460,7 +480,7 @@ const setFilter = (protyle: IProtyle, data: IAV, target: HTMLElement) => {
});
menu.addItem({
iconHTML: "",
label: `<input value="${target.getAttribute("data-value")}" class="b3-text-field fn__size200">`
label: `<input value="${options.filter.value[colType].content}" class="b3-text-field fn__size200">`
});
const textElement = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement);
textElement.addEventListener("keydown", (event) => {
@ -473,7 +493,6 @@ const setFilter = (protyle: IProtyle, data: IAV, target: HTMLElement) => {
event.preventDefault();
}
});
const rectTarget = target.getBoundingClientRect();
menu.open({x: rectTarget.left, y: rectTarget.bottom});
textElement.select();
};
@ -525,7 +544,20 @@ const addFilter = (options: {
}]);
options.menuElement.innerHTML = getFiltersHTML(options.data);
setPosition(options.menuElement, options.tabRect.right - options.menuElement.clientWidth, options.tabRect.bottom, options.tabRect.height);
setFilter(options.protyle, options.data, options.menuElement.querySelector(`[data-id="${column.id}"] .b3-chip`));
const filterElement = options.menuElement.querySelector(`[data-id="${column.id}"] .b3-chip`) as HTMLElement;
const colType = filterElement.getAttribute("data-coltype") as TAVCol;
setFilter({
filter: {
operator: filterElement.dataset.op as TAVFilterOperator,
column: filterElement.parentElement.parentElement.dataset.id,
value: {
[colType]: {content: filterElement.dataset.value}
}
},
protyle: options.protyle,
data: options.data,
target: filterElement
});
}
});
}

View file

@ -1,6 +1,5 @@
import {fetchPost} from "../../../util/fetch";
import {getColIconByType} from "./col";
import {showHeaderCellMenu} from "./cell";
import {getColIconByType, showColMenu} from "./col";
import {Constants} from "../../../constants";
export const avRender = (element: Element, cb?: () => void) => {
@ -142,7 +141,7 @@ export const refreshAV = (protyle: IProtyle, operation: IOperation) => {
Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-av-id="${avId}"]`)).forEach((item: HTMLElement) => {
item.removeAttribute("data-render");
avRender(item, () => {
showHeaderCellMenu(protyle, item, item.querySelector(".av__row--header").lastElementChild.previousElementSibling as HTMLElement);
showColMenu(protyle, item, item.querySelector(".av__row--header").lastElementChild.previousElementSibling as HTMLElement);
});
});
} else if (operation.action === "setAttrViewColWidth") {