diff --git a/changelog.md b/changelog.md index 647d328..5e8d1f5 100644 --- a/changelog.md +++ b/changelog.md @@ -10,7 +10,11 @@ Update multiple components with shadcn-aligned styling and improved animations - Updated dees-appui-tabs with animated sliding indicator for both horizontal and vertical layouts - Fixed indicator positioning to be perfectly centered on tab content - Indicator width is content width + 8px for minimal visual padding -- Adjusted tab spacing to start more to the left with tighter padding +- Fixed tab content centering by using consistent padding (12px → 16px on all sides) +- Fixed icon rendering by correcting property name from .iconName to .icon +- Added visual separators between tabs for better distinction +- Added subtle hover backgrounds for improved interactivity +- Refactored tabs component code for better maintainability and elegance - Improved overall spacing and visual consistency across components ## 2025-06-27 - 1.10.1 - fix(modal) diff --git a/readme.md b/readme.md index d20f0fe..9b8acf1 100644 --- a/readme.md +++ b/readme.md @@ -529,9 +529,9 @@ Base container component for application layout structure with integrated appbar // Main menu configuration (left sidebar) .mainmenuTabs=${[ - { key: 'dashboard', iconName: 'home', action: () => {} }, - { key: 'projects', iconName: 'folder', action: () => {} }, - { key: 'settings', iconName: 'cog', action: () => {} } + { key: 'dashboard', iconName: 'lucide:home', action: () => {} }, + { key: 'projects', iconName: 'lucide:folder', action: () => {} }, + { key: 'settings', iconName: 'lucide:settings', action: () => {} } ]} .mainmenuSelectedTab=${selectedTab} @@ -545,7 +545,7 @@ Base container component for application layout structure with integrated appbar // Main content tabs .maincontentTabs=${[ - { key: 'tab1', iconName: 'file', action: () => {} } + { key: 'tab1', iconName: 'lucide:file', action: () => {} } ]} // Event handlers diff --git a/ts_web/elements/dees-appui-base.demo.ts b/ts_web/elements/dees-appui-base.demo.ts index f8b995e..6b54f5a 100644 --- a/ts_web/elements/dees-appui-base.demo.ts +++ b/ts_web/elements/dees-appui-base.demo.ts @@ -65,10 +65,10 @@ export const demoFunc = () => { // Main menu tabs (left sidebar) const mainMenuTabs: ITab[] = [ - { key: 'dashboard', iconName: 'home', action: () => console.log('Dashboard selected') }, - { key: 'projects', iconName: 'folder', action: () => console.log('Projects selected') }, - { key: 'analytics', iconName: 'lineChart', action: () => console.log('Analytics selected') }, - { key: 'settings', iconName: 'settings', action: () => console.log('Settings selected') }, + { key: 'dashboard', iconName: 'lucide:home', action: () => console.log('Dashboard selected') }, + { key: 'projects', iconName: 'lucide:folder', action: () => console.log('Projects selected') }, + { key: 'analytics', iconName: 'lucide:lineChart', action: () => console.log('Analytics selected') }, + { key: 'settings', iconName: 'lucide:settings', action: () => console.log('Settings selected') }, ]; // Selector options (second sidebar) @@ -83,9 +83,9 @@ export const demoFunc = () => { // Main content tabs const mainContentTabs: ITab[] = [ - { key: 'Details', iconName: 'file', action: () => console.log('Details tab') }, - { key: 'Logs', iconName: 'list', action: () => console.log('Logs tab') }, - { key: 'Metrics', iconName: 'lineChart', action: () => console.log('Metrics tab') }, + { key: 'Details', iconName: 'lucide:file', action: () => console.log('Details tab') }, + { key: 'Logs', iconName: 'lucide:list', action: () => console.log('Logs tab') }, + { key: 'Metrics', iconName: 'lucide:lineChart', action: () => console.log('Metrics tab') }, ]; // Profile menu items diff --git a/ts_web/elements/dees-appui-maincontent.ts b/ts_web/elements/dees-appui-maincontent.ts index 615e975..04ced78 100644 --- a/ts_web/elements/dees-appui-maincontent.ts +++ b/ts_web/elements/dees-appui-maincontent.ts @@ -19,9 +19,9 @@ export class DeesAppuiMaincontent extends DeesElement { public static demo = () => html` console.log('Overview') }, - { key: 'Details', iconName: 'file', action: () => console.log('Details') }, - { key: 'Settings', iconName: 'cog', action: () => console.log('Settings') }, + { key: 'Overview', iconName: 'lucide:home', action: () => console.log('Overview') }, + { key: 'Details', iconName: 'lucide:file', action: () => console.log('Details') }, + { key: 'Settings', iconName: 'lucide:settings', action: () => console.log('Settings') }, ]} >
diff --git a/ts_web/elements/dees-appui-mainmenu.ts b/ts_web/elements/dees-appui-mainmenu.ts index 9863b84..3c9e943 100644 --- a/ts_web/elements/dees-appui-mainmenu.ts +++ b/ts_web/elements/dees-appui-mainmenu.ts @@ -22,10 +22,10 @@ export class DeesAppuiMainmenu extends DeesElement { public static demo = () => html` console.log('Dashboard') }, - { key: 'Projects', iconName: 'folder', action: () => console.log('Projects') }, - { key: 'Analytics', iconName: 'lineChart', action: () => console.log('Analytics') }, - { key: 'Settings', iconName: 'settings', action: () => console.log('Settings') }, + { key: 'Dashboard', iconName: 'lucide:home', action: () => console.log('Dashboard') }, + { key: 'Projects', iconName: 'lucide:folder', action: () => console.log('Projects') }, + { key: 'Analytics', iconName: 'lucide:lineChart', action: () => console.log('Analytics') }, + { key: 'Settings', iconName: 'lucide:settings', action: () => console.log('Settings') }, ]} > `; @@ -35,7 +35,7 @@ export class DeesAppuiMainmenu extends DeesElement { // INSTANCE @property({ type: Array }) public tabs: interfaces.ITab[] = [ - { key: '⚠️ Please set tabs', iconName: 'alertTriangle', action: () => console.warn('No tabs configured for mainmenu') }, + { key: '⚠️ Please set tabs', iconName: 'lucide:alertTriangle', action: () => console.warn('No tabs configured for mainmenu') }, ]; @property() @@ -112,7 +112,7 @@ export class DeesAppuiMainmenu extends DeesElement { this.updateTab(tabArg); }}" > - +
`; })} diff --git a/ts_web/elements/dees-appui-tabs.ts b/ts_web/elements/dees-appui-tabs.ts index 66a40c6..fb6a2bc 100644 --- a/ts_web/elements/dees-appui-tabs.ts +++ b/ts_web/elements/dees-appui-tabs.ts @@ -14,94 +14,94 @@ import * as domtools from '@design.estate/dees-domtools'; @customElement('dees-appui-tabs') export class DeesAppuiTabs extends DeesElement { - public static demo = () => html` - -
-
-
Horizontal Tabs with Animated Indicator
- console.log('Home clicked') }, - { key: 'Analytics Dashboard', iconName: 'lucide:lineChart', action: () => console.log('Analytics clicked') }, - { key: 'Reports', iconName: 'lucide:fileText', action: () => console.log('Reports clicked') }, - { key: 'User Settings', iconName: 'lucide:settings', action: () => console.log('Settings clicked') }, - { key: 'Help', iconName: 'lucide:helpCircle', action: () => console.log('Help clicked') }, - ]} - > -
- 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. -
-
+ public static demo = () => { + const horizontalTabs: interfaces.ITab[] = [ + { key: 'Home', iconName: 'lucide:home', action: () => console.log('Home clicked') }, + { key: 'Analytics Dashboard', iconName: 'lucide:lineChart', action: () => console.log('Analytics clicked') }, + { key: 'Reports', iconName: 'lucide:fileText', action: () => console.log('Reports clicked') }, + { key: 'User Settings', iconName: 'lucide:settings', action: () => console.log('Settings clicked') }, + { key: 'Help', iconName: 'lucide:helpCircle', action: () => console.log('Help clicked') }, + ]; + + const verticalTabs: interfaces.ITab[] = [ + { key: 'Profile', iconName: 'lucide:user', action: () => console.log('Profile clicked') }, + { key: 'Security', iconName: 'lucide:shield', action: () => console.log('Security clicked') }, + { key: 'Notifications', iconName: 'lucide:bell', action: () => console.log('Notifications clicked') }, + { key: 'Integrations', iconName: 'lucide:link', action: () => console.log('Integrations clicked') }, + { key: 'Advanced', iconName: 'lucide:code', action: () => console.log('Advanced clicked') }, + ]; + + const noIndicatorTabs: interfaces.ITab[] = [ + { key: 'All', action: () => console.log('All clicked') }, + { key: 'Active', action: () => console.log('Active clicked') }, + { key: 'Completed', action: () => console.log('Completed clicked') }, + { key: 'Archived', action: () => console.log('Archived clicked') }, + ]; + + const demoContent = (text: string) => html` +
+ ${text}
- -
-
Vertical Tabs Layout
-
- console.log('Profile clicked') }, - { key: 'Security', iconName: 'lucide:shield', action: () => console.log('Security clicked') }, - { key: 'Notifications', iconName: 'lucide:bell', action: () => console.log('Notifications clicked') }, - { key: 'Integrations', iconName: 'lucide:link', action: () => console.log('Integrations clicked') }, - { key: 'Advanced', iconName: 'lucide:code', action: () => console.log('Advanced clicked') }, - ]} - > -
- Vertical tabs work great for settings pages and navigation menus. The animated indicator smoothly transitions between selections. + `; + + return html` + +
+
+
Horizontal Tabs with Animated Indicator
+ + ${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.')} + +
+ +
+
Vertical Tabs Layout
+
+ + ${demoContent('Vertical tabs work great for settings pages and navigation menus. The animated indicator smoothly transitions between selections.')}
+ +
+
Without Indicator
+ + ${demoContent('Tabs can also be used without the animated indicator by setting showTabIndicator to false.')} + +
- -
-
Without Indicator
- console.log('All clicked') }, - { key: 'Active', action: () => console.log('Active clicked') }, - { key: 'Completed', action: () => console.log('Completed clicked') }, - { key: 'Archived', action: () => console.log('Archived clicked') }, - ]} - > -
- Tabs can also be used without the animated indicator by setting showTabIndicator to false. -
-
-
-
- `; + `; + }; // INSTANCE @property({ @@ -378,32 +378,44 @@ export class DeesAppuiTabs extends DeesElement { private indicatorInitialized = false; private updateTabIndicator() { - if (!this.selectedTab || !this.showTabIndicator) { - return; + if (!this.shouldShowIndicator()) return; + + const selectedTabElement = this.getSelectedTabElement(); + if (!selectedTabElement) return; + + const indicator = this.getIndicatorElement(); + if (!indicator) return; + + this.handleInitialTransition(indicator); + + if (this.tabStyle === 'horizontal') { + this.updateHorizontalIndicator(indicator, selectedTabElement); + } else { + this.updateVerticalIndicator(indicator, selectedTabElement); } + indicator.style.opacity = '1'; + } + + private shouldShowIndicator(): boolean { + return this.selectedTab && this.showTabIndicator && this.tabs.includes(this.selectedTab); + } + + private getSelectedTabElement(): HTMLElement | null { const selectedIndex = this.tabs.indexOf(this.selectedTab); - if (selectedIndex === -1) { - return; - } - - // Select the correct tab element using nth-child - const selector = this.tabStyle === 'horizontal' + const isHorizontal = this.tabStyle === 'horizontal'; + const selector = isHorizontal ? `.tabs-wrapper .tabsContainer .tab:nth-child(${selectedIndex + 1})` : `.vertical-wrapper .tabsContainer .tab:nth-child(${selectedIndex + 1})`; - const selectedTabElement = this.shadowRoot.querySelector(selector) as HTMLElement; - - if (!selectedTabElement) { - return; - } + return this.shadowRoot.querySelector(selector); + } - const indicator = this.shadowRoot.querySelector('.tabIndicator') as HTMLElement; - if (!indicator) { - return; - } + private getIndicatorElement(): HTMLElement | null { + return this.shadowRoot.querySelector('.tabIndicator'); + } - // Disable transition for initial positioning + private handleInitialTransition(indicator: HTMLElement): void { if (!this.indicatorInitialized) { indicator.classList.add('no-transition'); this.indicatorInitialized = true; @@ -412,35 +424,28 @@ export class DeesAppuiTabs extends DeesElement { indicator.classList.remove('no-transition'); }, 50); } + } - if (this.tabStyle === 'horizontal') { - const tabContent = selectedTabElement.querySelector('.tab-content') as HTMLElement; - - if (tabContent) { - // Use getBoundingClientRect for accurate positioning - const wrapperRect = indicator.parentElement.getBoundingClientRect(); - const contentRect = tabContent.getBoundingClientRect(); - - // Calculate the position relative to wrapper - const contentLeft = contentRect.left - wrapperRect.left; - - // Set width to match content plus small padding - const indicatorWidth = contentRect.width + 8; - - // Center the indicator on content (shift left by half the extra width) - const indicatorLeft = contentLeft - 4; - - indicator.style.width = `${indicatorWidth}px`; - indicator.style.left = `${indicatorLeft}px`; - } - } else { - // For vertical tabs - const tabsContainer = this.shadowRoot.querySelector('.vertical-wrapper .tabsContainer') as HTMLElement; - - indicator.style.top = `${selectedTabElement.offsetTop + tabsContainer.offsetTop}px`; - indicator.style.height = `${selectedTabElement.clientHeight}px`; - } + private updateHorizontalIndicator(indicator: HTMLElement, tabElement: HTMLElement): void { + const tabContent = tabElement.querySelector('.tab-content') as HTMLElement; + if (!tabContent) return; - indicator.style.opacity = '1'; + const wrapperRect = indicator.parentElement.getBoundingClientRect(); + const contentRect = tabContent.getBoundingClientRect(); + + const contentLeft = contentRect.left - wrapperRect.left; + const indicatorWidth = contentRect.width + 8; + const indicatorLeft = contentLeft - 4; + + indicator.style.width = `${indicatorWidth}px`; + indicator.style.left = `${indicatorLeft}px`; + } + + private updateVerticalIndicator(indicator: HTMLElement, tabElement: HTMLElement): void { + const tabsContainer = this.shadowRoot.querySelector('.vertical-wrapper .tabsContainer') as HTMLElement; + if (!tabsContainer) return; + + indicator.style.top = `${tabElement.offsetTop + tabsContainer.offsetTop}px`; + indicator.style.height = `${tabElement.clientHeight}px`; } } \ No newline at end of file diff --git a/ts_web/elements/dees-appui-view.ts b/ts_web/elements/dees-appui-view.ts index e215ee7..f50d071 100644 --- a/ts_web/elements/dees-appui-view.ts +++ b/ts_web/elements/dees-appui-view.ts @@ -35,17 +35,17 @@ export class DeesAppuiView extends DeesElement { id: 'demo-view', name: 'Demo View', description: 'A demonstration view', - iconName: 'home', + iconName: 'lucide:home', tabs: [ { key: 'overview', - iconName: 'chart-line', + iconName: 'lucide:lineChart', action: () => console.log('Overview tab'), content: html`
Overview Content
` }, { key: 'details', - iconName: 'file-alt', + iconName: 'lucide:fileText', action: () => console.log('Details tab'), content: html`
Details Content
` }