@@ -3,7 +3,7 @@ import '../participant_profile/avatar_icon';
33import { MobxLitElement } from '@adobe/lit-mobx' ;
44
55import { CSSResultGroup , html , nothing } from 'lit' ;
6- import { customElement , property } from 'lit/decorators.js' ;
6+ import { customElement , property , state } from 'lit/decorators.js' ;
77import { classMap } from 'lit/directives/class-map.js' ;
88import { unsafeHTML } from 'lit/directives/unsafe-html.js' ;
99
@@ -32,147 +32,146 @@ export class ChatMessageComponent extends MobxLitElement {
3232 private readonly participantService = core . getService ( ParticipantService ) ;
3333
3434 @property ( ) chat : ChatMessage | undefined = undefined ;
35- private maximizedImageUrl : string | null = null ;
35+ @ state ( ) private maximizedImageUrl : string | null = null ;
3636 private modalElement : HTMLDivElement | null = null ;
37- private keyboardHandler : ( ( e : KeyboardEvent ) => void ) | null = null ;
3837
3938 private openImageModal ( imageUrl : string ) {
4039 this . maximizedImageUrl = imageUrl ;
41- this . renderModalToBody ( ) ;
4240 }
4341
4442 private closeImageModal ( ) {
4543 this . maximizedImageUrl = null ;
44+ }
45+
46+ private handleEscapeKey = ( e : KeyboardEvent ) => {
47+ if ( e . key === 'Escape' && this . maximizedImageUrl ) {
48+ this . closeImageModal ( ) ;
49+ }
50+ } ;
51+
52+ override connectedCallback ( ) {
53+ super . connectedCallback ( ) ;
54+ document . addEventListener ( 'keydown' , this . handleEscapeKey ) ;
55+ }
56+
57+ override disconnectedCallback ( ) {
58+ super . disconnectedCallback ( ) ;
59+ document . removeEventListener ( 'keydown' , this . handleEscapeKey ) ;
4660 this . removeModalFromBody ( ) ;
4761 }
4862
63+ override updated ( changedProperties : Map < string , unknown > ) {
64+ super . updated ( changedProperties ) ;
65+
66+ if ( changedProperties . has ( 'maximizedImageUrl' ) ) {
67+ if ( this . maximizedImageUrl ) {
68+ this . renderModalToBody ( ) ;
69+ } else {
70+ this . removeModalFromBody ( ) ;
71+ }
72+ }
73+ }
74+
4975 private renderModalToBody ( ) {
5076 // Remove existing modal if present
5177 this . removeModalFromBody ( ) ;
5278
5379 // Create modal element
5480 this . modalElement = document . createElement ( 'div' ) ;
5581 this . modalElement . className = 'chat-image-modal' ;
56- this . modalElement . innerHTML = `
57- <div class="modal-backdrop">
58- <div class="modal-content">
59- <button class="close-button">✕</button>
60- <img src="${ this . maximizedImageUrl } " alt="Maximized Image" />
61- </div>
62- </div>
82+ this . modalElement . style . cssText = `
83+ position: fixed;
84+ inset: 0;
85+ background: rgba(0, 0, 0, 0.92);
86+ display: flex;
87+ align-items: center;
88+ justify-content: center;
89+ z-index: 1;
90+ cursor: pointer;
91+ animation: fadeIn 0.2s ease;
6392 ` ;
6493
65- // Add event listeners
66- const backdrop = this . modalElement . querySelector ( '.modal-backdrop' ) ;
67- const closeButton = this . modalElement . querySelector ( '.close-button' ) ;
68- const img = this . modalElement . querySelector ( 'img' ) ;
94+ const content = document . createElement ( 'div' ) ;
95+ content . style . cssText = `
96+ position: relative;
97+ max-width: 95vw;
98+ max-height: 95vh;
99+ display: flex;
100+ align-items: center;
101+ justify-content: center;
102+ ` ;
69103
70- backdrop ?. addEventListener ( 'click' , ( ) => this . closeImageModal ( ) ) ;
71- closeButton ?. addEventListener ( 'click' , ( ) => this . closeImageModal ( ) ) ;
72- img ?. addEventListener ( 'click' , ( e ) => e . stopPropagation ( ) ) ;
104+ const closeButton = document . createElement ( 'button' ) ;
105+ closeButton . textContent = '✕' ;
106+ closeButton . style . cssText = `
107+ position: fixed;
108+ top: 20px;
109+ right: 20px;
110+ background: var(--md-sys-color-surface);
111+ color: var(--md-sys-color-on-surface);
112+ border: 2px solid var(--md-sys-color-outline);
113+ border-radius: 50%;
114+ width: 40px;
115+ height: 40px;
116+ font-size: 24px;
117+ font-weight: bold;
118+ cursor: pointer;
119+ display: flex;
120+ align-items: center;
121+ justify-content: center;
122+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
123+ transition: all 0.2s ease;
124+ z-index: 2;
125+ ` ;
126+ closeButton . addEventListener ( 'click' , ( ) => this . closeImageModal ( ) ) ;
127+ closeButton . addEventListener ( 'mouseenter' , ( ) => {
128+ closeButton . style . background = 'var(--md-sys-color-surface-variant)' ;
129+ closeButton . style . transform = 'scale(1.1)' ;
130+ } ) ;
131+ closeButton . addEventListener ( 'mouseleave' , ( ) => {
132+ closeButton . style . background = 'var(--md-sys-color-surface)' ;
133+ closeButton . style . transform = 'scale(1)' ;
134+ } ) ;
73135
74- // Add keyboard listener for Escape key
75- this . keyboardHandler = ( e : KeyboardEvent ) => {
76- if ( e . key === 'Escape' ) {
77- this . closeImageModal ( ) ;
78- }
79- } ;
80- document . addEventListener ( 'keydown' , this . keyboardHandler ) ;
136+ const img = document . createElement ( 'img' ) ;
137+ img . src = this . maximizedImageUrl ! ;
138+ img . alt = 'Maximized Image' ;
139+ img . style . cssText = `
140+ max-width: 100%;
141+ max-height: 95vh;
142+ object-fit: contain;
143+ border-radius: 8px;
144+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
145+ ` ;
146+ img . addEventListener ( 'click' , ( e ) => e . stopPropagation ( ) ) ;
81147
82- // Add styles
83- this . injectModalStyles ( ) ;
148+ content . appendChild ( img ) ;
149+ this . modalElement . appendChild ( content ) ;
150+ this . modalElement . appendChild ( closeButton ) ;
151+ this . modalElement . addEventListener ( 'click' , ( ) => this . closeImageModal ( ) ) ;
84152
85- // Append to body
153+ // Append modal to body
86154 document . body . appendChild ( this . modalElement ) ;
155+
156+ // Add fade-in keyframes if not already present
157+ if ( ! document . getElementById ( 'chat-modal-keyframes' ) ) {
158+ const style = document . createElement ( 'style' ) ;
159+ style . id = 'chat-modal-keyframes' ;
160+ style . textContent = `
161+ @keyframes fadeIn {
162+ from { opacity: 0; }
163+ to { opacity: 1; }
164+ }
165+ ` ;
166+ document . head . appendChild ( style ) ;
167+ }
87168 }
88169
89170 private removeModalFromBody ( ) {
90171 if ( this . modalElement ) {
91172 this . modalElement . remove ( ) ;
92173 this . modalElement = null ;
93174 }
94- // Remove keyboard listener
95- if ( this . keyboardHandler ) {
96- document . removeEventListener ( 'keydown' , this . keyboardHandler ) ;
97- this . keyboardHandler = null ;
98- }
99- }
100-
101- private injectModalStyles ( ) {
102- // Check if styles already exist
103- if ( document . getElementById ( 'chat-message-modal-styles' ) ) {
104- return ;
105- }
106-
107- const styleElement = document . createElement ( 'style' ) ;
108- styleElement . id = 'chat-message-modal-styles' ;
109- styleElement . textContent = `
110- .chat-image-modal .modal-backdrop {
111- position: fixed;
112- top: 0;
113- left: 0;
114- width: 100vw;
115- height: 100vh;
116- background: rgba(0, 0, 0, 0.92);
117- display: flex;
118- align-items: center;
119- justify-content: center;
120- z-index: 999999;
121- cursor: pointer;
122- animation: fadeIn 0.2s ease;
123- }
124-
125- .chat-image-modal .modal-content {
126- position: relative;
127- max-width: 95vw;
128- max-height: 95vh;
129- display: flex;
130- align-items: center;
131- justify-content: center;
132- }
133-
134- .chat-image-modal .modal-content img {
135- max-width: 100%;
136- max-height: 95vh;
137- object-fit: contain;
138- border-radius: 8px;
139- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
140- }
141-
142- .chat-image-modal .close-button {
143- position: absolute;
144- top: -40px;
145- right: 0;
146- background: #fff;
147- color: #000;
148- border: none;
149- border-radius: 50%;
150- width: 36px;
151- height: 36px;
152- font-size: 20px;
153- cursor: pointer;
154- display: flex;
155- align-items: center;
156- justify-content: center;
157- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
158- transition: background 0.2s ease;
159- }
160-
161- .chat-image-modal .close-button:hover {
162- background: #f0f0f0;
163- }
164-
165- @keyframes fadeIn {
166- from { opacity: 0; }
167- to { opacity: 1; }
168- }
169- ` ;
170- document . head . appendChild ( styleElement ) ;
171- }
172-
173- override disconnectedCallback ( ) {
174- super . disconnectedCallback ( ) ;
175- this . removeModalFromBody ( ) ;
176175 }
177176
178177 override render ( ) {
0 commit comments