Skip to content

Commit 8a8260a

Browse files
committed
Add modal-with-toasts example and story
1 parent e53f2e8 commit 8a8260a

2 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import type { TemplateResult } from 'lit';
2+
import { LitElement, css, html } from 'lit';
3+
import { state } from 'lit/decorators.js';
4+
import { defineElement } from '../../internal/registration/index.js';
5+
import './modal-container.js';
6+
import { ref, createRef } from 'lit/directives/ref.js';
7+
import type { UUIModalElement } from './modal.element.js';
8+
9+
import './modal.js';
10+
import '../dialog-layout/dialog-layout.js';
11+
import '../button/button.js';
12+
import '../box/box.js';
13+
import '../toast-notification/toast-notification.js';
14+
import '../toast-notification-container/toast-notification-container.js';
15+
import '../toast-notification-layout/toast-notification-layout.js';
16+
import { UUIModalOpenEvent } from './modal.element.js';
17+
18+
@defineElement('modal-with-toasts-example')
19+
export class UUIModalWithToastsExampleElement extends LitElement {
20+
@state()
21+
private readonly _modals: TemplateResult<1>[] = [];
22+
23+
private _toastCounter = 0;
24+
private _dialogCounter = 0;
25+
private _messageCounter = 0;
26+
27+
private _onModalOpen = () => {
28+
const panel = this.shadowRoot?.getElementById('ai-chat-panel');
29+
if (panel?.matches(':popover-open')) {
30+
panel.hidePopover();
31+
panel.showPopover();
32+
}
33+
};
34+
35+
override connectedCallback() {
36+
super.connectedCallback();
37+
document.addEventListener(UUIModalOpenEvent, this._onModalOpen, true);
38+
this.updateComplete.then(() => {
39+
this.shadowRoot?.getElementById('toast-container')?.showPopover();
40+
});
41+
}
42+
43+
override disconnectedCallback() {
44+
super.disconnectedCallback();
45+
document.removeEventListener(UUIModalOpenEvent, this._onModalOpen, true);
46+
}
47+
48+
#addToast() {
49+
this._toastCounter++;
50+
const container = this.shadowRoot?.getElementById('toast-container');
51+
if (!container) return;
52+
53+
const toast = document.createElement('uui-toast-notification') as any;
54+
toast.color = ['', 'positive', 'danger'][Math.floor(Math.random() * 3)];
55+
56+
const layout = document.createElement(
57+
'uui-toast-notification-layout',
58+
) as any;
59+
layout.headline = 'Toast #' + this._toastCounter;
60+
61+
const message = document.createElement('span');
62+
message.textContent = 'Can you click me while a modal is open?';
63+
layout.appendChild(message);
64+
65+
toast.appendChild(layout);
66+
container.appendChild(toast);
67+
68+
container.hidePopover();
69+
container.showPopover();
70+
}
71+
72+
#toggleChat() {
73+
const panel = this.shadowRoot?.getElementById('ai-chat-panel');
74+
if (!panel) return;
75+
76+
if (panel.matches(':popover-open')) {
77+
panel.hidePopover();
78+
} else {
79+
panel.showPopover();
80+
}
81+
}
82+
83+
#addMessage() {
84+
this._messageCounter++;
85+
const chat = this.shadowRoot?.getElementById('chat-messages');
86+
if (!chat) return;
87+
88+
const msg = document.createElement('p');
89+
msg.textContent = 'Message #' + this._messageCounter + ' - I am working!';
90+
msg.style.padding = '4px 8px';
91+
msg.style.borderRadius = '4px';
92+
msg.style.background = 'var(--uui-color-selected, #e8f0fe)';
93+
chat.appendChild(msg);
94+
msg.scrollIntoView({ behavior: 'smooth' });
95+
}
96+
97+
#addDialog() {
98+
this._dialogCounter++;
99+
const modalRef = createRef<UUIModalElement>();
100+
const num = this._dialogCounter;
101+
const maxWidth = Math.max(300, 500 - (num - 1) * 50);
102+
103+
this._modals.push(html`
104+
<uui-modal-dialog ${ref(modalRef)}>
105+
<uui-dialog-layout
106+
style="max-width: ${maxWidth}px"
107+
headline="Dialog #${num}">
108+
<p>A modal dialog is open. Try clicking a toast notification!</p>
109+
${this.#renderButtons()}
110+
<uui-button
111+
@click=${() => modalRef.value?.close()}
112+
slot="actions"
113+
look="primary">
114+
close
115+
</uui-button>
116+
</uui-dialog-layout>
117+
</uui-modal-dialog>
118+
`);
119+
120+
this.requestUpdate('_modals');
121+
}
122+
123+
#addSidebar(size: 'small' | 'medium' | 'large' | 'full') {
124+
const modalRef = createRef<UUIModalElement>();
125+
126+
this._modals.push(html`
127+
<uui-modal-sidebar ${ref(modalRef)} size=${size}>
128+
<div
129+
style="background: var(--uui-color-background); display: flex; flex-direction: column; height: 100%;">
130+
<uui-box headline="Sidebar" style="margin: 12px">
131+
<p>A sidebar is open. Try clicking a toast notification!</p>
132+
${this.#renderButtons()}
133+
</uui-box>
134+
<div class="sidebar-buttons">
135+
<uui-button
136+
@click=${() => modalRef.value?.close()}
137+
slot="actions"
138+
look="primary">
139+
close
140+
</uui-button>
141+
</div>
142+
</div>
143+
</uui-modal-sidebar>
144+
`);
145+
146+
this.requestUpdate('_modals');
147+
}
148+
149+
#renderButtons() {
150+
return html`
151+
<div
152+
style="width: max-content; border: 1px solid; padding: 16px; display: flex; flex-direction: column; gap: 8px;">
153+
<uui-button look="primary" @click=${this.#addToast}>
154+
Add Toast
155+
</uui-button>
156+
<uui-button look="secondary" @click=${this.#toggleChat}>
157+
Toggle AI Chat
158+
</uui-button>
159+
<hr style="width: 100%" />
160+
<strong>Dialog</strong>
161+
<uui-button look="secondary" @click=${this.#addDialog}>
162+
Open
163+
</uui-button>
164+
<hr style="width: 100%" />
165+
<strong>Sidebars</strong>
166+
<div>
167+
<uui-button
168+
look="secondary"
169+
@click=${() => this.#addSidebar('small')}>
170+
small
171+
</uui-button>
172+
<uui-button
173+
look="secondary"
174+
@click=${() => this.#addSidebar('medium')}>
175+
medium
176+
</uui-button>
177+
<uui-button
178+
look="secondary"
179+
@click=${() => this.#addSidebar('large')}>
180+
large
181+
</uui-button>
182+
<uui-button look="secondary" @click=${() => this.#addSidebar('full')}>
183+
full
184+
</uui-button>
185+
</div>
186+
</div>
187+
`;
188+
}
189+
190+
render() {
191+
return html`
192+
${this.#renderButtons()}
193+
<uui-modal-container>${this._modals}</uui-modal-container>
194+
<uui-toast-notification-container
195+
id="toast-container"
196+
auto-close="10000"
197+
bottom-up
198+
popover="manual"
199+
style="position: fixed; inset: 0; height: 100vh; padding: var(--uui-size-layout-1); background: none; border: none;">
200+
</uui-toast-notification-container>
201+
<div
202+
id="ai-chat-panel"
203+
popover="manual"
204+
style="position: fixed; top: 0; right: 0; bottom: 0; width: 400px; max-width: 100%; margin: 0; padding: 0; border: none; background: var(--uui-color-surface, #fff); box-shadow: var(--uui-shadow-depth-5); box-sizing: border-box;">
205+
<div
206+
style="padding: 16px; height: 100%; box-sizing: border-box; display: flex; flex-direction: column;">
207+
<div
208+
style="display: flex; justify-content: space-between; align-items: center;">
209+
<h3 style="margin: 0;">AI Chat</h3>
210+
<uui-button look="secondary" compact @click=${this.#toggleChat}
211+
>✕</uui-button
212+
>
213+
</div>
214+
<div
215+
id="chat-messages"
216+
style="flex: 1; overflow: auto; border: 1px solid var(--uui-color-border); border-radius: 4px; padding: 8px; margin-bottom: 8px;">
217+
<p>Chat messages would appear here...</p>
218+
</div>
219+
<uui-button
220+
look="primary"
221+
style="align-self: flex-end;"
222+
@click=${this.#addMessage}
223+
>Send</uui-button
224+
>
225+
</div>
226+
</div>
227+
`;
228+
}
229+
230+
static override readonly styles = css`
231+
.sidebar-buttons {
232+
margin-top: auto;
233+
display: flex;
234+
align-items: center;
235+
justify-content: end;
236+
padding: 16px;
237+
background: var(--uui-color-surface);
238+
box-shadow: var(--uui-shadow-depth-4);
239+
}
240+
`;
241+
}

src/components/modal/modal.story.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import readme from './README.md?raw';
44
import { html } from 'lit';
55
import type { Meta, StoryObj } from '@storybook/web-components-vite';
66
import './modal-example.element';
7+
import './modal-with-toasts-example.element';
78

89
const meta: Meta = {
910
id: 'uui-modal',
@@ -22,3 +23,7 @@ type Story = StoryObj;
2223
export const Default: Story = {
2324
render: () => html`<modal-example></modal-example>`,
2425
};
26+
27+
export const ModalWithOverlays: Story = {
28+
render: () => html`<modal-with-toasts-example></modal-with-toasts-example>`,
29+
};

0 commit comments

Comments
 (0)