Modal Width Variations
Modals can have different widths: small, medium, large, fullscreen, or custom pixel values.
@@ -108,18 +191,25 @@ export const demoFunc = () => html`
{
DeesModal.createAndShow({
- heading: 'Fullscreen Modal',
+ heading: 'Fullscreen Editor',
width: 'fullscreen',
+ showHelpButton: true,
+ onHelp: async () => {
+ alert('In a real app, this would show editor documentation');
+ },
content: html`
- Fullscreen Experience
- 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.
+ Fullscreen Experience with Header Controls
+ This modal takes up almost the entire viewport with a 20px margin on all sides. The header buttons are particularly useful in fullscreen mode.
The content area can be as tall as needed and will scroll if necessary.
Large content area
`,
menuOptions: [{
- name: 'Close',
+ name: 'Save',
+ action: async (modal) => modal.destroy()
+ }, {
+ name: 'Cancel',
action: async (modal) => modal.destroy()
}],
});
@@ -217,7 +307,7 @@ export const demoFunc = () => html`
DeesModal.createAndShow({
heading: 'No Actions',
content: html`
- This modal has no buttons. Click outside or press ESC to close.
+ This modal has no bottom buttons. Use the X button or click outside to close.
This is useful for informational modals that don't require user action.
`,
menuOptions: [],
@@ -249,8 +339,11 @@ export const demoFunc = () => html`
DeesModal.createAndShow({
heading: 'Responsive Modal',
width: 'large',
+ showHelpButton: true,
+ onHelp: () => console.log('Help requested for responsive modal'),
content: html`
Resize your browser window to see how this modal adapts. On mobile viewports, it will automatically take the full width minus margins.
+ The header buttons remain accessible at all viewport sizes.
`,
menuOptions: [{
name: 'Close',
diff --git a/ts_web/elements/dees-modal.ts b/ts_web/elements/dees-modal.ts
index 1794789..2b3d736 100644
--- a/ts_web/elements/dees-modal.ts
+++ b/ts_web/elements/dees-modal.ts
@@ -19,6 +19,7 @@ import {
import * as domtools from '@design.estate/dees-domtools';
import { DeesWindowLayer } from './dees-windowlayer.js';
+import './dees-icon.js';
declare global {
interface HTMLElementTagNameMap {
@@ -38,6 +39,9 @@ export class DeesModal extends DeesElement {
width?: 'small' | 'medium' | 'large' | 'fullscreen' | number;
maxWidth?: number;
minWidth?: number;
+ showCloseButton?: boolean;
+ showHelpButton?: boolean;
+ onHelp?: () => void | Promise;
}) {
const body = document.body;
const modal = new DeesModal();
@@ -47,6 +51,9 @@ export class DeesModal extends DeesElement {
if (optionsArg.width) modal.width = optionsArg.width;
if (optionsArg.maxWidth) modal.maxWidth = optionsArg.maxWidth;
if (optionsArg.minWidth) modal.minWidth = optionsArg.minWidth;
+ if (optionsArg.showCloseButton !== undefined) modal.showCloseButton = optionsArg.showCloseButton;
+ if (optionsArg.showHelpButton !== undefined) modal.showHelpButton = optionsArg.showHelpButton;
+ if (optionsArg.onHelp) modal.onHelp = optionsArg.onHelp;
modal.windowLayer = await DeesWindowLayer.createAndShow({
blur: true,
});
@@ -80,6 +87,15 @@ export class DeesModal extends DeesElement {
@property({ type: Number })
public minWidth: number;
+ @property({ type: Boolean })
+ public showCloseButton: boolean = true;
+
+ @property({ type: Boolean })
+ public showHelpButton: boolean = false;
+
+ @property({ attribute: false })
+ public onHelp: () => void | Promise;
+
constructor() {
super();
}
@@ -155,13 +171,61 @@ export class DeesModal extends DeesElement {
}
.modal .heading {
- height: 32px;
+ height: 40px;
font-family: 'Geist Sans', sans-serif;
- line-height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 12px;
+ border-bottom: 1px solid ${cssManager.bdTheme('#e0e0e0', '#333')};
+ position: relative;
+ }
+
+ .modal .heading .header-buttons {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ position: absolute;
+ right: 8px;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+
+ .modal .heading .header-button {
+ width: 28px;
+ height: 28px;
+ border-radius: 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s;
+ background: transparent;
+ color: ${cssManager.bdTheme('#666', '#999')};
+ }
+
+ .modal .heading .header-button:hover {
+ background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.08)', 'rgba(255, 255, 255, 0.08)')};
+ color: ${cssManager.bdTheme('#333', '#fff')};
+ }
+
+ .modal .heading .header-button:active {
+ background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.12)', 'rgba(255, 255, 255, 0.12)')};
+ }
+
+ .modal .heading .header-button dees-icon {
+ width: 16px;
+ height: 16px;
+ display: block;
+ }
+
+ .modal .heading .heading-text {
+ flex: 1;
text-align: center;
font-weight: 600;
- font-size: 12px;
- border-bottom: 1px solid ${cssManager.bdTheme('#e0e0e0', '#333')};
+ font-size: 14px;
+ line-height: 40px;
+ padding: 0 40px;
}
.modal .content {
@@ -229,7 +293,21 @@ export class DeesModal extends DeesElement {
-
${this.heading}
+
${this.content}
${this.menuOptions.length > 0 ? html`
@@ -272,4 +350,10 @@ export class DeesModal extends DeesElement {
document.body.removeChild(this);
await this.windowLayer.destroy();
}
+
+ private async handleHelp() {
+ if (this.onHelp) {
+ await this.onHelp();
+ }
+ }
}