Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e51c906adb | |||
| 0626889bef | |||
| 3c1456c0c1 | |||
| cc71f232d2 | |||
| 8a4d69694c | |||
| e45810dd06 | |||
| 45a2743312 | |||
| c5b50f3eb0 |
32
changelog.md
32
changelog.md
@@ -1,5 +1,37 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-12-29 - 3.8.0 - feat(dees-appui-base)
|
||||
add interactive demo controls to manipulate appui via view activation context
|
||||
|
||||
- Store IViewActivationContext on the demo element (this.ctx) during onActivate
|
||||
- Add a new "Context Actions" UI section with buttons that call ctx.appui methods (toggle main/secondary menus, content tabs, collapse/expand main menu, set breadcrumbs, navigate to views, add activity entry, set/clear menu badges)
|
||||
- Include styles for ctx-actions and button variants (success, danger, hover states)
|
||||
- Change is limited to the demo file (dees-appui-base.demo.ts) and is non-breaking
|
||||
|
||||
## 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)
|
||||
add visibility toggles for main/secondary menus and ability to show/hide content tabs; expose corresponding setters and appconfig entries
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-catalog",
|
||||
"version": "3.6.0",
|
||||
"version": "3.8.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",
|
||||
|
||||
21
readme.md
21
readme.md
@@ -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) |
|
||||
| **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) |
|
||||
| **Visualization** | [`DeesChartArea`](#deeschartarea), [`DeesChartLog`](#deeschartlog) |
|
||||
| **Dialogs & Overlays** | [`DeesModal`](#deesmodal), [`DeesContextmenu`](#deescontextmenu), [`DeesSpeechbubble`](#deesspeechbubble), [`DeesWindowlayer`](#deeswindowlayer) |
|
||||
@@ -666,9 +666,26 @@ class MyApp extends DeesElement {
|
||||
- `navigateToView(viewId, params?)` - Navigate between views
|
||||
- `setAppBarMenus()`, `setBreadcrumbs()`, `setUser()` - Control the app bar
|
||||
- `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
|
||||
|
||||
**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`
|
||||
Main navigation menu component for application-wide navigation.
|
||||
|
||||
|
||||
@@ -76,8 +76,8 @@ tap.test('should close all parent menus when clicking action in nested submenu',
|
||||
expect(childItem).toBeTruthy();
|
||||
childItem!.click();
|
||||
|
||||
// Wait for menus to close
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
// Wait for menus to close (windowLayer destruction takes 300ms + context menu 100ms)
|
||||
await new Promise(resolve => setTimeout(resolve, 600));
|
||||
|
||||
// Verify action was called
|
||||
expect(actionCalled).toEqual(true);
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-catalog',
|
||||
version: '3.6.0',
|
||||
version: '3.8.0',
|
||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ class DemoDashboardView extends DeesElement {
|
||||
@state()
|
||||
accessor activated: boolean = false;
|
||||
|
||||
private ctx: IViewActivationContext;
|
||||
|
||||
onActivate(context: IViewActivationContext) {
|
||||
this.ctx = context;
|
||||
this.activated = true;
|
||||
console.log('Dashboard activated with context:', context);
|
||||
|
||||
@@ -75,6 +78,52 @@ class DemoDashboardView extends DeesElement {
|
||||
.metric { font-size: 32px; font-weight: 700; color: #fafafa; }
|
||||
.status { display: inline-block; padding: 2px 8px; border-radius: 9px; font-size: 12px; }
|
||||
.status.active { background: #14532d; color: #4ade80; }
|
||||
|
||||
.ctx-actions {
|
||||
margin-top: 32px;
|
||||
padding: 24px;
|
||||
background: rgba(255,255,255,0.02);
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.ctx-actions h2 { color: #fafafa; font-size: 16px; font-weight: 600; margin-bottom: 16px; }
|
||||
.button-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.ctx-btn {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||
color: #60a5fa;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.ctx-btn:hover {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
border-color: rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
.ctx-btn.danger {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-color: rgba(239, 68, 68, 0.3);
|
||||
color: #f87171;
|
||||
}
|
||||
.ctx-btn.danger:hover {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
border-color: rgba(239, 68, 68, 0.5);
|
||||
}
|
||||
.ctx-btn.success {
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
border-color: rgba(34, 197, 94, 0.3);
|
||||
color: #4ade80;
|
||||
}
|
||||
.ctx-btn.success:hover {
|
||||
background: rgba(34, 197, 94, 0.2);
|
||||
border-color: rgba(34, 197, 94, 0.5);
|
||||
}
|
||||
</style>
|
||||
<h1>Dashboard</h1>
|
||||
<p>Welcome back! Here's an overview of your system.</p>
|
||||
@@ -95,6 +144,26 @@ class DemoDashboardView extends DeesElement {
|
||||
<p style="color: #737373; font-size: 12px; margin: 0;">All systems operational</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ctx-actions">
|
||||
<h2>Context Actions (ctx.appui)</h2>
|
||||
<div class="button-grid">
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.setMainMenuVisible(false)}>Hide Main Menu</button>
|
||||
<button class="ctx-btn success" @click=${() => this.ctx?.appui.setMainMenuVisible(true)}>Show Main Menu</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.setSecondaryMenuVisible(false)}>Hide Secondary Menu</button>
|
||||
<button class="ctx-btn success" @click=${() => this.ctx?.appui.setSecondaryMenuVisible(true)}>Show Secondary Menu</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.setContentTabsVisible(false)}>Hide Content Tabs</button>
|
||||
<button class="ctx-btn success" @click=${() => this.ctx?.appui.setContentTabsVisible(true)}>Show Content Tabs</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.setMainMenuCollapsed(true)}>Collapse Main Menu</button>
|
||||
<button class="ctx-btn success" @click=${() => this.ctx?.appui.setMainMenuCollapsed(false)}>Expand Main Menu</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.setBreadcrumbs(['Dashboard', 'Overview', 'Stats'])}>Set Breadcrumbs</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.navigateToView('projects')}>Go to Projects</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.navigateToView('settings', { section: 'security' })}>Go to Settings/Security</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.activityLog.add({ type: 'custom', user: 'Demo User', message: 'Button clicked from ctx!', iconName: 'lucide:mouse-pointer-click' })}>Add Activity Entry</button>
|
||||
<button class="ctx-btn" @click=${() => this.ctx?.appui.setMainMenuBadge('tasks', 99)}>Set Tasks Badge to 99</button>
|
||||
<button class="ctx-btn danger" @click=${() => this.ctx?.appui.clearMainMenuBadge('tasks')}>Clear Tasks Badge</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,41 +53,36 @@ export class DeesAppuiMaincontent extends DeesElement {
|
||||
/* TODO: Migrate hardcoded values to --dees-* CSS variables */
|
||||
:host {
|
||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
||||
display: block;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: ${cssManager.bdTheme('#ffffff', '#161616')};
|
||||
}
|
||||
|
||||
.maincontainer {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
transition: grid-template-rows 0.3s ease;
|
||||
}
|
||||
|
||||
.topbar > * {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
:host([notabs]) .topbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:host([notabs]) .content-area {
|
||||
top: 0;
|
||||
grid-template-rows: 0fr;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -67,9 +67,8 @@ export const demoFunc = () => {
|
||||
<div class="demo-container">
|
||||
<div class="section">
|
||||
<div class="section-title">Horizontal Tabs with Animated Indicator</div>
|
||||
<dees-appui-tabs .tabs=${horizontalTabs}>
|
||||
${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>
|
||||
<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.')}
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
@@ -82,9 +81,8 @@ export const demoFunc = () => {
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Without Indicator</div>
|
||||
<dees-appui-tabs .showTabIndicator=${false} .tabs=${noIndicatorTabs}>
|
||||
${demoContent('Tabs can also be used without the animated indicator by setting showTabIndicator to false.')}
|
||||
</dees-appui-tabs>
|
||||
<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.')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -198,19 +198,12 @@ export class DeesAppuiTabs extends DeesElement {
|
||||
z-index: 1;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 32px 24px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderTabsWrapper()}
|
||||
<div class="content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,7 @@ export class DeesContextmenu extends DeesElement {
|
||||
private submenu: DeesContextmenu | null = null;
|
||||
private submenuTimeout: any = null;
|
||||
private parentMenu: DeesContextmenu | null = null;
|
||||
private isDestroying: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -416,6 +417,12 @@ export class DeesContextmenu extends DeesElement {
|
||||
}
|
||||
|
||||
public async destroy() {
|
||||
// Guard against double-destruction
|
||||
if (this.isDestroying) {
|
||||
return;
|
||||
}
|
||||
this.isDestroying = true;
|
||||
|
||||
// Clear timeout
|
||||
if (this.submenuTimeout) {
|
||||
clearTimeout(this.submenuTimeout);
|
||||
@@ -430,7 +437,7 @@ export class DeesContextmenu extends DeesElement {
|
||||
|
||||
// Only destroy window layer if this is not a submenu
|
||||
if (this.windowLayer && !this.parentMenu) {
|
||||
this.windowLayer.destroy();
|
||||
await this.windowLayer.destroy();
|
||||
}
|
||||
|
||||
this.style.opacity = '0';
|
||||
@@ -446,13 +453,14 @@ export class DeesContextmenu extends DeesElement {
|
||||
* Destroys this menu and all parent menus in the chain
|
||||
*/
|
||||
public async destroyAll() {
|
||||
// First destroy parent menus if they exist
|
||||
if (this.parentMenu) {
|
||||
await this.parentMenu.destroyAll();
|
||||
} else {
|
||||
// If we're at the top level, just destroy this menu
|
||||
await this.destroy();
|
||||
// Find the root menu (top-level parent)
|
||||
let rootMenu: DeesContextmenu = this;
|
||||
while (rootMenu.parentMenu) {
|
||||
rootMenu = rootMenu.parentMenu;
|
||||
}
|
||||
|
||||
// Destroy from the root - this will cascade through all submenus
|
||||
await rootMenu.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user