Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9422edbfa1 | |||
| 37c5e92d6d | |||
| c7503de11e | |||
| 408362f3be | |||
| b3f5ab3d31 |
11
changelog.md
11
changelog.md
@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-04-07 - 3.67.1 - fix(repo)
|
||||
no changes to commit
|
||||
|
||||
|
||||
## 2026-04-07 - 3.67.0 - feat(dees-table)
|
||||
improve inline cell editors with integrated input styling and auto-open dropdowns
|
||||
|
||||
- add a visually integrated mode to dees-input-text and dees-input-dropdown for table cell editing
|
||||
- auto-open dropdown editors when a table cell enters edit mode
|
||||
- refine table editing cell outline and dropdown value matching for inline editors
|
||||
|
||||
## 2026-04-07 - 3.66.0 - feat(dees-table)
|
||||
add virtualized row rendering for large tables and optimize table rendering performance
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-catalog",
|
||||
"version": "3.66.0",
|
||||
"version": "3.67.1",
|
||||
"private": false,
|
||||
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
||||
"main": "dist_ts_web/index.js",
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-catalog',
|
||||
version: '3.66.0',
|
||||
version: '3.67.1',
|
||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||
}
|
||||
|
||||
@@ -768,7 +768,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
${effectiveColumns
|
||||
.filter((c) => !c.hidden)
|
||||
.map((col) => {
|
||||
const isSortable = !!col.sortable;
|
||||
const isSortable = col.sortable !== false;
|
||||
const ariaSort = this.getAriaSort(col);
|
||||
return html`
|
||||
<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
|
||||
// hasn't yet scrolled past it.
|
||||
const shouldBeActive =
|
||||
tableRect.top < stick.top && tableRect.bottom > stick.top + Math.min(headerHeight, 1);
|
||||
// Active when the table top is above the stick line and any pixel of the
|
||||
// table still sits below it. As the table's bottom edge approaches the
|
||||
// stick line we shrink the floating container and slide the cloned header
|
||||
// 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) {
|
||||
this.__floatingActive = 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) {
|
||||
// Clone subtree doesn't exist yet — wait for the next render to
|
||||
// materialize it, then complete geometry sync.
|
||||
@@ -1100,10 +1109,19 @@ export class DeesTable<T> extends DeesElement {
|
||||
fh.style.left = `${clipLeft}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
|
||||
// table's left edge — shift it left when we clipped to the container.
|
||||
floatTable.style.width = `${tableRect.width}px`;
|
||||
floatTable.style.marginLeft = `${tableRect.left - clipLeft}px`;
|
||||
floatTable.style.transform = exitOffset > 0 ? `translateY(-${exitOffset}px)` : '';
|
||||
}
|
||||
|
||||
public async disconnectedCallback() {
|
||||
@@ -1496,7 +1514,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
// Maximum exposed slot: one beyond the current cascade, capped at the
|
||||
// number of sortable columns. If the column is already in the cascade we
|
||||
// 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(
|
||||
Math.max(cascadeLen + (existing ? 0 : 1), 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;
|
||||
}
|
||||
|
||||
@@ -2023,6 +2052,10 @@ export class DeesTable<T> extends DeesElement {
|
||||
'.editingCell dees-input-tags'
|
||||
) as any;
|
||||
el?.focus?.();
|
||||
// Dropdown editors should auto-open so the user can pick immediately.
|
||||
if (el?.tagName === 'DEES-INPUT-DROPDOWN') {
|
||||
el.updateComplete?.then(() => el.toggleSelectionBox?.());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2090,8 +2123,13 @@ export class DeesTable<T> extends DeesElement {
|
||||
case 'dropdown': {
|
||||
const options = (col.editorOptions?.options as any[]) ?? [];
|
||||
const selected =
|
||||
options.find((o: any) => (o?.option ?? o?.key ?? o) === value) ?? null;
|
||||
options.find((o: any) => {
|
||||
if (o == null) return false;
|
||||
if (typeof o === 'string') return o === raw;
|
||||
return o.key === raw || o.option === raw;
|
||||
}) ?? null;
|
||||
return html`<dees-input-dropdown
|
||||
.vintegrated=${true}
|
||||
.options=${options}
|
||||
.selectedOption=${selected}
|
||||
@selectedOption=${(e: CustomEvent<any>) => {
|
||||
@@ -2121,6 +2159,7 @@ export class DeesTable<T> extends DeesElement {
|
||||
case 'text':
|
||||
default:
|
||||
return html`<dees-input-text
|
||||
.vintegrated=${true}
|
||||
.value=${value == null ? '' : String(value)}
|
||||
@focusout=${(e: any) => onTextCommit(e.target)}
|
||||
@keydown=${(e: KeyboardEvent) => this.__handleEditorKey(e, item, col)}
|
||||
|
||||
@@ -386,6 +386,11 @@ export const tableStyles: CSSResult[] = [
|
||||
}
|
||||
td.editingCell {
|
||||
padding: 0;
|
||||
outline: 2px solid ${cssManager.bdTheme(
|
||||
'hsl(222.2 47.4% 51.2% / 0.6)',
|
||||
'hsl(217.2 91.2% 59.8% / 0.6)'
|
||||
)};
|
||||
outline-offset: -2px;
|
||||
}
|
||||
td.editingCell .innerCellContainer {
|
||||
padding: 0;
|
||||
|
||||
@@ -48,6 +48,7 @@ export interface Column<T = any> {
|
||||
header?: string | TemplateResult;
|
||||
value?: (row: T) => any;
|
||||
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;
|
||||
/** whether this column participates in per-column quick filtering (default: true) */
|
||||
filterable?: boolean;
|
||||
|
||||
@@ -46,6 +46,12 @@ export class DeesInputDropdown extends DeesInputBase<DeesInputDropdown> {
|
||||
})
|
||||
accessor enableSearch: boolean = true;
|
||||
|
||||
@property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
})
|
||||
accessor vintegrated: boolean = false;
|
||||
|
||||
@state()
|
||||
accessor isOpened = false;
|
||||
|
||||
@@ -126,6 +132,36 @@ export class DeesInputDropdown extends DeesInputBase<DeesInputDropdown> {
|
||||
.selectedBox.open::after {
|
||||
transform: translateY(-50%) rotate(180deg);
|
||||
}
|
||||
|
||||
/* Visually integrated mode: shed chrome to blend into a host component
|
||||
(e.g. a dees-table cell in edit mode). */
|
||||
:host([vintegrated]) dees-label {
|
||||
display: none;
|
||||
}
|
||||
:host([vintegrated]) .maincontainer {
|
||||
height: 40px;
|
||||
}
|
||||
:host([vintegrated]) .selectedBox {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 32px 0 16px;
|
||||
font-size: 13px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
}
|
||||
:host([vintegrated]) .selectedBox:hover:not(.disabled),
|
||||
:host([vintegrated]) .selectedBox:focus-visible {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
:host([vintegrated]) .selectedBox::after {
|
||||
right: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
|
||||
@@ -57,6 +57,12 @@ export class DeesInputText extends DeesInputBase {
|
||||
@property({})
|
||||
accessor validationFunction!: (value: string) => boolean;
|
||||
|
||||
@property({
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
})
|
||||
accessor vintegrated: boolean = false;
|
||||
|
||||
public static styles = [
|
||||
themeDefaultStyles,
|
||||
...DeesInputBase.baseStyles,
|
||||
@@ -194,6 +200,36 @@ export class DeesInputText extends DeesInputBase {
|
||||
border-color: ${cssManager.bdTheme('hsl(142.1 76.2% 36.3%)', 'hsl(142.1 70.6% 45.3%)')};
|
||||
box-shadow: 0 0 0 2px ${cssManager.bdTheme('hsl(142.1 76.2% 36.3% / 0.05)', 'hsl(142.1 70.6% 45.3% / 0.05)')};
|
||||
}
|
||||
|
||||
/* Visually integrated mode: shed chrome to blend into a host component
|
||||
(e.g. a dees-table cell in edit mode). */
|
||||
:host([vintegrated]) dees-label,
|
||||
:host([vintegrated]) .validationContainer {
|
||||
display: none;
|
||||
}
|
||||
:host([vintegrated]) .maincontainer {
|
||||
height: 40px;
|
||||
}
|
||||
:host([vintegrated]) input {
|
||||
height: 40px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
font-size: 13px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
}
|
||||
:host([vintegrated]) input:hover:not(:disabled):not(:focus),
|
||||
:host([vintegrated]) input:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
:host([vintegrated]) .showPassword {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user