mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-22 17:40:13 +01:00
This commit is contained in:
parent
eb65bb4ec0
commit
cb907abf9a
12 changed files with 742 additions and 393 deletions
|
|
@ -154,6 +154,7 @@
|
||||||
color: var(--b3-theme-on-background);
|
color: var(--b3-theme-on-background);
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
margin: 1px 0;
|
||||||
|
|
||||||
&[disabled="disabled"] {
|
&[disabled="disabled"] {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
import {transaction} from "../../wysiwyg/transaction";
|
import {transaction} from "../../wysiwyg/transaction";
|
||||||
import {hasClosestBlock, hasClosestByClassName} from "../../util/hasClosest";
|
import {hasClosestBlock, hasClosestByClassName} from "../../util/hasClosest";
|
||||||
|
import {openMenuPanel} from "./openMenuPanel";
|
||||||
|
|
||||||
export const popTextCell = (protyle: IProtyle, cellElement: HTMLElement) => {
|
export const popTextCell = (protyle: IProtyle, cellElement: HTMLElement) => {
|
||||||
const type = cellElement.parentElement.parentElement.firstElementChild.children[parseInt(cellElement.getAttribute("data-index")) + 1].getAttribute("data-dtype") as TAVCol;
|
const type = cellElement.parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElement.getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol;
|
||||||
const cellRect = cellElement.getBoundingClientRect();
|
const cellRect = cellElement.getBoundingClientRect();
|
||||||
let html = "";
|
let html = "";
|
||||||
const style = `style="position:absolute;left: ${cellRect.left}px;top: ${cellRect.top}px;width:${Math.max(cellRect.width, 200)}px;height: ${cellRect.height}px"`
|
const style = `style="position:absolute;left: ${cellRect.left}px;top: ${cellRect.top}px;width:${Math.max(cellRect.width, 200)}px;height: ${cellRect.height}px"`
|
||||||
|
const blockElement = hasClosestBlock(cellElement);
|
||||||
if (type === "block" || type === "text") {
|
if (type === "block" || type === "text") {
|
||||||
html = `<textarea ${style} class="b3-text-field">${cellElement.textContent}</textarea>`;
|
html = `<textarea ${style} class="b3-text-field">${cellElement.textContent}</textarea>`;
|
||||||
} else if (type === "number") {
|
} else if (type === "number") {
|
||||||
html = `<input type="number" value="${cellElement.textContent}" ${style} class="b3-text-field">`;
|
html = `<input type="number" value="${cellElement.textContent}" ${style} class="b3-text-field">`;
|
||||||
|
} else if (type === "select" && blockElement) {
|
||||||
|
openMenuPanel(protyle, blockElement, "select", {cellElement});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
document.body.insertAdjacentHTML("beforeend", `<div class="av__mask">
|
document.body.insertAdjacentHTML("beforeend", `<div class="av__mask">
|
||||||
${html}
|
${html}
|
||||||
|
|
|
||||||
|
|
@ -51,19 +51,20 @@ export const updateHeader = (rowElement: HTMLElement) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeCol = (cellElement:HTMLElement) => {
|
const removeCol = (cellElement:HTMLElement) => {
|
||||||
const index = cellElement.getAttribute("data-index");
|
|
||||||
const blockElement = hasClosestBlock(cellElement);
|
const blockElement = hasClosestBlock(cellElement);
|
||||||
if (!blockElement) {
|
if (!blockElement) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const colId = cellElement.getAttribute("data-col-id");
|
||||||
blockElement.querySelectorAll(".av__row").forEach((item) => {
|
blockElement.querySelectorAll(".av__row").forEach((item) => {
|
||||||
item.querySelector(`[data-index="${index}"]`).remove();
|
item.querySelector(`[data-col-id="${colId}"]`).remove();
|
||||||
});
|
});
|
||||||
|
cellElement.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const showColMenu = (protyle: IProtyle, blockElement: HTMLElement, cellElement: HTMLElement) => {
|
export const showColMenu = (protyle: IProtyle, blockElement: HTMLElement, cellElement: HTMLElement) => {
|
||||||
const type = cellElement.getAttribute("data-dtype") as TAVCol;
|
const type = cellElement.getAttribute("data-dtype") as TAVCol;
|
||||||
const colId = cellElement.getAttribute("data-id");
|
const colId = cellElement.getAttribute("data-col-id");
|
||||||
const avId = blockElement.getAttribute("data-av-id");
|
const avId = blockElement.getAttribute("data-av-id");
|
||||||
const menu = new Menu("av-header-cell", () => {
|
const menu = new Menu("av-header-cell", () => {
|
||||||
const newValue = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value;
|
const newValue = (window.siyuan.menus.menu.element.querySelector(".b3-text-field") as HTMLInputElement).value;
|
||||||
|
|
|
||||||
222
app/src/protyle/render/av/filter.ts
Normal file
222
app/src/protyle/render/av/filter.ts
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
import {Menu} from "../../../plugin/Menu";
|
||||||
|
import {transaction} from "../../wysiwyg/transaction";
|
||||||
|
import {hasClosestByClassName} from "../../util/hasClosest";
|
||||||
|
import {getColIconByType} from "./col";
|
||||||
|
import {setPosition} from "../../../util/setPosition";
|
||||||
|
|
||||||
|
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) {
|
||||||
|
let cellValue: IAVCellValue;
|
||||||
|
if (colType === "number") {
|
||||||
|
if (textElement.value) {
|
||||||
|
cellValue = {
|
||||||
|
content: parseFloat(textElement.value),
|
||||||
|
isNotEmpty: true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cellValue = {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cellValue = {
|
||||||
|
content: textElement.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filter.value[colType] = cellValue;
|
||||||
|
filter.operator = (window.siyuan.menus.menu.element.querySelector(".b3-select") as HTMLSelectElement).value as TAVFilterOperator;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
transaction(options.protyle, [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: options.data.id,
|
||||||
|
data: {
|
||||||
|
filters: options.data.filters
|
||||||
|
}
|
||||||
|
}], [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: options.data.id,
|
||||||
|
data: {
|
||||||
|
filters: oldFilters
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
const menuElement = hasClosestByClassName(options.target, "b3-menu");
|
||||||
|
if (menuElement) {
|
||||||
|
menuElement.innerHTML = getFiltersHTML(options.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (menu.isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let selectHTML = "";
|
||||||
|
switch (colType) {
|
||||||
|
case "text":
|
||||||
|
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;
|
||||||
|
case "number":
|
||||||
|
selectHTML = `<option ${"=" === options.filter.operator ? "selected" : ""} value="=">=</option>
|
||||||
|
<option ${"!=" === options.filter.operator ? "selected" : ""} value="!=">!=</option>
|
||||||
|
<option ${">" === options.filter.operator ? "selected" : ""} value=">">></option>
|
||||||
|
<option ${"<" === options.filter.operator ? "selected" : ""} value="<"><</option>
|
||||||
|
<option ${">=" === options.filter.operator ? "selected" : ""} value=">=">≥</option>
|
||||||
|
<option ${"<=" === options.filter.operator ? "selected" : ""} value="<=">≤</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;
|
||||||
|
}
|
||||||
|
menu.addItem({
|
||||||
|
iconHTML: "",
|
||||||
|
label: `<select style="margin: 4px 0" class="b3-select fn__size200">${selectHTML}</select>`
|
||||||
|
});
|
||||||
|
menu.addItem({
|
||||||
|
iconHTML: "",
|
||||||
|
label: `<input style="margin: 4px 0" 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) => {
|
||||||
|
if (event.isComposing) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
menu.close();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
menu.open({x: rectTarget.left, y: rectTarget.bottom});
|
||||||
|
textElement.select();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addFilter = (options: {
|
||||||
|
data: IAV,
|
||||||
|
rect: DOMRect,
|
||||||
|
menuElement: HTMLElement,
|
||||||
|
tabRect: DOMRect,
|
||||||
|
avId: string,
|
||||||
|
protyle: IProtyle
|
||||||
|
}) => {
|
||||||
|
const menu = new Menu("av-add-filter");
|
||||||
|
options.data.columns.forEach((column) => {
|
||||||
|
let hasFilter = false;
|
||||||
|
options.data.filters.find((filter) => {
|
||||||
|
if (filter.column === column.id) {
|
||||||
|
hasFilter = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!hasFilter) {
|
||||||
|
menu.addItem({
|
||||||
|
label: column.name,
|
||||||
|
icon: getColIconByType(column.type),
|
||||||
|
click: () => {
|
||||||
|
const oldFilters = Object.assign([], options.data.filters);
|
||||||
|
let cellValue = {}
|
||||||
|
if (column.type !== "number") {
|
||||||
|
cellValue = {content: ""}
|
||||||
|
}
|
||||||
|
options.data.filters.push({
|
||||||
|
column: column.id,
|
||||||
|
operator: "Contains",
|
||||||
|
value: {
|
||||||
|
[column.type]: cellValue
|
||||||
|
},
|
||||||
|
});
|
||||||
|
transaction(options.protyle, [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: options.avId,
|
||||||
|
data: {
|
||||||
|
filters: options.data.filters
|
||||||
|
}
|
||||||
|
}], [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: options.avId,
|
||||||
|
data: {
|
||||||
|
filters: oldFilters
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
options.menuElement.innerHTML = getFiltersHTML(options.data);
|
||||||
|
setPosition(options.menuElement, options.tabRect.right - options.menuElement.clientWidth, options.tabRect.bottom, options.tabRect.height);
|
||||||
|
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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
menu.open({
|
||||||
|
x: options.rect.left,
|
||||||
|
y: options.rect.bottom,
|
||||||
|
h: options.rect.height,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFiltersHTML = (data: IAV) => {
|
||||||
|
let html = "";
|
||||||
|
const genFilterItem = (filter: IAVFilter) => {
|
||||||
|
let filterHTML = "";
|
||||||
|
data.columns.find((item) => {
|
||||||
|
if (item.id === filter.column) {
|
||||||
|
const filterValue = (filter.value && filter.value[item.type] && filter.value[item.type].content) ? filter.value[item.type].content : "";
|
||||||
|
filterHTML += `<span data-type="setFilter" data-coltype="${item.type}" data-op="${filter.operator}" data-value="${filterValue}" class="b3-chip${filterValue ? " b3-chip--primary" : ""}">
|
||||||
|
<svg><use xlink:href="#${getColIconByType(item.type)}"></use></svg>
|
||||||
|
<span class="fn__ellipsis">${item.name}${filterValue ? ": " + filterValue : ""}</span>
|
||||||
|
</span>`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return filterHTML;
|
||||||
|
};
|
||||||
|
data.filters.forEach((item: IAVFilter) => {
|
||||||
|
html += `<button class="b3-menu__item" draggable="true" data-id="${item.column}">
|
||||||
|
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
||||||
|
<div class="fn__flex-1">${genFilterItem(item)}</div>
|
||||||
|
<svg class="b3-menu__action" data-type="removeFilter"><use xlink:href="#iconTrashcan"></use></svg>
|
||||||
|
</button>`;
|
||||||
|
});
|
||||||
|
return `<button class="b3-menu__item" data-type="nobg">
|
||||||
|
<span class="block__icon" style="padding: 8px;margin-left: -4px;" data-type="goConfig">
|
||||||
|
<svg><use xlink:href="#iconLeft"></use></svg>
|
||||||
|
</span>
|
||||||
|
<span class="b3-menu__label ft__center">${window.siyuan.languages.filter}</span>
|
||||||
|
<svg class="b3-menu__action" data-type="close" style="opacity: 1"><use xlink:href="#iconCloseRound"></use></svg>
|
||||||
|
</button>
|
||||||
|
<button class="b3-menu__separator"></button>
|
||||||
|
${html}
|
||||||
|
<button class="b3-menu__item${data.filters.length === data.columns.length ? " fn__none" : ""}" data-type="addFilter">
|
||||||
|
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
|
||||||
|
<span class="b3-menu__label">${window.siyuan.languages.new}</span>
|
||||||
|
</button>
|
||||||
|
<button class="b3-menu__item${html ? "" : " fn__none"}" data-type="removeFilters">
|
||||||
|
<svg class="b3-menu__icon"><use xlink:href="#iconTrashcan"></use></svg>
|
||||||
|
<span class="b3-menu__label">${window.siyuan.languages.delete}</span>
|
||||||
|
</button>`;
|
||||||
|
};
|
||||||
|
|
@ -3,10 +3,15 @@ import {fetchPost} from "../../../util/fetch";
|
||||||
import {addCol} from "./addCol";
|
import {addCol} from "./addCol";
|
||||||
import {getColIconByType} from "./col";
|
import {getColIconByType} from "./col";
|
||||||
import {setPosition} from "../../../util/setPosition";
|
import {setPosition} from "../../../util/setPosition";
|
||||||
import {Menu} from "../../../plugin/Menu";
|
import {hasClosestByAttribute} from "../../util/hasClosest";
|
||||||
import {hasClosestByAttribute, hasClosestByClassName} from "../../util/hasClosest";
|
import {bindSelectEvent, getSelectHTML, setSelectOption} from "./select";
|
||||||
|
import {addFilter, getFiltersHTML} from "./filter";
|
||||||
|
import {addSort, bindSortsEvent, getSortsHTML} from "./sort";
|
||||||
|
|
||||||
export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type: "properties" | "config" | "sorts" | "filters" = "config") => {
|
export const openMenuPanel = (protyle: IProtyle,
|
||||||
|
blockElement: HTMLElement,
|
||||||
|
type: "select" | "properties" | "config" | "sorts" | "filters" = "config",
|
||||||
|
options?: any) => {
|
||||||
let avPanelElement = document.querySelector(".av__panel");
|
let avPanelElement = document.querySelector(".av__panel");
|
||||||
if (avPanelElement) {
|
if (avPanelElement) {
|
||||||
avPanelElement.remove();
|
avPanelElement.remove();
|
||||||
|
|
@ -25,16 +30,26 @@ export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type
|
||||||
html = getSortsHTML(data);
|
html = getSortsHTML(data);
|
||||||
} else if (type === "filters") {
|
} else if (type === "filters") {
|
||||||
html = getFiltersHTML(data);
|
html = getFiltersHTML(data);
|
||||||
|
} else if (type === "select") {
|
||||||
|
html = getSelectHTML(data, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.insertAdjacentHTML("beforeend", `<div class="av__panel">
|
document.body.insertAdjacentHTML("beforeend", `<div class="av__panel">
|
||||||
<div class="b3-dialog__scrim" data-type="close"></div>
|
<div class="b3-dialog__scrim" data-type="close"></div>
|
||||||
<div class="b3-menu">${html}</div>
|
<div class="b3-menu">${html}</div>
|
||||||
</div>`);
|
</div>`);
|
||||||
|
|
||||||
avPanelElement = document.querySelector(".av__panel");
|
avPanelElement = document.querySelector(".av__panel");
|
||||||
const menuElement = avPanelElement.lastElementChild as HTMLElement;
|
const menuElement = avPanelElement.lastElementChild as HTMLElement;
|
||||||
const tabRect = blockElement.querySelector(".layout-tab-bar").getBoundingClientRect();
|
const tabRect = blockElement.querySelector(".layout-tab-bar").getBoundingClientRect();
|
||||||
|
if (options && options.cellElement) {
|
||||||
|
const cellRect = options.cellElement.getBoundingClientRect();
|
||||||
|
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
|
||||||
|
bindSelectEvent(protyle, data, menuElement, options);
|
||||||
|
menuElement.querySelector("input").select();
|
||||||
|
menuElement.querySelector("input").focus();
|
||||||
|
} else {
|
||||||
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
|
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
|
||||||
|
}
|
||||||
|
|
||||||
bindSortsEvent(protyle, menuElement, data);
|
bindSortsEvent(protyle, menuElement, data);
|
||||||
avPanelElement.addEventListener("dragstart", (event) => {
|
avPanelElement.addEventListener("dragstart", (event) => {
|
||||||
|
|
@ -155,7 +170,6 @@ export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type
|
||||||
}
|
}
|
||||||
dragoverElement = targetElement;
|
dragoverElement = targetElement;
|
||||||
});
|
});
|
||||||
|
|
||||||
avPanelElement.addEventListener("dragleave", (event) => {
|
avPanelElement.addEventListener("dragleave", (event) => {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
const targetElement = hasClosestByAttribute(target, "data-id", null);
|
const targetElement = hasClosestByAttribute(target, "data-id", null);
|
||||||
|
|
@ -169,6 +183,7 @@ export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type
|
||||||
window.siyuan.dragElement = undefined;
|
window.siyuan.dragElement = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
avPanelElement.addEventListener("click", (event) => {
|
avPanelElement.addEventListener("click", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let target = event.target as HTMLElement;
|
let target = event.target as HTMLElement;
|
||||||
|
|
@ -416,6 +431,10 @@ export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type
|
||||||
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
|
setPosition(menuElement, tabRect.right - menuElement.clientWidth, tabRect.bottom, tabRect.height);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
break;
|
break;
|
||||||
|
} else if (type === "editOption") {
|
||||||
|
setSelectOption(protyle, data, options, target)
|
||||||
|
event.stopPropagation();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
target = target.parentElement;
|
target = target.parentElement;
|
||||||
}
|
}
|
||||||
|
|
@ -423,358 +442,12 @@ export const openMenuPanel = (protyle: IProtyle, blockElement: HTMLElement, type
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addSort = (options: {
|
|
||||||
data: IAV,
|
|
||||||
rect: DOMRect,
|
|
||||||
menuElement: HTMLElement,
|
|
||||||
tabRect: DOMRect,
|
|
||||||
avId: string,
|
|
||||||
protyle: IProtyle
|
|
||||||
}) => {
|
|
||||||
const menu = new Menu("av-add-sort");
|
|
||||||
options.data.columns.forEach((column) => {
|
|
||||||
let hasSort = false;
|
|
||||||
options.data.sorts.find((sort) => {
|
|
||||||
if (sort.column === column.id) {
|
|
||||||
hasSort = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!hasSort) {
|
|
||||||
menu.addItem({
|
|
||||||
label: column.name,
|
|
||||||
icon: getColIconByType(column.type),
|
|
||||||
click: () => {
|
|
||||||
const oldSorts = Object.assign([], options.data.sorts);
|
|
||||||
options.data.sorts.push({
|
|
||||||
column: column.id,
|
|
||||||
order: "ASC",
|
|
||||||
});
|
|
||||||
transaction(options.protyle, [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: options.avId,
|
|
||||||
data: {
|
|
||||||
sorts: options.data.sorts
|
|
||||||
}
|
|
||||||
}], [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: options.avId,
|
|
||||||
data: {
|
|
||||||
sorts: oldSorts
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
options.menuElement.innerHTML = getSortsHTML(options.data);
|
|
||||||
bindSortsEvent(options.protyle, options.menuElement, options.data);
|
|
||||||
setPosition(options.menuElement, options.tabRect.right - options.menuElement.clientWidth, options.tabRect.bottom, options.tabRect.height);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
menu.open({
|
|
||||||
x: options.rect.left,
|
|
||||||
y: options.rect.bottom,
|
|
||||||
h: options.rect.height,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const bindSortsEvent = (protyle: IProtyle, menuElement: HTMLElement, data: IAV) => {
|
|
||||||
menuElement.querySelectorAll("select").forEach((item: HTMLSelectElement) => {
|
|
||||||
item.addEventListener("change", () => {
|
|
||||||
const colId = item.parentElement.getAttribute("data-id");
|
|
||||||
const oldSort = JSON.parse(JSON.stringify(data.sorts));
|
|
||||||
if (item.previousElementSibling.classList.contains("b3-menu__icon")) {
|
|
||||||
data.sorts.find((sort: IAVSort) => {
|
|
||||||
if (sort.column === colId) {
|
|
||||||
sort.column = item.value;
|
|
||||||
item.parentElement.setAttribute("data-id", item.value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data.sorts.find((sort: IAVSort) => sort.column === colId).order = item.value as "ASC" | "DESC";
|
|
||||||
}
|
|
||||||
transaction(protyle, [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: data.id,
|
|
||||||
data: {
|
|
||||||
sorts: data.sorts
|
|
||||||
}
|
|
||||||
}], [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: data.id,
|
|
||||||
data: {
|
|
||||||
sorts: oldSort
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSortsHTML = (data: IAV) => {
|
|
||||||
let html = "";
|
|
||||||
const genSortItem = (id: string) => {
|
|
||||||
let sortHTML = "";
|
|
||||||
data.columns.forEach((item) => {
|
|
||||||
sortHTML += `<option value="${item.id}" ${item.id === id ? "selected" : ""}>${item.name}</option>`;
|
|
||||||
});
|
|
||||||
return sortHTML;
|
|
||||||
};
|
|
||||||
data.sorts.forEach((item: IAVSort) => {
|
|
||||||
html += `<button draggable="true" class="b3-menu__item" data-id="${item.column}">
|
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
|
||||||
<select class="b3-select" style="width: 106px;margin: 4px 0">
|
|
||||||
${genSortItem(item.column)}
|
|
||||||
</select>
|
|
||||||
<span class="fn__space"></span>
|
|
||||||
<select class="b3-select" style="width: 106px;margin: 4px 0">
|
|
||||||
<option value="ASC" ${item.order === "ASC" ? "selected" : ""}>${window.siyuan.languages.asc}</option>
|
|
||||||
<option value="DESC" ${item.order === "DESC" ? "selected" : ""}>${window.siyuan.languages.desc}</option>
|
|
||||||
</select>
|
|
||||||
<svg class="b3-menu__action" data-type="removeSort"><use xlink:href="#iconTrashcan"></use></svg>
|
|
||||||
</button>`;
|
|
||||||
});
|
|
||||||
return `<button class="b3-menu__item" data-type="nobg">
|
|
||||||
<span class="block__icon" style="padding: 8px;margin-left: -4px;" data-type="goConfig">
|
|
||||||
<svg><use xlink:href="#iconLeft"></use></svg>
|
|
||||||
</span>
|
|
||||||
<span class="b3-menu__label ft__center">${window.siyuan.languages.sort}</span>
|
|
||||||
<svg class="b3-menu__action" data-type="close" style="opacity: 1"><use xlink:href="#iconCloseRound"></use></svg>
|
|
||||||
</button>
|
|
||||||
<button class="b3-menu__separator"></button>
|
|
||||||
${html}
|
|
||||||
<button class="b3-menu__item${data.sorts.length === data.columns.length ? " fn__none" : ""}" data-type="addSort">
|
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
|
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.new}</span>
|
|
||||||
</button>
|
|
||||||
<button class="b3-menu__item${html ? "" : " fn__none"}" data-type="removeSorts">
|
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconTrashcan"></use></svg>
|
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.delete}</span>
|
|
||||||
</button>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
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) {
|
|
||||||
let cellValue: IAVCellValue;
|
|
||||||
if (colType === "number") {
|
|
||||||
if (textElement.value) {
|
|
||||||
cellValue = {
|
|
||||||
content: parseFloat(textElement.value),
|
|
||||||
isNotEmpty: true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cellValue = {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cellValue = {
|
|
||||||
content: textElement.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filter.value[colType] = cellValue;
|
|
||||||
filter.operator = (window.siyuan.menus.menu.element.querySelector(".b3-select") as HTMLSelectElement).value as TAVFilterOperator;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
transaction(options.protyle, [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: options.data.id,
|
|
||||||
data: {
|
|
||||||
filters: options.data.filters
|
|
||||||
}
|
|
||||||
}], [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: options.data.id,
|
|
||||||
data: {
|
|
||||||
filters: oldFilters
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
const menuElement = hasClosestByClassName(options.target, "b3-menu");
|
|
||||||
if (menuElement) {
|
|
||||||
menuElement.innerHTML = getFiltersHTML(options.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (menu.isOpen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let selectHTML = "";
|
|
||||||
switch (colType) {
|
|
||||||
case "text":
|
|
||||||
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;
|
|
||||||
case "number":
|
|
||||||
selectHTML = `<option ${"=" === options.filter.operator ? "selected" : ""} value="=">=</option>
|
|
||||||
<option ${"!=" === options.filter.operator ? "selected" : ""} value="!=">!=</option>
|
|
||||||
<option ${">" === options.filter.operator ? "selected" : ""} value=">">></option>
|
|
||||||
<option ${"<" === options.filter.operator ? "selected" : ""} value="<"><</option>
|
|
||||||
<option ${">=" === options.filter.operator ? "selected" : ""} value=">=">≥</option>
|
|
||||||
<option ${"<=" === options.filter.operator ? "selected" : ""} value="<=">≤</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;
|
|
||||||
}
|
|
||||||
menu.addItem({
|
|
||||||
iconHTML: "",
|
|
||||||
label: `<select style="margin: 4px 0" class="b3-select fn__size200">${selectHTML}</select>`
|
|
||||||
});
|
|
||||||
menu.addItem({
|
|
||||||
iconHTML: "",
|
|
||||||
label: `<input style="margin: 4px 0" 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) => {
|
|
||||||
if (event.isComposing) {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.key === "Enter") {
|
|
||||||
menu.close();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
menu.open({x: rectTarget.left, y: rectTarget.bottom});
|
|
||||||
textElement.select();
|
|
||||||
};
|
|
||||||
|
|
||||||
const addFilter = (options: {
|
|
||||||
data: IAV,
|
|
||||||
rect: DOMRect,
|
|
||||||
menuElement: HTMLElement,
|
|
||||||
tabRect: DOMRect,
|
|
||||||
avId: string,
|
|
||||||
protyle: IProtyle
|
|
||||||
}) => {
|
|
||||||
const menu = new Menu("av-add-filter");
|
|
||||||
options.data.columns.forEach((column) => {
|
|
||||||
let hasFilter = false;
|
|
||||||
options.data.filters.find((filter) => {
|
|
||||||
if (filter.column === column.id) {
|
|
||||||
hasFilter = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!hasFilter) {
|
|
||||||
menu.addItem({
|
|
||||||
label: column.name,
|
|
||||||
icon: getColIconByType(column.type),
|
|
||||||
click: () => {
|
|
||||||
const oldFilters = Object.assign([], options.data.filters);
|
|
||||||
let cellValue = {}
|
|
||||||
if (column.type !== "number") {
|
|
||||||
cellValue = {content: ""}
|
|
||||||
}
|
|
||||||
options.data.filters.push({
|
|
||||||
column: column.id,
|
|
||||||
operator: "Contains",
|
|
||||||
value: {
|
|
||||||
[column.type]: cellValue
|
|
||||||
},
|
|
||||||
});
|
|
||||||
transaction(options.protyle, [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: options.avId,
|
|
||||||
data: {
|
|
||||||
filters: options.data.filters
|
|
||||||
}
|
|
||||||
}], [{
|
|
||||||
action: "setAttrView",
|
|
||||||
id: options.avId,
|
|
||||||
data: {
|
|
||||||
filters: oldFilters
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
options.menuElement.innerHTML = getFiltersHTML(options.data);
|
|
||||||
setPosition(options.menuElement, options.tabRect.right - options.menuElement.clientWidth, options.tabRect.bottom, options.tabRect.height);
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
menu.open({
|
|
||||||
x: options.rect.left,
|
|
||||||
y: options.rect.bottom,
|
|
||||||
h: options.rect.height,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFiltersHTML = (data: IAV) => {
|
|
||||||
let html = "";
|
|
||||||
const genFilterItem = (filter: IAVFilter) => {
|
|
||||||
let filterHTML = "";
|
|
||||||
data.columns.find((item) => {
|
|
||||||
if (item.id === filter.column) {
|
|
||||||
const filterValue = (filter.value && filter.value[item.type] && filter.value[item.type].content) ? filter.value[item.type].content : "";
|
|
||||||
filterHTML += `<span data-type="setFilter" data-coltype="${item.type}" data-op="${filter.operator}" data-value="${filterValue}" class="b3-chip${filterValue ? " b3-chip--primary" : ""}">
|
|
||||||
<svg><use xlink:href="#${getColIconByType(item.type)}"></use></svg>
|
|
||||||
<span class="fn__ellipsis">${item.name}${filterValue ? ": " + filterValue : ""}</span>
|
|
||||||
</span>`;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return filterHTML;
|
|
||||||
};
|
|
||||||
data.filters.forEach((item: IAVFilter) => {
|
|
||||||
html += `<button class="b3-menu__item" draggable="true" data-type="nobg" data-id="${item.column}">
|
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
|
||||||
<div class="fn__flex-1">${genFilterItem(item)}</div>
|
|
||||||
<svg class="b3-menu__action" data-type="removeFilter"><use xlink:href="#iconTrashcan"></use></svg>
|
|
||||||
</button>`;
|
|
||||||
});
|
|
||||||
return `<button class="b3-menu__item" data-type="nobg">
|
|
||||||
<span class="block__icon" style="padding: 8px;margin-left: -4px;" data-type="goConfig">
|
|
||||||
<svg><use xlink:href="#iconLeft"></use></svg>
|
|
||||||
</span>
|
|
||||||
<span class="b3-menu__label ft__center">${window.siyuan.languages.filter}</span>
|
|
||||||
<svg class="b3-menu__action" data-type="close" style="opacity: 1"><use xlink:href="#iconCloseRound"></use></svg>
|
|
||||||
</button>
|
|
||||||
<button class="b3-menu__separator"></button>
|
|
||||||
${html}
|
|
||||||
<button class="b3-menu__item${data.filters.length === data.columns.length ? " fn__none" : ""}" data-type="addFilter">
|
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
|
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.new}</span>
|
|
||||||
</button>
|
|
||||||
<button class="b3-menu__item${html ? "" : " fn__none"}" data-type="removeFilters">
|
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconTrashcan"></use></svg>
|
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.delete}</span>
|
|
||||||
</button>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPropertiesHTML = (data: IAV) => {
|
const getPropertiesHTML = (data: IAV) => {
|
||||||
let showHTML = "";
|
let showHTML = "";
|
||||||
let hideHTML = "";
|
let hideHTML = "";
|
||||||
data.columns.forEach((item: IAVColumn) => {
|
data.columns.forEach((item: IAVColumn) => {
|
||||||
if (item.hidden) {
|
if (item.hidden) {
|
||||||
hideHTML += `<button class="b3-menu__item" draggable="true" data-type="nobg" data-id="${item.id}">
|
hideHTML += `<button class="b3-menu__item" draggable="true" data-id="${item.id}">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
||||||
<div class="fn__flex-1">
|
<div class="fn__flex-1">
|
||||||
<span class="b3-chip">
|
<span class="b3-chip">
|
||||||
|
|
@ -786,7 +459,7 @@ const getPropertiesHTML = (data: IAV) => {
|
||||||
<svg class="b3-menu__action"><use xlink:href="#iconEdit"></use></svg>
|
<svg class="b3-menu__action"><use xlink:href="#iconEdit"></use></svg>
|
||||||
</button>`;
|
</button>`;
|
||||||
} else {
|
} else {
|
||||||
showHTML += `<button class="b3-menu__item" draggable="true" data-type="nobg" data-id="${item.id}">
|
showHTML += `<button class="b3-menu__item" draggable="true" data-id="${item.id}">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
||||||
<div class="fn__flex-1">
|
<div class="fn__flex-1">
|
||||||
<span class="b3-chip">
|
<span class="b3-chip">
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,11 @@ export const avRender = (element: Element, cb?: () => void) => {
|
||||||
const data = response.data.av as IAV;
|
const data = response.data.av as IAV;
|
||||||
// header
|
// header
|
||||||
let tableHTML = '<div class="av__row av__row--header"><div class="av__firstcol"><svg style="height: 42px"><use xlink:href="#iconUncheck"></use></svg></div>';
|
let tableHTML = '<div class="av__row av__row--header"><div class="av__firstcol"><svg style="height: 42px"><use xlink:href="#iconUncheck"></use></svg></div>';
|
||||||
let index = 0;
|
|
||||||
data.columns.forEach((column: IAVColumn) => {
|
data.columns.forEach((column: IAVColumn) => {
|
||||||
if (column.hidden) {
|
if (column.hidden) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tableHTML += `<div class="av__cell" data-index="${index}" data-id="${column.id}" data-dtype="${column.type}"
|
tableHTML += `<div class="av__cell" data-col-id="${column.id}" data-dtype="${column.type}"
|
||||||
style="width: ${column.width || "200px"};
|
style="width: ${column.width || "200px"};
|
||||||
${column.wrap ? "" : "white-space: nowrap;"}">
|
${column.wrap ? "" : "white-space: nowrap;"}">
|
||||||
<div draggable="true" class="av__cellheader">
|
<div draggable="true" class="av__cellheader">
|
||||||
|
|
@ -36,7 +35,6 @@ ${column.wrap ? "" : "white-space: nowrap;"}">
|
||||||
</div>
|
</div>
|
||||||
<div class="av__widthdrag"></div>
|
<div class="av__widthdrag"></div>
|
||||||
</div>`;
|
</div>`;
|
||||||
index++;
|
|
||||||
});
|
});
|
||||||
tableHTML += `<div class="block__icons">
|
tableHTML += `<div class="block__icons">
|
||||||
<div class="block__icon block__icon--show" data-type="av-header-add"><svg><use xlink:href="#iconAdd"></use></svg></div>
|
<div class="block__icon block__icon--show" data-type="av-header-add"><svg><use xlink:href="#iconAdd"></use></svg></div>
|
||||||
|
|
@ -58,24 +56,28 @@ ${column.wrap ? "" : "white-space: nowrap;"}">
|
||||||
}
|
}
|
||||||
let text: string;
|
let text: string;
|
||||||
if (cell.valueType === "text") {
|
if (cell.valueType === "text") {
|
||||||
text = cell.value?.text.content || "";
|
text = `<span class="av__celltext">${cell.value?.text.content || ""}</span>`;
|
||||||
} else if (cell.valueType === "block") {
|
} else if (cell.valueType === "block") {
|
||||||
text = cell.value?.block.content || "";
|
text = `<span class="av__celltext">${cell.value?.block.content || ""}</span>`;
|
||||||
} else if (cell.valueType === "number") {
|
} else if (cell.valueType === "number") {
|
||||||
text = cell.value?.number.content || "";
|
text = `<span class="av__celltext">${cell.value?.number.content || ""}</span>`;
|
||||||
} else if (cell.valueType === "select") {
|
} else if (cell.valueType === "select") {
|
||||||
text = cell.value?.select.content || "";
|
if (cell.value?.select.content) {
|
||||||
} else if (cell.valueType === "mSelect") {
|
text = `<span class="av__celltext"><span class="b3-chip b3-chip--middle" style="background-color:var(--b3-font-background${cell.value.select.color});color:var(--b3-font-color${cell.value.select.color})">${cell.value.select.content}</span></span>`;
|
||||||
text = cell.value?.mSelect.content || "";
|
} else {
|
||||||
} else if (cell.valueType === "date") {
|
text = `<span class="av__celltext"></span>`;
|
||||||
text = cell.value?.date.content || "";
|
|
||||||
}
|
}
|
||||||
tableHTML += `<div class="av__cell" data-id="${cell.id}" data-index="${index}"
|
} else if (cell.valueType === "mSelect") {
|
||||||
|
text = `<span class="av__celltext">${cell.value?.mSelect.content || ""}</span>`;
|
||||||
|
} else if (cell.valueType === "date") {
|
||||||
|
text = `<span class="av__celltext">${cell.value?.date.content || ""}</span>`;
|
||||||
|
}
|
||||||
|
tableHTML += `<div class="av__cell" data-id="${cell.id}" data-col-id="${data.columns[index].id}"
|
||||||
${cell.valueType === "block" ? 'data-block-id="' + (cell.value.block.id || "") + '"' : ""}
|
${cell.valueType === "block" ? 'data-block-id="' + (cell.value.block.id || "") + '"' : ""}
|
||||||
style="width: ${data.columns[index].width || "200px"};
|
style="width: ${data.columns[index].width || "200px"};
|
||||||
${cell.bgColor ? `background-color:${cell.bgColor};` : ""}
|
${cell.bgColor ? `background-color:${cell.bgColor};` : ""}
|
||||||
${data.columns[index].wrap ? "" : "white-space: nowrap;"}
|
${data.columns[index].wrap ? "" : "white-space: nowrap;"}
|
||||||
${cell.color ? `color:${cell.color};` : ""}"><span class="av__celltext">${text}</span></div>`;
|
${cell.color ? `color:${cell.color};` : ""}">${text}</div>`;
|
||||||
});
|
});
|
||||||
tableHTML += "<div></div></div>";
|
tableHTML += "<div></div></div>";
|
||||||
});
|
});
|
||||||
|
|
@ -146,13 +148,12 @@ export const refreshAV = (protyle: IProtyle, operation: IOperation) => {
|
||||||
});
|
});
|
||||||
} else if (operation.action === "setAttrViewColWidth") {
|
} else if (operation.action === "setAttrViewColWidth") {
|
||||||
Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-av-id="${avId}"]`)).forEach((item: HTMLElement) => {
|
Array.from(protyle.wysiwyg.element.querySelectorAll(`[data-av-id="${avId}"]`)).forEach((item: HTMLElement) => {
|
||||||
const cellElement = item.querySelector(`.av__cell[data-id="${operation.id}"]`) as HTMLElement;
|
const cellElement = item.querySelector(`.av__cell[data-col-id="${operation.id}"]`) as HTMLElement;
|
||||||
if (!cellElement || cellElement.style.width === operation.data) {
|
if (!cellElement || cellElement.style.width === operation.data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const index = cellElement.dataset.index;
|
|
||||||
item.querySelectorAll(".av__row").forEach(rowItem => {
|
item.querySelectorAll(".av__row").forEach(rowItem => {
|
||||||
(rowItem.querySelector(`[data-index="${index}"]`) as HTMLElement).style.width = operation.data;
|
(rowItem.querySelector(`[data-col-id="${operation.id}"]`) as HTMLElement).style.width = operation.data;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (operation.action === "setAttrView" && typeof operation.data.name === "string") {
|
} else if (operation.action === "setAttrView" && typeof operation.data.name === "string") {
|
||||||
|
|
|
||||||
304
app/src/protyle/render/av/select.ts
Normal file
304
app/src/protyle/render/av/select.ts
Normal file
|
|
@ -0,0 +1,304 @@
|
||||||
|
import {Menu} from "../../../plugin/Menu";
|
||||||
|
import {transaction} from "../../wysiwyg/transaction";
|
||||||
|
import {hasClosestByClassName} from "../../util/hasClosest";
|
||||||
|
import {confirmDialog} from "../../../dialog/confirmDialog";
|
||||||
|
import {setPosition} from "../../../util/setPosition";
|
||||||
|
import {upDownHint} from "../../../util/upDownHint";
|
||||||
|
|
||||||
|
const filterSelectHTML = (key: string, options: { name: string, color: string }[]) => {
|
||||||
|
let html = "";
|
||||||
|
let hasMatch = false;
|
||||||
|
if (options) {
|
||||||
|
options.forEach(item => {
|
||||||
|
if (!key ||
|
||||||
|
(key.toLowerCase().indexOf(item.name.toLowerCase()) > -1 ||
|
||||||
|
item.name.toLowerCase().indexOf(key.toLowerCase()) > -1)) {
|
||||||
|
html += `<button class="b3-menu__item${html ? "" : " b3-menu__item--current"}" draggable="true" data-name="${item.name}" data-color="${item.color}">
|
||||||
|
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
||||||
|
<div class="fn__flex-1">
|
||||||
|
<span class="b3-chip" style="background-color:var(--b3-font-background${item.color});color:var(--b3-font-color)${item.color}">
|
||||||
|
<span class="fn__ellipsis">${item.name}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<svg class="b3-menu__action" data-type="editOption"><use xlink:href="#iconEdit"></use></svg>
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
if (key === item.name) {
|
||||||
|
hasMatch = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!hasMatch && key) {
|
||||||
|
const colorIndex = (options?.length || 0) % 13 + 1;
|
||||||
|
html += `<button class="b3-menu__item${html ? "" : " b3-menu__item--current"}" data-name="${key}" data-color="${colorIndex}">
|
||||||
|
<div class="fn__flex-1">
|
||||||
|
<span class="b3-chip" style="background-color:var(--b3-font-background${colorIndex});color:var(--b3-font-color${colorIndex})">
|
||||||
|
<span class="fn__ellipsis">${key}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span class="b3-menu__accelerator">Enter</span>
|
||||||
|
</button>`
|
||||||
|
}
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setSelectOption = (protyle: IProtyle, data: IAV, options: {
|
||||||
|
cellElement: HTMLElement;
|
||||||
|
}, target: HTMLElement,) => {
|
||||||
|
const name = target.parentElement.dataset.name;
|
||||||
|
const menu = new Menu("av-select-option", () => {
|
||||||
|
transaction(protyle, [{
|
||||||
|
action: "updateAttrViewColOption",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: {
|
||||||
|
oldName: name,
|
||||||
|
newName: inputElement.value
|
||||||
|
},
|
||||||
|
}], [{
|
||||||
|
action: "updateAttrViewColOption",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: {
|
||||||
|
oldName: inputElement.value,
|
||||||
|
newName: name
|
||||||
|
},
|
||||||
|
}]);
|
||||||
|
})
|
||||||
|
if (menu.isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const menuElement = hasClosestByClassName(target, "b3-menu");
|
||||||
|
if (!menuElement) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const color = target.parentElement.dataset.color;
|
||||||
|
const colId = options.cellElement.dataset.colId;
|
||||||
|
menu.addItem({
|
||||||
|
iconHTML: "",
|
||||||
|
label: `<input class="b3-text-field" style="margin: 4px 0" value="${name}">`
|
||||||
|
})
|
||||||
|
menu.addItem({
|
||||||
|
label: window.siyuan.languages.delete,
|
||||||
|
icon: "iconTrashcan",
|
||||||
|
click() {
|
||||||
|
confirmDialog(window.siyuan.languages.deleteOpConfirm, window.siyuan.languages.confirmDelete, () => {
|
||||||
|
let colOptions: { name: string, color: string }[] = []
|
||||||
|
data.columns.find(column => {
|
||||||
|
if (column.id === colId) {
|
||||||
|
colOptions = column.options;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const newName = target.parentElement.dataset.name
|
||||||
|
transaction(protyle, [{
|
||||||
|
action: "removeAttrViewColOption",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: newName,
|
||||||
|
}], [{
|
||||||
|
action: "updateAttrViewColOptions",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: colOptions
|
||||||
|
}]);
|
||||||
|
colOptions.find((item, index) => {
|
||||||
|
if (item.name === newName) {
|
||||||
|
colOptions.splice(index, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
menuElement.innerHTML = getSelectHTML(data, options);
|
||||||
|
const cellRect = options.cellElement.getBoundingClientRect();
|
||||||
|
setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
|
||||||
|
bindSelectEvent(protyle, data, menuElement, options);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
menu.addSeparator()
|
||||||
|
Array.from(Array(13).keys()).forEach(index => {
|
||||||
|
menu.addItem({
|
||||||
|
current: parseInt(color) === index + 1,
|
||||||
|
iconHTML: "",
|
||||||
|
label: `<span class="color__square" style="padding: 5px;margin: 2px;color: var(--b3-font-color${index + 1});background-color: var(--b3-font-background${index + 1});">A</span>`,
|
||||||
|
click() {
|
||||||
|
transaction(protyle, [{
|
||||||
|
action: "updateAttrViewColOption",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: {
|
||||||
|
oldColor: color,
|
||||||
|
newColor: (index + 1).toString()
|
||||||
|
},
|
||||||
|
}], [{
|
||||||
|
action: "updateAttrViewColOption",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: {
|
||||||
|
oldColor: (index + 1).toString(),
|
||||||
|
newColor: color
|
||||||
|
},
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const rect = target.getBoundingClientRect();
|
||||||
|
menu.open({
|
||||||
|
x: rect.left,
|
||||||
|
y: rect.bottom,
|
||||||
|
h: rect.height,
|
||||||
|
})
|
||||||
|
const inputElement = window.siyuan.menus.menu.element.querySelector("input");
|
||||||
|
inputElement.select();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const bindSelectEvent = (protyle: IProtyle, data: IAV, menuElement: HTMLElement, options: {
|
||||||
|
cellElement: HTMLElement
|
||||||
|
}) => {
|
||||||
|
const inputElement = menuElement.querySelector("input");
|
||||||
|
const rowId = options.cellElement.parentElement.dataset.id
|
||||||
|
const colId = options.cellElement.dataset.colId;
|
||||||
|
const cellId = options.cellElement.dataset.id
|
||||||
|
let colData: IAVColumn;
|
||||||
|
data.columns.find((item: IAVColumn) => {
|
||||||
|
if (item.id === colId) {
|
||||||
|
colData = item;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!colData.options) {
|
||||||
|
colData.options = [];
|
||||||
|
}
|
||||||
|
let cellData: IAVCell
|
||||||
|
data.rows.find(row => {
|
||||||
|
if (row.id === rowId) {
|
||||||
|
row.cells.find(cell => {
|
||||||
|
if (cell.id === cellId) {
|
||||||
|
cellData = cell
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
inputElement.addEventListener("input", (event: InputEvent) => {
|
||||||
|
if (event.isComposing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
menuElement.lastElementChild.innerHTML = filterSelectHTML(inputElement.value, colData.options);
|
||||||
|
})
|
||||||
|
inputElement.addEventListener("compositionend", (event: InputEvent) => {
|
||||||
|
if (event.isComposing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
menuElement.lastElementChild.innerHTML = filterSelectHTML(inputElement.value, colData.options);
|
||||||
|
})
|
||||||
|
inputElement.addEventListener("keydown", (event: KeyboardEvent) => {
|
||||||
|
if (event.isComposing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let currentElement = upDownHint(menuElement.lastElementChild, event, "b3-menu__item--current");
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
if (!currentElement) {
|
||||||
|
currentElement = menuElement.querySelector(".b3-menu__item--current");
|
||||||
|
}
|
||||||
|
let oldValue
|
||||||
|
if (colData.type !== "select") {
|
||||||
|
oldValue = Object.assign([], cellData.value.mSelect.content);
|
||||||
|
cellData.value.mSelect.content.push({
|
||||||
|
color: currentElement.dataset.color,
|
||||||
|
content: currentElement.dataset.name
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
oldValue = Object.assign({}, cellData.value.select);
|
||||||
|
cellData.value.select = {
|
||||||
|
color: currentElement.dataset.color,
|
||||||
|
content: currentElement.dataset.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentElement.querySelector(".b3-menu__accelerator")) {
|
||||||
|
colData.options.push({
|
||||||
|
color: currentElement.dataset.color,
|
||||||
|
name: currentElement.dataset.name
|
||||||
|
})
|
||||||
|
transaction(protyle, [{
|
||||||
|
action: "updateAttrViewColOptions",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: colData.options
|
||||||
|
}, {
|
||||||
|
action: "updateAttrViewCell",
|
||||||
|
id: cellId,
|
||||||
|
rowID: rowId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: {
|
||||||
|
[colData.type]: cellData.value
|
||||||
|
}
|
||||||
|
}], [{
|
||||||
|
action: "removeAttrViewColOption",
|
||||||
|
id: colId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: currentElement.dataset.name,
|
||||||
|
}]);
|
||||||
|
} else {
|
||||||
|
transaction(protyle, [{
|
||||||
|
action: "updateAttrViewCell",
|
||||||
|
id: cellId,
|
||||||
|
rowID: rowId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: cellData.value
|
||||||
|
}], [{
|
||||||
|
action: "updateAttrViewCell",
|
||||||
|
id: cellId,
|
||||||
|
rowID: rowId,
|
||||||
|
parentID: data.id,
|
||||||
|
data: {
|
||||||
|
[colData.type]: oldValue
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
if (colData.type === "select") {
|
||||||
|
menuElement.parentElement.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSelectHTML = (data: IAV, options: { cellElement: HTMLElement }) => {
|
||||||
|
const cellId = options.cellElement.dataset.id
|
||||||
|
const colId = options.cellElement.dataset["colId"]
|
||||||
|
const colData = data.columns.find(item => {
|
||||||
|
if (item.id === colId) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let selectedHTML = ""
|
||||||
|
data.rows.find(row => {
|
||||||
|
if (options.cellElement.parentElement.dataset.id === row.id) {
|
||||||
|
row.cells.find(cell => {
|
||||||
|
if (cell.id === cellId && cell.value) {
|
||||||
|
if (colData.type === "mSelect") {
|
||||||
|
cell.value.mSelect?.content.forEach((value: string) => {
|
||||||
|
let colorIndex = ""
|
||||||
|
colData.options.find(option => {
|
||||||
|
if (option.name === value) {
|
||||||
|
colorIndex = option.color
|
||||||
|
}
|
||||||
|
})
|
||||||
|
selectedHTML += `<div class="b3-chip" style="background-color:var(--b3-font-background${colorIndex});color:var(--b3-font-color${colorIndex})">${value}<svg class="b3-chip__close" data-type="remove-option"><use xlink:href="#iconCloseRound"></use></svg></div>`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
selectedHTML += `<div class="b3-chip" style="background-color:var(--b3-font-background${cell.value.select.color})";color:var(--b3-font-color${cell.value.select.color})>${options.cellElement.textContent.trim()}<svg class="b3-chip__close" data-type="remove-option"><use xlink:href="#iconCloseRound"></use></svg></div>`
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return `<div class="b3-chips">
|
||||||
|
${selectedHTML}
|
||||||
|
<input class="b3-text-field fn__block">
|
||||||
|
</div>
|
||||||
|
<div>${filterSelectHTML("", colData.options)}</div>`
|
||||||
|
}
|
||||||
133
app/src/protyle/render/av/sort.ts
Normal file
133
app/src/protyle/render/av/sort.ts
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
import {Menu} from "../../../plugin/Menu";
|
||||||
|
import {getColIconByType} from "./col";
|
||||||
|
import {transaction} from "../../wysiwyg/transaction";
|
||||||
|
import {setPosition} from "../../../util/setPosition";
|
||||||
|
|
||||||
|
export const addSort = (options: {
|
||||||
|
data: IAV,
|
||||||
|
rect: DOMRect,
|
||||||
|
menuElement: HTMLElement,
|
||||||
|
tabRect: DOMRect,
|
||||||
|
avId: string,
|
||||||
|
protyle: IProtyle
|
||||||
|
}) => {
|
||||||
|
const menu = new Menu("av-add-sort");
|
||||||
|
options.data.columns.forEach((column) => {
|
||||||
|
let hasSort = false;
|
||||||
|
options.data.sorts.find((sort) => {
|
||||||
|
if (sort.column === column.id) {
|
||||||
|
hasSort = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!hasSort) {
|
||||||
|
menu.addItem({
|
||||||
|
label: column.name,
|
||||||
|
icon: getColIconByType(column.type),
|
||||||
|
click: () => {
|
||||||
|
const oldSorts = Object.assign([], options.data.sorts);
|
||||||
|
options.data.sorts.push({
|
||||||
|
column: column.id,
|
||||||
|
order: "ASC",
|
||||||
|
});
|
||||||
|
transaction(options.protyle, [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: options.avId,
|
||||||
|
data: {
|
||||||
|
sorts: options.data.sorts
|
||||||
|
}
|
||||||
|
}], [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: options.avId,
|
||||||
|
data: {
|
||||||
|
sorts: oldSorts
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
options.menuElement.innerHTML = getSortsHTML(options.data);
|
||||||
|
bindSortsEvent(options.protyle, options.menuElement, options.data);
|
||||||
|
setPosition(options.menuElement, options.tabRect.right - options.menuElement.clientWidth, options.tabRect.bottom, options.tabRect.height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
menu.open({
|
||||||
|
x: options.rect.left,
|
||||||
|
y: options.rect.bottom,
|
||||||
|
h: options.rect.height,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bindSortsEvent = (protyle: IProtyle, menuElement: HTMLElement, data: IAV) => {
|
||||||
|
menuElement.querySelectorAll("select").forEach((item: HTMLSelectElement) => {
|
||||||
|
item.addEventListener("change", () => {
|
||||||
|
const colId = item.parentElement.getAttribute("data-id");
|
||||||
|
const oldSort = JSON.parse(JSON.stringify(data.sorts));
|
||||||
|
if (item.previousElementSibling.classList.contains("b3-menu__icon")) {
|
||||||
|
data.sorts.find((sort: IAVSort) => {
|
||||||
|
if (sort.column === colId) {
|
||||||
|
sort.column = item.value;
|
||||||
|
item.parentElement.setAttribute("data-id", item.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data.sorts.find((sort: IAVSort) => sort.column === colId).order = item.value as "ASC" | "DESC";
|
||||||
|
}
|
||||||
|
transaction(protyle, [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: data.id,
|
||||||
|
data: {
|
||||||
|
sorts: data.sorts
|
||||||
|
}
|
||||||
|
}], [{
|
||||||
|
action: "setAttrView",
|
||||||
|
id: data.id,
|
||||||
|
data: {
|
||||||
|
sorts: oldSort
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSortsHTML = (data: IAV) => {
|
||||||
|
let html = "";
|
||||||
|
const genSortItem = (id: string) => {
|
||||||
|
let sortHTML = "";
|
||||||
|
data.columns.forEach((item) => {
|
||||||
|
sortHTML += `<option value="${item.id}" ${item.id === id ? "selected" : ""}>${item.name}</option>`;
|
||||||
|
});
|
||||||
|
return sortHTML;
|
||||||
|
};
|
||||||
|
data.sorts.forEach((item: IAVSort) => {
|
||||||
|
html += `<button draggable="true" class="b3-menu__item" data-id="${item.column}">
|
||||||
|
<svg class="b3-menu__icon"><use xlink:href="#iconDrag"></use></svg>
|
||||||
|
<select class="b3-select" style="width: 106px;margin: 4px 0">
|
||||||
|
${genSortItem(item.column)}
|
||||||
|
</select>
|
||||||
|
<span class="fn__space"></span>
|
||||||
|
<select class="b3-select" style="width: 106px;margin: 4px 0">
|
||||||
|
<option value="ASC" ${item.order === "ASC" ? "selected" : ""}>${window.siyuan.languages.asc}</option>
|
||||||
|
<option value="DESC" ${item.order === "DESC" ? "selected" : ""}>${window.siyuan.languages.desc}</option>
|
||||||
|
</select>
|
||||||
|
<svg class="b3-menu__action" data-type="removeSort"><use xlink:href="#iconTrashcan"></use></svg>
|
||||||
|
</button>`;
|
||||||
|
});
|
||||||
|
return `<button class="b3-menu__item" data-type="nobg">
|
||||||
|
<span class="block__icon" style="padding: 8px;margin-left: -4px;" data-type="goConfig">
|
||||||
|
<svg><use xlink:href="#iconLeft"></use></svg>
|
||||||
|
</span>
|
||||||
|
<span class="b3-menu__label ft__center">${window.siyuan.languages.sort}</span>
|
||||||
|
<svg class="b3-menu__action" data-type="close" style="opacity: 1"><use xlink:href="#iconCloseRound"></use></svg>
|
||||||
|
</button>
|
||||||
|
<button class="b3-menu__separator"></button>
|
||||||
|
${html}
|
||||||
|
<button class="b3-menu__item${data.sorts.length === data.columns.length ? " fn__none" : ""}" data-type="addSort">
|
||||||
|
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
|
||||||
|
<span class="b3-menu__label">${window.siyuan.languages.new}</span>
|
||||||
|
</button>
|
||||||
|
<button class="b3-menu__item${html ? "" : " fn__none"}" data-type="removeSorts">
|
||||||
|
<svg class="b3-menu__icon"><use xlink:href="#iconTrashcan"></use></svg>
|
||||||
|
<span class="b3-menu__label">${window.siyuan.languages.delete}</span>
|
||||||
|
</button>`;
|
||||||
|
};
|
||||||
|
|
@ -355,12 +355,12 @@ export class WYSIWYG {
|
||||||
const avId = nodeElement.getAttribute("data-av-id");
|
const avId = nodeElement.getAttribute("data-av-id");
|
||||||
const dragElement = target.parentElement;
|
const dragElement = target.parentElement;
|
||||||
const oldWidth = dragElement.clientWidth;
|
const oldWidth = dragElement.clientWidth;
|
||||||
const dragIndex = dragElement.getAttribute("data-index");
|
const dragColId = dragElement.getAttribute("data-col-id");
|
||||||
let newWidth: string;
|
let newWidth: string;
|
||||||
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
|
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
|
||||||
newWidth = oldWidth + (moveEvent.clientX - event.clientX) + "px";
|
newWidth = oldWidth + (moveEvent.clientX - event.clientX) + "px";
|
||||||
dragElement.parentElement.parentElement.querySelectorAll(".av__row").forEach(item => {
|
dragElement.parentElement.parentElement.querySelectorAll(".av__row").forEach(item => {
|
||||||
(item.querySelector(`[data-index="${dragIndex}"]`) as HTMLElement).style.width = newWidth;
|
(item.querySelector(`[data-col-id="${dragColId}"]`) as HTMLElement).style.width = newWidth;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -372,12 +372,12 @@ export class WYSIWYG {
|
||||||
documentSelf.onselect = null;
|
documentSelf.onselect = null;
|
||||||
transaction(protyle, [{
|
transaction(protyle, [{
|
||||||
action: "setAttrViewColWidth",
|
action: "setAttrViewColWidth",
|
||||||
id: dragElement.getAttribute("data-id"),
|
id: dragColId,
|
||||||
parentID: avId,
|
parentID: avId,
|
||||||
data: newWidth
|
data: newWidth
|
||||||
}], [{
|
}], [{
|
||||||
action: "setAttrViewColWidth",
|
action: "setAttrViewColWidth",
|
||||||
id: dragElement.getAttribute("data-id"),
|
id: dragColId,
|
||||||
parentID: avId,
|
parentID: avId,
|
||||||
data: oldWidth + "px"
|
data: oldWidth + "px"
|
||||||
}]);
|
}]);
|
||||||
|
|
|
||||||
|
|
@ -651,7 +651,7 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, focus: b
|
||||||
updateRef(protyle, operation.id);
|
updateRef(protyle, operation.id);
|
||||||
} else if (operation.action === "append") {
|
} else if (operation.action === "append") {
|
||||||
reloadProtyle(protyle, false);
|
reloadProtyle(protyle, false);
|
||||||
} else if (["addAttrViewCol", "insertAttrViewBlock", "updateAttrViewCol", "updateAttrViewCell", "sortAttrViewRow",
|
} else if (["addAttrViewCol", "insertAttrViewBlock", "updateAttrViewCol", "updateAttrViewColOptions", "updateAttrViewCell", "sortAttrViewRow",
|
||||||
"sortAttrViewCol", "setAttrViewColHidden", "setAttrViewColWrap", "setAttrViewColWidth", "setAttrView"].includes(operation.action)) {
|
"sortAttrViewCol", "setAttrViewColHidden", "setAttrViewColWrap", "setAttrViewColWidth", "setAttrView"].includes(operation.action)) {
|
||||||
refreshAV(protyle, operation);
|
refreshAV(protyle, operation);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
app/src/types/index.d.ts
vendored
10
app/src/types/index.d.ts
vendored
|
|
@ -29,6 +29,9 @@ type TOperation =
|
||||||
| "setAttrViewColHidden"
|
| "setAttrViewColHidden"
|
||||||
| "setAttrViewColWrap"
|
| "setAttrViewColWrap"
|
||||||
| "setAttrViewColWidth"
|
| "setAttrViewColWidth"
|
||||||
|
| "updateAttrViewColOptions"
|
||||||
|
| "removeAttrViewColOption"
|
||||||
|
| "updateAttrViewColOption"
|
||||||
| "setAttrView"
|
| "setAttrView"
|
||||||
type TBazaarType = "templates" | "icons" | "widgets" | "themes" | "plugins"
|
type TBazaarType = "templates" | "icons" | "widgets" | "themes" | "plugins"
|
||||||
type TCardType = "doc" | "notebook" | "all"
|
type TCardType = "doc" | "notebook" | "all"
|
||||||
|
|
@ -869,6 +872,11 @@ interface IAVColumn {
|
||||||
wrap: boolean,
|
wrap: boolean,
|
||||||
hidden: boolean,
|
hidden: boolean,
|
||||||
type: TAVCol,
|
type: TAVCol,
|
||||||
|
// 选项列表
|
||||||
|
options?: {
|
||||||
|
name: string,
|
||||||
|
color: string,
|
||||||
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IAVRow {
|
interface IAVRow {
|
||||||
|
|
@ -888,7 +896,7 @@ interface IAVCell {
|
||||||
|
|
||||||
interface IAVCellValue {
|
interface IAVCellValue {
|
||||||
content?: any
|
content?: any
|
||||||
content2?: string
|
content2?: string // 用于日期
|
||||||
color?: string
|
color?: string
|
||||||
id?: string
|
id?: string
|
||||||
isNotEmpty?: boolean
|
isNotEmpty?: boolean
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
export const upDownHint = (listElement: Element, event: KeyboardEvent) => {
|
export const upDownHint = (listElement: Element, event: KeyboardEvent, classActiveName = "b3-list-item--focus") => {
|
||||||
let currentHintElement: HTMLElement = listElement.querySelector(".b3-list-item--focus");
|
let currentHintElement: HTMLElement = listElement.querySelector("." + classActiveName);
|
||||||
|
const className = classActiveName.split("--")[0];
|
||||||
if (event.key === "ArrowDown") {
|
if (event.key === "ArrowDown") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
currentHintElement.classList.remove("b3-list-item--focus");
|
currentHintElement.classList.remove(classActiveName);
|
||||||
if (!currentHintElement.nextElementSibling) {
|
if (!currentHintElement.nextElementSibling) {
|
||||||
listElement.children[0].classList.add("b3-list-item--focus");
|
listElement.children[0].classList.add(classActiveName);
|
||||||
} else {
|
} else {
|
||||||
if (currentHintElement.nextElementSibling.classList.contains("b3-list-item")) {
|
if (currentHintElement.nextElementSibling.classList.contains(className)) {
|
||||||
currentHintElement.nextElementSibling.classList.add("b3-list-item--focus");
|
currentHintElement.nextElementSibling.classList.add(classActiveName);
|
||||||
} else {
|
} else {
|
||||||
currentHintElement.nextElementSibling.nextElementSibling.classList.add("b3-list-item--focus");
|
currentHintElement.nextElementSibling.nextElementSibling.classList.add(classActiveName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentHintElement = listElement.querySelector(".b3-list-item--focus");
|
currentHintElement = listElement.querySelector("." + classActiveName);
|
||||||
if (listElement.scrollTop < currentHintElement.offsetTop - listElement.clientHeight + currentHintElement.clientHeight ||
|
if (listElement.scrollTop < currentHintElement.offsetTop - listElement.clientHeight + currentHintElement.clientHeight ||
|
||||||
listElement.scrollTop > currentHintElement.offsetTop) {
|
listElement.scrollTop > currentHintElement.offsetTop) {
|
||||||
currentHintElement.scrollIntoView(listElement.scrollTop > currentHintElement.offsetTop);
|
currentHintElement.scrollIntoView(listElement.scrollTop > currentHintElement.offsetTop);
|
||||||
|
|
@ -22,18 +23,18 @@ export const upDownHint = (listElement: Element, event: KeyboardEvent) => {
|
||||||
} else if (event.key === "ArrowUp") {
|
} else if (event.key === "ArrowUp") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
currentHintElement.classList.remove("b3-list-item--focus");
|
currentHintElement.classList.remove(classActiveName);
|
||||||
if (!currentHintElement.previousElementSibling) {
|
if (!currentHintElement.previousElementSibling) {
|
||||||
const length = listElement.children.length;
|
const length = listElement.children.length;
|
||||||
listElement.children[length - 1].classList.add("b3-list-item--focus");
|
listElement.children[length - 1].classList.add(classActiveName);
|
||||||
} else {
|
} else {
|
||||||
if (currentHintElement.previousElementSibling.classList.contains("b3-list-item")) {
|
if (currentHintElement.previousElementSibling.classList.contains(className)) {
|
||||||
currentHintElement.previousElementSibling.classList.add("b3-list-item--focus");
|
currentHintElement.previousElementSibling.classList.add(classActiveName);
|
||||||
} else {
|
} else {
|
||||||
currentHintElement.previousElementSibling.previousElementSibling.classList.add("b3-list-item--focus");
|
currentHintElement.previousElementSibling.previousElementSibling.classList.add(classActiveName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentHintElement = listElement.querySelector(".b3-list-item--focus");
|
currentHintElement = listElement.querySelector("." + classActiveName);
|
||||||
if (listElement.scrollTop < currentHintElement.offsetTop - listElement.clientHeight + currentHintElement.clientHeight ||
|
if (listElement.scrollTop < currentHintElement.offsetTop - listElement.clientHeight + currentHintElement.clientHeight ||
|
||||||
listElement.scrollTop > currentHintElement.offsetTop - currentHintElement.clientHeight * 2) {
|
listElement.scrollTop > currentHintElement.offsetTop - currentHintElement.clientHeight * 2) {
|
||||||
currentHintElement.scrollIntoView(listElement.scrollTop > currentHintElement.offsetTop - currentHintElement.clientHeight * 2);
|
currentHintElement.scrollIntoView(listElement.scrollTop > currentHintElement.offsetTop - currentHintElement.clientHeight * 2);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue