diff --git a/packages/grid/src/styles/vaadin-grid-base-styles.js b/packages/grid/src/styles/vaadin-grid-base-styles.js index b117a86793..8515c1dfc2 100644 --- a/packages/grid/src/styles/vaadin-grid-base-styles.js +++ b/packages/grid/src/styles/vaadin-grid-base-styles.js @@ -22,8 +22,8 @@ export const gridStyles = css` cursor: default; --_border-color: var(--vaadin-grid-border-color, var(--vaadin-border-color-secondary)); --_border-width: 0; - --_row-border-width: var(--vaadin-grid-row-border-width, 1px); - --_column-border-width: var(--vaadin-grid-column-border-width, 0px); + --_row-border-width: var(--vaadin-grid-row-border-width, 4px); + --_column-border-width: var(--vaadin-grid-column-border-width, 4px); border-radius: var(--_border-radius); --_border-radius: 0; } @@ -95,13 +95,6 @@ export const gridStyles = css` position: sticky; left: 0; width: 100%; - } - - :host([overflow~='top']) #header, - :host([overflow~='bottom']) #footer, - :host([navigating]) #header:has(tr:last-child:focus-within), - :host([navigating]) #footer:has(tr:first-child:focus-within), - [empty-state] #header { z-index: 2; } @@ -154,32 +147,37 @@ export const gridStyles = css` height: 100%; } + [part~='row'] { + background: var(--vaadin-grid-row-background-color, var(--vaadin-background-color)); + } + [part~='cell'] { padding: 0; box-sizing: border-box; - background: var(--vaadin-grid-cell-background, var(--vaadin-background-color)); + background-color: inherit; + background-repeat: no-repeat; + background-origin: padding-box; + background-image: linear-gradient(var(--vaadin-grid-cell-background-color, transparent)); + border: var(--_row-border-width) var(--_border-color); + } + + #header::before, + #footer::before { + position: absolute; + inset: 0; border-block: var(--_row-border-width) solid var(--_border-color); - margin-top: calc(var(--_row-border-width) * -1); - - /* - Box-shadows are used to create a "fake" border at the end of the cell/row, which is visible when a row/cell ends - before the edge of the grid viewport, as well as frozen columns and rows (header and footer). - If there are frozen columns, we'll make the "fake box-shadow border" on the header and footer opaque by rendering - both the border color and cell background color, so that a semi-transparent border color doesn't "stack" when - scrolling horizontally. - */ - --_fake-border: - 0 calc(var(--_top, 0) * var(--_row-border-width) * -1) 0 0 var(--_border-color), - 0 calc(var(--_top-opaque, 0) * var(--_row-border-width) * -1) 0 0 - var(--vaadin-grid-cell-background-color, var(--vaadin-background-color)), - 0 calc(var(--_bottom, 0) * var(--_row-border-width)) 0 0 var(--_border-color), - 0 calc(var(--_bottom-opaque, 0) * var(--_row-border-width)) 0 0 - var(--vaadin-grid-cell-background-color, var(--vaadin-background-color)), - calc(var(--_start, 0) * var(--_column-border-width) * -1) 0 0 0 var(--_border-color), - calc(var(--_end, 0) * var(--_column-border-width)) 0 0 0 var(--_border-color), - calc(var(--_end-opaque, 0) * var(--_column-border-width)) 0 0 0 - var(--vaadin-grid-cell-background-color, var(--vaadin-background-color)); - box-shadow: var(--_fake-border); + transform: translateX(var(--_grid-horizontal-scroll-position)); + } + + :host([overflow~='top']) #table[has-header] #header::before { + content: ''; + inset-block-end: calc(var(--_row-border-width) * -1); + } + + :host([overflow~='bottom']) #table[has-footer] #footer::before, + :host(:not([overflow~='top'])) #table[has-footer] #footer::before { + content: ''; + inset-block-start: calc(var(--_row-border-width) * -1); } [part~='cell']:where(:not([part~='details-cell'])) { @@ -190,87 +188,51 @@ export const gridStyles = css` position: relative; align-items: center; white-space: nowrap; - border-inline-start: var(--_column-border-width) solid var(--_border-color); } - [part~='first-column-cell'] { - border-inline-start: 0; + [part~='body-cell'] { + background-image: + linear-gradient(var(--_hover-background-color, transparent)), + linear-gradient(var(--_cell-selected-background-color, transparent)), + linear-gradient(var(--vaadin-grid-row-highlight-background-color, transparent)), + linear-gradient(var(--vaadin-grid-cell-background-color, transparent)); } - [part~='first-header-row-cell'], - [part~='first-footer-row-cell'], - [part~='first-row-cell'] { - margin-top: 0; - border-top: 0; + #table[has-header] [part~='first-row-cell'] { + border-block-start-style: solid; } - table[has-header] [part~='first-row-cell'] { - border-top: var(--_row-border-width) solid var(--_border-color); + #table[has-footer] [part~='last-row-cell'], + #table[has-footer] [part~='last-row'] [part~='details-cell'], + :host(:not([overflow~='bottom']):not([overflow~='top'])) [part~='last-row-cell'], + :host(:not([overflow~='bottom']):not([overflow~='top'])) [part~='last-row'] [part~='details-cell'] { + border-block-end-style: solid; } - [part~='last-column-cell'] { - --_end: 1; + [part~='header-cell']:not([part~='first-header-row-cell']), + [part~='footer-cell']:not([part~='first-footer-row-cell']), + [part~='body-cell']:not([part~='first-row-cell']) { + border-block-start-style: solid; } - [part~='last-column-cell']:is([part~='header-cell'], [part~='footer-cell']) { - --_end-opaque: 1; + [part~='details-opened-row-cell'] { + border-block-end-style: solid; } - [part~='last-row-cell']:where(:not([part~='details-opened-row-cell'])), - [part~='last-footer-row-cell'] { - border-bottom: 0; - --_bottom: 1; + [part~='header-cell']:not([part~='first-column-cell']), + [part~='footer-cell']:not([part~='first-column-cell']), + [part~='body-cell']:not([part~='first-column-cell']) { + border-inline-start-style: solid; } [part~='last-frozen-cell'] { - --_end: 1; - } - - [part~='last-frozen-cell'] + [part~='cell'] { - border-inline-start-color: transparent; - } - - [part~='first-frozen-to-end-cell'] { - border-inline-start: 0; - --_start: 1; - } - - [part~='last-header-row-cell'] { - border-bottom: 0; - } - - :host([overflow~='top']) [part~='last-header-row-cell'], - [empty-state] [part~='last-header-row-cell'] { - --_bottom: 1; - } - - #header:has(:is([frozen], [frozen-to-end])) [part~='last-header-row-cell'] { - --_bottom-opaque: 1; - } - - :host([overflow~='bottom']) [part~='first-footer-row-cell'], - [empty-state] [part~='first-footer-row-cell'] { - --_top: 1; - } - - #footer:has(:is([frozen], [frozen-to-end])) [part~='first-footer-row-cell'] { - --_top-opaque: 1; - } - - table[has-footer] [part~='last-row-cell']:not([part~='details-opened-row-cell']) { - border-bottom: var(--_row-border-width) solid var(--_border-color); - --_bottom: 0; - } + &:not([part~='last-column-cell']) { + border-inline-end-style: solid; + } - [part~='body-cell']:where(:not([part~='details-cell'])) { - --_highlight-background-color: var(--vaadin-grid-row-highlight-background-color, transparent); - --_highlight-background-image: linear-gradient( - var(--_highlight-background-color), - var(--_highlight-background-color) - ); - background: - var(--_hover-background-image, none), var(--_selected-background-image, none), var(--_highlight-background-image), - var(--vaadin-grid-cell-background-color, var(--vaadin-background-color)); + &:not([part~='last-column-cell']) + [part~='cell'] { + border-inline-start-style: none; + } } /* Variant: wrap cell contents */ @@ -280,10 +242,6 @@ export const gridStyles = css` } /* Variant: row stripes */ - [part~='odd-row'] { - --vaadin-grid-cell-background-color: var(--vaadin-grid-row-odd-background-color, var(--vaadin-background-color)); - } - :host([theme~='row-stripes']) [part~='odd-row'] { --vaadin-grid-cell-background-color: var( --vaadin-grid-row-odd-background-color, @@ -292,30 +250,19 @@ export const gridStyles = css` } /* Raise highlighted rows above others */ - - [part~='row']:focus, - [part~='row']:focus-within, - [part~='body-row']:where([selected]) { - z-index: 3; - } - - @container style(--vaadin-grid-row-odd-background-color) { - [part~='odd-row'] { - z-index: 1; + [part~='row'], + [part~='frozen-cell'], + [part~='frozen-to-end-cell'] { + &:focus, + &:focus-within { + z-index: 3; } } /* Row hover */ @media (any-hover: hover) { - @container style(--vaadin-grid-row-hover-background-color) { - [part~='body-row']:hover { - z-index: 2; - } - } - - [part~='body-row']:hover [part~='cell']:where(:not([part~='details-cell'])) { + [part~='body-row']:hover [part~='body-cell'] { --_hover-background-color: var(--vaadin-grid-row-hover-background-color, transparent); - --_hover-background-image: linear-gradient(var(--_hover-background-color), var(--_hover-background-color)); } } @@ -323,12 +270,6 @@ export const gridStyles = css` position: absolute; bottom: 0; width: 100%; - margin-top: 0; - border-top: 0; - } - - [part~='last-row-cell'] + [part~='details-cell'] { - border-bottom: 0; } [part~='cell'] ::slotted(vaadin-grid-cell-content) { @@ -348,12 +289,11 @@ export const gridStyles = css` } /* Selected row */ - [part~='body-row'][selected] { - --_selected-background-color: var( + [part~='selected-row-cell'] { + --_cell-selected-background-color: var( --vaadin-grid-row-selected-background-color, color-mix(in srgb, currentColor 8%, transparent) ); - --_selected-background-image: linear-gradient(var(--_selected-background-color), var(--_selected-background-color)); } /* Empty state */ @@ -368,7 +308,6 @@ export const gridStyles = css` inset: 0; flex: 1; overflow: hidden; - margin-top: calc(var(--_row-border-width) * -1); } #emptystaterow { @@ -381,8 +320,16 @@ export const gridStyles = css` flex: 1; overflow: auto; padding: var(--vaadin-grid-cell-padding, var(--vaadin-padding-container)); - border-top: var(--_row-border-width) solid transparent; outline: none; + border-block: var(--_row-border-width) var(--_border-color); + } + + #table[has-header] #emptystatecell { + border-block-start-style: solid; + } + + #table[has-footer] #emptystatecell { + border-block-end-style: solid; } #emptystatecell:focus-visible { @@ -508,13 +455,21 @@ export const gridStyles = css` /* Focus outline element, also used for d'n'd indication */ :is([part~='row'], [part~='cell'])::after { position: absolute; - inset: calc(var(--_row-border-width) * -1) calc(var(--_column-border-width) * -1); z-index: 3; + inset: 0; pointer-events: none; outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color); outline-offset: calc(var(--vaadin-focus-ring-width) * -1); } + [part~='cell']::after { + inset-block: calc(var(--_row-border-width) * -1); + } + + [part~='cell']:where(:not([part~='details-cell']))::after { + inset-inline: calc(var(--_column-border-width) * -1); + } + [part~='first-column-cell']::after { inset-inline-start: 0; } @@ -523,26 +478,31 @@ export const gridStyles = css` inset-inline-end: 0; } - #header [part~='row']:first-child::after, + [part~='row']::after { + inset: 0 0 calc(var(--_row-border-width) * -1); + transform: translateX(var(--_grid-horizontal-scroll-position)); + } + [part~='first-header-row-cell']::after, - [part*='first-row']::after { - top: 0; + #table:not([has-header]) [part~='first-row-cell']::after { + inset-block-start: 0; } - table[has-header] [part~='first-row-cell']::after { - top: calc(var(--_row-border-width) * -1); + [part~='first-footer-row']::after { + inset-block-start: calc(var(--_row-border-width) * -1); } - #footer [part~='row']:last-child::after, - [part~='last-footer-row-cell']::after, [part~='last-row']::after, - [part~='last-row-cell']::after { - bottom: 0; + [part~='last-footer-row']::after, + [part~='last-footer-row-cell']::after { + inset-block-end: 0; } - #header [part~='row']:last-child::after, - table[has-footer] [part*='last-row']::after { - bottom: calc(var(--_row-border-width) * -1); + :host([overflow~='top']) #table:not([has-footer]) { + [part~='last-row'] [part~='details-cell']::after, + [part~='last-row']:not([part~='details-opened-row']) [part~='last-row-cell']::after { + inset-block-end: 0; + } } :host([navigating]) [part~='row']:focus, @@ -550,12 +510,6 @@ export const gridStyles = css` outline: 0; } - [part~='row']::after { - transform: translateX(var(--_grid-horizontal-scroll-position)); - inset-inline: 0; - bottom: 0; - } - [part~='row']:focus-visible, [part~='cell']:focus-visible { outline: 0; @@ -603,8 +557,8 @@ export const gridStyles = css` } [part~='row'][dragstart] [part~='cell'] { - border-block-color: transparent !important; - box-shadow: none; + border-block: none !important; + padding-block: var(--_row-border-width) !important; } [part~='row'][dragstart] [part~='cell'][last-column] { @@ -661,7 +615,7 @@ export const gridStyles = css` content: '-'; } - @media (forced-colors: active) { + /* @media (forced-colors: active) { :host([overflow~='top']) [part~='last-header-row-cell'] { border-bottom: var(--_row-border-width) solid; margin-bottom: calc(var(--_row-border-width) * -1); @@ -671,5 +625,5 @@ export const gridStyles = css` border-top: var(--_row-border-width) solid; margin-top: calc(var(--_row-border-width) * -1); } - } + } */ `; diff --git a/packages/grid/src/vaadin-grid-drag-and-drop-mixin.js b/packages/grid/src/vaadin-grid-drag-and-drop-mixin.js index 119abc762f..e2e94f6c56 100644 --- a/packages/grid/src/vaadin-grid-drag-and-drop-mixin.js +++ b/packages/grid/src/vaadin-grid-drag-and-drop-mixin.js @@ -248,7 +248,7 @@ export const DragAndDropMixin = (superClass) => let row = e.composedPath().find((node) => node.localName === 'tr'); // Update the horizontal scroll position property of the row being dragged over - this.__updateRowScrollPositionProperty(row); + // this.__updateRowScrollPositionProperty(row); if (!this._flatSize || this.dropMode === DropMode.ON_GRID) { // The grid is empty or "on-grid" drop mode was used, always default to "empty" diff --git a/packages/grid/src/vaadin-grid-mixin.js b/packages/grid/src/vaadin-grid-mixin.js index 5580b1597e..e986a82dfd 100644 --- a/packages/grid/src/vaadin-grid-mixin.js +++ b/packages/grid/src/vaadin-grid-mixin.js @@ -710,13 +710,13 @@ export const GridMixin = (superClass) => while (this.$.header.children.length < columnTree.length) { const headerRow = document.createElement('tr'); - headerRow.setAttribute('part', 'row'); + headerRow.setAttribute('part', 'row header-row'); headerRow.setAttribute('role', 'row'); headerRow.setAttribute('tabindex', '-1'); this.$.header.appendChild(headerRow); const footerRow = document.createElement('tr'); - footerRow.setAttribute('part', 'row'); + footerRow.setAttribute('part', 'row footer-row'); footerRow.setAttribute('role', 'row'); footerRow.setAttribute('tabindex', '-1'); this.$.footer.appendChild(footerRow); @@ -729,17 +729,19 @@ export const GridMixin = (superClass) => iterateChildren(this.$.header, (headerRow, index, rows) => { this.__initRow(headerRow, columnTree[index], 'header', index === columnTree.length - 1); - const cells = getBodyRowCells(headerRow); - updateCellsPart(cells, 'first-header-row-cell', index === 0); - updateCellsPart(cells, 'last-header-row-cell', index === rows.length - 1); + updateBooleanRowStates(headerRow, { + 'first-header': index === 0, + 'last-header': index === rows.length - 1, + }); }); iterateChildren(this.$.footer, (footerRow, index, rows) => { this.__initRow(footerRow, columnTree[columnTree.length - 1 - index], 'footer', index === 0); - const cells = getBodyRowCells(footerRow); - updateCellsPart(cells, 'first-footer-row-cell', index === 0); - updateCellsPart(cells, 'last-footer-row-cell', index === rows.length - 1); + updateBooleanRowStates(footerRow, { + 'first-footer': index === 0, + 'last-footer': index === rows.length - 1, + }); }); // Sizer rows diff --git a/packages/grid/src/vaadin-grid-scroll-mixin.js b/packages/grid/src/vaadin-grid-scroll-mixin.js index 5ac22f0444..4f90d24236 100644 --- a/packages/grid/src/vaadin-grid-scroll-mixin.js +++ b/packages/grid/src/vaadin-grid-scroll-mixin.js @@ -445,26 +445,7 @@ export const ScrollMixin = (superClass) => } }); - const focusedRow = this.shadowRoot.querySelector("[part~='row']:focus"); - if (focusedRow) { - // Update the horizontal scroll position property of the focused row - this.__updateRowScrollPositionProperty(focusedRow); - } - } - - /** - * Synchronizes the internal `--_grid-horizontal-scroll-position` CSS property - * of the given row with the current horizontal scroll position of the grid. - * @private - */ - __updateRowScrollPositionProperty(row) { - if (row instanceof HTMLTableRowElement === false) { - return; - } - const newValue = `${this.__horizontalScrollPosition}px`; - if (row.style.getPropertyValue('--_grid-horizontal-scroll-position') !== newValue) { - row.style.setProperty('--_grid-horizontal-scroll-position', newValue); - } + this.$.table.style.setProperty('--_grid-horizontal-scroll-position', `${this.__horizontalScrollPosition}px`); } /** @private */