Mercurial > libervia-web
comparison libervia/web/pages/_browser/slideshow.py @ 1518:eb00d593801d
refactoring: rename `libervia` to `libervia.web` + update imports following backend changes
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 16:49:28 +0200 |
parents | libervia/pages/_browser/slideshow.py@ff5005783443 |
children | 860e33127245 |
comparison
equal
deleted
inserted
replaced
1517:b8ed9726525b | 1518:eb00d593801d |
---|---|
1 from browser import document, window, html, timer, DOMNode | |
2 from js_modules.swiper import Swiper | |
3 from template import Template | |
4 | |
5 | |
6 class SlideShow: | |
7 | |
8 def __init__(self): | |
9 self.swiper = None | |
10 slideshow_tpl = Template('photo/slideshow.html') | |
11 self.slideshow_elt = slideshow_tpl.get_elt() | |
12 self.comments_count_elt = self.slideshow_elt.select_one('.comments__count') | |
13 self.wrapper = self.slideshow_elt.select_one(".swiper-wrapper") | |
14 self.hidden_elts = {} | |
15 self.control_hidden = False | |
16 self.click_timer = None | |
17 self._class_to_remove = set() | |
18 self._exit_callback = None | |
19 | |
20 @property | |
21 def current_slide(self): | |
22 if self.swiper is None: | |
23 return None | |
24 try: | |
25 return DOMNode(self.swiper.slides[self.swiper.realIndex]) | |
26 # getting missing item in JS arrays returns KeyError | |
27 except KeyError: | |
28 return None | |
29 | |
30 @property | |
31 def current_item(self): | |
32 """item attached to the current slide, if any""" | |
33 current = self.current_slide | |
34 if current is None: | |
35 return None | |
36 try: | |
37 return current._item | |
38 except AttributeError: | |
39 return None | |
40 | |
41 @property | |
42 def current_options(self): | |
43 """options attached to the current slide, if any""" | |
44 current = self.current_slide | |
45 if current is None: | |
46 return None | |
47 try: | |
48 return current._options | |
49 except AttributeError: | |
50 return None | |
51 | |
52 @property | |
53 def index(self): | |
54 if self.swiper is None: | |
55 return None | |
56 return self.swiper.realIndex | |
57 | |
58 @index.setter | |
59 def index(self, idx): | |
60 if self.swiper is not None: | |
61 self.swiper.slideTo(idx, 0) | |
62 | |
63 def attach(self): | |
64 # we hide other elts to avoid scrolling issues | |
65 for elt in document.body.children: | |
66 try: | |
67 self.hidden_elts[elt] = elt.style.display | |
68 except AttributeError: | |
69 pass | |
70 # FIXME: this is a workaround needed because Brython's children method | |
71 # is returning all nodes, | |
72 # cf. https://github.com/brython-dev/brython/issues/1657 | |
73 # to be removed when Brython is fixed. | |
74 else: | |
75 elt.style.display = "none" | |
76 document.body <= self.slideshow_elt | |
77 self.swiper = Swiper.new( | |
78 ".swiper-container", | |
79 { | |
80 # default 0 value results in lot of accidental swipes, notably when media | |
81 # player is used | |
82 "threshold": 10, | |
83 "pagination": { | |
84 "el": ".swiper-pagination", | |
85 }, | |
86 "navigation": { | |
87 "nextEl": ".swiper-button-next", | |
88 "prevEl": ".swiper-button-prev", | |
89 }, | |
90 "scrollbar": { | |
91 "el": ".swiper-scrollbar", | |
92 }, | |
93 "grabCursor": True, | |
94 "keyboard": { | |
95 "enabled": True, | |
96 "onlyInViewport": False, | |
97 }, | |
98 "mousewheel": True, | |
99 "zoom": { | |
100 "maxRatio": 15, | |
101 "toggle": False, | |
102 }, | |
103 } | |
104 ) | |
105 window.addEventListener("keydown", self.on_key_down, True) | |
106 self.slideshow_elt.select_one(".click_to_close").bind("click", self.on_close) | |
107 self.slideshow_elt.select_one(".click_to_comment").bind("click", self.on_comment) | |
108 | |
109 # we don't use swiper.on for "click" and "dblclick" (or "doubleTap" in swiper | |
110 # terms) because it breaks event propagation management, which cause trouble with | |
111 # media player | |
112 self.slideshow_elt.bind("click", self.on_click) | |
113 self.slideshow_elt.bind("dblclick", self.on_dblclick) | |
114 self.swiper.on("slideChange", self.on_slide_change) | |
115 self.on_slide_change(self.swiper) | |
116 self.fullscreen(True) | |
117 | |
118 def add_slide(self, elt, item_data=None, options=None): | |
119 slide_elt = html.DIV(Class="swiper-slide") | |
120 zoom_cont_elt = html.DIV(Class="swiper-zoom-container") | |
121 slide_elt <= zoom_cont_elt | |
122 zoom_cont_elt <= elt | |
123 slide_elt._item = item_data | |
124 if options is not None: | |
125 slide_elt._options = options | |
126 self.swiper.appendSlide([slide_elt]) | |
127 self.swiper.update() | |
128 | |
129 def quit(self): | |
130 # we unhide | |
131 for elt, display in self.hidden_elts.items(): | |
132 elt.style.display = display | |
133 self.hidden_elts.clear() | |
134 self.slideshow_elt.remove() | |
135 self.slideshow_elt = None | |
136 self.swiper.destroy(True, True) | |
137 self.swiper = None | |
138 | |
139 def fullscreen(self, active=None): | |
140 """Activate/desactivate fullscreen | |
141 | |
142 @param acvite: can be: | |
143 - True to activate | |
144 - False to desactivate | |
145 - Auto to switch fullscreen mode | |
146 """ | |
147 try: | |
148 fullscreen_elt = document.fullscreenElement | |
149 request_fullscreen = self.slideshow_elt.requestFullscreen | |
150 except AttributeError: | |
151 print("fullscreen is not available on this browser") | |
152 else: | |
153 if active is None: | |
154 active = fullscreen_elt == None | |
155 if active: | |
156 request_fullscreen() | |
157 else: | |
158 try: | |
159 document.exitFullscreen() | |
160 except AttributeError: | |
161 print("exitFullscreen not available on this browser") | |
162 | |
163 def on_key_down(self, evt): | |
164 if evt.key == 'Escape': | |
165 self.quit() | |
166 else: | |
167 return | |
168 evt.preventDefault() | |
169 | |
170 def on_slide_change(self, swiper): | |
171 if self._exit_callback is not None: | |
172 self._exit_callback() | |
173 self._exit_callback = None | |
174 item = self.current_item | |
175 if item is not None: | |
176 comments_count = item.get('comments_count') | |
177 self.comments_count_elt.text = comments_count or '' | |
178 | |
179 for cls in self._class_to_remove: | |
180 self.slideshow_elt.classList.remove(cls) | |
181 | |
182 self._class_to_remove.clear() | |
183 | |
184 options = self.current_options | |
185 if options is not None: | |
186 for flag in options.get('flags', []): | |
187 cls = f"flag_{flag.lower()}" | |
188 self.slideshow_elt.classList.add(cls) | |
189 self._class_to_remove.add(cls) | |
190 self._exit_callback = options.get("exit_callback", None) | |
191 | |
192 def toggle_hide_controls(self, evt): | |
193 self.click_timer = None | |
194 # we don't want to hide controls when a control is clicked | |
195 # so we check all ancestors if we are not in a control | |
196 current = evt.target | |
197 while current and current != self.slideshow_elt: | |
198 print(f"current: {current}") | |
199 if 'slideshow_control' in current.classList: | |
200 return | |
201 current = current.parent | |
202 for elt in self.slideshow_elt.select('.slideshow_control'): | |
203 elt.style.display = '' if self.control_hidden else 'none' | |
204 self.control_hidden = not self.control_hidden | |
205 | |
206 def on_click(self, evt): | |
207 evt.stopPropagation() | |
208 evt.preventDefault() | |
209 # we use a timer so double tap can cancel the click | |
210 # this avoid double tap side effect | |
211 if self.click_timer is None: | |
212 self.click_timer = timer.set_timeout( | |
213 lambda: self.toggle_hide_controls(evt), 300) | |
214 | |
215 def on_dblclick(self, evt): | |
216 evt.stopPropagation() | |
217 evt.preventDefault() | |
218 if self.click_timer is not None: | |
219 timer.clear_timeout(self.click_timer) | |
220 self.click_timer = None | |
221 if self.swiper.zoom.scale != 1: | |
222 self.swiper.zoom.toggle() | |
223 else: | |
224 # "in" is reserved in Python, so we call it using dict syntax | |
225 self.swiper.zoom["in"]() | |
226 | |
227 def on_close(self, evt): | |
228 evt.stopPropagation() | |
229 evt.preventDefault() | |
230 self.quit() | |
231 | |
232 def on_comment_close(self, evt): | |
233 evt.stopPropagation() | |
234 side_panel = self.comments_panel_elt.select_one('.comments_side_panel') | |
235 side_panel.classList.remove('open') | |
236 side_panel.bind("transitionend", lambda evt: self.comments_panel_elt.remove()) | |
237 | |
238 def on_comments_panel_click(self, evt): | |
239 # we stop stop propagation to avoid the closing of the panel | |
240 evt.stopPropagation() | |
241 | |
242 def on_comment(self, evt): | |
243 item = self.current_item | |
244 if item is None: | |
245 return | |
246 comments_panel_tpl = Template('blog/comments_panel.html') | |
247 try: | |
248 comments = item['comments']['items'] | |
249 except KeyError: | |
250 comments = [] | |
251 self.comments_panel_elt = comments_panel_tpl.get_elt({ | |
252 "comments": comments, | |
253 "comments_service": item['comments_service'], | |
254 "comments_node": item['comments_node'], | |
255 | |
256 }) | |
257 self.slideshow_elt <= self.comments_panel_elt | |
258 side_panel = self.comments_panel_elt.select_one('.comments_side_panel') | |
259 timer.set_timeout(lambda: side_panel.classList.add("open"), 0) | |
260 for close_elt in self.comments_panel_elt.select('.click_to_close'): | |
261 close_elt.bind("click", self.on_comment_close) | |
262 side_panel.bind("click", self.on_comments_panel_click) |