update z-index showcase
This commit is contained in:
@ -106,6 +106,11 @@ export class DeesToast extends DeesElement {
|
||||
return toast;
|
||||
}
|
||||
|
||||
// Alias for consistency with DeesModal
|
||||
public static async createAndShow(options: IToastOptions | string) {
|
||||
return this.show(options);
|
||||
}
|
||||
|
||||
// Convenience methods
|
||||
public static info(message: string, duration?: number) {
|
||||
return this.show({ message, type: 'info', duration });
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from './mainpage.js';
|
||||
export * from './input-showcase.js';
|
||||
export * from './zindex-showcase.js';
|
464
ts_web/pages/zindex-showcase.ts
Normal file
464
ts_web/pages/zindex-showcase.ts
Normal file
@ -0,0 +1,464 @@
|
||||
import { html, css, cssManager } from '@design.estate/dees-element';
|
||||
import { DeesModal } from '../elements/dees-modal.js';
|
||||
import { DeesToast } from '../elements/dees-toast.js';
|
||||
import { DeesContextmenu } from '../elements/dees-contextmenu.js';
|
||||
import '../elements/dees-button.js';
|
||||
import '../elements/dees-input-dropdown.js';
|
||||
import '../elements/dees-form.js';
|
||||
import '../elements/dees-panel.js';
|
||||
import '../elements/dees-input-text.js';
|
||||
import '../elements/dees-input-radiogroup.js';
|
||||
import '../elements/dees-appui-profiledropdown.js';
|
||||
|
||||
export const showcasePage = () => html`
|
||||
<style>
|
||||
${css`
|
||||
.page-wrapper {
|
||||
display: block;
|
||||
background: ${cssManager.bdTheme('#f5f5f5', '#0a0a0a')};
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.showcase-content {
|
||||
padding: 40px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
||||
margin-bottom: 16px;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.intro {
|
||||
color: ${cssManager.bdTheme('#666', '#999')};
|
||||
font-size: 18px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.hierarchy-visual {
|
||||
background: ${cssManager.bdTheme('#fff', '#1a1a1a')};
|
||||
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#333')};
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.hierarchy-visual h3 {
|
||||
margin-top: 0;
|
||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
||||
}
|
||||
|
||||
.layer-stack {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.layer {
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-family: 'Geist Mono', monospace;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.layer:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.layer.base {
|
||||
background: ${cssManager.bdTheme('#f0f0f0', '#222')};
|
||||
color: ${cssManager.bdTheme('#666', '#999')};
|
||||
}
|
||||
|
||||
.layer.fixed {
|
||||
background: ${cssManager.bdTheme('#e3f2fd', '#1e3a5f')};
|
||||
color: ${cssManager.bdTheme('#1976d2', '#90caf9')};
|
||||
}
|
||||
|
||||
.layer.dropdown {
|
||||
background: ${cssManager.bdTheme('#f3e5f5', '#4a148c')};
|
||||
color: ${cssManager.bdTheme('#7b1fa2', '#ce93d8')};
|
||||
}
|
||||
|
||||
.layer.modal {
|
||||
background: ${cssManager.bdTheme('#e8f5e9', '#1b5e20')};
|
||||
color: ${cssManager.bdTheme('#388e3c', '#81c784')};
|
||||
}
|
||||
|
||||
.layer.context {
|
||||
background: ${cssManager.bdTheme('#fff3e0', '#e65100')};
|
||||
color: ${cssManager.bdTheme('#f57c00', '#ffb74d')};
|
||||
}
|
||||
|
||||
.layer.toast {
|
||||
background: ${cssManager.bdTheme('#ffebee', '#b71c1c')};
|
||||
color: ${cssManager.bdTheme('#d32f2f', '#ef5350')};
|
||||
}
|
||||
|
||||
.layer-name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.layer-value {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.warning-box {
|
||||
background: ${cssManager.bdTheme('#fff3cd', '#332701')};
|
||||
border: 1px solid ${cssManager.bdTheme('#ffeaa7', '#664400')};
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
margin-bottom: 24px;
|
||||
color: ${cssManager.bdTheme('#856404', '#ffecb5')};
|
||||
}
|
||||
|
||||
.warning-box strong {
|
||||
color: ${cssManager.bdTheme('#856404', '#ffd93d')};
|
||||
}
|
||||
|
||||
code {
|
||||
background: ${cssManager.bdTheme('#f5f5f5', '#2a2a2a')};
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Geist Mono', monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.test-area {
|
||||
position: relative;
|
||||
height: 200px;
|
||||
background: ${cssManager.bdTheme('#fafafa', '#1a1a1a')};
|
||||
border: 2px dashed ${cssManager.bdTheme('#ccc', '#444')};
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.profile-demo {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<div class="showcase-content">
|
||||
<h1>Z-Index Hierarchy Showcase</h1>
|
||||
<p class="intro">
|
||||
This page demonstrates the z-index management system that ensures proper stacking order for all overlay components.
|
||||
Test the different scenarios to see how overlays interact with each other.
|
||||
</p>
|
||||
|
||||
<div class="warning-box">
|
||||
<strong>⚠️ Important:</strong> The z-index values are managed centrally in <code>00zindex.ts</code>.
|
||||
Never use arbitrary z-index values in components - always import and use the predefined layers.
|
||||
</div>
|
||||
|
||||
<div class="hierarchy-visual">
|
||||
<h3>Z-Index Layer Stack (Top to Bottom)</h3>
|
||||
<div class="layer-stack">
|
||||
<div class="layer base">
|
||||
<span class="layer-name">Base Content</span>
|
||||
<span class="layer-value">z-index: auto</span>
|
||||
</div>
|
||||
<div class="layer fixed">
|
||||
<span class="layer-name">Fixed Navigation</span>
|
||||
<span class="layer-value">z-index: 10-250</span>
|
||||
</div>
|
||||
<div class="layer dropdown">
|
||||
<span class="layer-name">Dropdown Overlays</span>
|
||||
<span class="layer-value">z-index: 1999-2000</span>
|
||||
</div>
|
||||
<div class="layer modal">
|
||||
<span class="layer-name">Modal Dialogs</span>
|
||||
<span class="layer-value">z-index: 2999-3000</span>
|
||||
</div>
|
||||
<div class="layer context">
|
||||
<span class="layer-name">Context Menus & WYSIWYG</span>
|
||||
<span class="layer-value">z-index: 4000-4500</span>
|
||||
</div>
|
||||
<div class="layer toast">
|
||||
<span class="layer-name">Toast Notifications</span>
|
||||
<span class="layer-value">z-index: 5000</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dees-panel .title=${'1. Basic Overlay Tests'} .subtitle=${'Test individual overlay components'}>
|
||||
<div class="demo-grid">
|
||||
<div>
|
||||
<h4>Dropdown Test</h4>
|
||||
<dees-input-dropdown
|
||||
.label=${'Select Option'}
|
||||
.options=${[
|
||||
{option: 'Show Toast', key: 'toast', payload: 'toast'},
|
||||
{option: 'Option 2', key: 'opt2', payload: '2'},
|
||||
{option: 'Option 3', key: 'opt3', payload: '3'},
|
||||
{option: 'Option 4', key: 'opt4', payload: '4'},
|
||||
]}
|
||||
@change=${async (e: CustomEvent) => {
|
||||
if (e.detail.value?.payload === 'toast') {
|
||||
DeesToast.createAndShow({ message: 'Toast appears above dropdown!', type: 'success' });
|
||||
}
|
||||
}}
|
||||
></dees-input-dropdown>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Context Menu Test</h4>
|
||||
<div class="test-area" @contextmenu=${(e: MouseEvent) => {
|
||||
DeesContextmenu.openContextMenuWithOptions(e, [
|
||||
{ name: 'Show Toast', iconName: 'bell', action: async () => {
|
||||
DeesToast.createAndShow({ message: 'Toast from context menu!', type: 'info' });
|
||||
}},
|
||||
{ divider: true },
|
||||
{ name: 'Item 2', iconName: 'check', action: async () => {} },
|
||||
{ name: 'Item 3', iconName: 'copy', action: async () => {} },
|
||||
]);
|
||||
}}>
|
||||
<span style="color: ${cssManager.bdTheme('#999', '#666')}">Right-click here for context menu</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Toast Notification</h4>
|
||||
<dees-button @click=${async () => {
|
||||
DeesToast.createAndShow({ message: 'I appear on top of everything!', type: 'success' });
|
||||
}}>Show Toast</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'2. Modal with Dropdown'} .subtitle=${'Critical test: Dropdown inside modal should appear above modal'}>
|
||||
<p>This tests the most common z-index conflict scenario.</p>
|
||||
<dees-button @click=${async () => {
|
||||
const modal = await DeesModal.createAndShow({
|
||||
heading: 'Modal with Dropdown',
|
||||
width: 'medium',
|
||||
showHelpButton: true,
|
||||
onHelp: async () => {
|
||||
DeesToast.createAndShow({ message: 'Help requested! Toast appears above modal.', type: 'info' });
|
||||
},
|
||||
content: html`
|
||||
<p>The dropdown below should appear <strong>above</strong> this modal:</p>
|
||||
<dees-form>
|
||||
<dees-input-dropdown
|
||||
.label=${'Select Country'}
|
||||
.options=${[
|
||||
{option: 'United States', key: 'us', payload: 'US'},
|
||||
{option: 'Canada', key: 'ca', payload: 'CA'},
|
||||
{option: 'United Kingdom', key: 'uk', payload: 'UK'},
|
||||
{option: 'Germany', key: 'de', payload: 'DE'},
|
||||
{option: 'France', key: 'fr', payload: 'FR'},
|
||||
{option: 'Japan', key: 'jp', payload: 'JP'},
|
||||
{option: 'Australia', key: 'au', payload: 'AU'},
|
||||
{option: 'Brazil', key: 'br', payload: 'BR'},
|
||||
]}
|
||||
.required=${true}
|
||||
></dees-input-dropdown>
|
||||
|
||||
<dees-input-text
|
||||
.label=${'Additional Field'}
|
||||
.placeholder=${'Just to show form context'}
|
||||
></dees-input-text>
|
||||
</dees-form>
|
||||
<p style="margin-top: 16px; color: ${cssManager.bdTheme('#666', '#999')}">
|
||||
You can also right-click anywhere in this modal to test context menus.
|
||||
</p>
|
||||
`,
|
||||
menuOptions: [
|
||||
{ name: 'Cancel', action: async (modal) => modal.destroy() },
|
||||
{ name: 'Save', action: async (modal) => modal.destroy() }
|
||||
]
|
||||
});
|
||||
|
||||
// Add context menu to modal content
|
||||
const modalContent = modal.shadowRoot.querySelector('.modal .content');
|
||||
if (modalContent) {
|
||||
modalContent.addEventListener('contextmenu', async (e: MouseEvent) => {
|
||||
DeesContextmenu.openContextMenuWithOptions(e, [
|
||||
{ name: 'Context menu in modal', iconName: 'check', action: async () => {} },
|
||||
{ divider: true },
|
||||
{ name: 'Show Toast', iconName: 'bell', action: async () => {
|
||||
DeesToast.createAndShow({ message: 'Toast from modal context menu!', type: 'warning' });
|
||||
}}
|
||||
]);
|
||||
});
|
||||
}
|
||||
}}>Open Modal with Dropdown</dees-button>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'3. Complex Stacking Scenario'} .subtitle=${'Multiple overlays active simultaneously'}>
|
||||
<p>This creates a complex scenario with multiple overlays to test the complete hierarchy.</p>
|
||||
<dees-button @click=${async () => {
|
||||
// Show base modal
|
||||
const modal1 = await DeesModal.createAndShow({
|
||||
heading: 'Base Modal',
|
||||
width: 'large',
|
||||
content: html`
|
||||
<h4>Level 1: Modal</h4>
|
||||
<p>This is the base modal. Try the following:</p>
|
||||
<ol>
|
||||
<li>Open the dropdown below</li>
|
||||
<li>Right-click for context menu</li>
|
||||
<li>Click "Show Second Modal" to stack modals</li>
|
||||
</ol>
|
||||
|
||||
<dees-input-dropdown
|
||||
.label=${'Test Dropdown in Modal'}
|
||||
.options=${[
|
||||
{option: 'Trigger Toast', key: 'toast', payload: 'toast'},
|
||||
{option: 'Option 2', key: 'opt2', payload: '2'},
|
||||
{option: 'Option 3', key: 'opt3', payload: '3'},
|
||||
]}
|
||||
@change=${async (e: CustomEvent) => {
|
||||
if (e.detail.value?.payload === 'toast') {
|
||||
DeesToast.createAndShow({ message: 'Toast triggered from dropdown in modal!', type: 'success' });
|
||||
}
|
||||
}}
|
||||
></dees-input-dropdown>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
<dees-button @click=${async () => {
|
||||
const modal2 = await DeesModal.createAndShow({
|
||||
heading: 'Second Modal',
|
||||
width: 'small',
|
||||
content: html`
|
||||
<h4>Level 2: Stacked Modal</h4>
|
||||
<p>This modal appears on top of the first one.</p>
|
||||
<p>The dropdown here should still work:</p>
|
||||
<dees-input-dropdown
|
||||
.label=${'Nested Dropdown'}
|
||||
.options=${[
|
||||
{option: 'Option A', key: 'a'},
|
||||
{option: 'Option B', key: 'b'},
|
||||
{option: 'Option C', key: 'c'},
|
||||
]}
|
||||
></dees-input-dropdown>
|
||||
`,
|
||||
menuOptions: [
|
||||
{ name: 'Close', action: async (modal) => modal.destroy() }
|
||||
]
|
||||
});
|
||||
}}>Show Second Modal</dees-button>
|
||||
</div>
|
||||
`,
|
||||
menuOptions: [
|
||||
{ name: 'Close All', action: async (modal) => {
|
||||
modal.destroy();
|
||||
// Also show a toast
|
||||
DeesToast.createAndShow({ message: 'All modals closed!', type: 'info' });
|
||||
}}
|
||||
]
|
||||
});
|
||||
}}>Start Complex Stack Test</dees-button>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'4. Profile Dropdown'} .subtitle=${'Testing app UI dropdowns'}>
|
||||
<p>Profile dropdowns and similar UI elements use the dropdown z-index layer.</p>
|
||||
<div class="profile-demo">
|
||||
<dees-appui-profiledropdown
|
||||
.user=${{
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
avatar: 'https://randomuser.me/api/portraits/lego/1.jpg',
|
||||
status: 'online'
|
||||
}}
|
||||
.menuItems=${[
|
||||
{ name: 'Show Toast', iconName: 'bell', action: async () => {
|
||||
DeesToast.createAndShow({ message: 'Profile action triggered!', type: 'success' });
|
||||
}},
|
||||
{ divider: true },
|
||||
{ name: 'Settings', iconName: 'settings', action: async () => {} },
|
||||
{ name: 'Logout', iconName: 'logOut', action: async () => {} }
|
||||
]}
|
||||
></dees-appui-profiledropdown>
|
||||
</div>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'5. Edge Cases'} .subtitle=${'Special scenarios and gotchas'}>
|
||||
<div class="demo-grid">
|
||||
<div>
|
||||
<h4>Multiple Toasts</h4>
|
||||
<dees-button @click=${async () => {
|
||||
DeesToast.createAndShow({ message: 'First toast', type: 'info' });
|
||||
setTimeout(() => {
|
||||
DeesToast.createAndShow({ message: 'Second toast', type: 'warning' });
|
||||
}, 500);
|
||||
setTimeout(() => {
|
||||
DeesToast.createAndShow({ message: 'Third toast', type: 'success' });
|
||||
}, 1000);
|
||||
}}>Show Multiple Toasts</dees-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4>Fullscreen Modal</h4>
|
||||
<dees-button @click=${async () => {
|
||||
await DeesModal.createAndShow({
|
||||
heading: 'Fullscreen Modal Test',
|
||||
width: 'fullscreen',
|
||||
content: html`
|
||||
<p>Even in fullscreen, overlays should work properly:</p>
|
||||
<dees-input-radiogroup
|
||||
.label=${'Select Option'}
|
||||
.options=${['Option 1', 'Option 2', 'Option 3']}
|
||||
></dees-input-radiogroup>
|
||||
<dees-input-dropdown
|
||||
.label=${'Dropdown in Fullscreen'}
|
||||
.options=${[
|
||||
{option: 'Works properly', key: '1'},
|
||||
{option: 'Above modal', key: '2'},
|
||||
]}
|
||||
></dees-input-dropdown>
|
||||
`,
|
||||
menuOptions: [
|
||||
{ name: 'Exit Fullscreen', action: async (modal) => modal.destroy() }
|
||||
]
|
||||
});
|
||||
}}>Open Fullscreen</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
</dees-panel>
|
||||
|
||||
<dees-panel .title=${'Usage Guidelines'}>
|
||||
<h4>Best Practices:</h4>
|
||||
<ul>
|
||||
<li>Always use the predefined z-index values from <code>00zindex.ts</code></li>
|
||||
<li>Never use arbitrary z-index values like <code>z-index: 9999</code></li>
|
||||
<li>When creating new overlay components, choose the appropriate layer</li>
|
||||
<li>Test overlay interactions, especially dropdowns in modals</li>
|
||||
<li>Remember that toasts should always be on top</li>
|
||||
</ul>
|
||||
|
||||
<h4>Import Example:</h4>
|
||||
<pre style="background: ${cssManager.bdTheme('#f5f5f5', '#2a2a2a')}; padding: 16px; border-radius: 6px; overflow-x: auto;">
|
||||
<code>import { zIndexLayers } from './00zindex.js';
|
||||
|
||||
// In your component styles:
|
||||
z-index: \${zIndexLayers.overlay.modal};</code></pre>
|
||||
</dees-panel>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
Reference in New Issue
Block a user