Compare commits

...

5 Commits

Author SHA1 Message Date
9422edbfa1 v3.67.1
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-07 21:35:21 +00:00
37c5e92d6d fix(repo): no changes to commit 2026-04-07 21:35:21 +00:00
c7503de11e update 2026-04-07 21:31:43 +00:00
408362f3be v3.67.0
Some checks failed
Default (tags) / security (push) Failing after 0s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-07 21:04:52 +00:00
b3f5ab3d31 feat(dees-table): improve inline cell editors with integrated input styling and auto-open dropdowns 2026-04-07 21:04:52 +00:00
8 changed files with 137 additions and 9 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -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.'
}

View File

@@ -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)}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
`,
];

View File

@@ -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;
}
`,
];