Nui
element.hpp
Go to the documentation of this file.
1 #pragma once
2 
7 
8 #include <nui/frontend/val.hpp>
9 
10 #include <concepts>
11 #include <string>
12 #include <vector>
13 #include <memory>
14 #include <functional>
15 
16 namespace 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 
41  class Element : public ChildlessElement
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 
62  explicit Element(Nui::val val)
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;
79  Element& operator=(Element&&) = delete;
80 
82  {
83  clearChildren();
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  {
125  clearChildren();
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  else
281  return insert(begin() + static_cast<decltype(children_)::difference_type>(where), element);
282  }
283 
284  auto& operator[](std::size_t index)
285  {
286  return children_[index];
287  }
288  auto const& operator[](std::size_t index) const
289  {
290  return children_[index];
291  }
292 
293  auto erase(iterator where)
294  {
295  return children_.erase(where);
296  }
297  auto erase(iterator first, iterator last)
298  {
299  return children_.erase(first, last);
300  }
301 
303  {
304  children_.clear();
305  }
306 
307  bool hasChildren() const
308  {
309  return !children_.empty();
310  }
311 
312  std::size_t childCount() const
313  {
314  return children_.size();
315  }
316 
317  std::string tagName() const
318  {
319  return element_["tagName"].as<std::string>();
320  }
321 
322  private:
323  void replaceElementImpl(HtmlElement const& element)
324  {
325  clearChildren();
326  if (unsetup_)
327  unsetup_();
328  unsetup_ = {};
329 
330 #ifndef NDEBUG
331  if (element_.isUndefined())
332  throw std::runtime_error("Element is undefined");
333 #endif
334 
335  auto replacement = createElement(element);
336  element_.call<Nui::val>("replaceWith", replacement);
337  element_ = std::move(replacement);
338  setup(element);
339  if (deferredSetup_)
340  deferredSetup_(element);
341  }
342 
343  private:
344  using destroy_fn = void (*)(Nui::val&);
345  destroy_fn destroy_ = Detail::destroyByRemove;
346  collection_type children_;
347  std::function<void()> unsetup_;
348  std::function<void(HtmlElement const& element)> deferredSetup_;
349  };
350 
351  inline std::shared_ptr<Element> makeStandaloneElement(std::invocable<Element&, Renderer const&> auto&& fn)
352  {
353  auto elem = std::make_shared<Element>();
354  fn(*elem, Renderer{.type = RendererType::Emplace});
355  return elem;
356  }
357 }
358 
359 #include <nui/frontend/elements/impl/html_element.tpp>
360 #include <nui/frontend/elements/impl/range_renderer.tpp>
Nui::val element_
Definition: basic_element.hpp:72
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
auto emplaceElement(std::invocable< Element &, Renderer const & > auto &&fn)
Definition: element.hpp:155
const_iterator begin() const
Definition: element.hpp:102
std::string tagName() const
Definition: element.hpp:317
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:302
collection_type::const_iterator const_iterator
Definition: element.hpp:46
Element(Element &&)=delete
~Element()
Definition: element.hpp:81
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
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 & operator[](std::size_t index)
Definition: element.hpp:284
auto const & operator[](std::size_t index) const
Definition: element.hpp:288
Element & operator=(Element &&)=delete
static std::shared_ptr< Element > makeElement(HtmlElement const &element)
Definition: element.hpp:87
auto erase(iterator where)
Definition: element.hpp:293
std::size_t childCount() const
Definition: element.hpp:312
Element & operator=(Element const &)=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
void appendElements(std::vector< std::function< std::shared_ptr< Element >(Element &, Renderer const &)>> const &elements)
Definition: element.hpp:175
Element(HtmlElement const &elem)
Definition: element.hpp:49
Element(Element const &)=delete
auto erase(iterator first, iterator last)
Definition: element.hpp:297
bool hasChildren() const
Definition: element.hpp:307
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
constexpr static auto invalidEventId
Definition: event_context.hpp:41
Definition: html_element.hpp:60
std::vector< Attribute > const & attributes() const
Definition: html_element.hpp:346
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:351
emscripten::val val
Definition: val.hpp:5
Definition: materialize.hpp:52
RendererType type
Definition: materialize.hpp:53