comparison libervia/pages/_browser/editor.py @ 1415:8415d882b686

browser: new `editor` module: This module provide tools for dynamtic item edition. For now it implements forms autosave, and a tags editor.
author Goffi <goffi@goffi.org>
date Thu, 29 Apr 2021 16:45:39 +0200
parents
children 925a7c498cda
comparison
equal deleted inserted replaced
1414:97b8ce9ce54b 1415:8415d882b686
1 """text edition management"""
2
3 from browser import document, window, bind, timer
4 from browser.local_storage import storage
5 from browser.object_storage import ObjectStorage
6 from template import Template
7
8 object_storage = ObjectStorage(storage)
9 profile = window.profile
10
11 # how often we save forms, in seconds
12 AUTOSAVE_FREQUENCY = 20
13
14
15 def serialise_form(form_elt):
16 ret = {}
17 for elt in form_elt.elements:
18 if elt.tagName == "INPUT":
19 if elt.type in ("hidden", "submit"):
20 continue
21 elif elt.type == "text":
22 ret[elt.name] = elt.value
23 else:
24 print(f"elt.type not managet yet: {elt.type}")
25 continue
26 elif elt.tagName == "TEXTAREA":
27 ret[elt.name] = elt.value
28 elif elt.tagName in ("BUTTON",):
29 continue
30 else:
31 print(f"tag not managet yet: {elt.tagName}")
32 continue
33 return ret
34
35
36 def restore_form(form_elt, data):
37 for elt in form_elt.elements:
38 if elt.tagName not in ("INPUT", "TEXTAREA"):
39 continue
40 try:
41 value = data[elt.name]
42 except KeyError:
43 continue
44 else:
45 elt.value = value
46
47
48 def set_form_autosave(form_id):
49 """Save locally form data regularly and restore it until it's submitted
50
51 form is saved every AUTOSAVE_FREQUENCY seconds and when visibility is lost.
52 Saved data is restored when the method is called.
53 Saved data is cleared when the form is submitted.
54 """
55 if profile is None:
56 print(f"No session started, won't save and restore form {form_id}")
57 return
58
59 form_elt = document[form_id]
60 submitted = False
61
62 key = {"profile": profile, "type": "form_autosave", "form": form_id}
63 try:
64 form_saved_data = object_storage[key]
65 except KeyError:
66 last_serialised = None
67 else:
68 print(f"restoring content of form {form_id!r}")
69 last_serialised = form_saved_data
70 restore_form(form_elt, form_saved_data)
71
72 def save_form():
73 if not submitted:
74 nonlocal last_serialised
75 serialised = serialise_form(form_elt)
76 if serialised != last_serialised:
77 last_serialised = serialised
78 print(f"saving content of form {form_id!r}")
79 object_storage[key] = serialised
80
81 @bind(form_elt, "submit")
82 def on_submit(evt):
83 nonlocal submitted
84 submitted = True
85 print(f"clearing stored content of form {form_id!r}")
86 try:
87 del object_storage[key]
88 except KeyError:
89 print("key error")
90 pass
91
92 @bind(document, "visibilitychange")
93 def on_visibiliy_change(evt):
94 print("visibility change")
95 if document.visibilityState != "visible":
96 save_form()
97
98 timer.set_interval(save_form, AUTOSAVE_FREQUENCY * 1000)
99
100
101 class TagsEditor:
102
103 def __init__(self, input_selector):
104 print("installing Tags Editor")
105 self.input_elt = document.select_one(input_selector)
106 self.input_elt.style.display = "none"
107 tags_editor_tpl = Template('editor/tags_editor.html')
108 self.tag_tpl = Template('editor/tag.html')
109
110 editor_elt = tags_editor_tpl.get_elt()
111 self.input_elt.parent <= editor_elt
112 self.tag_input_elt = editor_elt.select_one(".tag_input")
113 self.tag_input_elt.bind("keydown", self.on_key_down)
114 self._current_tags = None
115 self.tags_map = {}
116 for tag in self.current_tags:
117 self.add_tag(tag, force=True)
118
119 @property
120 def current_tags(self):
121 if self._current_tags is None:
122 self._current_tags = {
123 t.strip() for t in self.input_elt.value.split(',') if t.strip()
124 }
125 return self._current_tags
126
127 @current_tags.setter
128 def current_tags(self, tags):
129 self._current_tags = tags
130
131 def add_tag(self, tag, force=False):
132 tag = tag.strip()
133 if not force and (not tag or tag in self.current_tags):
134 return
135 self.current_tags = self.current_tags | {tag}
136 self.input_elt.value = ','.join(self.current_tags)
137 tag_elt = self.tag_tpl.get_elt({"label": tag})
138 self.tags_map[tag] = tag_elt
139 self.tag_input_elt.parent.insertBefore(tag_elt, self.tag_input_elt)
140 tag_elt.select_one(".click_to_delete").bind(
141 "click", lambda evt: self.on_tag_click(evt, tag)
142 )
143
144 def remove_tag(self, tag):
145 try:
146 tag_elt = self.tags_map[tag]
147 except KeyError:
148 print(f"trying to remove an inexistant tag: {tag}")
149 else:
150 self.current_tags = self.current_tags - {tag}
151 self.input_elt.value = ','.join(self.current_tags)
152 tag_elt.remove()
153
154 def on_tag_click(self, evt, tag):
155 evt.stopPropagation()
156 self.remove_tag(tag)
157
158 def on_key_down(self, evt):
159 if evt.key in (",", "Enter"):
160 evt.stopPropagation()
161 evt.preventDefault()
162 self.add_tag(self.tag_input_elt.value)
163 self.tag_input_elt.value = ""