Skip to content

Commit

Permalink
Attribute view support sticky layout (#9617)
Browse files Browse the repository at this point in the history
* 🎨 Frozen the first col of the attribute view

* 🎨 Optimized the add row

* 🎨 Fixed the style of the first and last rows to misalign in attribute view

the height of the breadcrumbs caused the first and last rows to misalign in attribute view

* 🎨 Improve attribute view row gutter

* Update _av.scss
  • Loading branch information
Zuoqiu-Yingyi authored Nov 10, 2023
1 parent 4190c7b commit 433f4d5
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 80 deletions.
66 changes: 41 additions & 25 deletions app/src/assets/scss/business/_av.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@
opacity: 0;
display: flex;

&[draggable=true] {
cursor: grab;
}

svg {
height: 25px;
}
Expand All @@ -90,6 +94,10 @@
cursor: pointer;
}

&__body {
float: left;
}

&__row {
display: flex;
border-bottom: 1px solid var(--b3-theme-surface-lighter);
Expand All @@ -113,6 +121,10 @@
.av__gutters {
opacity: 1;
}

.av__firstcol svg {
opacity: 1;
}
}

&--select {
Expand All @@ -136,11 +148,23 @@
}
}

&--add {
.av__firstcol svg {
opacity: 1;
}

&:hover {
background-color: var(--b3-list-icon-hover);
}
}

&--header,
&--footer {
background-color: var(--b3-theme-background);
position: relative;
}

&--add,
&--footer {
display: flex;
border-top: 1px solid var(--b3-theme-surface-lighter);
Expand Down Expand Up @@ -184,27 +208,6 @@
}
}
}

&--add {
color: var(--b3-theme-on-surface);
padding: 5px 5px 5px 7px;
display: flex;
align-items: center;
transition: background 20ms ease-in 0s;
font-size: 87.5%;

svg {
height: 12px;
width: 12px;
color: var(--b3-theme-on-surface);
margin-right: 5px;
flex-shrink: 0;
}

&:hover {
background-color: var(--b3-list-icon-hover);
}
}
}

&__cell {
Expand Down Expand Up @@ -234,6 +237,13 @@
border-radius: var(--b3-border-radius);
}

&--add {
position: sticky;
left: 25px;
font-size: 87.5%;
padding: 0 0.5em;
}

.block__icon {
position: absolute;
right: 5px;
Expand Down Expand Up @@ -265,6 +275,7 @@

&__celltext {
overflow: hidden;
line-height: normal;

.b3-chip {
margin: 1px 2px;
Expand All @@ -283,6 +294,14 @@
}

&__firstcol {
background-color: var(--b3-theme-background);
border-right: 1px solid var(--b3-theme-surface-lighter);

position: sticky;
left: 0;
width: 24px;
z-index: 1;

svg {
color: var(--b3-theme-on-surface);
height: 33px;
Expand All @@ -292,10 +311,6 @@
box-sizing: border-box;
float: left;
}

&:hover svg {
opacity: 1;
}
}

&__widthdrag {
Expand Down Expand Up @@ -380,6 +395,7 @@
.protyle-wysiwyg--select,
.protyle-wysiwyg--hl {
.av__row--header,
.av__firstcol,
.av__row--footer {
background-color: transparent;
}
Expand Down
16 changes: 8 additions & 8 deletions app/src/protyle/render/av/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
if (event.shiftKey) {
const rowElement = hasClosestByClassName(event.target, "av__row");
if (rowElement && !rowElement.classList.contains("av__row--header")) {
selectRow(rowElement.querySelector(".av__firstcol"), "toggle");
selectRow(rowElement.querySelector(".av__check"), "toggle");
return true;
}
}
Expand Down Expand Up @@ -63,8 +63,8 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
return true;
}

const gutterElement = hasClosestByClassName(event.target, "ariaLabel");
if (gutterElement && gutterElement.parentElement.classList.contains("av__gutters")) {
const gutterElement = hasClosestByClassName(event.target, "av__gutter");
if (gutterElement) {
const rowElement = gutterElement.parentElement.parentElement;
if (gutterElement.dataset.action === "add") {
const avID = blockElement.getAttribute("data-av-id");
Expand Down Expand Up @@ -97,7 +97,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
return true;
}

const checkElement = hasClosestByClassName(event.target, "av__firstcol");
const checkElement = hasClosestByClassName(event.target, "av__check");
if (checkElement) {
window.siyuan.menus.menu.remove();
selectRow(checkElement, "toggle");
Expand Down Expand Up @@ -199,10 +199,10 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
if (cellElement && !cellElement.parentElement.classList.contains("av__row--header")) {
const type = cellElement.parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElement.getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol;
if (type === "updated" || type === "created" || (type === "block" && !cellElement.getAttribute("data-detached"))) {
selectRow(cellElement.parentElement.querySelector(".av__firstcol"), "toggle");
selectRow(cellElement.parentElement.querySelector(".av__check"), "toggle");
} else {
cellElement.parentElement.parentElement.querySelectorAll(".av__row--select").forEach(item => {
item.querySelector(".av__firstcol use").setAttribute("xlink:href", "#iconUncheck");
item.querySelector(".av__check use").setAttribute("xlink:href", "#iconUncheck");
item.classList.remove("av__row--select");
});
updateHeader(cellElement.parentElement);
Expand Down Expand Up @@ -259,7 +259,7 @@ export const avContextmenu = (protyle: IProtyle, rowElement: HTMLElement, positi
blockElement.querySelectorAll(".av__row--select").forEach(item => {
item.classList.remove("av__row--select");
});
blockElement.querySelectorAll(".av__firstcol use").forEach(item => {
blockElement.querySelectorAll(".av__check use").forEach(item => {
item.setAttribute("xlink:href", "#iconUncheck");
});
}
Expand All @@ -269,7 +269,7 @@ export const avContextmenu = (protyle: IProtyle, rowElement: HTMLElement, positi
return true;
}
rowElement.classList.add("av__row--select");
rowElement.querySelector(".av__firstcol use").setAttribute("xlink:href", "#iconCheck");
rowElement.querySelector(".av__check use").setAttribute("xlink:href", "#iconCheck");
const rowIds: string[] = [];
const blockIds: string[] = [];
const rowElements = blockElement.querySelectorAll(".av__row--select:not(.av__row--header)");
Expand Down
15 changes: 7 additions & 8 deletions app/src/protyle/render/av/keydown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const avKeydown = (event: KeyboardEvent, nodeElement: HTMLElement, protyl
if (selectCellElement) {
if (event.key === "Escape") {
selectCellElement.classList.remove("av__cell--select");
selectRow(selectCellElement.parentElement.querySelector(".av__firstcol"), "select");
selectRow(selectCellElement.parentElement.querySelector(".av__check"), "select");
event.preventDefault();
return true;
}
Expand Down Expand Up @@ -100,21 +100,21 @@ export const avKeydown = (event: KeyboardEvent, nodeElement: HTMLElement, protyl
}
if (event.key === "Escape") {
event.preventDefault();
selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll");
selectRow(selectRowElements[0].querySelector(".av__check"), "unselectAll");
return true;
}
if (event.key === "Enter") {
selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll");
selectRow(selectRowElements[0].querySelector(".av__check"), "unselectAll");
popTextCell(protyle, [selectRowElements[0].querySelector(".av__cell")]);
event.preventDefault();
return true;
}
// TODO event.shiftKey
if (event.key === "ArrowUp") {
const previousRowElement = selectRowElements[0].previousElementSibling;
selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll");
selectRow(selectRowElements[0].querySelector(".av__check"), "unselectAll");
if (previousRowElement && !previousRowElement.classList.contains("av__row--header")) {
selectRow(previousRowElement.querySelector(".av__firstcol"), "select");
selectRow(previousRowElement.querySelector(".av__check"), "select");
cellScrollIntoView(nodeElement, previousRowElement.getBoundingClientRect(), true);
} else {
nodeElement.classList.add("protyle-wysiwyg--select");
Expand All @@ -124,9 +124,9 @@ export const avKeydown = (event: KeyboardEvent, nodeElement: HTMLElement, protyl
}
if (event.key === "ArrowDown") {
const nextRowElement = selectRowElements[selectRowElements.length - 1].nextElementSibling;
selectRow(selectRowElements[0].querySelector(".av__firstcol"), "unselectAll");
selectRow(selectRowElements[0].querySelector(".av__check"), "unselectAll");
if (nextRowElement && !nextRowElement.classList.contains("av__row--add")) {
selectRow(nextRowElement.querySelector(".av__firstcol"), "select");
selectRow(nextRowElement.querySelector(".av__check"), "select");
cellScrollIntoView(nodeElement, nextRowElement.getBoundingClientRect(), true);
} else {
nodeElement.classList.add("protyle-wysiwyg--select");
Expand All @@ -137,4 +137,3 @@ export const avKeydown = (event: KeyboardEvent, nodeElement: HTMLElement, protyl
}
return false;
};

40 changes: 24 additions & 16 deletions app/src/protyle/render/av/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as dayjs from "dayjs";
import {unicode2Emoji} from "../../../emoji";
import {focusBlock} from "../../util/selection";
import {isMac} from "../../util/compatibility";
import {stickyScrollY} from "../../scroll/stickyScroll";

export const avRender = (element: Element, protyle: IProtyle, cb?: () => void) => {
let avElements: Element[] = [];
Expand Down Expand Up @@ -40,8 +41,6 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: () => void) =
e.firstElementChild.innerHTML = html;
}
const left = e.querySelector(".av__scroll")?.scrollLeft || 0;
const headerTransform = (e.querySelector(".av__row--header") as HTMLElement)?.style.transform;
const footerTransform = (e.querySelector(".av__row--footer") as HTMLElement)?.style.transform;
let selectCellId = "";
const selectCellElement = e.querySelector(".av__cell--select") as HTMLElement;
if (selectCellElement) {
Expand All @@ -52,7 +51,7 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: () => void) =
}, (response) => {
const data = response.data.view as IAVTable;
// header
let tableHTML = '<div class="av__row av__row--header"><div class="av__firstcol"><svg style="height: 32px"><use xlink:href="#iconUncheck"></use></svg></div>';
let tableHTML = '<div class="av__row av__row--header"><div class="av__firstcol av__check"><svg style="height: 32px"><use xlink:href="#iconUncheck"></use></svg></div>';
let calcHTML = "";
data.columns.forEach((column: IAVColumn) => {
if (column.hidden) {
Expand Down Expand Up @@ -80,10 +79,10 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '<svg><use x
data.rows.forEach((row: IAVRow) => {
tableHTML += `<div class="av__row" data-id="${row.id}">
<div class="av__gutters">
<button class="ariaLabel" data-action="add" data-position="right" aria-label="${isMac() ? window.siyuan.languages.addBelowAbove : window.siyuan.languages.addBelowAbove.replace("⌥", "Alt+")}"><svg><use xlink:href="#iconAdd"></use></svg></button>
<button class="ariaLabel" draggable="true" data-position="right" aria-label="${window.siyuan.languages.rowTip}"><svg><use xlink:href="#iconDrag"></use></svg></button>
<button class="av__gutter ariaLabel" data-action="add" data-position="right" aria-label="${isMac() ? window.siyuan.languages.addBelowAbove : window.siyuan.languages.addBelowAbove.replace("⌥", "Alt+")}"><svg><use xlink:href="#iconAdd"></use></svg></button>
<button class="av__gutter ariaLabel" draggable="true" data-position="right" aria-label="${window.siyuan.languages.rowTip}"><svg><use xlink:href="#iconDrag"></use></svg></button>
</div>
<div class="av__firstcol"><svg><use xlink:href="#iconUncheck"></use></svg></div>`;
<div class="av__firstcol av__check"><svg><use xlink:href="#iconUncheck"></use></svg></div>`;
row.cells.forEach((cell, index) => {
if (data.columns[index].hidden) {
return;
Expand Down Expand Up @@ -150,7 +149,7 @@ style="width: ${column.width || "200px"}">${getCalcValue(column) || '<svg><use x
}
if (["text", "template", "url", "email", "phone", "number", "date", "created", "updated"].includes(cell.valueType)) {
if (cell.value && cell.value[cell.valueType as "url"].content) {
text += `<span ${cell.valueType !== "number" ? "" : 'style="right:auto;left:5px"'} data-type="copy" class="b3-tooltips b3-tooltips__n block__icon" aria-label="${window.siyuan.languages.copy}"><svg><use xlink:href="#iconCopy"></use></svg></span>`;
text += `<span ${cell.valueType !== "number" ? "" : 'style="right:auto; left:5px;"'} data-type="copy" class="b3-tooltips b3-tooltips__n block__icon" aria-label="${window.siyuan.languages.copy}"><svg><use xlink:href="#iconCopy"></use></svg></span>`;
}
}
tableHTML += `<div class="av__cell" data-id="${cell.id}" data-col-id="${data.columns[index].id}"
Expand Down Expand Up @@ -196,13 +195,17 @@ ${cell.color ? `color:${cell.color};` : ""}">${text}</div>`;
<div class="av__counter fn__none"></div>
</div>
<div class="av__scroll">
<div style="float: left;">
<div class="av__body">
${tableHTML}
<div class="av__row--add">
<svg><use xlink:href="#iconAdd"></use></svg>
${window.siyuan.languages.addAttr}
<div class="av__firstcol">
<svg><use xlink:href="#iconAdd"></use></svg>
</div>
<div class="av__cell--add">
${window.siyuan.languages.addAttr}
</div>
</div>
<div class="av__row--footer"><div style="width: 24px"></div>${calcHTML}</div>
<div class="av__row--footer"><div class="av__firstcol"></div>${calcHTML}</div>
</div>
</div>
</div>`;
Expand All @@ -212,11 +215,16 @@ ${cell.color ? `color:${cell.color};` : ""}">${text}</div>`;
if (left) {
e.querySelector(".av__scroll").scrollLeft = left;
}
if (headerTransform) {
(e.querySelector(".av__row--header") as HTMLElement).style.transform = headerTransform;
}
if (footerTransform) {
(e.querySelector(".av__row--footer") as HTMLElement).style.transform = footerTransform;
const bodyElement = e.querySelector<HTMLElement>(".av__body");
if (bodyElement) {
const headerElement = bodyElement.querySelector<HTMLElement>(".av__row--header");
const footerElement = bodyElement.querySelector<HTMLElement>(".av__row--footer");
stickyScrollY(
protyle.contentElement,
bodyElement as HTMLElement,
headerElement ? [{element: headerElement}] : [],
footerElement ? [{element: footerElement}] : [],
);
}
if (selectCellId) {
const newCellElement = e.querySelector(`.av__row[data-id="${selectCellId.split(Constants.ZWSP)[0]}"] .av__cell[data-col-id="${selectCellId.split(Constants.ZWSP)[1]}"]`);
Expand Down
4 changes: 2 additions & 2 deletions app/src/protyle/render/av/row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ export const selectRow = (checkElement: Element, type: "toggle" | "select" | "un
const useElement = checkElement.querySelector("use");
if (rowElement.classList.contains("av__row--header") || type === "unselectAll") {
if ("#iconCheck" === useElement.getAttribute("xlink:href")) {
rowElement.parentElement.querySelectorAll(".av__firstcol").forEach(item => {
rowElement.parentElement.querySelectorAll(".av__check").forEach(item => {
item.querySelector("use").setAttribute("xlink:href", "#iconUncheck");
item.parentElement.classList.remove("av__row--select");
});
} else {
rowElement.parentElement.querySelectorAll(".av__firstcol").forEach(item => {
rowElement.parentElement.querySelectorAll(".av__check").forEach(item => {
item.querySelector("use").setAttribute("xlink:href", "#iconCheck");
item.parentElement.classList.add("av__row--select");
});
Expand Down
Loading

0 comments on commit 433f4d5

Please sign in to comment.