Nui
Loading...
Searching...
No Matches
html_element.hpp
Go to the documentation of this file.
1#pragma once
2
14#include <nui/concepts.hpp>
16
17#include <nui/frontend/val.hpp>
18
19#include <vector>
20#include <utility>
21#include <concepts>
22#include <memory>
23#include <functional>
24
25namespace Nui
26{
27 class HtmlElement;
28
29 //----------------------------------------------------------------------------------------------
30 // Workaround Helper Classes for Linkage Bug in WASM-LD.
31 //----------------------------------------------------------------------------------------------
32 template <typename HtmlElem>
34 {
35 public:
37 HtmlElem htmlElement,
38 std::vector<std::function<std::shared_ptr<Dom::Element>(Dom::Element&, Renderer const&)>> children);
39
40 std::shared_ptr<Dom::Element> operator()(Dom::Element& parentElement, Renderer const& gen) const;
41
42 private:
43 HtmlElem htmlElement_;
44 std::vector<std::function<std::shared_ptr<Dom::Element>(Dom::Element&, Renderer const&)>> children_;
45 };
46 template <typename HtmlElem>
48 {
49 public:
50 explicit TrivialRenderer(HtmlElem htmlElement);
51 std::shared_ptr<Dom::Element> operator()(Dom::Element& parentElement, Renderer const& gen) const;
52
53 private:
54 HtmlElem htmlElement_;
55 };
56 //----------------------------------------------------------------------------------------------
58 {
59 public:
60 friend class DomElement;
61
62 HtmlElement(HtmlElement const&) = default;
64 HtmlElement& operator=(HtmlElement const&) = default;
66 virtual ~HtmlElement() = default;
67
68 HtmlElement(char const* name, HtmlElementBridge const* bridge, std::vector<Attribute> const& attributes)
69 : name_{name}
70 , bridge_{bridge}
71 , attributes_{attributes}
72 {}
73 HtmlElement(char const* name, HtmlElementBridge const* bridge, std::vector<Attribute>&& attributes)
74 : name_{name}
75 , bridge_{bridge}
76 , attributes_{std::move(attributes)}
77 {}
78 template <typename... T>
79 HtmlElement(char const* name, HtmlElementBridge const* bridge, T&&... attributes)
80 : name_{name}
81 , bridge_{bridge}
82 , attributes_{std::forward<T>(attributes)...}
83 {}
84
86 {
87 return {name_, bridge_, attributes_};
88 }
89
90 private:
91 template <typename... ObservedValues, std::invocable GeneratorT>
92 auto reactiveRender(ObservedValueCombinator<ObservedValues...> observedValues, GeneratorT&& elementRenderer) &&
93 {
94 return [self = this->clone(),
95 observedValues = std::move(observedValues),
96 elementRenderer =
97 std::forward<GeneratorT>(elementRenderer)](auto& parentElement, Renderer const& gen) {
98 using ElementType = std::decay_t<decltype(parentElement)>;
99
100 // function is called when observed values change to refabricate the children.
101 auto childrenRefabricator = std::make_shared<std::function<void()>>();
102
103 auto&& createdSelf = renderElement(gen, parentElement, self);
104
105 if (gen.type == RendererType::Inplace)
106 {
107 *childrenRefabricator = [observedValues,
108 elementRenderer,
109 fragmentContext = Detail::FragmentContext<ElementType>{},
110 createdSelfWeak = std::weak_ptr<ElementType>(createdSelf),
111 childrenRefabricator]() mutable {
112 fragmentContext.clear();
113
114 auto parent = createdSelfWeak.lock();
115 if (!parent || observedValues.isAnyExpired())
116 {
117 childrenRefabricator.reset();
118 return;
119 }
120
121 Detail::makeChildrenUpdateEvent(observedValues, childrenRefabricator, createdSelfWeak);
122
123 // regenerate children
124 if constexpr ((std::is_same_v<decltype(elementRenderer()), std::string>))
125 parent->setTextContent(elementRenderer());
126 else
127 fragmentContext.push(elementRenderer()(*parent, Renderer{.type = RendererType::Fragment}));
128 };
129 }
130 else
131 {
132 *childrenRefabricator = [observedValues,
133 elementRenderer,
134 createdSelfWeak = std::weak_ptr<ElementType>(createdSelf),
135 childrenRefabricator]() mutable {
136 auto parent = createdSelfWeak.lock();
137 if (!parent || observedValues.isAnyExpired())
138 {
139 childrenRefabricator.reset();
140 return;
141 }
142
143 // clear children
144 parent->clearChildren();
145
146 Detail::makeChildrenUpdateEvent(observedValues, childrenRefabricator, createdSelfWeak);
147
148 // regenerate children
149 if constexpr ((std::is_same_v<decltype(elementRenderer()), std::string>))
150 parent->setTextContent(elementRenderer());
151 else
152 elementRenderer()(*parent, Renderer{.type = RendererType::Append});
153 };
154 }
155
156 (*childrenRefabricator)();
157 return createdSelf;
158 };
159 }
160
161 template <typename RangeType, typename GeneratorT>
162 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
163 auto rangeRender(RangeType&& valueRange, GeneratorT&& elementRenderer) &&
164 {
165 return [self = this->clone(),
166 rangeRenderer = std::make_shared<
167 Detail::RangeRenderer<std::decay_t<RangeType>, GeneratorT, RangeType::isRandomAccess>>(
168 std::forward<RangeType>(valueRange).underlying(), std::forward<GeneratorT>(elementRenderer))](
169 auto& parentElement, Renderer const& gen) mutable {
170 if (gen.type == RendererType::Inplace)
171 throw std::runtime_error("fragments are not supported for range generators");
172
173 auto&& materialized = renderElement(gen, parentElement, self);
174 (*rangeRenderer)(materialized);
175 return materialized;
176 };
177 }
178
179 public:
180 // Children functions:
181 template <typename... ElementT>
182 requires requires(ElementT&&... elements) {
183 std::vector<std::function<std::shared_ptr<Dom::Element>(Dom::Element&, Renderer const&)>>{
184 std::forward<ElementT>(elements)...};
185 }
186 auto operator()(ElementT&&... elements) &&
187 {
188 return std::function<std::shared_ptr<Dom::Element>(Dom::Element&, Renderer const&)>{
190 this->clone(),
191 std::vector<std::function<std::shared_ptr<Dom::Element>(Dom::Element&, Renderer const&)>>{
192 std::forward<ElementT>(elements)...}}};
193
194 // Unknown Linkage BUG in wasm-ld :(
195 // return
196 // [self = this->clone(),
197 // children = std::vector<std::function<std::shared_ptr<Dom::Element>(Dom::Element&, Renderer
198 // const&)>>{
199 // std::forward<ElementT>(elements)...}](auto& parentElement, Renderer const& gen) {
200 // auto materialized = renderElement(gen, parentElement, self);
201 // materialized->appendElements(children);
202 // return materialized;
203 // };
204 }
205
206 // Trivial case:
207 auto operator()() const&&
208 {
209 return std::function<std::shared_ptr<Dom::Element>(Dom::Element&, Renderer const&)>{
211
212 // Unknown Linkage BUG in wasm-ld :(
213 // return [self = this->clone()](auto& parentElement, Renderer const& gen) {
214 // return renderElement(gen, parentElement, self);
215 // };
216 }
217
218 // Text content functions:
219 template <typename T>
220 requires std::same_as<std::decay_t<T>, std::string>
221 auto operator()(T&& text) &&
222 {
223 return [self = this->clone(), text = std::forward<T>(text)](auto& parentElement, Renderer const& gen) {
224 auto materialized = renderElement(gen, parentElement, self);
225 materialized->setTextContent(text);
226 return materialized;
227 };
228 }
229 auto operator()(std::string_view view) const&&
230 {
231 return [self = this->clone(), view](auto& parentElement, Renderer const& gen) {
232 auto materialized = renderElement(gen, parentElement, self);
233 materialized->setTextContent(view);
234 return materialized;
235 };
236 }
237 auto operator()(char const* text) const&&
238 {
239 return [self = this->clone(), text](auto& parentElement, Renderer const& gen) {
240 auto materialized = renderElement(gen, parentElement, self);
241 materialized->setTextContent(text);
242 return materialized;
243 };
244 }
245 template <typename T>
246 requires Fundamental<T>
247 auto operator()(T fundamental) &&
248 {
249 return [self = this->clone(), fundamental](auto& parentElement, Renderer const& gen) {
250 auto materialized = renderElement(gen, parentElement, self);
251 materialized->setTextContent(std::to_string(fundamental));
252 return materialized;
253 };
254 }
255
256 // Generator functions:
257 template <typename GeneratorT>
259 auto operator()(GeneratorT&& textGenerator) &&
260 {
261 return [self = this->clone(),
262 textGenerator = std::forward<GeneratorT>(textGenerator)](auto& parentElement, Renderer const& gen) {
263 auto materialized = renderElement(gen, parentElement, self);
264 materialized->setTextContent(textGenerator());
265 return materialized;
266 };
267 }
268 template <std::invocable GeneratorT>
270 auto operator()(GeneratorT&& elementRenderer) &&
271 {
272 return [self = this->clone(), elementRenderer = std::forward<GeneratorT>(elementRenderer)](
273 auto& parentElement, Renderer const& gen) {
274 auto materialized = renderElement(gen, parentElement, self);
275 elementRenderer()(*materialized, Renderer{.type = RendererType::Append});
276 return materialized;
277 };
278 }
279 template <typename T, std::invocable<T&, Renderer const&> GeneratorT>
281 auto operator()(GeneratorT&& elementRenderer) &&
282 {
283 return [self = this->clone(), elementRenderer = std::forward<GeneratorT>(elementRenderer)](
284 auto& parentElement, Renderer const& gen) {
285 auto materialized = renderElement(gen, parentElement, self);
286 materialized->setTextContent(elementRenderer(*materialized, Renderer{.type = RendererType::Append}));
287 return materialized;
288 };
289 }
290
291 // Reactive functions:
292 template <typename... ObservedValues, std::invocable GeneratorT>
294 {
295 return std::move(*this).operator()(std::move(combinator).split(), std::move(combinator).generator());
296 }
297 template <typename... ObservedValues, std::invocable GeneratorT>
298 auto operator()(ObservedValueCombinator<ObservedValues...> observedValues, GeneratorT&& elementRenderer) &&
299 {
300 return std::move(*this).reactiveRender(
301 std::move(observedValues), std::forward<GeneratorT>(elementRenderer));
302 }
303
304 // Range functions:
305 template <typename ObservedValue, typename GeneratorT>
306 auto operator()(ObservedRange<ObservedValue> observedRange, GeneratorT&& elementRenderer) &&
307 {
308 return std::move(*this).rangeRender(std::move(observedRange), std::forward<GeneratorT>(elementRenderer));
309 }
310 template <typename ObservedValue, typename GeneratorT>
311 auto operator()(std::pair<ObservedRange<ObservedValue>, GeneratorT>&& mapPair) &&
312 {
313 return std::move(*this).rangeRender(std::move(mapPair.first), std::move(mapPair.second));
314 }
315 template <typename IteratorT, typename GeneratorT, typename... ObservedT>
316 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward): false positive
317 auto operator()(UnoptimizedRange<IteratorT, ObservedT...>&& unoptimizedRange, GeneratorT&& elementRenderer) &&
318 {
319 return [self = this->clone(),
320 rangeRenderer =
321 std::make_shared<Detail::UnoptimizedRangeRenderer<IteratorT, GeneratorT, ObservedT...>>(
322 std::move(unoptimizedRange), std::forward<GeneratorT>(elementRenderer))](
323 auto& parentElement, Renderer const& gen) {
324 if (gen.type == RendererType::Inplace)
325 throw std::runtime_error("fragments are not supported for range generators");
326
327 auto&& materialized = renderElement(gen, parentElement, self);
328 (*rangeRenderer)(materialized);
329 return materialized;
330 };
331 }
332
333 // Observed text and number content functions:
334 auto operator()(Observed<std::string> const& observedString) &&
335 {
336 return std::move(*this).operator()(observe(observedString), [&observedString]() -> std::string {
337 return observedString.value();
338 });
339 }
340 template <typename T>
341 requires Fundamental<T>
342 auto operator()(Observed<T> const& observedNumber) &&
343 {
344 return std::move(*this).operator()(observe(observedNumber), [&observedNumber]() -> std::string {
345 return std::to_string(observedNumber.value());
346 });
347 }
348
349 std::vector<Attribute> const& attributes() const
350 {
351 return attributes_;
352 }
353
354 char const* name() const
355 {
356 return name_;
357 }
358
360 {
361 return bridge_;
362 }
363
364 private:
365 char const* name_;
366 HtmlElementBridge const* bridge_;
367 std::vector<Attribute> attributes_;
368 };
369}
Definition fragment_context.hpp:10
Definition range_renderer.hpp:19
Definition element.hpp:42
Definition html_element.hpp:58
HtmlElement & operator=(HtmlElement &&)=default
HtmlElement clone() const
Definition html_element.hpp:85
HtmlElement(char const *name, HtmlElementBridge const *bridge, std::vector< Attribute > const &attributes)
Definition html_element.hpp:68
auto operator()(GeneratorT &&textGenerator) &&
Definition html_element.hpp:259
std::vector< Attribute > const & attributes() const
Definition html_element.hpp:349
auto operator()(std::pair< ObservedRange< ObservedValue >, GeneratorT > &&mapPair) &&
Definition html_element.hpp:311
HtmlElementBridge const * bridge() const
Definition html_element.hpp:359
HtmlElement(HtmlElement &&)=default
auto operator()(Observed< T > const &observedNumber) &&
Definition html_element.hpp:342
friend class DomElement
Definition html_element.hpp:60
auto operator()(GeneratorT &&elementRenderer) &&
Definition html_element.hpp:281
auto operator()(std::string_view view) const &&
Definition html_element.hpp:229
auto operator()(ObservedValueCombinatorWithGenerator< GeneratorT, ObservedValues... > combinator) &&
Definition html_element.hpp:293
HtmlElement & operator=(HtmlElement const &)=default
HtmlElement(char const *name, HtmlElementBridge const *bridge, T &&... attributes)
Definition html_element.hpp:79
auto operator()(Observed< std::string > const &observedString) &&
Definition html_element.hpp:334
auto operator()(T fundamental) &&
Definition html_element.hpp:247
HtmlElement(char const *name, HtmlElementBridge const *bridge, std::vector< Attribute > &&attributes)
Definition html_element.hpp:73
virtual ~HtmlElement()=default
auto operator()() const &&
Definition html_element.hpp:207
auto operator()(char const *text) const &&
Definition html_element.hpp:237
auto operator()(ElementT &&... elements) &&
Definition html_element.hpp:186
auto operator()(T &&text) &&
Definition html_element.hpp:221
HtmlElement(HtmlElement const &)=default
auto operator()(ObservedValueCombinator< ObservedValues... > observedValues, GeneratorT &&elementRenderer) &&
Definition html_element.hpp:298
auto operator()(UnoptimizedRange< IteratorT, ObservedT... > &&unoptimizedRange, GeneratorT &&elementRenderer) &&
Definition html_element.hpp:317
auto operator()(ObservedRange< ObservedValue > observedRange, GeneratorT &&elementRenderer) &&
Definition html_element.hpp:306
char const * name() const
Definition html_element.hpp:354
Definition range.hpp:18
Definition observed_value_combinator.hpp:108
Definition observed_value_combinator.hpp:159
Definition observed_value.hpp:1283
Definition range.hpp:46
Definition concepts.hpp:11
Definition concepts.hpp:32
void makeChildrenUpdateEvent(auto &observedValues, auto &childrenRefabricator, auto &createdSelfWeak)
Definition make_children_update_event.hpp:7
static constexpr auto extractJsonMember(nlohmann::json const &json) -> decltype(auto)
Definition rpc_hub.hpp:29
Definition file_dialog.hpp:6
auto renderElement(Renderer const &gen, auto &element, auto const &htmlElement)
Definition materialize.hpp:61
ObservedValueCombinator< std::decay_t< Detail::ObservedAddReference_t< ObservedValues > >... > observe(ObservedValues &&... observedValues)
Definition observed_value_combinator.hpp:192
Definition html_element.hpp:34
ChildrenRenderer(HtmlElem htmlElement, std::vector< std::function< std::shared_ptr< Dom::Element >(Dom::Element &, Renderer const &)> > children)
std::shared_ptr< Dom::Element > operator()(Dom::Element &parentElement, Renderer const &gen) const
Definition html_element_bridge.hpp:14
Definition materialize.hpp:57
RendererType type
Definition materialize.hpp:58
Definition html_element.hpp:48
std::shared_ptr< Dom::Element > operator()(Dom::Element &parentElement, Renderer const &gen) const
TrivialRenderer(HtmlElem htmlElement)