diff --git a/ts_web/elements/dees-modal.demo.ts b/ts_web/elements/dees-modal.demo.ts index 569a511..a974aff 100644 --- a/ts_web/elements/dees-modal.demo.ts +++ b/ts_web/elements/dees-modal.demo.ts @@ -40,6 +40,89 @@ export const demoFunc = () => html`
+
+

Header Buttons

+

Modals can have optional header buttons for help and closing.

+
+ { + DeesModal.createAndShow({ + heading: 'With Help Button', + showHelpButton: true, + onHelp: async () => { + const helpModal = await DeesModal.createAndShow({ + heading: 'Help', + width: 'small', + showCloseButton: true, + showHelpButton: false, + content: html` +

This is the help content for the modal.

+

You can provide context-specific help here.

+ `, + menuOptions: [{ + name: 'Got it', + action: async (modal) => modal.destroy() + }], + }); + }, + content: html` +

This modal has a help button in the header. Click it to see help content.

+

The close button is also visible by default.

+ `, + menuOptions: [{ + name: 'OK', + action: async (modal) => modal.destroy() + }], + }); + }}>With Help Button
+ + { + DeesModal.createAndShow({ + heading: 'No Close Button', + showCloseButton: false, + content: html` +

This modal has no close button in the header.

+

You must use the action buttons or click outside to close it.

+ `, + menuOptions: [{ + name: 'Close', + action: async (modal) => modal.destroy() + }], + }); + }}>No Close Button
+ + { + DeesModal.createAndShow({ + heading: 'Both Buttons', + showHelpButton: true, + showCloseButton: true, + onHelp: () => alert('Help clicked!'), + content: html` +

This modal has both help and close buttons.

+ `, + menuOptions: [{ + name: 'Done', + action: async (modal) => modal.destroy() + }], + }); + }}>Both Buttons
+ + { + DeesModal.createAndShow({ + heading: 'Clean Header', + showCloseButton: false, + showHelpButton: false, + content: html` +

This modal has a clean header with no buttons.

+ `, + menuOptions: [{ + name: 'Close', + action: async (modal) => modal.destroy() + }], + }); + }}>Clean Header
+
+
+

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 {