diff --git a/packages/design-system/src/elements/toniq-list-table/toniq-list-table.element.ts b/packages/design-system/src/elements/toniq-list-table/toniq-list-table.element.ts index c066913..ddc8d04 100644 --- a/packages/design-system/src/elements/toniq-list-table/toniq-list-table.element.ts +++ b/packages/design-system/src/elements/toniq-list-table/toniq-list-table.element.ts @@ -18,6 +18,7 @@ import { keyed, listen, nothing, + onDomCreated, onResize, perInstance, renderIf, @@ -235,6 +236,11 @@ export const ToniqListTable = defineToniqElement()({ position: relative; background: ${toniqColors.pageInteraction.backgroundColor}; cursor: pointer; + gap: ${cssVars['toniq-list-table-row-gap'].value}; + } + + .row-wrapper:not(:last-of-type):after { + border: ${cssVars['toniq-list-table-border-width'].value} solid transparent; } .row-wrapper:not(:first-of-type) { @@ -247,22 +253,13 @@ export const ToniqListTable = defineToniqElement()({ align-items: start; } - .row-wrapper:not(:first-of-type):hover:before { - content: ''; - position: absolute; - top: 0; - height: 2px; - width: 100%; - background-color: ${toniqColors.dropShadow.backgroundColor}; + .row-wrapper:not(:first-of-type):hover { + border-bottom-color: ${toniqColors.dropShadow.backgroundColor}; + border-top-color: ${toniqColors.dropShadow.backgroundColor}; } - .row-wrapper:not(:first-of-type):hover:after { - content: ''; - position: absolute; - bottom: 0; - height: 2px; - width: 100%; - background-color: ${toniqColors.dropShadow.backgroundColor}; + .row-wrapper:not(:first-of-type):hover + .row-wrapper { + border-top-color: transparent; } .row-wrapper:not(:first-of-type):hover .row-item, @@ -270,7 +267,7 @@ export const ToniqListTable = defineToniqElement()({ border-top-color: transparent; } - .row-wrapper:not(:first-of-type) .row-item { + .row-wrapper:not(:first-of-type) { border-top-color: ${toniqColors.dividerFaint.foregroundColor}; } @@ -294,27 +291,40 @@ export const ToniqListTable = defineToniqElement()({ text-wrap: nowrap; } - .row-item { + .row-wrapper { border: ${cssVars['toniq-list-table-border-width'].value} solid transparent; } .row-item.sticky { position: sticky; - filter: drop-shadow(rgba(0, 0, 0, 0.12) 4px 1px 3px); - will-change: filter; z-index: 2; } - .row-wrapper:last-child .row-item { - border-bottom-color: ${toniqColors.dividerFaint.foregroundColor}; + .row-wrapper .row-item.sticky:has(~ .row-item.sticky) { + margin-right: -36px; + padding-right: 36px; + } + + .row-wrapper .row-item.sticky:not(:has(~ .row-item.sticky)) { + padding-right: 8px; } - .row-item:not(:first-child) .row-content { - padding-left: calc(${cssVars['toniq-list-table-row-gap'].value} / 2); + .row-wrapper .row-item.sticky:not(:has(~ .row-item.sticky)):after { + content: ''; + position: absolute; + right: -10px; + bottom: 0; + height: 100%; + width: 10px; + background-image: linear-gradient( + to right, + ${toniqColors.dividerFaint.foregroundColor}, + transparent + ); } - .row-item:not(:last-child) .row-content { - padding-right: calc(${cssVars['toniq-list-table-row-gap'].value} / 2); + .row-wrapper:last-child { + border-bottom-color: ${toniqColors.dividerFaint.foregroundColor}; } .row-item:last-of-type, @@ -371,6 +381,7 @@ export const ToniqListTable = defineToniqElement()({ }, isLoading: false, tableListLeft: 0, + firstResize: true, }, initCallback({inputs, state, updateState}) { const enabledColumns = inputs.columns.filter((column) => !column.disabled); @@ -417,6 +428,9 @@ export const ToniqListTable = defineToniqElement()({ 'blocked-pagination': !!inputs.showLoading, })} ${listen(ToniqPagination.events.pageChange, (event) => { + if (!inputs.nonBlocking) { + updateRowItems(); + } dispatch(new events.pageChange(event.detail)); })} > @@ -424,6 +438,57 @@ export const ToniqListTable = defineToniqElement()({ : nothing; function listItem(row: ListTableRow, rowIndex: number) { + const rowItemsTemplate = repeat( + enabledColumns, + (item, index) => index, + (item, index) => { + const itemKey = item.key as keyof typeof row; + const contents = row.cells[itemKey]; + + const rowItemLeftStyle = css` + left: ${unsafeCSS(`${state.rowStyles[itemKey]?.left}px`)}; + `; + + const rowItemMinWidthStyle = css` + min-width: ${index >= enabledColumns.length - 1 + ? unsafeCSS('unset') + : unsafeCSS(`${state.rowStyles[itemKey]?.width}px`)}; + `; + + return html` +
+
+ ${renderIf( + rowIndex === 0, + html` + + ${item.title} + + `, + contents, + )} +
+
+ `; + }, + ); + return html`
()({ }) : nothing} > - ${repeat( - enabledColumns, - (item, index) => index, - (item, index) => { - const itemKey = item.key as keyof typeof row; - const contents = row.cells[itemKey]; - - const rowItemLeftStyle = css` - left: ${unsafeCSS(`${state.rowStyles[itemKey]?.left}px`)}; - `; - - const rowItemMinWidthStyle = css` - min-width: ${index >= enabledColumns.length - 1 - ? unsafeCSS('unset') - : unsafeCSS(`${state.rowStyles[itemKey]?.width}px`)}; - `; - - const rowItemMaxWidthStyle = css` - max-width: ${index >= enabledColumns.length - 1 - ? unsafeCSS('unset') - : unsafeCSS(`${state.rowStyles[itemKey]?.width}px`)}; - `; - - return html` -
-
- ${renderIf( - rowIndex === 0, - html` - - ${item.title} - - `, - html` - ${contents} - `, - )} -
-
- `; - }, - )} + ${rowItemsTemplate}
`; } const isLoading = (inputs.nonBlocking ? false : state.isLoading) || !!inputs.showLoading; + + function updateRowItems() { + updateState({ + rowStyles: enabledColumns.reduce((accum, item) => { + accum[item.key as string] = { + width: undefined, + left: undefined, + }; + return accum; + }, state.rowStyles), + }); + + enabledColumns.forEach((column) => { + const columnKey = column.key as string; + + const rowItems = host.shadowRoot + .querySelector('.table-list') + ?.querySelectorAll(`.row-item[data-column="${columnKey}"]`); + + if (rowItems) { + rowItems.forEach((rowItem) => { + const left = rowItem.getBoundingClientRect().left; + const currentWidth = getElementWidthWithMarginPadding( + rowItem.querySelector('.row-content') as HTMLElement, + ).width; + + const prevWidth = state.rowStyles[columnKey]?.width; + + if ((prevWidth && currentWidth > prevWidth) || !prevWidth) { + updateState({ + rowStyles: { + ...state.rowStyles, + [columnKey]: { + width: currentWidth, + left: state.tableListLeft + ? left - state.tableListLeft + : left, + }, + }, + }); + } + }); + } + }); + + updateState({ + isLoading: false, + }); + } + return html`
()({ 'can-scroll': state.canScroll, })} > - ${keyed( - inputs.pagination?.currentPage, - html` -
{ - tableUpdate(event.target); - - updateState({ - rowStyles: enabledColumns.reduce((accum, item) => { - accum[item.key as string] = { - width: undefined, - left: undefined, - }; - return accum; - }, state.rowStyles), - }); - - setTimeout(() => { - enabledColumns.forEach((column) => { - const columnKey = column.key as string; - - const rowItems = host.shadowRoot - .querySelector('.table-list') - ?.querySelectorAll( - `.row-item[data-column="${columnKey}"]`, - ); - - if (rowItems) { - rowItems.forEach((rowItem) => { - const left = rowItem.getBoundingClientRect().left; - const currentWidth = - getElementWidthWithMarginPadding( - rowItem.querySelector( - '.row-content', - ) as HTMLElement, - ).width; - if ( - !state.rowStyles[columnKey]?.width || - currentWidth > - (state.rowStyles[columnKey] - ?.width as number) - ) { - updateState({ - rowStyles: { - ...state.rowStyles, - [columnKey]: { - width: currentWidth, - left: state.tableListLeft - ? left - state.tableListLeft - : left, - }, - }, - }); - } - }); - } - }); - - updateState({ - isLoading: false, - }); - }, 0); - })} - ${listen('scroll', (event) => { - tableUpdate(event.target); - })} - ${listen('keydown', (event) => { - if (inputs.showLoading) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - })} - > - ${repeat( - rows, - (item, index) => index, - (item: ListTableRow, index: number) => { - return listItem(item, index); - }, - )} - ${renderIf( - state.canScroll, - html` -
- <${ToniqIcon.assign({ - icon: ChevronsRight16Icon, - })}> -
- `, - )} -
- `, - )} - +
{ + tableUpdate(event); + updateRowItems(); + })} + ${onResize((event) => { + tableUpdate(event.target); + if (state.firstResize) { + updateRowItems(); + updateState({ + firstResize: false, + }); + } + requestAnimationFrame(() => updateRowItems()); + })} + ${listen('scroll', (event) => { + tableUpdate(event.target); + })} + ${listen('keydown', (event) => { + if (inputs.showLoading) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + })} + > + ${keyed( + rows, + repeat( + rows, + (item, index) => index, + (item: ListTableRow, index: number) => { + return listItem(item, index); + }, + ), + )} + ${renderIf( + state.canScroll, + html` +
+ <${ToniqIcon.assign({ + icon: ChevronsRight16Icon, + })}> +
+ `, + )} +
()({ function getElementWidthWithMarginPadding(element: HTMLElement) { const style = getComputedStyle(element); - const width = element.offsetWidth; + const width = + element.offsetWidth ?? element.clientWidth ?? element.getBoundingClientRect().width; const marginLeft = parseFloat(style.marginLeft); const marginRight = parseFloat(style.marginRight); const paddingLeft = parseFloat(style.paddingLeft); @@ -631,7 +640,7 @@ function getElementWidthWithMarginPadding(element: HTMLElement) { const gap = parseFloat(style.gap) || 0; return { - width: width + marginLeft + marginRight + gap, + width: width + marginLeft + marginRight + paddingLeft + paddingRight + gap, margin: { left: marginLeft, right: marginRight,