Mercurial > libervia-web
comparison libervia/web/pages/_browser/dialog.py @ 1625:698eaabfca0e
browser (chat): side/panel and keyword handling:
- `dialog.Modal` can now be used with a `position` argument. The default `center` keeps
the old behaviour of modal in the middle of the screen, while using one of the
direction makes the modal appear from top/bottom/right/left with an animation. The
closing cross has been removed in favor of clicking/touching any part outside of the
modal.
- A method to create mockup clones of elements is now used to make placeholders. It is
used for the input panel in the main area when it is moved to a sub-panel modal, this
way there is not element disappearing.
- Clicking on a keyword now shows a sub-messages panel with all messages with this
keyword, in a similar way as for threads. Writing a messages there will add the keyword
automatically.
- Sub-messages panel will auto-scroll when messages are added.
rel 458
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 06 Jun 2025 11:08:05 +0200 |
parents | 3a60bf3762ef |
children |
comparison
equal
deleted
inserted
replaced
1624:fd421f1be8f5 | 1625:698eaabfca0e |
---|---|
1 """manage common dialogs""" | 1 """manage common dialogs""" |
2 | 2 |
3 from typing import Callable, Literal | |
3 from browser import document, window, timer, console as log | 4 from browser import document, window, timer, console as log |
4 from template import Template | 5 from template import Template |
5 | 6 |
6 log.warning = log.warn | 7 log.warning = log.warn |
7 | 8 |
181 elt.bind('click', lambda __: self.retry(notif_elt)) | 182 elt.bind('click', lambda __: self.retry(notif_elt)) |
182 | 183 |
183 | 184 |
184 class Modal: | 185 class Modal: |
185 | 186 |
186 def __init__(self, content_elt, is_card=False, closable=False, close_cb=None): | 187 def __init__( |
187 """Init a Modal instance. | 188 self, |
188 | 189 content_elt, |
189 @param content_elt: Content of the modal. | 190 is_card: bool = False, |
190 @param is_card: If True, a Modal card will be used. The ``content_elt`` must be a | 191 closable: bool = False, |
191 <div> with the "modal-card" class. | 192 close_cb: Callable|None = None, |
192 @param closable: if True, add a close cross at the top right of the modal. | 193 position: Literal["center", "left", "right", "top", "bottom"] = "center" |
193 """ | 194 ) -> None: |
195 self.position = position | |
194 self.is_card = is_card | 196 self.is_card = is_card |
195 if is_card: | 197 if is_card and position == 'center': |
196 if not content_elt.classList.contains("modal-card"): | 198 if not content_elt.classList.contains("modal-card"): |
197 raise ValueError( | 199 raise ValueError( |
198 'Element must have a "modal-card" class when `is_card` is used' | 200 'Element must have a "modal-card" class when `is_card` is used for center modal' |
199 ) | 201 ) |
200 self.closable = closable | 202 self.closable = closable |
201 self._close_cb = close_cb | 203 self._close_cb = close_cb |
202 self._tpl = Template("dialogs/modal.html") | 204 self._tpl = Template("dialogs/modal.html") |
203 self.content_elt = content_elt | 205 self.content_elt = content_elt |
204 self._modal_elt = None | 206 self._modal_elt = None |
205 self.reset() | 207 self._closing = False |
206 | 208 self._is_panel = position != 'center' |
207 def reset(self): | 209 |
208 """Reset values of callbacks and notif element""" | 210 def _cleanup(self): |
209 if self._modal_elt is not None: | 211 if self._modal_elt is not None: |
210 self._modal_elt.remove() | 212 self._modal_elt.remove() |
211 self._modal_elt = None | 213 self._modal_elt = None |
212 | 214 self._closing = False |
213 def _default_cancel_cb(self, evt, notif_elt): | |
214 notif_elt.remove() | |
215 | 215 |
216 def close(self): | 216 def close(self): |
217 """Close the dialog.""" | 217 if self._modal_elt is None or self._closing: |
218 if self._modal_elt is None: | 218 return |
219 log.warning("Calling close on an unshown dialog.") | 219 |
220 self.reset() | 220 self._closing = True |
221 | 221 |
222 def on_close_click(self, evt) -> None: | 222 self._modal_elt.classList.remove('is-active') |
223 evt.preventDefault() | 223 |
224 evt.stopPropagation() | 224 if self._is_panel: |
225 if self._close_cb is not None: | 225 self._modal_elt.classList.add('closing') |
226 self._close_cb() | 226 |
227 self.close() | 227 def on_close_finished(): |
228 if self._close_cb: | |
229 self._close_cb() | |
230 self._cleanup() | |
231 | |
232 timer.set_timeout(on_close_finished, 300) | |
233 | |
234 def on_background_click(self, evt): | |
235 if self.closable and not self._closing: | |
236 evt.preventDefault() | |
237 evt.stopPropagation() | |
238 self.close() | |
228 | 239 |
229 def show(self) -> None: | 240 def show(self) -> None: |
230 modal_elt = self._tpl.get_elt({ | 241 if self._modal_elt: |
242 self._cleanup() | |
243 | |
244 self._modal_elt = self._tpl.get_elt({ | |
231 "closable": self.closable, | 245 "closable": self.closable, |
232 }) | 246 "position": self.position, |
233 self._modal_elt = modal_elt | 247 "is_panel": self._is_panel |
234 if self.is_card: | 248 }) |
235 container_elt = modal_elt | 249 |
250 if self.position == 'center': | |
251 container_elt = self._modal_elt.select_one(".modal-content") | |
252 container_elt <= self.content_elt | |
236 else: | 253 else: |
237 container_elt = modal_elt.select_one(".modal-content") | 254 container_elt = self._modal_elt.select_one(".modal-panel-container") |
238 container_elt <= self.content_elt | 255 container_elt <= self.content_elt |
239 | 256 container_elt.classList.add(f"is-{self.position}") |
240 document['notifs_area'] <= modal_elt | 257 |
241 for ok_elt in modal_elt.select(".modal-close"): | 258 document['notifs_area'] <= self._modal_elt |
242 ok_elt.bind("click", self.on_close_click) | 259 |
260 if self.closable: | |
261 bg = self._modal_elt.select_one(".modal-background, .modal-panel-background") | |
262 if bg: | |
263 bg.bind("click", self.on_background_click) | |
264 | |
265 # Add active class after a small delay to trigger animation | |
266 timer.set_timeout(lambda: self._modal_elt.classList.add('is-active'), 10) | |
243 | 267 |
244 | 268 |
245 notification = Notification() | 269 notification = Notification() |