Mercurial > libervia-web
diff 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 | 332822ceae85 |
line wrap: on
line diff
--- a/libervia/web/pages/_browser/dialog.py Wed May 21 15:58:56 2025 +0200 +++ b/libervia/web/pages/_browser/dialog.py Fri Jun 06 11:08:05 2025 +0200 @@ -1,5 +1,6 @@ """manage common dialogs""" +from typing import Callable, Literal from browser import document, window, timer, console as log from template import Template @@ -183,63 +184,86 @@ class Modal: - def __init__(self, content_elt, is_card=False, closable=False, close_cb=None): - """Init a Modal instance. - - @param content_elt: Content of the modal. - @param is_card: If True, a Modal card will be used. The ``content_elt`` must be a - <div> with the "modal-card" class. - @param closable: if True, add a close cross at the top right of the modal. - """ + def __init__( + self, + content_elt, + is_card: bool = False, + closable: bool = False, + close_cb: Callable|None = None, + position: Literal["center", "left", "right", "top", "bottom"] = "center" + ) -> None: + self.position = position self.is_card = is_card - if is_card: + if is_card and position == 'center': if not content_elt.classList.contains("modal-card"): raise ValueError( - 'Element must have a "modal-card" class when `is_card` is used' + 'Element must have a "modal-card" class when `is_card` is used for center modal' ) self.closable = closable self._close_cb = close_cb self._tpl = Template("dialogs/modal.html") self.content_elt = content_elt self._modal_elt = None - self.reset() + self._closing = False + self._is_panel = position != 'center' - def reset(self): - """Reset values of callbacks and notif element""" + def _cleanup(self): if self._modal_elt is not None: self._modal_elt.remove() self._modal_elt = None - - def _default_cancel_cb(self, evt, notif_elt): - notif_elt.remove() + self._closing = False def close(self): - """Close the dialog.""" - if self._modal_elt is None: - log.warning("Calling close on an unshown dialog.") - self.reset() + if self._modal_elt is None or self._closing: + return + + self._closing = True + + self._modal_elt.classList.remove('is-active') + + if self._is_panel: + self._modal_elt.classList.add('closing') - def on_close_click(self, evt) -> None: - evt.preventDefault() - evt.stopPropagation() - if self._close_cb is not None: - self._close_cb() - self.close() + def on_close_finished(): + if self._close_cb: + self._close_cb() + self._cleanup() + + timer.set_timeout(on_close_finished, 300) + + def on_background_click(self, evt): + if self.closable and not self._closing: + evt.preventDefault() + evt.stopPropagation() + self.close() def show(self) -> None: - modal_elt = self._tpl.get_elt({ + if self._modal_elt: + self._cleanup() + + self._modal_elt = self._tpl.get_elt({ "closable": self.closable, + "position": self.position, + "is_panel": self._is_panel }) - self._modal_elt = modal_elt - if self.is_card: - container_elt = modal_elt + + if self.position == 'center': + container_elt = self._modal_elt.select_one(".modal-content") + container_elt <= self.content_elt else: - container_elt = modal_elt.select_one(".modal-content") - container_elt <= self.content_elt + container_elt = self._modal_elt.select_one(".modal-panel-container") + container_elt <= self.content_elt + container_elt.classList.add(f"is-{self.position}") + + document['notifs_area'] <= self._modal_elt - document['notifs_area'] <= modal_elt - for ok_elt in modal_elt.select(".modal-close"): - ok_elt.bind("click", self.on_close_click) + if self.closable: + bg = self._modal_elt.select_one(".modal-background, .modal-panel-background") + if bg: + bg.bind("click", self.on_background_click) + + # Add active class after a small delay to trigger animation + timer.set_timeout(lambda: self._modal_elt.classList.add('is-active'), 10) notification = Notification()