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)