This commit is contained in:
2026-04-07 21:31:43 +00:00
parent 408362f3be
commit c7503de11e
2 changed files with 36 additions and 6 deletions

View File

@@ -768,7 +768,7 @@ export class DeesTable<T> extends DeesElement {
${effectiveColumns ${effectiveColumns
.filter((c) => !c.hidden) .filter((c) => !c.hidden)
.map((col) => { .map((col) => {
const isSortable = !!col.sortable; const isSortable = col.sortable !== false;
const ariaSort = this.getAriaSort(col); const ariaSort = this.getAriaSort(col);
return html` return html`
<th <th
@@ -1053,14 +1053,23 @@ export class DeesTable<T> extends DeesElement {
} }
} }
// Active when the table top is above the stick line and the table bottom // Active when the table top is above the stick line and any pixel of the
// hasn't yet scrolled past it. // table still sits below it. As the table's bottom edge approaches the
const shouldBeActive = // stick line we shrink the floating container and slide the cloned header
tableRect.top < stick.top && tableRect.bottom > stick.top + Math.min(headerHeight, 1); // up inside it, so the header appears to scroll off with the table
// instead of snapping away in one frame.
const distance = tableRect.bottom - stick.top;
const shouldBeActive = tableRect.top < stick.top && distance > 0;
if (shouldBeActive !== this.__floatingActive) { if (shouldBeActive !== this.__floatingActive) {
this.__floatingActive = shouldBeActive; this.__floatingActive = shouldBeActive;
fh.classList.toggle('active', shouldBeActive); fh.classList.toggle('active', shouldBeActive);
if (!shouldBeActive) {
// Reset inline geometry so the next activation starts clean.
fh.style.height = '';
const ft = this.__floatingTableEl;
if (ft) ft.style.transform = '';
}
if (shouldBeActive) { if (shouldBeActive) {
// Clone subtree doesn't exist yet — wait for the next render to // Clone subtree doesn't exist yet — wait for the next render to
// materialize it, then complete geometry sync. // materialize it, then complete geometry sync.
@@ -1100,10 +1109,19 @@ export class DeesTable<T> extends DeesElement {
fh.style.left = `${clipLeft}px`; fh.style.left = `${clipLeft}px`;
fh.style.width = `${clipWidth}px`; fh.style.width = `${clipWidth}px`;
// Exit animation: when the table's bottom edge is within `headerHeight`
// pixels of the stick line, shrink the container and translate the
// inner table up by the same amount. overflow:hidden on .floatingHeader
// clips the overflow, producing a scroll-off effect.
const visibleHeight = Math.min(headerHeight, distance);
const exitOffset = headerHeight - visibleHeight;
fh.style.height = `${visibleHeight}px`;
// The inner table is positioned so the visible region matches the real // The inner table is positioned so the visible region matches the real
// table's left edge — shift it left when we clipped to the container. // table's left edge — shift it left when we clipped to the container.
floatTable.style.width = `${tableRect.width}px`; floatTable.style.width = `${tableRect.width}px`;
floatTable.style.marginLeft = `${tableRect.left - clipLeft}px`; floatTable.style.marginLeft = `${tableRect.left - clipLeft}px`;
floatTable.style.transform = exitOffset > 0 ? `translateY(-${exitOffset}px)` : '';
} }
public async disconnectedCallback() { public async disconnectedCallback() {
@@ -1496,7 +1514,7 @@ export class DeesTable<T> extends DeesElement {
// Maximum exposed slot: one beyond the current cascade, capped at the // Maximum exposed slot: one beyond the current cascade, capped at the
// number of sortable columns. If the column is already in the cascade we // number of sortable columns. If the column is already in the cascade we
// never need to grow the slot count. // never need to grow the slot count.
const sortableColumnCount = effectiveColumns.filter((c) => !!c.sortable).length; const sortableColumnCount = effectiveColumns.filter((c) => c.sortable !== false).length;
const maxSlot = Math.min( const maxSlot = Math.min(
Math.max(cascadeLen + (existing ? 0 : 1), 1), Math.max(cascadeLen + (existing ? 0 : 1), 1),
Math.max(sortableColumnCount, 1) Math.max(sortableColumnCount, 1)
@@ -1602,6 +1620,17 @@ export class DeesTable<T> extends DeesElement {
}); });
} }
items.push({ divider: true });
items.push({
name: this.showColumnFilters ? 'Hide column filters' : 'Show column filters',
iconName: this.showColumnFilters ? 'lucide:filterX' : 'lucide:filter',
action: async () => {
this.showColumnFilters = !this.showColumnFilters;
this.requestUpdate();
return null;
},
});
return items; return items;
} }

View File

@@ -48,6 +48,7 @@ export interface Column<T = any> {
header?: string | TemplateResult; header?: string | TemplateResult;
value?: (row: T) => any; value?: (row: T) => any;
renderer?: (value: any, row: T, ctx: { rowIndex: number; colIndex: number; column: Column<T> }) => TemplateResult | string; renderer?: (value: any, row: T, ctx: { rowIndex: number; colIndex: number; column: Column<T> }) => TemplateResult | string;
/** Whether this column can be sorted by clicking its header. Defaults to `true`; set to `false` to disable. */
sortable?: boolean; sortable?: boolean;
/** whether this column participates in per-column quick filtering (default: true) */ /** whether this column participates in per-column quick filtering (default: true) */
filterable?: boolean; filterable?: boolean;