update modals
This commit is contained in:
@ -1,37 +1,263 @@
|
||||
import { html } from '@design.estate/dees-element';
|
||||
import { html, css, cssManager } from '@design.estate/dees-element';
|
||||
import { DeesModal } from './dees-modal.js';
|
||||
|
||||
export const demoFunc = () => html`
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'This is a heading',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text
|
||||
.label=${'Username'}
|
||||
>
|
||||
</dees-input-text>
|
||||
<dees-input-text
|
||||
.label=${'Password'}
|
||||
>
|
||||
</dees-input-text>
|
||||
</dees-form>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Cancel',
|
||||
iconName: null,
|
||||
action: async (deesModalArg) => {
|
||||
deesModalArg.destroy();
|
||||
return null;
|
||||
}
|
||||
}, {
|
||||
name: 'Ok',
|
||||
iconName: null,
|
||||
action: async (deesModalArg) => {
|
||||
deesModalArg.destroy();
|
||||
return null;
|
||||
}
|
||||
}],
|
||||
});
|
||||
}}>open modal</dees-button>
|
||||
<style>
|
||||
${css`
|
||||
.demo-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 24px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
background: ${cssManager.bdTheme('#f8f9fa', '#1a1a1a')};
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#333')};
|
||||
}
|
||||
|
||||
.demo-section h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
||||
}
|
||||
|
||||
.demo-section p {
|
||||
color: ${cssManager.bdTheme('#666', '#999')};
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.button-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
<div class="demo-container">
|
||||
<div class="demo-section">
|
||||
<h3>Modal Width Variations</h3>
|
||||
<p>Modals can have different widths: small, medium, large, fullscreen, or custom pixel values.</p>
|
||||
<div class="button-grid">
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Small Modal',
|
||||
width: 'small',
|
||||
content: html`
|
||||
<p>This is a small modal with a width of 380px. Perfect for simple confirmations or brief messages.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Cancel',
|
||||
action: async (modal) => modal.destroy()
|
||||
}, {
|
||||
name: 'OK',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Small Modal</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Medium Modal (Default)',
|
||||
width: 'medium',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text .label=${'Username'}></dees-input-text>
|
||||
<dees-input-text .label=${'Email'} .inputType=${'email'}></dees-input-text>
|
||||
<dees-input-text .label=${'Password'} .inputType=${'password'}></dees-input-text>
|
||||
</dees-form>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Cancel',
|
||||
action: async (modal) => modal.destroy()
|
||||
}, {
|
||||
name: 'Sign Up',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Medium Modal</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Large Modal',
|
||||
width: 'large',
|
||||
content: html`
|
||||
<h4>Wide Content Area</h4>
|
||||
<p>This large modal is 800px wide and perfect for displaying more complex content like forms with multiple columns, tables, or detailed information.</p>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px;">
|
||||
<dees-input-text .label=${'First Name'}></dees-input-text>
|
||||
<dees-input-text .label=${'Last Name'}></dees-input-text>
|
||||
<dees-input-text .label=${'Company'}></dees-input-text>
|
||||
<dees-input-text .label=${'Position'}></dees-input-text>
|
||||
</div>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Cancel',
|
||||
action: async (modal) => modal.destroy()
|
||||
}, {
|
||||
name: 'Save',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Large Modal</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Fullscreen Modal',
|
||||
width: 'fullscreen',
|
||||
content: html`
|
||||
<h4>Fullscreen Experience</h4>
|
||||
<p>This modal takes up almost the entire viewport with a 20px margin on all sides. Great for immersive experiences, detailed editors, or when you need maximum space.</p>
|
||||
<p>The content area can be as tall as needed and will scroll if necessary.</p>
|
||||
<div style="height: 200px; background: ${cssManager.bdTheme('#f0f0f0', '#2a2a2a')}; border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-top: 16px;">
|
||||
<span style="color: ${cssManager.bdTheme('#999', '#666')}">Large content area</span>
|
||||
</div>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Close',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Fullscreen Modal</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Custom Width & Constraints</h3>
|
||||
<p>You can also set custom pixel widths and min/max constraints.</p>
|
||||
<div class="button-grid">
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Custom Width (700px)',
|
||||
width: 700,
|
||||
content: html`
|
||||
<p>This modal has a custom width of exactly 700 pixels.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Close',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Custom 700px</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'With Max Width',
|
||||
width: 'large',
|
||||
maxWidth: 600,
|
||||
content: html`
|
||||
<p>This modal is set to 'large' but constrained by a maxWidth of 600px.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Got it',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Max Width 600px</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'With Min Width',
|
||||
width: 300,
|
||||
minWidth: 400,
|
||||
content: html`
|
||||
<p>This modal width is set to 300px but has a minWidth of 400px, so it will be 400px wide.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'OK',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Min Width 400px</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Button Variations</h3>
|
||||
<p>Modals can have different button configurations with proper spacing.</p>
|
||||
<div class="button-grid">
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Multiple Actions',
|
||||
content: html`
|
||||
<p>This modal demonstrates multiple buttons with proper spacing between them.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Delete',
|
||||
action: async (modal) => modal.destroy()
|
||||
}, {
|
||||
name: 'Cancel',
|
||||
action: async (modal) => modal.destroy()
|
||||
}, {
|
||||
name: 'Save Changes',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Three Buttons</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Single Action',
|
||||
content: html`
|
||||
<p>Sometimes you just need one button.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Acknowledge',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Single Button</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'No Actions',
|
||||
content: html`
|
||||
<p>This modal has no buttons. Click outside or press ESC to close.</p>
|
||||
<p style="margin-top: 16px; color: ${cssManager.bdTheme('#666', '#999')};">This is useful for informational modals that don't require user action.</p>
|
||||
`,
|
||||
menuOptions: [],
|
||||
});
|
||||
}}>No Buttons</dees-button>
|
||||
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Long Button Labels',
|
||||
content: html`
|
||||
<p>Testing button layout with longer labels.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Discard All Changes',
|
||||
action: async (modal) => modal.destroy()
|
||||
}, {
|
||||
name: 'Save and Continue Editing',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Long Labels</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
<h3>Responsive Behavior</h3>
|
||||
<p>All modals automatically become full-width on mobile devices (< 768px viewport width) for better usability.</p>
|
||||
<dees-button @click=${() => {
|
||||
DeesModal.createAndShow({
|
||||
heading: 'Responsive Modal',
|
||||
width: 'large',
|
||||
content: html`
|
||||
<p>Resize your browser window to see how this modal adapts. On mobile viewports, it will automatically take the full width minus margins.</p>
|
||||
`,
|
||||
menuOptions: [{
|
||||
name: 'Close',
|
||||
action: async (modal) => modal.destroy()
|
||||
}],
|
||||
});
|
||||
}}>Test Responsive</dees-button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
@ -35,12 +35,18 @@ export class DeesModal extends DeesElement {
|
||||
heading: string;
|
||||
content: TemplateResult;
|
||||
menuOptions: plugins.tsclass.website.IMenuItem<DeesModal>[];
|
||||
width?: 'small' | 'medium' | 'large' | 'fullscreen' | number;
|
||||
maxWidth?: number;
|
||||
minWidth?: number;
|
||||
}) {
|
||||
const body = document.body;
|
||||
const modal = new DeesModal();
|
||||
modal.heading = optionsArg.heading;
|
||||
modal.content = optionsArg.content;
|
||||
modal.menuOptions = optionsArg.menuOptions;
|
||||
if (optionsArg.width) modal.width = optionsArg.width;
|
||||
if (optionsArg.maxWidth) modal.maxWidth = optionsArg.maxWidth;
|
||||
if (optionsArg.minWidth) modal.minWidth = optionsArg.minWidth;
|
||||
modal.windowLayer = await DeesWindowLayer.createAndShow({
|
||||
blur: true,
|
||||
});
|
||||
@ -49,6 +55,7 @@ export class DeesModal extends DeesElement {
|
||||
});
|
||||
body.append(modal.windowLayer);
|
||||
body.append(modal);
|
||||
return modal;
|
||||
}
|
||||
|
||||
// INSTANCE
|
||||
@ -64,6 +71,15 @@ export class DeesModal extends DeesElement {
|
||||
@state({})
|
||||
public menuOptions: plugins.tsclass.website.IMenuItem<DeesModal>[] = [];
|
||||
|
||||
@property({ type: String })
|
||||
public width: 'small' | 'medium' | 'large' | 'fullscreen' | number = 'medium';
|
||||
|
||||
@property({ type: Number })
|
||||
public maxWidth: number;
|
||||
|
||||
@property({ type: Number })
|
||||
public minWidth: number;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
@ -92,7 +108,6 @@ export class DeesModal extends DeesElement {
|
||||
will-change: transform;
|
||||
transform: translateY(0px) scale(0.95);
|
||||
opacity: 0;
|
||||
width: 480px;
|
||||
min-height: 120px;
|
||||
background: ${cssManager.bdTheme('#ffffff', '#111')};
|
||||
border-radius: 8px;
|
||||
@ -100,6 +115,33 @@ export class DeesModal extends DeesElement {
|
||||
transition: all 0.2s;
|
||||
overflow: hidden;
|
||||
box-shadow: ${cssManager.bdTheme('0px 2px 10px rgba(0, 0, 0, 0.1)', '0px 2px 5px rgba(0, 0, 0, 0.5)')};
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
/* Width variations */
|
||||
.modal.width-small {
|
||||
width: 380px;
|
||||
}
|
||||
|
||||
.modal.width-medium {
|
||||
width: 560px;
|
||||
}
|
||||
|
||||
.modal.width-large {
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
.modal.width-fullscreen {
|
||||
width: calc(100vw - 40px);
|
||||
height: calc(100vh - 40px);
|
||||
max-height: calc(100vh - 40px);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.modal {
|
||||
width: calc(100vw - 40px) !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.modal.show {
|
||||
@ -130,26 +172,22 @@ export class DeesModal extends DeesElement {
|
||||
flex-direction: row;
|
||||
border-top: 1px solid ${cssManager.bdTheme('#e0e0e0', '#333')};
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.modal .bottomButtons .bottomButton {
|
||||
margin: 8px 0px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: all 0.2s;
|
||||
background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.05)', 'rgba(255, 255, 255, 0.05)')};
|
||||
}
|
||||
|
||||
.modal .bottomButtons .bottomButton:first-child {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.modal .bottomButtons .bottomButton:last-child {
|
||||
margin-right: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.modal .bottomButtons .bottomButton:hover {
|
||||
@ -178,25 +216,32 @@ export class DeesModal extends DeesElement {
|
||||
];
|
||||
|
||||
public render(): TemplateResult {
|
||||
const widthClass = typeof this.width === 'string' ? `width-${this.width}` : '';
|
||||
const customWidth = typeof this.width === 'number' ? `${this.width}px` : '';
|
||||
const maxWidthStyle = this.maxWidth ? `${this.maxWidth}px` : '';
|
||||
const minWidthStyle = this.minWidth ? `${this.minWidth}px` : '';
|
||||
|
||||
return html`
|
||||
<style>
|
||||
.modal .bottomButtons {
|
||||
grid-template-columns: ${cssManager.cssGridColumns(this.menuOptions.length, 0)};
|
||||
}
|
||||
${customWidth ? `.modal { width: ${customWidth}; }` : ''}
|
||||
${maxWidthStyle ? `.modal { max-width: ${maxWidthStyle}; }` : ''}
|
||||
${minWidthStyle ? `.modal { min-width: ${minWidthStyle}; }` : ''}
|
||||
</style>
|
||||
<div class="modalContainer" @click=${this.handleOutsideClick}>
|
||||
<div class="modal">
|
||||
<div class="modal ${widthClass}">
|
||||
<div class="heading">${this.heading}</div>
|
||||
<div class="content">${this.content}</div>
|
||||
<div class="bottomButtons">
|
||||
${this.menuOptions.map(
|
||||
(actionArg, index) => html`
|
||||
<div class="bottomButton ${index === this.menuOptions.length - 1 ? 'primary' : ''} ${actionArg.name === 'OK' ? 'ok' : ''}" @click=${() => {
|
||||
actionArg.action(this);
|
||||
}}>${actionArg.name}</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
${this.menuOptions.length > 0 ? html`
|
||||
<div class="bottomButtons">
|
||||
${this.menuOptions.map(
|
||||
(actionArg, index) => html`
|
||||
<div class="bottomButton ${index === this.menuOptions.length - 1 ? 'primary' : ''} ${actionArg.name === 'OK' ? 'ok' : ''}" @click=${() => {
|
||||
actionArg.action(this);
|
||||
}}>${actionArg.name}</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
Reference in New Issue
Block a user