feat(appui-tabs): add support for left/right tab action buttons and content tab action APIs
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-10 - 3.44.0 - feat(appui-tabs)
|
||||||
|
add support for left/right tab action buttons and content tab action APIs
|
||||||
|
|
||||||
|
- Introduce ITabAction interface and add actionsLeft/actionsRight properties to dees-appui-tabs, dees-appui-maincontent, and dees-appui.
|
||||||
|
- Render action buttons with new styles and renderActions() helper, including disabled state and click handlers; wire actions into tab components.
|
||||||
|
- Add public clear() on dees-appui-tabs and improve tab selection logic to reset selection when tabs become empty or when the selected tab is removed.
|
||||||
|
- Expose setContentTabActionsLeft and setContentTabActionsRight on the DeesAppui programmatic API and update interfaces/appconfig accordingly.
|
||||||
|
- Update demos to showcase action buttons, add clear-all behavior, and adjust layout/styling for action areas.
|
||||||
|
|
||||||
## 2026-03-09 - 3.43.4 - fix(media)
|
## 2026-03-09 - 3.43.4 - fix(media)
|
||||||
remove deprecated dees-pdf and dees-pdf-preview components and bump several dependencies
|
remove deprecated dees-pdf and dees-pdf-preview components and bump several dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@design.estate/dees-catalog',
|
name: '@design.estate/dees-catalog',
|
||||||
version: '3.43.4',
|
version: '3.44.0',
|
||||||
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.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ export class DeesAppuiMaincontent extends DeesElement {
|
|||||||
@property({ type: Number })
|
@property({ type: Number })
|
||||||
accessor tabsAutoHideThreshold: number = 0;
|
accessor tabsAutoHideThreshold: number = 0;
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor tabActionsLeft: interfaces.ITabAction[] = [];
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor tabActionsRight: interfaces.ITabAction[] = [];
|
||||||
|
|
||||||
public static styles = [
|
public static styles = [
|
||||||
themeDefaultStyles,
|
themeDefaultStyles,
|
||||||
cssManager.defaultStyles,
|
cssManager.defaultStyles,
|
||||||
@@ -106,6 +112,8 @@ export class DeesAppuiMaincontent extends DeesElement {
|
|||||||
.tabStyle=${'horizontal'}
|
.tabStyle=${'horizontal'}
|
||||||
.autoHide=${this.tabsAutoHide}
|
.autoHide=${this.tabsAutoHide}
|
||||||
.autoHideThreshold=${this.tabsAutoHideThreshold}
|
.autoHideThreshold=${this.tabsAutoHideThreshold}
|
||||||
|
.actionsLeft=${this.tabActionsLeft}
|
||||||
|
.actionsRight=${this.tabActionsRight}
|
||||||
@tab-select=${(e: CustomEvent) => this.handleTabSelect(e)}
|
@tab-select=${(e: CustomEvent) => this.handleTabSelect(e)}
|
||||||
@tab-close=${(e: CustomEvent) => this.handleTabClose(e)}
|
@tab-close=${(e: CustomEvent) => this.handleTabClose(e)}
|
||||||
></dees-appui-tabs>
|
></dees-appui-tabs>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { html, cssManager, css, DeesElement, customElement, state } from '@desig
|
|||||||
import * as interfaces from '../../interfaces/index.js';
|
import * as interfaces from '../../interfaces/index.js';
|
||||||
import type { DeesAppuiTabs } from './dees-appui-tabs.js';
|
import type { DeesAppuiTabs } from './dees-appui-tabs.js';
|
||||||
|
|
||||||
// Interactive demo component for closeable tabs
|
// Interactive demo component for closeable tabs with action buttons
|
||||||
@customElement('demo-closeable-tabs')
|
@customElement('demo-closeable-tabs')
|
||||||
class DemoCloseableTabs extends DeesElement {
|
class DemoCloseableTabs extends DeesElement {
|
||||||
@state()
|
@state()
|
||||||
@@ -18,24 +18,6 @@ class DemoCloseableTabs extends DeesElement {
|
|||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.1)', 'rgba(59, 130, 246, 0.1)')};
|
|
||||||
border: 1px solid ${cssManager.bdTheme('rgba(59, 130, 246, 0.3)', 'rgba(59, 130, 246, 0.3)')};
|
|
||||||
color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 13px;
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.2)', 'rgba(59, 130, 246, 0.2)')};
|
|
||||||
}
|
|
||||||
.info {
|
.info {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
@@ -66,17 +48,27 @@ class DemoCloseableTabs extends DeesElement {
|
|||||||
this.tabs = this.tabs.filter(t => t.key !== tabKey);
|
this.tabs = this.tabs.filter(t => t.key !== tabKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private clearAll() {
|
||||||
|
const tabsEl = this.shadowRoot!.querySelector('dees-appui-tabs') as DeesAppuiTabs;
|
||||||
|
tabsEl?.clear();
|
||||||
|
this.tabs = [];
|
||||||
|
this.tabCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const rightActions: interfaces.ITabAction[] = [
|
||||||
|
{ id: 'add', iconName: 'lucide:plus', action: () => this.addTab(), tooltip: 'New Tab' },
|
||||||
|
{ id: 'clear', iconName: 'lucide:trash2', action: () => this.clearAll(), tooltip: 'Clear All Tabs' },
|
||||||
|
];
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<dees-appui-tabs
|
<dees-appui-tabs
|
||||||
.tabs=${this.tabs}
|
.tabs=${this.tabs}
|
||||||
|
.actionsRight=${rightActions}
|
||||||
@tab-close=${(e: CustomEvent) => this.removeTab(e.detail.tab.key)}
|
@tab-close=${(e: CustomEvent) => this.removeTab(e.detail.tab.key)}
|
||||||
></dees-appui-tabs>
|
></dees-appui-tabs>
|
||||||
<div class="controls">
|
|
||||||
<button @click=${() => this.addTab()}>+ Add New Tab</button>
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
Click the X button on tabs to close them. The "Main" tab is not closeable.
|
Click the X button on tabs to close them. Use the + button to add tabs and the trash button to clear all.
|
||||||
<br>Current tabs: ${this.tabs.length}
|
<br>Current tabs: ${this.tabs.length}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -232,6 +224,16 @@ export const demoFunc = () => {
|
|||||||
{ key: 'Archived', action: () => console.log('Archived clicked') },
|
{ key: 'Archived', action: () => console.log('Archived clicked') },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const actionsLeft: interfaces.ITabAction[] = [
|
||||||
|
{ id: 'back', iconName: 'lucide:arrowLeft', action: () => console.log('Back'), tooltip: 'Go Back' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const actionsRight: interfaces.ITabAction[] = [
|
||||||
|
{ id: 'add', iconName: 'lucide:plus', action: () => console.log('Add tab'), tooltip: 'New Tab' },
|
||||||
|
{ id: 'search', iconName: 'lucide:search', action: () => console.log('Search'), tooltip: 'Search Tabs' },
|
||||||
|
{ id: 'disabled', iconName: 'lucide:lock', action: () => {}, tooltip: 'Disabled Action', disabled: true },
|
||||||
|
];
|
||||||
|
|
||||||
const demoContent = (text: string) => html`
|
const demoContent = (text: string) => html`
|
||||||
<div style="padding: 24px; color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};">
|
<div style="padding: 24px; color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};">
|
||||||
${text}
|
${text}
|
||||||
@@ -279,7 +281,17 @@ export const demoFunc = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">Closeable Tabs (Browser-style)</div>
|
<div class="section-title">Tabs with Action Buttons</div>
|
||||||
|
<dees-appui-tabs
|
||||||
|
.tabs=${horizontalTabs}
|
||||||
|
.actionsLeft=${actionsLeft}
|
||||||
|
.actionsRight=${actionsRight}
|
||||||
|
></dees-appui-tabs>
|
||||||
|
${demoContent('Action buttons can be placed on either side of the tab bar. They remain fixed while tabs scroll. The lock icon shows a disabled action.')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-title">Closeable Tabs with Actions</div>
|
||||||
<demo-closeable-tabs></demo-closeable-tabs>
|
<demo-closeable-tabs></demo-closeable-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
@property({ type: Number })
|
@property({ type: Number })
|
||||||
accessor autoHideThreshold: number = 0;
|
accessor autoHideThreshold: number = 0;
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor actionsLeft: interfaces.ITabAction[] = [];
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor actionsRight: interfaces.ITabAction[] = [];
|
||||||
|
|
||||||
// Scroll state for fade indicators
|
// Scroll state for fade indicators
|
||||||
@state()
|
@state()
|
||||||
private accessor canScrollLeft: boolean = false;
|
private accessor canScrollLeft: boolean = false;
|
||||||
@@ -73,6 +79,8 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
border-bottom: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
border-bottom: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scroll fade indicators */
|
/* Scroll fade indicators */
|
||||||
@@ -105,6 +113,72 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll-area {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab action buttons */
|
||||||
|
.tab-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-actions.left {
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 8px;
|
||||||
|
border-right: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-actions.right {
|
||||||
|
padding-right: 12px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border-left: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-action-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s ease, color 0.15s ease;
|
||||||
|
background: transparent;
|
||||||
|
color: ${cssManager.bdTheme('#71717a', '#71717a')};
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-action-button:hover {
|
||||||
|
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.06)', 'rgba(255, 255, 255, 0.06)')};
|
||||||
|
color: ${cssManager.bdTheme('#09090b', '#fafafa')};
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-action-button:active {
|
||||||
|
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.1)', 'rgba(255, 255, 255, 0.1)')};
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-action-button.disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-action-button.disabled:hover {
|
||||||
|
background: transparent;
|
||||||
|
color: ${cssManager.bdTheme('#71717a', '#71717a')};
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-action-button dees-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.tabsContainer {
|
.tabsContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@@ -121,12 +195,14 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: transparent transparent;
|
scrollbar-color: transparent transparent;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show scrollbar on hover */
|
/* Show scrollbar on hover */
|
||||||
.tabs-wrapper:hover .tabsContainer.horizontal {
|
.tabs-wrapper:hover .tabsContainer.horizontal,
|
||||||
|
.scroll-area:hover .tabsContainer.horizontal {
|
||||||
scrollbar-color: ${cssManager.bdTheme('rgba(0,0,0,0.2)', 'rgba(255,255,255,0.2)')} transparent;
|
scrollbar-color: ${cssManager.bdTheme('rgba(0,0,0,0.2)', 'rgba(255,255,255,0.2)')} transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,11 +220,13 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
transition: background 0.2s ease;
|
transition: background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-wrapper:hover .tabsContainer.horizontal::-webkit-scrollbar-thumb {
|
.tabs-wrapper:hover .tabsContainer.horizontal::-webkit-scrollbar-thumb,
|
||||||
|
.scroll-area:hover .tabsContainer.horizontal::-webkit-scrollbar-thumb {
|
||||||
background: ${cssManager.bdTheme('rgba(0,0,0,0.2)', 'rgba(255,255,255,0.2)')};
|
background: ${cssManager.bdTheme('rgba(0,0,0,0.2)', 'rgba(255,255,255,0.2)')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs-wrapper:hover .tabsContainer.horizontal::-webkit-scrollbar-thumb:hover {
|
.tabs-wrapper:hover .tabsContainer.horizontal::-webkit-scrollbar-thumb:hover,
|
||||||
|
.scroll-area:hover .tabsContainer.horizontal::-webkit-scrollbar-thumb:hover {
|
||||||
background: ${cssManager.bdTheme('rgba(0,0,0,0.35)', 'rgba(255,255,255,0.35)')};
|
background: ${cssManager.bdTheme('rgba(0,0,0,0.35)', 'rgba(255,255,255,0.35)')};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,13 +409,20 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
const containerClass = `tabsContainer ${this.tabStyle}`;
|
const containerClass = `tabsContainer ${this.tabStyle}`;
|
||||||
|
|
||||||
if (isHorizontal) {
|
if (isHorizontal) {
|
||||||
|
const hasLeftActions = this.actionsLeft && this.actionsLeft.length > 0;
|
||||||
|
const hasRightActions = this.actionsRight && this.actionsRight.length > 0;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="${wrapperClass}">
|
<div class="${wrapperClass}">
|
||||||
<div class="scroll-fade scroll-fade-left ${this.canScrollLeft ? 'visible' : ''}"></div>
|
${hasLeftActions ? this.renderActions(this.actionsLeft, 'left') : ''}
|
||||||
<div class="${containerClass}" @scroll=${this.handleScroll}>
|
<div class="scroll-area">
|
||||||
${this.tabs.map(tab => this.renderTab(tab, isHorizontal))}
|
<div class="scroll-fade scroll-fade-left ${this.canScrollLeft ? 'visible' : ''}"></div>
|
||||||
|
<div class="${containerClass}" @scroll=${this.handleScroll}>
|
||||||
|
${this.tabs.map(tab => this.renderTab(tab, isHorizontal))}
|
||||||
|
</div>
|
||||||
|
<div class="scroll-fade scroll-fade-right ${this.canScrollRight ? 'visible' : ''}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="scroll-fade scroll-fade-right ${this.canScrollRight ? 'visible' : ''}"></div>
|
${hasRightActions ? this.renderActions(this.actionsRight, 'right') : ''}
|
||||||
${this.showTabIndicator ? html`<div class="tabIndicator"></div>` : ''}
|
${this.showTabIndicator ? html`<div class="tabIndicator"></div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -353,6 +438,22 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private renderActions(actions: interfaces.ITabAction[], position: 'left' | 'right'): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="tab-actions ${position}">
|
||||||
|
${actions.map(action => html`
|
||||||
|
<div
|
||||||
|
class="tab-action-button ${action.disabled ? 'disabled' : ''}"
|
||||||
|
title="${action.tooltip || action.id}"
|
||||||
|
@click=${() => !action.disabled && action.action()}
|
||||||
|
>
|
||||||
|
<dees-icon .icon=${action.iconName}></dees-icon>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
private renderTab(tab: interfaces.IMenuItem, isHorizontal: boolean): TemplateResult {
|
private renderTab(tab: interfaces.IMenuItem, isHorizontal: boolean): TemplateResult {
|
||||||
const isSelected = tab === this.selectedTab;
|
const isSelected = tab === this.selectedTab;
|
||||||
const classes = `tab ${isSelected ? 'selectedTab' : ''}`;
|
const classes = `tab ${isSelected ? 'selectedTab' : ''}`;
|
||||||
@@ -406,6 +507,14 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all tabs and reset selection.
|
||||||
|
*/
|
||||||
|
public clear(): void {
|
||||||
|
this.tabs = [];
|
||||||
|
this.selectedTab = null;
|
||||||
|
}
|
||||||
|
|
||||||
private closeTab(e: Event, tab: interfaces.IMenuItem) {
|
private closeTab(e: Event, tab: interfaces.IMenuItem) {
|
||||||
e.stopPropagation(); // Don't select tab when closing
|
e.stopPropagation(); // Don't select tab when closing
|
||||||
|
|
||||||
@@ -423,14 +532,9 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
if (this.tabs && this.tabs.length > 0) {
|
// Tab selection is handled by updated() lifecycle
|
||||||
this.selectTab(this.tabs[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up ResizeObserver for scroll state updates
|
|
||||||
this.setupResizeObserver();
|
this.setupResizeObserver();
|
||||||
|
|
||||||
// Initial scroll state check
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.updateScrollState();
|
this.updateScrollState();
|
||||||
});
|
});
|
||||||
@@ -503,8 +607,24 @@ export class DeesAppuiTabs extends DeesElement {
|
|||||||
async updated(changedProperties: Map<string, any>) {
|
async updated(changedProperties: Map<string, any>) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
|
|
||||||
if (changedProperties.has('tabs') && this.tabs && this.tabs.length > 0 && !this.selectedTab) {
|
if (changedProperties.has('tabs')) {
|
||||||
this.selectTab(this.tabs[0]);
|
if (!this.tabs || this.tabs.length === 0) {
|
||||||
|
// Tabs are empty => reset selection
|
||||||
|
if (this.selectedTab !== null) {
|
||||||
|
this.selectedTab = null;
|
||||||
|
this.dispatchEvent(new CustomEvent('tab-select', {
|
||||||
|
detail: { tab: null },
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else if (this.selectedTab && !this.tabs.includes(this.selectedTab)) {
|
||||||
|
// Selected tab was removed => select first available
|
||||||
|
this.selectTab(this.tabs[0]);
|
||||||
|
} else if (!this.selectedTab) {
|
||||||
|
// Tabs exist but nothing selected => select first
|
||||||
|
this.selectTab(this.tabs[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changedProperties.has('selectedTab') || changedProperties.has('tabs')) {
|
if (changedProperties.has('selectedTab') || changedProperties.has('tabs')) {
|
||||||
|
|||||||
@@ -143,6 +143,12 @@ export class DeesAppui extends DeesElement {
|
|||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
accessor maincontentSelectedTab: interfaces.IMenuItem | undefined = undefined;
|
accessor maincontentSelectedTab: interfaces.IMenuItem | undefined = undefined;
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor contentTabActionsLeft: interfaces.ITabAction[] = [];
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
accessor contentTabActionsRight: interfaces.ITabAction[] = [];
|
||||||
|
|
||||||
// References to child components
|
// References to child components
|
||||||
@state()
|
@state()
|
||||||
accessor appbar: DeesAppuiBar | undefined = undefined;
|
accessor appbar: DeesAppuiBar | undefined = undefined;
|
||||||
@@ -306,6 +312,8 @@ export class DeesAppui extends DeesElement {
|
|||||||
.showTabs=${this.maincontentTabsVisible}
|
.showTabs=${this.maincontentTabsVisible}
|
||||||
.tabsAutoHide=${this.contentTabsAutoHide}
|
.tabsAutoHide=${this.contentTabsAutoHide}
|
||||||
.tabsAutoHideThreshold=${this.contentTabsAutoHideThreshold}
|
.tabsAutoHideThreshold=${this.contentTabsAutoHideThreshold}
|
||||||
|
.tabActionsLeft=${this.contentTabActionsLeft}
|
||||||
|
.tabActionsRight=${this.contentTabActionsRight}
|
||||||
@tab-select=${(e: CustomEvent) => this.handleContentTabSelect(e)}
|
@tab-select=${(e: CustomEvent) => this.handleContentTabSelect(e)}
|
||||||
@tab-close=${(e: CustomEvent) => this.handleContentTabClose(e)}
|
@tab-close=${(e: CustomEvent) => this.handleContentTabClose(e)}
|
||||||
>
|
>
|
||||||
@@ -699,6 +707,20 @@ export class DeesAppui extends DeesElement {
|
|||||||
return this.maincontentSelectedTab;
|
return this.maincontentSelectedTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set content tab action buttons on the left side
|
||||||
|
*/
|
||||||
|
public setContentTabActionsLeft(actions: interfaces.ITabAction[]): void {
|
||||||
|
this.contentTabActionsLeft = [...actions];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set content tab action buttons on the right side
|
||||||
|
*/
|
||||||
|
public setContentTabActionsRight(actions: interfaces.ITabAction[]): void {
|
||||||
|
this.contentTabActionsRight = [...actions];
|
||||||
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// PROGRAMMATIC API: ACTIVITY LOG
|
// PROGRAMMATIC API: ACTIVITY LOG
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { TemplateResult } from '@design.estate/dees-element';
|
import type { TemplateResult } from '@design.estate/dees-element';
|
||||||
import type { IAppBarMenuItem } from './appbarmenuitem.js';
|
import type { IAppBarMenuItem } from './appbarmenuitem.js';
|
||||||
import type { IMenuItem } from './tab.js';
|
import type { IMenuItem, ITabAction } from './tab.js';
|
||||||
import type { IMenuGroup } from './menugroup.js';
|
import type { IMenuGroup } from './menugroup.js';
|
||||||
import type { ISecondaryMenuGroup, ISecondaryMenuItem } from './secondarymenu.js';
|
import type { ISecondaryMenuGroup, ISecondaryMenuItem } from './secondarymenu.js';
|
||||||
|
|
||||||
@@ -134,6 +134,8 @@ export type TDeesAppui = HTMLElement & {
|
|||||||
removeContentTab: (tabKey: string) => void;
|
removeContentTab: (tabKey: string) => void;
|
||||||
selectContentTab: (tabKey: string) => void;
|
selectContentTab: (tabKey: string) => void;
|
||||||
getSelectedContentTab: () => IMenuItem | undefined;
|
getSelectedContentTab: () => IMenuItem | undefined;
|
||||||
|
setContentTabActionsLeft: (actions: ITabAction[]) => void;
|
||||||
|
setContentTabActionsRight: (actions: ITabAction[]) => void;
|
||||||
activityLog: IActivityLogAPI;
|
activityLog: IActivityLogAPI;
|
||||||
setActivityLogVisible: (visible: boolean) => void;
|
setActivityLogVisible: (visible: boolean) => void;
|
||||||
toggleActivityLog: () => void;
|
toggleActivityLog: () => void;
|
||||||
|
|||||||
@@ -7,3 +7,11 @@ export interface IMenuItem {
|
|||||||
closeable?: boolean;
|
closeable?: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ITabAction {
|
||||||
|
id: string;
|
||||||
|
iconName: string;
|
||||||
|
action: () => void | Promise<void>;
|
||||||
|
tooltip?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user