Nui
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
element.hpp
Go to the documentation of this file.
1#pragma once
2
7
9
10#include <concepts>
11#include <string>
12#include <vector>
13#include <memory>
14#include <functional>
15
16namespace Nui::Dom
17{
18 namespace Detail
19 {
21 {
22 val.call<void>("remove");
23 }
24 [[maybe_unused]] static void destroyByParentChildRemoval(Nui::val& val)
25 {
26 if (val.hasOwnProperty("parentNode"))
27 {
28 auto parent = val["parentNode"];
29 if (!parent.isUndefined() && !parent.isNull())
30 parent.call<void>("removeChild", val);
31 else
32 val.call<void>("remove");
33 }
34 else
35 val.call<void>("remove");
36 }
37 static void doNotDestroy(Nui::val&)
38 {}
39 }
40
42 {
43 public:
44 using collection_type = std::vector<std::shared_ptr<Element>>;
45 using iterator = collection_type::iterator;
46 using const_iterator = collection_type::const_iterator;
47 using value_type = collection_type::value_type;
48
49 explicit Element(HtmlElement const& elem)
50 : ChildlessElement{elem}
51 , children_{}
52 , unsetup_{}
53 , deferredSetup_{}
54 {}
55
63 : ChildlessElement{std::move(val)}
64 , children_{}
65 , unsetup_{}
66 , deferredSetup_{}
67 {}
68
69 explicit Element()
71 , children_{}
72 , unsetup_{}
73 , deferredSetup_{}
74 {}
75
76 Element(Element const&) = delete;
77 Element(Element&&) = delete;
78 Element& operator=(Element const&) = delete;
80
81 ~Element() override
82 {
84 destroy_(element_);
85 }
86
87 static std::shared_ptr<Element> makeElement(HtmlElement const& element)
88 {
89 auto elem = std::make_shared<Element>(element);
90 elem->setup(element);
91 return elem;
92 }
93
95 {
96 return std::begin(children_);
97 }
99 {
100 return std::end(children_);
101 }
103 {
104 return std::begin(children_);
105 }
107 {
108 return std::end(children_);
109 }
110
111 void appendElement(std::invocable<Element&, Renderer const&> auto&& fn)
112 {
113 fn(*this, Renderer{.type = RendererType::Append});
114 }
115 auto appendElement(HtmlElement const& element)
116 {
117 auto elem = makeElement(element);
118 element_.call<Nui::val>("appendChild", elem->element_);
119 if (elem->deferredSetup_)
120 elem->deferredSetup_(element);
121 return children_.emplace_back(std::move(elem));
122 }
123 auto slotFor(value_type const& value)
124 {
126 if (unsetup_)
127 unsetup_();
128 unsetup_ = {};
129
130 element_.call<Nui::val>("replaceWith", value->val());
131 element_ = value->val();
132 destroy_ = Detail::doNotDestroy;
133 return shared_from_base<Element>();
134 }
135 void replaceElement(std::invocable<Element&, Renderer const&> auto&& fn)
136 {
137 fn(*this, Renderer{.type = RendererType::Replace});
138 }
139 auto replaceElement(HtmlElement const& element)
140 {
141 replaceElementImpl(element);
142 return shared_from_base<Element>();
143 }
144 auto emplaceElement(HtmlElement const& element)
145 {
146 if (!element_.isUndefined())
147 throw std::runtime_error("Element is not empty, cannot emplace");
148
149 element_ = createElement(element);
150 setup(element);
151 if (deferredSetup_)
152 deferredSetup_(element);
153 return shared_from_base<Element>();
154 }
155 auto emplaceElement(std::invocable<Element&, Renderer const&> auto&& fn)
156 {
157 fn(*this, Renderer{.type = RendererType::Emplace});
158 return shared_from_base<Element>();
159 }
160
161 void setTextContent(std::string const& text)
162 {
163 element_.set("textContent", text);
164 }
165 void setTextContent(char const* text)
166 {
167 element_.set("textContent", text);
168 }
169 void setTextContent(std::string_view text)
170 {
171 element_.set("textContent", text);
172 }
173
174 void
175 appendElements(std::vector<std::function<std::shared_ptr<Element>(Element&, Renderer const&)>> const& elements)
176 {
177 for (auto const& element : elements)
178 appendElement(element);
179 }
180
181 auto insert(iterator where, HtmlElement const& element)
182 {
183 if (where == end())
184 return appendElement(element);
185 auto elem = makeElement(element);
186 element_.call<Nui::val>("insertBefore", elem->element_, (*where)->element_);
187 if (elem->deferredSetup_)
188 elem->deferredSetup_(element);
189 return *children_.insert(where, std::move(elem));
190 }
191
195 void setup(HtmlElement const& element)
196 {
197 std::vector<std::function<void()>> eventClearers;
198 eventClearers.reserve(element.attributes().size());
199 std::vector<std::size_t> deferredIndices;
200
201 for (std::size_t i = 0; i != element.attributes().size(); ++i)
202 {
203 auto const& attribute = element.attributes()[i];
204
205 if (attribute.defer())
206 {
207 deferredIndices.push_back(i);
208 continue;
209 }
210 if (attribute.isRegular())
211 attribute.setOn(*this);
212
213 auto clear = attribute.getEventClear();
214 if (clear)
215 {
216 const auto id = attribute.createEvent(weak_from_base<Element>());
218 {
219 eventClearers.push_back([clear = std::move(clear), id]() {
220 clear(id);
221 });
222 }
223 }
224 }
225 if (!eventClearers.empty())
226 {
227 eventClearers.shrink_to_fit();
228 unsetup_ = [eventClearers = std::move(eventClearers)]() {
229 for (auto const& clear : eventClearers)
230 clear();
231 };
232 }
233 else
234 {
235 unsetup_ = []() {};
236 }
237
238 if (!deferredIndices.empty())
239 {
240 deferredSetup_ = [this, deferredIndices = std::move(deferredIndices)](HtmlElement const& element) {
241 std::vector<std::function<void()>> eventClearers;
242 eventClearers.reserve(deferredIndices.size());
243
244 for (auto index : deferredIndices)
245 {
246 auto const& attribute = element.attributes()[index];
247
248 if (attribute.isRegular())
249 attribute.setOn(*this);
250
251 auto clear = attribute.getEventClear();
252 if (clear)
253 {
254 const auto id = attribute.createEvent(weak_from_base<Element>());
256 {
257 eventClearers.push_back([clear = std::move(clear), id]() {
258 clear(id);
259 });
260 }
261 }
262 }
263 if (!eventClearers.empty())
264 {
265 eventClearers.shrink_to_fit();
266 unsetup_ = [unsetup1 = std::move(unsetup_), eventClearers = std::move(eventClearers)]() {
267 unsetup1();
268 for (auto const& clear : eventClearers)
269 clear();
270 };
271 }
272 };
273 }
274 }
275
276 auto insert(std::size_t where, HtmlElement const& element)
277 {
278 if (where >= children_.size())
279 return appendElement(element);
280 return insert(begin() + static_cast<decltype(children_)::difference_type>(where), element);
281 }
282
283 auto& operator[](std::size_t index)
284 {
285 return children_[index];
286 }
287 auto const& operator[](std::size_t index) const
288 {
289 return children_[index];
290 }
291
292 auto erase(iterator where)
293 {
294 return children_.erase(where);
295 }
296 auto erase(iterator first, iterator last)
297 {
298 return children_.erase(first, last);
299 }
300
302 {
303 children_.clear();
304 }
305
306 bool hasChildren() const
307 {
308 return !children_.empty();
309 }
310
311 std::size_t childCount() const
312 {
313 return children_.size();
314 }
315
316 std::string tagName() const
317 {
318 return element_["tagName"].as<std::string>();
319 }
320
321 private:
322 void replaceElementImpl(HtmlElement const& element)
323 {
325 if (unsetup_)
326 unsetup_();
327 unsetup_ = {};
328
329#ifndef NDEBUG
330 if (element_.isUndefined())
331 throw std::runtime_error("Element is undefined");
332#endif
333
334 auto replacement = createElement(element);
335 element_.call<Nui::val>("replaceWith", replacement);
336 element_ = std::move(replacement);
337 setup(element);
338 if (deferredSetup_)
339 deferredSetup_(element);
340 }
341
342 private:
343 using destroy_fn = void (*)(Nui::val&);
344 destroy_fn destroy_ = Detail::destroyByRemove;
345 collection_type children_;
346 std::function<void()> unsetup_;
347 std::function<void(HtmlElement const& element)> deferredSetup_;
348 };
349
350 inline std::shared_ptr<Element> makeStandaloneElement(std::invocable<Element&, Renderer const&> auto&& fn)
351 {
352 auto elem = std::make_shared<Element>();
353 fn(*elem, Renderer{.type = RendererType::Emplace});
354 return elem;
355 }
356}
357
358#include <nui/frontend/elements/impl/html_element.tpp>
359#include <nui/frontend/elements/impl/range_renderer.tpp>
Nui::val element_
Definition basic_element.hpp:79
The basic element cannot have children and does not hold explicit ownership of them.
Definition childless_element.hpp:19
static Nui::val createElement(HtmlElement const &element)
Definition childless_element.hpp:166
Definition element.hpp:42
iterator begin()
Definition element.hpp:94
auto appendElement(HtmlElement const &element)
Definition element.hpp:115
void replaceElement(std::invocable< Element &, Renderer const & > auto &&fn)
Definition element.hpp:135
~Element() override
Definition element.hpp:81
auto emplaceElement(std::invocable< Element &, Renderer const & > auto &&fn)
Definition element.hpp:155
auto & operator[](std::size_t index)
Definition element.hpp:283
const_iterator begin() const
Definition element.hpp:102
std::string tagName() const
Definition element.hpp:316
auto slotFor(value_type const &value)
Definition element.hpp:123
const_iterator end() const
Definition element.hpp:106
auto insert(std::size_t where, HtmlElement const &element)
Definition element.hpp:276
void clearChildren()
Definition element.hpp:301
collection_type::const_iterator const_iterator
Definition element.hpp:46
static std::shared_ptr< Element > makeElement(HtmlElement const &element)
Definition element.hpp:87
Element(Element &&)=delete
void setup(HtmlElement const &element)
Relies on weak_from_this and cannot be used from the constructor.
Definition element.hpp:195
auto replaceElement(HtmlElement const &element)
Definition element.hpp:139
auto emplaceElement(HtmlElement const &element)
Definition element.hpp:144
void setTextContent(std::string_view text)
Definition element.hpp:169
void appendElements(std::vector< std::function< std::shared_ptr< Element >(Element &, Renderer const &)> > const &elements)
Definition element.hpp:175
collection_type::value_type value_type
Definition element.hpp:47
void setTextContent(std::string const &text)
Definition element.hpp:161
void setTextContent(char const *text)
Definition element.hpp:165
Element(Nui::val val)
This constructor takes ownership of a val.
Definition element.hpp:62
auto erase(iterator where)
Definition element.hpp:292
std::size_t childCount() const
Definition element.hpp:311
auto const & operator[](std::size_t index) const
Definition element.hpp:287
Element & operator=(Element &&)=delete
Element()
Definition element.hpp:69
void appendElement(std::invocable< Element &, Renderer const & > auto &&fn)
Definition element.hpp:111
iterator end()
Definition element.hpp:98
Element(HtmlElement const &elem)
Definition element.hpp:49
Element(Element const &)=delete
auto erase(iterator first, iterator last)
Definition element.hpp:296
bool hasChildren() const
Definition element.hpp:306
auto insert(iterator where, HtmlElement const &element)
Definition element.hpp:181
std::vector< std::shared_ptr< Element > > collection_type
Definition element.hpp:44
collection_type::iterator iterator
Definition element.hpp:45
Element & operator=(Element const &)=delete
static constexpr auto invalidEventId
Definition event_context.hpp:41
Definition html_element.hpp:58
std::vector< Attribute > const & attributes() const
Definition html_element.hpp:349
static void destroyByParentChildRemoval(Nui::val &val)
Definition element.hpp:24
static void destroyByRemove(Nui::val &val)
Definition element.hpp:20
static void doNotDestroy(Nui::val &)
Definition element.hpp:37
Definition basic_element.hpp:12
std::shared_ptr< Element > makeStandaloneElement(std::invocable< Element &, Renderer const & > auto &&fn)
Definition element.hpp:350
emscripten::val val
Definition val.hpp:5
Definition materialize.hpp:57
RendererType type
Definition materialize.hpp:58