Compare commits

..

6 Commits

Author SHA1 Message Date
3c1456c0c1 v3.7.1
Some checks failed
Default (tags) / security (push) Failing after 13s
Default (tags) / test (push) Failing after 14s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-29 11:44:16 +00:00
cc71f232d2 fix(dees-appui-maincontent): migrate main content layout to CSS Grid and enable topbar collapse transitions 2025-12-29 11:44:16 +00:00
8a4d69694c v3.7.0
Some checks failed
Default (tags) / security (push) Failing after 14s
Default (tags) / test (push) Failing after 14s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-29 11:30:49 +00:00
e45810dd06 feat(dees-contextmenu,dees-appui-tabs,test): Prevent double-destruction of context menus, await window layer teardown, update destroyAll behavior, remove tabs content slot, and adjust tests 2025-12-29 11:30:49 +00:00
45a2743312 v3.6.1
Some checks failed
Default (tags) / security (push) Failing after 14s
Default (tags) / test (push) Failing after 15s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-12-29 03:04:39 +00:00
c5b50f3eb0 fix(readme): document new App UI APIs to control main/secondary menu and content tabs visibility 2025-12-29 03:04:39 +00:00
9 changed files with 84 additions and 49 deletions

View File

@@ -1,5 +1,29 @@
# Changelog # Changelog
## 2025-12-29 - 3.7.1 - fix(dees-appui-maincontent)
migrate main content layout to CSS Grid and enable topbar collapse transitions
- Replace absolute positioning with CSS Grid on :host and .maincontainer to enable natural document flow
- Make .topbar a grid and animate collapse via grid-template-rows; switch :host([notabs]) to grid-template-rows: 0fr instead of display:none to allow transitions
- Set .maincontainer to display:contents and add min-height:0 on content areas and topbar children to fix overflow/scrolling and flex/grid height issues
- Remove positional styles (position:absolute/top/left/right/bottom) so content scrolls correctly and layout is more robust
## 2025-12-29 - 3.7.0 - feat(dees-contextmenu,dees-appui-tabs,test)
Prevent double-destruction of context menus, await window layer teardown, update destroyAll behavior, remove tabs content slot, and adjust tests
- Add isDestroying guard in DeesContextmenu.destroy to avoid double-destruction.
- Await windowLayer.destroy() to ensure the window layer is fully torn down before continuing.
- Ensure submenu timeouts are cleared and submenu.destroy() is awaited during teardown.
- Change destroyAll to locate the root/top-level menu and destroy from the root to cascade teardown reliably.
- Remove the .content wrapper and the <slot> output from dees-appui-tabs (demo updated to render content outside the component) — this is a breaking change to the tabs API (slotted content no longer rendered).
- Increase test timeout in test.contextmenu-nested-close.browser.ts to 600ms to account for ~300ms windowLayer destruction + ~100ms context menu delay.
## 2025-12-29 - 3.6.1 - fix(readme)
document new App UI APIs to control main/secondary menu and content tabs visibility
- Added docs for setMainMenuCollapsed(), setMainMenuVisible(), setSecondaryMenuCollapsed(), setSecondaryMenuVisible(), and setContentTabsVisible() programmatic APIs.
- Included a TypeScript example showing how to hide secondary menu, hide content tabs, and collapse the main menu in a view's onActivate hook.
## 2025-12-29 - 3.6.0 - feat(dees-appui) ## 2025-12-29 - 3.6.0 - feat(dees-appui)
add visibility toggles for main/secondary menus and ability to show/hide content tabs; expose corresponding setters and appconfig entries add visibility toggles for main/secondary menus and ability to show/hide content tabs; expose corresponding setters and appconfig entries

View File

@@ -1,6 +1,6 @@
{ {
"name": "@design.estate/dees-catalog", "name": "@design.estate/dees-catalog",
"version": "3.6.0", "version": "3.7.1",
"private": false, "private": false,
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.", "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", "main": "dist_ts_web/index.js",

View File

@@ -54,7 +54,7 @@ For developers working on this library, please refer to the [UI Components Playb
|----------|------------| |----------|------------|
| **Core UI** | [`DeesButton`](#deesbutton), [`DeesButtonExit`](#deesbuttonexit), [`DeesButtonGroup`](#deesbuttongroup), [`DeesBadge`](#deesbadge), [`DeesChips`](#deeschips), [`DeesHeading`](#deesheading), [`DeesHint`](#deeshint), [`DeesIcon`](#deesicon), [`DeesLabel`](#deeslabel), [`DeesPanel`](#deespanel), [`DeesSearchbar`](#deessearchbar), [`DeesSpinner`](#deesspinner), [`DeesToast`](#deestoast), [`DeesWindowcontrols`](#deeswindowcontrols) | | **Core UI** | [`DeesButton`](#deesbutton), [`DeesButtonExit`](#deesbuttonexit), [`DeesButtonGroup`](#deesbuttongroup), [`DeesBadge`](#deesbadge), [`DeesChips`](#deeschips), [`DeesHeading`](#deesheading), [`DeesHint`](#deeshint), [`DeesIcon`](#deesicon), [`DeesLabel`](#deeslabel), [`DeesPanel`](#deespanel), [`DeesSearchbar`](#deessearchbar), [`DeesSpinner`](#deesspinner), [`DeesToast`](#deestoast), [`DeesWindowcontrols`](#deeswindowcontrols) |
| **Forms** | [`DeesForm`](#deesform), [`DeesInputText`](#deesinputtext), [`DeesInputCheckbox`](#deesinputcheckbox), [`DeesInputDropdown`](#deesinputdropdown), [`DeesInputRadiogroup`](#deesinputradiogroup), [`DeesInputFileupload`](#deesinputfileupload), [`DeesInputIban`](#deesinputiban), [`DeesInputPhone`](#deesinputphone), [`DeesInputQuantitySelector`](#deesinputquantityselector), [`DeesInputMultitoggle`](#deesinputmultitoggle), [`DeesInputTags`](#deesinputtags), [`DeesInputTypelist`](#deesinputtypelist), [`DeesInputRichtext`](#deesinputrichtext), [`DeesInputWysiwyg`](#deesinputwysiwyg), [`DeesInputDatepicker`](#deesinputdatepicker), [`DeesInputSearchselect`](#deesinputsearchselect), [`DeesFormSubmit`](#deesformsubmit) | | **Forms** | [`DeesForm`](#deesform), [`DeesInputText`](#deesinputtext), [`DeesInputCheckbox`](#deesinputcheckbox), [`DeesInputDropdown`](#deesinputdropdown), [`DeesInputRadiogroup`](#deesinputradiogroup), [`DeesInputFileupload`](#deesinputfileupload), [`DeesInputIban`](#deesinputiban), [`DeesInputPhone`](#deesinputphone), [`DeesInputQuantitySelector`](#deesinputquantityselector), [`DeesInputMultitoggle`](#deesinputmultitoggle), [`DeesInputTags`](#deesinputtags), [`DeesInputTypelist`](#deesinputtypelist), [`DeesInputRichtext`](#deesinputrichtext), [`DeesInputWysiwyg`](#deesinputwysiwyg), [`DeesInputDatepicker`](#deesinputdatepicker), [`DeesInputSearchselect`](#deesinputsearchselect), [`DeesFormSubmit`](#deesformsubmit) |
| **Layout** | [`DeesAppuiBase`](#deesappuibase), [`DeesAppuiMainmenu`](#deesappuimainmenu), [`DeesAppuiSecondarymenu`](#deesappuisecondarymenu), [`DeesAppuiMaincontent`](#deesappuimaincontent), [`DeesAppuiAppbar`](#deesappuiappbar), [`DeesAppuiActivitylog`](#deesappuiactivitylog), [`DeesAppuiProfiledropdown`](#deesappuiprofiledropdown), [`DeesAppuiTabs`](#deesappuitabs), [`DeesAppuiView`](#deesappuiview), [`DeesMobileNavigation`](#deesmobilenavigation), [`DeesDashboardGrid`](#deesdashboardgrid) | | **Layout** | [`DeesAppuiBase`](#deesappuibase), [`DeesAppuiMainmenu`](#deesappuimainmenu), [`DeesAppuiSecondarymenu`](#deesappuisecondarymenu), [`DeesAppuiMaincontent`](#deesappuimaincontent), [`DeesAppuiAppbar`](#deesappuiappbar), [`DeesAppuiActivitylog`](#deesappuiactivitylog), [`DeesAppuiProfiledropdown`](#deesappuiprofiledropdown), [`DeesAppuiTabs`](#deesappuitabs), [`DeesMobileNavigation`](#deesmobilenavigation), [`DeesDashboardGrid`](#deesdashboardgrid) |
| **Data Display** | [`DeesTable`](#deestable), [`DeesDataviewCodebox`](#deesdataviewcodebox), [`DeesDataviewStatusobject`](#deesdataviewstatusobject), [`DeesPdf`](#deespdf), [`DeesStatsGrid`](#deesstatsgrid), [`DeesPagination`](#deespagination) | | **Data Display** | [`DeesTable`](#deestable), [`DeesDataviewCodebox`](#deesdataviewcodebox), [`DeesDataviewStatusobject`](#deesdataviewstatusobject), [`DeesPdf`](#deespdf), [`DeesStatsGrid`](#deesstatsgrid), [`DeesPagination`](#deespagination) |
| **Visualization** | [`DeesChartArea`](#deeschartarea), [`DeesChartLog`](#deeschartlog) | | **Visualization** | [`DeesChartArea`](#deeschartarea), [`DeesChartLog`](#deeschartlog) |
| **Dialogs & Overlays** | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) | | **Dialogs & Overlays** | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) |
@@ -666,9 +666,26 @@ class MyApp extends DeesElement {
- `navigateToView(viewId, params?)` - Navigate between views - `navigateToView(viewId, params?)` - Navigate between views
- `setAppBarMenus()`, `setBreadcrumbs()`, `setUser()` - Control the app bar - `setAppBarMenus()`, `setBreadcrumbs()`, `setUser()` - Control the app bar
- `setMainMenu()`, `setMainMenuSelection()`, `setMainMenuBadge()` - Control main navigation - `setMainMenu()`, `setMainMenuSelection()`, `setMainMenuBadge()` - Control main navigation
- `setSecondaryMenu()`, `setContentTabs()` - Control view-specific UI - `setMainMenuCollapsed()`, `setMainMenuVisible()` - Control main menu visibility
- `setSecondaryMenu()`, `setSecondaryMenuCollapsed()`, `setSecondaryMenuVisible()` - Control secondary menu
- `setContentTabs()`, `setContentTabsVisible()` - Control view-specific UI
- `activityLog.add()`, `activityLog.addMany()`, `activityLog.clear()` - Manage activity entries - `activityLog.add()`, `activityLog.addMany()`, `activityLog.clear()` - Manage activity entries
**View Visibility Control:**
```typescript
// In your view's onActivate hook
onActivate(context: IViewActivationContext) {
// Hide secondary menu for a fullscreen view
context.appui.setSecondaryMenuVisible(false);
// Hide content tabs
context.appui.setContentTabsVisible(false);
// Collapse main menu to give more space
context.appui.setMainMenuCollapsed(true);
}
```
#### `DeesAppuiMainmenu` #### `DeesAppuiMainmenu`
Main navigation menu component for application-wide navigation. Main navigation menu component for application-wide navigation.

View File

@@ -76,8 +76,8 @@ tap.test('should close all parent menus when clicking action in nested submenu',
expect(childItem).toBeTruthy(); expect(childItem).toBeTruthy();
childItem!.click(); childItem!.click();
// Wait for menus to close // Wait for menus to close (windowLayer destruction takes 300ms + context menu 100ms)
await new Promise(resolve => setTimeout(resolve, 200)); await new Promise(resolve => setTimeout(resolve, 600));
// Verify action was called // Verify action was called
expect(actionCalled).toEqual(true); expect(actionCalled).toEqual(true);

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@design.estate/dees-catalog', name: '@design.estate/dees-catalog',
version: '3.6.0', version: '3.7.1',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
} }

View File

@@ -53,41 +53,36 @@ export class DeesAppuiMaincontent extends DeesElement {
/* TODO: Migrate hardcoded values to --dees-* CSS variables */ /* TODO: Migrate hardcoded values to --dees-* CSS variables */
:host { :host {
color: ${cssManager.bdTheme('#333', '#fff')}; color: ${cssManager.bdTheme('#333', '#fff')};
display: block; display: grid;
grid-template-rows: auto 1fr;
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative;
background: ${cssManager.bdTheme('#ffffff', '#161616')}; background: ${cssManager.bdTheme('#ffffff', '#161616')};
} }
.maincontainer { .maincontainer {
position: absolute; display: contents;
height: 100%;
right: 0px;
top: 0px;
width: 100%;
} }
.topbar { .topbar {
position: absolute; display: grid;
width: 100%; grid-template-rows: 1fr;
overflow: hidden;
user-select: none; user-select: none;
transition: grid-template-rows 0.3s ease;
}
.topbar > * {
min-height: 0;
} }
.content-area { .content-area {
position: absolute;
top: 60px;
left: 0;
right: 0;
bottom: 0;
overflow: auto; overflow: auto;
min-height: 0;
} }
:host([notabs]) .topbar { :host([notabs]) .topbar {
display: none; grid-template-rows: 0fr;
}
:host([notabs]) .content-area {
top: 0;
} }
`, `,
]; ];

View File

@@ -67,9 +67,8 @@ export const demoFunc = () => {
<div class="demo-container"> <div class="demo-container">
<div class="section"> <div class="section">
<div class="section-title">Horizontal Tabs with Animated Indicator</div> <div class="section-title">Horizontal Tabs with Animated Indicator</div>
<dees-appui-tabs .tabs=${horizontalTabs}> <dees-appui-tabs .tabs=${horizontalTabs}></dees-appui-tabs>
${demoContent('Select a tab to see the smooth sliding animation of the indicator. The indicator automatically adjusts its width to match the tab content with minimal padding.')} ${demoContent('Select a tab to see the smooth sliding animation of the indicator. The indicator automatically adjusts its width to match the tab content with minimal padding.')}
</dees-appui-tabs>
</div> </div>
<div class="section"> <div class="section">
@@ -82,9 +81,8 @@ export const demoFunc = () => {
<div class="section"> <div class="section">
<div class="section-title">Without Indicator</div> <div class="section-title">Without Indicator</div>
<dees-appui-tabs .showTabIndicator=${false} .tabs=${noIndicatorTabs}> <dees-appui-tabs .showTabIndicator=${false} .tabs=${noIndicatorTabs}></dees-appui-tabs>
${demoContent('Tabs can also be used without the animated indicator by setting showTabIndicator to false.')} ${demoContent('Tabs can also be used without the animated indicator by setting showTabIndicator to false.')}
</dees-appui-tabs>
</div> </div>
</div> </div>
`; `;

View File

@@ -198,19 +198,12 @@ export class DeesAppuiTabs extends DeesElement {
z-index: 1; z-index: 1;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
} }
.content {
padding: 32px 24px;
}
`, `,
]; ];
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
${this.renderTabsWrapper()} ${this.renderTabsWrapper()}
<div class="content">
<slot></slot>
</div>
`; `;
} }

View File

@@ -134,6 +134,7 @@ export class DeesContextmenu extends DeesElement {
private submenu: DeesContextmenu | null = null; private submenu: DeesContextmenu | null = null;
private submenuTimeout: any = null; private submenuTimeout: any = null;
private parentMenu: DeesContextmenu | null = null; private parentMenu: DeesContextmenu | null = null;
private isDestroying: boolean = false;
constructor() { constructor() {
super(); super();
@@ -416,27 +417,33 @@ export class DeesContextmenu extends DeesElement {
} }
public async destroy() { public async destroy() {
// Guard against double-destruction
if (this.isDestroying) {
return;
}
this.isDestroying = true;
// Clear timeout // Clear timeout
if (this.submenuTimeout) { if (this.submenuTimeout) {
clearTimeout(this.submenuTimeout); clearTimeout(this.submenuTimeout);
this.submenuTimeout = null; this.submenuTimeout = null;
} }
// Destroy submenu first // Destroy submenu first
if (this.submenu) { if (this.submenu) {
await this.submenu.destroy(); await this.submenu.destroy();
this.submenu = null; this.submenu = null;
} }
// Only destroy window layer if this is not a submenu // Only destroy window layer if this is not a submenu
if (this.windowLayer && !this.parentMenu) { if (this.windowLayer && !this.parentMenu) {
this.windowLayer.destroy(); await this.windowLayer.destroy();
} }
this.style.opacity = '0'; this.style.opacity = '0';
this.style.transform = 'scale(0.95) translateY(-10px)'; this.style.transform = 'scale(0.95) translateY(-10px)';
await domtools.plugins.smartdelay.delayFor(100); await domtools.plugins.smartdelay.delayFor(100);
if (this.parentElement) { if (this.parentElement) {
this.parentElement.removeChild(this); this.parentElement.removeChild(this);
} }
@@ -446,13 +453,14 @@ export class DeesContextmenu extends DeesElement {
* Destroys this menu and all parent menus in the chain * Destroys this menu and all parent menus in the chain
*/ */
public async destroyAll() { public async destroyAll() {
// First destroy parent menus if they exist // Find the root menu (top-level parent)
if (this.parentMenu) { let rootMenu: DeesContextmenu = this;
await this.parentMenu.destroyAll(); while (rootMenu.parentMenu) {
} else { rootMenu = rootMenu.parentMenu;
// If we're at the top level, just destroy this menu
await this.destroy();
} }
// Destroy from the root - this will cascade through all submenus
await rootMenu.destroy();
} }
} }