Compare commits

...

15 Commits

Author SHA1 Message Date
5948fc83ea v3.70.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-08 14:51:49 +00:00
72e3d6a09e feat(dees-table): add opt-in flash highlighting for updated table cells 2026-04-08 14:51:49 +00:00
de6f4a3ac5 v3.69.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-08 09:57:57 +00:00
eecdc51557 fix(ui): refine heading emphasis and animate app dashboard subview expansion 2026-04-08 09:57:57 +00:00
c841c49e1e v3.69.0
Some checks failed
Default (tags) / security (push) Failing after 1s
Default (tags) / test (push) Failing after 0s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2026-04-08 08:58:05 +00:00
2595d822d0 feat(dees-heading): add numeric aliases for horizontal rule heading levels and refine heading spacing styles 2026-04-08 08:58:05 +00:00
3ae0541065 v3.68.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-08 08:05:53 +00:00
4b735b768a feat(dees-simple-appdash): add nested sidebar subviews and preserve submit labels from slotted text 2026-04-08 08:05:53 +00:00
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
8d954b17ad v3.66.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 15:56:55 +00:00
ac9cc8cfed feat(dees-table): add virtualized row rendering for large tables and optimize table rendering performance 2026-04-07 15:56:55 +00:00
14 changed files with 1380 additions and 216 deletions

View File

@@ -1,5 +1,50 @@
# Changelog
## 2026-04-08 - 3.70.0 - feat(dees-table)
add opt-in flash highlighting for updated table cells
- introduces highlight-updates and highlight-duration properties for diff-based cell update highlighting
- adds a warning banner when flash highlighting is enabled without rowKey
- keeps selection stable across data refreshes and avoids flashing user-edited cells
- includes a live demo showcasing flashing updates and reduced-motion support
## 2026-04-08 - 3.69.1 - fix(ui)
refine heading emphasis and animate app dashboard subview expansion
- Adjust heading color hierarchy so h1-h2 use primary text while h3-h6 use secondary text, and reduce h1 font weight for better visual balance
- Replace app dashboard subview conditional rendering with animated expand/collapse behavior using grid transitions and inert state handling
## 2026-04-08 - 3.69.0 - feat(dees-heading)
add numeric aliases for horizontal rule heading levels and refine heading spacing styles
- Support level="7" as an alias for "hr" and level="8" as an alias for "hr-small".
- Update heading and hr variant styles to use design tokens for spacing and colors, with per-level margin tuning.
- Extend the demo to show both named and numeric hr heading level variants.
## 2026-04-08 - 3.68.0 - feat(dees-simple-appdash)
add nested sidebar subviews and preserve submit labels from slotted text
- support grouped navigation items with expandable subviews and parent-to-first-subview fallback in the app dashboard
- allow dees-form-submit to derive its button text from light DOM content when no explicit text property is set
## 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
- add a virtualized mode with configurable overscan to render only visible rows while preserving scroll height
- improve table render performance with memoized column and view-data computation plus deferred floating header rendering
- update the dees-table demo to showcase virtualized scrolling in the fixed-height example
## 2026-04-07 - 3.65.0 - feat(dees-table)
add schema-based in-cell editing with keyboard navigation and cell edit events

View File

@@ -1,6 +1,6 @@
{
"name": "@design.estate/dees-catalog",
"version": "3.65.0",
"version": "3.70.0",
"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.65.0',
version: '3.70.0',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
}

View File

@@ -1,6 +1,7 @@
import { type ITableAction } from './dees-table.js';
import * as plugins from '../../00plugins.js';
import { html, css, cssManager } from '@design.estate/dees-element';
import '@design.estate/dees-wcctools/demotools';
interface ITableDemoData {
date: string;
@@ -700,7 +701,8 @@ export const demoFunc = () => html`
<dees-table
id="scrollSmallHeight"
.fixedHeight=${true}
heading1="People Directory (Scrollable)"
.virtualized=${true}
heading1="People Directory (Scrollable, Virtualized)"
heading2="Forced scrolling with many items"
.columns=${[
{ key: 'id', header: 'ID', sortable: true },
@@ -741,6 +743,71 @@ export const demoFunc = () => html`
] as ITableAction[]}
></dees-table>
</div>
<dees-demowrapper .runAfterRender=${async (elementArg: HTMLElement) => {
const tableEl = elementArg.querySelector('#demoLiveFlash') as any;
if (!tableEl) return;
// Guard against double-start if runAfterRender fires more than once
// (e.g. across hot-reload cycles).
if (tableEl.__liveFlashTimerId) {
window.clearInterval(tableEl.__liveFlashTimerId);
}
const tick = () => {
if (!Array.isArray(tableEl.data) || tableEl.data.length === 0) return;
const next = tableEl.data.map((r: any) => ({ ...r }));
const count = 1 + Math.floor(Math.random() * 3);
for (let i = 0; i < count; i++) {
const idx = Math.floor(Math.random() * next.length);
const delta = +((Math.random() * 2 - 1) * 3).toFixed(2);
const newPrice = Math.max(1, +(next[idx].price + delta).toFixed(2));
next[idx] = {
...next[idx],
price: newPrice,
change: delta,
updatedAt: new Date().toLocaleTimeString(),
};
}
tableEl.data = next;
};
tableEl.__liveFlashTimerId = window.setInterval(tick, 1500);
}}>
<div class="demo-section">
<h2 class="demo-title">Live Updates with Flash Highlighting</h2>
<p class="demo-description">
Opt-in cell-flash via <code>highlight-updates="flash"</code>. The ticker below mutates
random rows every 1.5s and reassigns <code>.data</code>. Updated cells briefly flash
amber and fade out. Requires <code>rowKey</code> (here <code>"symbol"</code>). Honors
<code>prefers-reduced-motion</code>. Row selection persists across updates click a
row, then watch it stay selected as the data churns.
</p>
<dees-table
id="demoLiveFlash"
.rowKey=${'symbol'}
highlight-updates="flash"
.selectionMode=${'multi'}
heading1="Live Market Feed"
heading2="Flashing cells indicate updated values"
.columns=${[
{ key: 'symbol', header: 'Symbol', sortable: true },
{ key: 'price', header: 'Price', sortable: true },
{ key: 'change', header: 'Δ', sortable: true },
{ key: 'updatedAt', header: 'Updated' },
]}
.data=${[
{ symbol: 'AAPL', price: 182.52, change: 0, updatedAt: '—' },
{ symbol: 'MSFT', price: 414.18, change: 0, updatedAt: '—' },
{ symbol: 'GOOG', price: 168.74, change: 0, updatedAt: '—' },
{ symbol: 'AMZN', price: 186.13, change: 0, updatedAt: '—' },
{ symbol: 'TSLA', price: 248.50, change: 0, updatedAt: '—' },
{ symbol: 'NVDA', price: 877.35, change: 0, updatedAt: '—' },
{ symbol: 'META', price: 492.96, change: 0, updatedAt: '—' },
{ symbol: 'NFLX', price: 605.88, change: 0, updatedAt: '—' },
{ symbol: 'AMD', price: 165.24, change: 0, updatedAt: '—' },
{ symbol: 'INTC', price: 42.15, change: 0, updatedAt: '—' },
]}
></dees-table>
</div>
</dees-demowrapper>
</div>
</div>
`;

File diff suppressed because it is too large Load Diff

View File

@@ -373,6 +373,72 @@ export const tableStyles: CSSResult[] = [
line-height: 24px;
}
/* ---- Cell flash highlighting (opt-in via highlight-updates="flash") ----
Bloomberg/TradingView-style: the text itself briefly takes an accent
color then fades back to the default. No background tint, no layout
shift, no weight change. Readable, modern, subtle.
Consumers can override per instance:
dees-table#myTable { --dees-table-flash-color: hsl(142 76% 40%); }
*/
:host {
--dees-table-flash-color: ${cssManager.bdTheme(
'hsl(32 95% 44%)',
'hsl(45 93% 62%)'
)};
--dees-table-flash-easing: cubic-bezier(0.22, 0.61, 0.36, 1);
}
.innerCellContainer.flashing {
animation: dees-table-cell-flash
var(--dees-table-flash-duration, 900ms)
var(--dees-table-flash-easing);
}
/* Hold the accent color briefly, then fade back to the theme's default
text color. Inherits to child text and to SVG icons that use
currentColor. Cells with explicit color overrides in renderers are
intentionally unaffected. */
@keyframes dees-table-cell-flash {
0%,
35% { color: var(--dees-table-flash-color); }
100% { color: var(--dees-color-text-primary); }
}
@media (prefers-reduced-motion: reduce) {
.innerCellContainer.flashing {
animation: none;
color: var(--dees-table-flash-color);
}
}
/* Dev-time warning banner shown when highlight-updates="flash" but
rowKey is missing. Consumers should never ship this to production. */
.flashConfigWarning {
display: flex;
align-items: center;
gap: 8px;
margin: 8px 16px 0;
padding: 8px 12px;
border-left: 3px solid ${cssManager.bdTheme('hsl(38 92% 50%)', 'hsl(48 96% 63%)')};
background: ${cssManager.bdTheme('hsl(48 96% 89% / 0.6)', 'hsl(48 96% 30% / 0.15)')};
color: ${cssManager.bdTheme('hsl(32 81% 29%)', 'hsl(48 96% 80%)')};
font-size: 12px;
line-height: 1.4;
border-radius: 4px;
}
.flashConfigWarning dees-icon {
width: 14px;
height: 14px;
flex: 0 0 auto;
}
.flashConfigWarning code {
padding: 1px 4px;
border-radius: 3px;
background: ${cssManager.bdTheme('hsl(0 0% 100% / 0.6)', 'hsl(0 0% 0% / 0.3)')};
font-family: ${cssGeistFontFamily};
font-size: 11px;
}
/* Editable cell affordances */
td.editable {
cursor: text;
@@ -386,6 +452,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

@@ -75,12 +75,23 @@ export class DeesFormSubmit extends DeesElement {
.text=${this.text}
?disabled=${this.disabled}
@clicked=${this.submit}
>
<slot></slot>
</dees-button>
></dees-button>
`;
}
public async firstUpdated() {
// Capture light DOM text content as the button label. dees-button wipes
// its own light DOM during extractLightDom(), so we cannot simply forward
// a <slot> into it — we have to hoist the text onto the .text property
// ourselves before handing it to dees-button.
if (!this.text) {
const slotText = this.textContent?.trim();
if (slotText) {
this.text = slotText;
}
}
}
public async submit() {
if (this.disabled) {
return;

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

View File

@@ -8,7 +8,9 @@ export function demoFunc() {
<dees-heading level="4">This is a H4 heading</dees-heading>
<dees-heading level="5">This is a H5 heading</dees-heading>
<dees-heading level="6">This is a H6 heading</dees-heading>
<dees-heading level="hr">This is an hr heading</dees-heading>
<dees-heading level="hr-small">This is an hr small heading</dees-heading>
<dees-heading level="hr">This is an hr heading (level="hr")</dees-heading>
<dees-heading level="7">This is an hr heading (level="7")</dees-heading>
<dees-heading level="hr-small">This is an hr-small heading (level="hr-small")</dees-heading>
<dees-heading level="8">This is an hr-small heading (level="8")</dees-heading>
`;
}

View File

@@ -27,68 +27,104 @@ export class DeesHeading extends DeesElement {
// properties
/**
* Heading level: 1-6 for h1-h6, or 'hr' for horizontal rule style
* Heading level:
* '1'-'6' → <h1>..<h6>
* '7'|'hr' → horizontal-rule style heading
* '8'|'hr-small' → small horizontal-rule style heading
*/
@property({ type: String, reflect: true })
accessor level: '1' | '2' | '3' | '4' | '5' | '6' | 'hr' | 'hr-small' = '1';
accessor level: '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | 'hr' | 'hr-small' = '1';
// STATIC STYLES
public static styles: CSSResult[] = [
themeDefaultStyles,
cssManager.defaultStyles,
css`
/* TODO: Migrate hardcoded values to --dees-* CSS variables */
/* Heading styles */
h1, h2, h3, h4, h5, h6 {
margin: 16px 0 8px;
font-weight: 600;
color: ${cssManager.bdTheme('#000', '#fff')};
:host {
display: block;
}
h1 { font-size: 32px; font-family: ${cssCalSansFontFamily}; letter-spacing: 0.025em;}
h2 { font-size: 28px; }
h3 { font-size: 24px; }
h4 { font-size: 20px; }
h5 { font-size: 16px; }
h6 { font-size: 14px; }
/* Heading styles.
* Color hierarchy: h1-h2 stay prominent with text-primary; h3-h6 step
* down to text-secondary so they read as subheadings instead of
* mini-h1s. Keeps the visual loudness out of smaller headings. */
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
}
h1, h2 {
color: var(--dees-color-text-primary);
}
h3, h4, h5, h6 {
color: var(--dees-color-text-secondary);
}
/* Per-level typography + spacing.
* Margin scales with importance: h1 gets the most breathing room,
* h6 the least. Top margin > bottom margin so headings group with
* the content that follows them. */
h1 {
/* h1 uses weight 500, not 600: the Cal Sans display font is
* already stylized enough that bold + max contrast + 32px stacks
* too much emphasis. 500 keeps the typographic impact without
* shouting. */
font-weight: 500;
font-size: 32px;
font-family: ${cssCalSansFontFamily};
letter-spacing: 0.025em;
margin: var(--dees-spacing-2xl) 0 var(--dees-spacing-lg);
}
h2 {
font-size: 28px;
margin: var(--dees-spacing-xl) 0 var(--dees-spacing-md);
}
h3 {
font-size: 24px;
margin: var(--dees-spacing-xl) 0 var(--dees-spacing-md);
}
h4 {
font-size: 20px;
margin: var(--dees-spacing-lg) 0 var(--dees-spacing-sm);
}
h5 {
font-size: 16px;
margin: var(--dees-spacing-md) 0 var(--dees-spacing-sm);
}
h6 {
font-size: 14px;
margin: var(--dees-spacing-md) 0 var(--dees-spacing-xs);
}
/* Horizontal rule style heading */
.heading-hr {
display: flex;
align-items: center;
text-align: center;
margin: 16px 0;
color: ${cssManager.bdTheme('#999', '#555')};
margin: var(--dees-spacing-lg) 0;
color: var(--dees-color-text-muted);
}
/* Fade lines toward and away from text for hr style */
.heading-hr::before {
content: '';
flex: 1;
height: 1px;
/* fade in toward center */
background: ${cssManager.bdTheme(
'linear-gradient(to right, transparent, #ccc)',
'linear-gradient(to right, transparent, #333)'
)};
margin: 0 8px;
background: linear-gradient(to right, transparent, var(--dees-color-border-strong));
margin: 0 var(--dees-spacing-sm);
}
.heading-hr::after {
content: '';
flex: 1;
height: 1px;
/* fade out away from center */
background: ${cssManager.bdTheme(
'linear-gradient(to right, #ccc, transparent)',
'linear-gradient(to right, #333, transparent)'
)};
margin: 0 8px;
background: linear-gradient(to right, var(--dees-color-border-strong), transparent);
margin: 0 var(--dees-spacing-sm);
}
/* Small hr variant with reduced margins */
.heading-hr.heading-hr-small {
margin: 8px 0;
margin: var(--dees-spacing-sm) 0;
font-size: 12px;
}
.heading-hr.heading-hr-small::before,
.heading-hr.heading-hr-small::after {
margin: 0 8px;
margin: 0 var(--dees-spacing-sm);
}
`,
];
@@ -109,8 +145,10 @@ export class DeesHeading extends DeesElement {
return html`<h5><slot></slot></h5>`;
case '6':
return html`<h6><slot></slot></h6>`;
case '7':
case 'hr':
return html`<div class="heading-hr"><slot></slot></div>`;
case '8':
case 'hr-small':
return html`<div class="heading-hr heading-hr-small"><slot></slot></div>`;
default:

View File

@@ -353,12 +353,35 @@ export const demoFunc = () => html`
name: 'Analytics',
iconName: 'lucide:lineChart',
element: DemoViewAnalytics,
subViews: [
{
name: 'Overview',
iconName: 'lucide:activity',
element: DemoViewAnalytics,
},
{
name: 'Reports',
iconName: 'lucide:fileText',
element: DemoViewDashboard,
},
],
},
{
name: 'Settings',
iconName: 'lucide:settings',
element: DemoViewSettings,
}
subViews: [
{
name: 'Profile',
iconName: 'lucide:user',
element: DemoViewSettings,
},
{
name: 'Billing',
iconName: 'lucide:creditCard',
element: DemoViewSettings,
},
],
},
] as IView[]}
@logout=${() => {
console.log('Logout event triggered');

View File

@@ -26,7 +26,8 @@ declare global {
export interface IView {
name: string;
iconName?: string;
element: DeesElement['constructor']['prototype'];
element?: DeesElement['constructor']['prototype'];
subViews?: IView[];
}
export type TGlobalMessageType = 'info' | 'success' | 'warning' | 'error';
@@ -250,6 +251,78 @@ export class DeesSimpleAppDash extends DeesElement {
white-space: nowrap;
}
.viewTab .chevron {
flex: 0 0 auto;
font-size: 14px;
opacity: 0.5;
transform: rotate(-90deg);
transition: transform 0.2s ease, opacity 0.15s ease;
}
.viewTab.hasSubs:hover .chevron {
opacity: 0.75;
}
.viewTab.hasSubs.groupActive .chevron {
transform: rotate(0deg);
opacity: 0.9;
}
.subViews {
display: grid;
grid-template-rows: 0fr;
margin-left: 12px;
position: relative;
transition:
grid-template-rows 0.25s cubic-bezier(0.4, 0, 0.2, 1),
margin-top 0.25s cubic-bezier(0.4, 0, 0.2, 1),
margin-bottom 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.subViews.expanded {
grid-template-rows: 1fr;
margin-top: 2px;
margin-bottom: 4px;
}
.subViews::before {
content: '';
position: absolute;
left: 0;
top: 4px;
bottom: 4px;
width: 1px;
background: var(--dees-color-border-default);
opacity: 0;
transition: opacity 0.2s ease;
}
.subViews.expanded::before {
opacity: 1;
}
.subViews-inner {
min-height: 0;
overflow: hidden;
display: flex;
flex-direction: column;
gap: 2px;
padding-left: 12px;
}
.viewTab.sub {
padding: 8px 12px;
font-size: 12px;
}
.viewTab.sub dees-icon {
font-size: 14px;
}
.viewTab.sub.selected::before {
left: -12px;
}
.appActions {
padding: 12px 8px;
border-top: 1px solid var(--dees-color-border-default);
@@ -563,10 +636,12 @@ export class DeesSimpleAppDash extends DeesElement {
<div class="viewTabs-container">
<div class="section-label">Navigation</div>
<div class="viewTabs">
${this.viewTabs.map(
(view) => html`
${this.viewTabs.map((view) => {
const hasSubs = !!view.subViews?.length;
const groupActive = hasSubs && this.isGroupActive(view);
return html`
<div
class="viewTab ${this.selectedView === view ? 'selected' : ''}"
class="viewTab ${this.selectedView === view ? 'selected' : ''} ${hasSubs ? 'hasSubs' : ''} ${groupActive ? 'groupActive' : ''}"
@click=${() => this.loadView(view)}
>
${view.iconName ? html`
@@ -575,9 +650,39 @@ export class DeesSimpleAppDash extends DeesElement {
<dees-icon .icon="${'lucide:file'}"></dees-icon>
`}
<span>${view.name}</span>
${hasSubs ? html`
<dees-icon class="chevron" .icon="${'lucide:chevronDown'}"></dees-icon>
` : ''}
</div>
`
)}
${hasSubs ? html`
<div
class="subViews ${groupActive ? 'expanded' : ''}"
?inert=${!groupActive}
>
<div class="subViews-inner">
${view.subViews!.map(
(sub) => html`
<div
class="viewTab sub ${this.selectedView === sub ? 'selected' : ''}"
@click=${(e: Event) => {
e.stopPropagation();
this.loadView(sub);
}}
>
${sub.iconName ? html`
<dees-icon .icon="${sub.iconName.includes(':') ? sub.iconName : `lucide:${sub.iconName}`}"></dees-icon>
` : html`
<dees-icon .icon="${'lucide:dot'}"></dees-icon>
`}
<span>${sub.name}</span>
</div>
`
)}
</div>
</div>
` : ''}
`;
})}
</div>
</div>
<div class="appActions">
@@ -771,8 +876,23 @@ export class DeesSimpleAppDash extends DeesElement {
}
private isGroupActive(view: IView): boolean {
if (this.selectedView === view) return true;
return view.subViews?.some((sv) => sv === this.selectedView) ?? false;
}
private currentView!: DeesElement;
public async loadView(viewArg: IView) {
// Group-only parent: resolve to first sub view with an element
if (!viewArg.element && viewArg.subViews?.length) {
const firstNavigable = viewArg.subViews.find((sv) => sv.element);
if (firstNavigable) {
return this.loadView(firstNavigable);
}
return; // nothing navigable — ignore click
}
if (!viewArg.element) return; // safety: no element and no subs → no-op
const appcontent = this.shadowRoot!.querySelector('.appcontent')!;
const view = new viewArg.element();
if (this.currentView) {
@@ -781,7 +901,7 @@ export class DeesSimpleAppDash extends DeesElement {
appcontent.appendChild(view);
this.currentView = view;
this.selectedView = viewArg;
// Emit view-select event
this.dispatchEvent(new CustomEvent('view-select', {
detail: { view: viewArg },