12#include <glaze/glaze.hpp>
13#include <initializer_list>
27#include "../core/logger.hpp"
29#include "../core/registry.hpp"
49namespace morph::ui::author {
63 using std::logic_error::logic_error;
69template <
typename Model>
78template <
typename Action>
89 [[nodiscard]]
static std::vector<std::string>
secretFields() {
return {}; }
93template <
typename Model,
typename Action>
94concept Runs =
requires(Model& model,
Action& action) { model.execute(action); };
101template <
typename Result>
102[[nodiscard]] std::optional<std::vector<std::string>>
listFields() {
103 if constexpr (!std::is_class_v<Result> || (!glz::reflectable<Result> && !glz::glaze_object_t<Result>)) {
106 std::vector<std::string>
names;
107 [&
names]<std::size_t... I>(std::index_sequence<I...>) {
109 [[maybe_unused]]
auto add = [&
names]<std::size_t Index> {
111 std::remove_cvref_t<decltype(::morph::detail::reflectedMember<Index>(std::declval<Result&>()))>;
112 if constexpr (glz::writable_array_t<Member>) {
113 names.emplace_back(glz::reflect<Result>::keys[Index]);
116 (add.template operator()<I>(), ...);
117 }(std::make_index_sequence<glz::reflect<Result>::size>{});
126[[nodiscard]]
inline std::optional<std::vector<ActionEffect>>
effectsInfo(std::string_view actionId) {
131 std::vector<ActionEffect> out;
132 out.reserve(declared->size());
133 for (
auto const& effect : *declared) {
134 std::string_view kind =
"none";
140 kind =
"changesInstance";
144 .record = effect.record.empty() ? std::nullopt : std::optional<std::string>{effect.record}});
150 for (
auto const& effect : *declared) {
151 if (!effect.record.empty() &&
156 for (
auto const& change : derived.
records) {
157 ActionEffect added{.kind =
"changes", .record = change.record};
158 if (std::ranges::find(out, added) == out.end()) {
159 out.push_back(std::move(added));
169[[nodiscard]]
inline std::vector<ActionRecordInfo>
recordsInfo(std::string_view actionId) {
170 std::vector<ActionRecordInfo> out;
172 out.push_back(
ActionRecordInfo{.record = held.record, .keyField = held.keyField, .paths = held.paths});
182 std::map<std::string, ModelInfo, std::less<>>
models;
184 std::map<std::string, ActionInfo, std::less<>>
actions;
186 std::map<std::string, std::string, std::less<>>
views;
188 std::map<std::string, std::string, std::less<>>
wizards;
193 for (
auto const& [type, info] : other.
models) {
194 models.emplace(type, info);
205 template <
typename Model>
218 template <
typename Model,
typename Action>
229 decltype(std::declval<Model&>().execute(std::declval<Action&>()))>>(),
238 auto shared = std::make_shared<CatalogFacts const>(*
this);
240 out.
model = [shared](std::string_view typeId) -> std::optional<ModelInfo> {
241 auto const found = shared->models.find(typeId);
242 if (found == shared->models.end()) {
245 return found->second;
247 out.
action = [shared](std::string_view actionId) -> std::optional<ActionInfo> {
248 auto const found = shared->actions.find(actionId);
249 if (found == shared->actions.end()) {
252 return found->second;
255 if (!
views.empty()) {
309template <
typename T,
typename Alloc>
310struct IsVector<std::vector<T, Alloc>> : std::true_type {};
326template <
typename From,
typename To>
328 if constexpr (std::same_as<From, Any> || std::same_as<To, Any>) {
331 return std::same_as<From, To> || std::is_convertible_v<From, To> || std::is_constructible_v<To, From>;
340template <
typename From,
typename To>
354 std::same_as<L, bool> || (std::integral<L> && !std::same_as<L, char>) ||
355 std::convertible_to<L const&, std::string_view> || std::same_as<L, math::Rational> || std::same_as<L, Decimal> ||
356 std::same_as<L, Quantity> || std::same_as<L, time::DateTime> || std::same_as<L, std::nullptr_t>;
374template <std::
size_t N>
422template <
typename L,
typename R>
428template <
typename L,
typename R>
437 if constexpr (std::same_as<L, bool> || std::same_as<L, Decimal> || std::same_as<L, Quantity> ||
438 std::same_as<L, time::DateTime> || std::same_as<L, std::nullptr_t>) {
439 return Value{literal};
440 }
else if constexpr (std::same_as<L, math::Rational>) {
442 }
else if constexpr (std::integral<L>) {
443 return Value{
static_cast<std::int64_t
>(literal)};
445 return Value{std::string{std::string_view{literal}}};
455 if constexpr (std::same_as<L, Value>) {
459 items.reserve(literal.size());
460 for (
auto const& item : literal) {
463 return Value{std::move(items)};
474 std::get<Json::object_t>(
object.data).insert_or_assign(std::string{key}, std::move(value));
482 if (!std::get<Json::object_t>(section.data).empty()) {
483 put(
object, key, section);
490[[nodiscard]]
inline std::size_t
members(
Json const&
object) {
return std::get<Json::object_t>(
object.data).size(); }
498 if (std::holds_alternative<Json::object_t>(json.data) || std::holds_alternative<Json::array_t>(json.data)) {
499 Json wrapped = Json::object_t{};
511 Json json = Json::object_t{};
520 static constexpr std::array<char const*, 4> kNames = {
"stretch",
"start",
"center",
"end"};
521 return kNames.at(
static_cast<std::size_t
>(align));
528 static constexpr std::array<char const*, 4> kNames = {
"start",
"center",
"end",
"between"};
529 return kNames.at(
static_cast<std::size_t
>(justify));
536 if (
auto const*
const object = std::get_if<Json::object_t>(&expression.data)) {
537 auto const*
const found =
object->find(
"lit");
538 if (object->size() == 1 && found != object->end()) {
539 return found->second;
543 if (std::holds_alternative<Json::array_t>(expression.data)) {
553[[nodiscard]]
inline Json call(std::string_view function, std::vector<Json> args) {
554 Json json = Json::object_t{};
557 Json::array_t(std::make_move_iterator(args.begin()), std::make_move_iterator(args.end())));
564[[nodiscard]]
inline std::optional<std::pair<std::string, std::string>>
namedRead(
Json const& json) {
565 auto const*
const object = std::get_if<Json::object_t>(&json.data);
566 if (
object ==
nullptr || object->size() != 1) {
569 auto const& [
form, path] = *
object->begin();
570 auto const*
const text = std::get_if<std::string>(&path.data);
571 if (
text ==
nullptr || (
form !=
"ref" &&
form !=
"row")) {
586 "a field is read below a name -- a param, state, let, query, row, element or bound "
587 "name -- and not below a computed value; field `" +
588 std::string{field} +
"`"};
590 std::string path = read->second;
595 return single(read->first, std::move(path));
603[[nodiscard]]
inline std::string
refPath(
Json const& json, std::string_view what) {
605 if (!read || read->first !=
"ref") {
606 throw AuthoringError{std::string{what} +
" names a state, an element or a declared value"};
617template <
typename Class,
typename Member>
618[[nodiscard]] std::string
memberName(Member Class::* member) {
620 void const*
const target = std::addressof(probe.*member);
622 [&]<std::size_t... Index>(std::index_sequence<Index...>) {
623 auto visit = [&]<std::size_t I>() {
625 if constexpr (std::is_reference_v<
decltype(value)>) {
626 if (
static_cast<void const*
>(std::addressof(value)) == target) {
627 found = std::string{glz::reflect<Class>::keys.at(I)};
631 (visit.template operator()<Index>(), ...);
632 }(std::make_index_sequence<glz::reflect<Class>::size>{});
634 throw AuthoringError{
"a member pointer names a member its class's JSON does not have"};
647template <
typename T = Any>
659 template <
typename L>
667 template <
typename U>
668 requires(!std::same_as<U, T>) && (detail::assignable<U, T>())
670 : _json{other._json} {}
677 out._json = std::make_shared<Json const>(std::move(
json));
683 [[nodiscard]]
bool present() const noexcept {
return _json !=
nullptr; }
689 if (_json ==
nullptr) {
690 throw AuthoringError{
"an absent expression is used as an operand"};
701 template <
typename Class,
typename Member>
702 requires std::same_as<T, Any> || std::same_as<detail::UnwrappedT<T>, Class>
703 [[nodiscard]] Handle<Member>
at(Member Class::* member)
const {
711 [[nodiscard]] Handle<Any>
at(std::string_view field)
const {
718 [[nodiscard]] Handle
orElse(Handle
const& fallback)
const {
723 template <
typename U>
726 template <
typename L>
727 [[nodiscard]]
static auto normalise(L
const& literal) {
728 if constexpr (std::convertible_to<L const&, std::string_view> && !std::same_as<L, std::nullptr_t>) {
729 return std::string{std::string_view{literal}};
735 std::shared_ptr<Json const> _json;
750 return operand.json();
764template <
typename Result,
typename L,
typename R>
777template <
typename L,
typename R>
778 requires detail::HandleOperands<L, R>
789template <
typename L,
typename R>
790 requires detail::HandleOperands<L, R>
801template <
typename L,
typename R>
802 requires detail::HandleOperands<L, R>
813template <
typename L,
typename R>
814 requires detail::HandleOperands<L, R>
825template <
typename L,
typename R>
826 requires detail::HandleOperands<L, R>
827[[nodiscard]] Handle<bool> operator<(L
const& lhs, R
const& rhs) {
828 return detail::binary<bool>(
"lt", lhs, rhs);
837template <
typename L,
typename R>
838 requires detail::HandleOperands<L, R>
839[[nodiscard]] Handle<bool> operator<=(L
const& lhs, R
const& rhs) {
840 return detail::binary<bool>(
"le", lhs, rhs);
849template <
typename L,
typename R>
850 requires detail::HandleOperands<L, R>
861template <
typename L,
typename R>
862 requires detail::HandleOperands<L, R>
873template <
typename L,
typename R>
874 requires detail::HandleOperands<L, R>
885template <
typename L,
typename R>
886 requires detail::HandleOperands<L, R>
916template <
typename L,
typename R>
917 requires detail::HandleOperands<L, R>
928template <
typename L,
typename R>
929 requires detail::HandleOperands<L, R>
939template <
typename R = Any>
940[[nodiscard]]
Handle<R> call(std::string_view function, std::vector<AnyHandle>
const& args) {
941 std::vector<Json> json;
942 json.reserve(args.size());
944 json.push_back(arg.json());
971 std::vector<AnyHandle> operands;
972 operands.reserve(args.size() + 1);
973 operands.push_back(pattern);
974 std::ranges::move(args, std::back_inserter(operands));
989 std::vector<Json> json;
990 json.reserve(lists.size());
991 for (
Handle<std::vector<T>>
const&
list : lists) {
992 json.push_back(
list.json());
1025 return call(
"round", {value, places});
1040 return call(
"parseDecimal", {
text, places});
1064 std::vector<std::pair<std::string, AnyHandle>>
const& args = {}) {
1065 Json json = Json::object_t{};
1066 detail::put(json,
"t", key);
1067 detail::put(json,
"fallback", fallback);
1068 if (!args.empty()) {
1069 Json record = Json::object_t{};
1070 for (
auto const& [name, value] : args) {
1071 detail::put(record, name, value.json());
1084template <
typename T>
1086 Json json = Json::object_t{};
1094[[nodiscard]]
inline AnyHandle obj(std::vector<std::pair<std::string, AnyHandle>>
const& fields) {
1095 Json record = Json::object_t{};
1096 for (
auto const& [name, value] : fields) {
1124 Json::array_t array;
1125 array.reserve(items.size());
1127 array.push_back(item.json());
1136template <
typename T = Any>
1144template <
typename T = Any>
1154template <
typename Class,
typename Member>
1170template <
typename E = Any>
1173 Json json = Json::object_t{};
1186template <
typename E = Any>
1189 Json json = Json::object_t{};
1201template <
typename Class>
1243 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
1306 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
1321 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
1331template <typename Action, typename Value = typename ::morph::model::ActionTraits<Action>::Result>
1343 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
1354 template <
typename Class,
typename Member>
1355 requires std::same_as<Class, detail::UnwrappedT<Result>>
1357 return value().at(member);
1395template <
typename T>
1408 template <
typename Class,
typename Member>
1420template <
typename Action>
1432 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
1470template <
typename Action>
1479 template <
typename Member,
typename V>
1486 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
1490 [[nodiscard]]
Json const&
json() const noexcept {
return _json; }
1494 Json _json = Json::object_t{};
1500template <
typename Action>
1512 [[nodiscard]]
bool empty() const noexcept {
return _fields.empty(); }
1517 Json record = Json::object_t{};
1525 std::vector<BodyField<Action>> _fields;
1530template <
typename Model>
1539 [[nodiscard]] std::string
const&
alias() const noexcept {
return _alias; }
1572 : _borrowed{std::move(borrow.alias)} {}
1577 template <
typename T>
1583 [[nodiscard]] std::optional<Json>
json()
const {
1584 if (_key.present()) {
1587 if (!_borrowed.empty()) {
1591 return std::nullopt;
1594 return Json(std::string{
"private"});
1596 return Json(std::string{
"shared"});
1602 std::string _borrowed;
1657 Commands(std::initializer_list<Command> commands);
1661 [[nodiscard]]
bool empty() const noexcept {
return _items.empty(); }
1665 [[nodiscard]]
Json json()
const {
return Json::array_t(_items.begin(), _items.end()); }
1668 std::vector<Json> _items;
1682 [[nodiscard]]
Json const&
json() const noexcept {
return _json; }
1685 Json _json = Json::object_t{};
1688inline Commands::Commands(
Command const& command) : _items{command.
json()} {}
1690inline Commands::Commands(std::initializer_list<Command> commands) {
1691 _items.reserve(commands.size());
1692 for (
Command const& command : commands) {
1693 _items.push_back(command.
json());
1699template <
typename Action>
1751 Nodes(std::initializer_list<Node> nodes);
1755 Nodes(std::vector<Node>
const& nodes);
1759 [[nodiscard]]
bool empty() const noexcept {
return _items.empty(); }
1763 [[nodiscard]]
Json json()
const {
return Json::array_t(_items.begin(), _items.end()); }
1770 void add(
Node const& node);
1772 std::vector<Json> _items;
1787 [[nodiscard]]
Json const&
json() const noexcept {
return _json; }
1850 Json record = Json::object_t{};
1851 if (literalWidth && literalHeight) {
1920 Json part = Json::object_t{};
1922 detail::put(part,
"name", name.
json());
1924 if (!role.empty()) {
1925 detail::put(part,
"role", role);
1927 detail::put(out._json,
"a11y", std::move(part));
1946 Json part = Json::object_t{};
1961 auto const entry = std::get<Json::object_t>(out._json.data).try_emplace(std::string{
"keys"}, Json::object_t{});
1987 Json _json = Json::object_t{};
1988 CatalogFacts _facts;
1994 _items.reserve(nodes.size());
1995 for (
Node const& node : nodes) {
2001 _items.reserve(nodes.size());
2002 for (
Node const& node : nodes) {
2007inline void Nodes::add(
Node const& node) {
2008 _items.push_back(node.
json());
2018template <
typename T>
2024 }
else if constexpr (std::same_as<T, bool>) {
2026 }
else if constexpr (std::integral<T>) {
2028 }
else if constexpr (std::same_as<T, std::string>) {
2030 }
else if constexpr (std::same_as<T, math::Rational> || std::same_as<T, Decimal>) {
2032 }
else if constexpr (std::same_as<T, Quantity>) {
2034 }
else if constexpr (std::same_as<T, time::DateTime>) {
2055 template <
typename Model>
2057 Json decl = Json::object_t{};
2059 if (
auto instance = options.instance.json()) {
2060 detail::put(decl,
"instance", std::move(*instance));
2062 detail::put(_models, alias, std::move(decl));
2063 _facts.
model<Model>();
2064 return ModelHandle<Model>{alias};
2071 template <
typename T>
2073 return declareState<T>(name, {});
2081 template <
typename T>
2083 return declareState<T>(name, init);
2091 template <
typename L>
2102 template <
typename T>
2116 template <
typename Action,
typename Model>
2120 declareQuery<Model, Action>(name, model.alias(),
"action", body, options);
2121 return QueryHandle<Action>{name};
2134 template <
typename Action,
typename Model>
2135 requires Runs<Model, Action>
2138 declareQuery<Model, Action>(name, model.alias(),
"evaluate", body, options);
2139 return QueryHandle<Action, Action>{name};
2149 template <
typename Action,
typename Model>
2150 requires Runs<Model, Action>
2153 Json decl = Json::object_t{};
2154 detail::put(decl,
"model", model.alias());
2156 if (!options.body.empty()) {
2157 detail::put(decl,
"body", options.body.json());
2159 writeMutation(decl, options.concurrency, options.serialKey, options.optimistic, options.onSuccess,
2160 options.onError, options.offlineQueue);
2161 detail::put(_mutations, name, std::move(decl));
2162 _facts.
action<Model, Action>();
2163 return MutationHandle<Action>{name};
2171 Json decl = Json::object_t{};
2177 _watches.push_back(std::move(decl));
2183 Json decl = Json::object_t{};
2189 if (!options.
targets.empty()) {
2190 Json::array_t targets;
2191 targets.reserve(options.
targets.size());
2193 targets.emplace_back(target.
name());
2200 _checks.push_back(std::move(decl));
2209 return declared == 0;
2220 if (!_watches.empty()) {
2223 if (!_checks.empty()) {
2238 template <
typename T>
2240 Json decl = Json::object_t{};
2241 if constexpr (!std::same_as<T, Any>) {
2242 detail::put(decl,
"type", detail::typeName<T>());
2245 detail::put(decl,
"init", init.
json());
2247 detail::put(_state, name, std::move(decl));
2248 return author::ref<T>(name);
2251 template <
typename Model,
typename Action>
2252 void declareQuery(std::string
const& name, std::string
const& alias,
char const* actionKey,
2253 Body<Action>
const& body, QueryOptions
const& options) {
2254 Json decl = Json::object_t{};
2255 detail::put(decl,
"model", alias);
2256 detail::put(decl, actionKey, std::string{::morph::model::ActionTraits<Action>::typeId()});
2257 if (!body.empty()) {
2258 detail::put(decl,
"body", body.json());
2260 if (options.when.present()) {
2261 detail::put(decl,
"when", options.when.json());
2263 auto duration = [&decl](
char const* key, std::chrono::milliseconds value) {
2264 if (value.count() != 0) {
2265 detail::put(decl, key, value.count());
2268 duration(
"debounce", options.debounce);
2269 duration(
"refreshEvery", options.refreshEvery);
2270 duration(
"staleTime", options.staleTime);
2271 detail::put(_queries, name, std::move(decl));
2274 static void writeMutation(Json& decl, Mode concurrency, AnyHandle
const& serialKey,
2275 std::optional<Optimistic>
const& optimistic, Commands
const& onSuccess,
2276 Commands
const& onError,
bool offlineQueue) {
2277 static constexpr std::array<char const*, 4> kModes = {
"exclusive",
"serial",
"latest",
"parallel"};
2278 if (concurrency != Mode::Exclusive) {
2279 detail::put(decl,
"concurrency", std::string{kModes.at(
static_cast<std::size_t
>(concurrency))});
2281 if (serialKey.present()) {
2282 detail::put(decl,
"serialKey", serialKey.json());
2285 if (!optimistic->query) {
2286 throw AuthoringError{
"an `optimistic` write names the query whose row it writes"};
2288 Json part = Json::object_t{};
2289 detail::put(part,
"query", optimistic->query->name());
2290 if (!optimistic->at.empty()) {
2291 detail::put(part,
"at", optimistic->at);
2293 detail::put(part,
"key", optimistic->key.json());
2294 detail::put(part,
"with", optimistic->with.json());
2295 detail::put(decl,
"optimistic", std::move(part));
2297 if (!onSuccess.empty()) {
2298 detail::put(decl,
"onSuccess", onSuccess.json());
2300 if (!onError.empty()) {
2301 detail::put(decl,
"onError", onError.json());
2304 detail::put(decl,
"offline", std::string{
"queue"});
2308 Json _models = Json::object_t{};
2309 Json _state = Json::object_t{};
2310 Json _lets = Json::object_t{};
2311 Json _queries = Json::object_t{};
2312 Json _mutations = Json::object_t{};
2313 Json::array_t _watches;
2314 Json::array_t _checks;
2315 CatalogFacts _facts;
2332 static constexpr std::array<char const*, 3> kNames = {
"touched",
"submit",
"always"};
2333 return kNames.at(
static_cast<std::size_t
>(when));
2355 std::vector<std::pair<std::string, PropType>>
props;
2357 std::vector<std::pair<std::string, std::string>>
events;
2372 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
2387 [[nodiscard]] std::string
const&
name() const noexcept {
return _name; }
2446 Json json = Json::object_t{};
2458 Json json = Json::object_t{};
2463 detail::put(json,
"emphasis", std::string{
"primary"});
2465 detail::put(json,
"emphasis", std::string{
"danger"});
2467 detail::put(json,
"emphasis", std::string{
"ghost"});
2482 auto&
object = std::get<Json::object_t>(json.data);
2483 Json::object_t::iterator found =
object.find(key);
2484 if (found ==
object.end()) {
2486 found =
object.find(key);
2488 std::get<Json::array_t>(found->second.data).push_back(std::move(item));
2500 template <
typename T>
2568 static constexpr std::array<char const*, 6> kPropTypes = {
"string",
"bool",
"int",
"exact",
"number",
"json"};
2569 Json decl = Json::object_t{};
2570 if (!options.
qml.empty()) {
2573 if (!options.
host.empty()) {
2577 Json props = Json::object_t{};
2578 for (
auto const& [
prop, type] : options.
props) {
2579 detail::put(props,
prop, std::string{kPropTypes.at(
static_cast<std::size_t
>(type))});
2582 if (!options.
events.empty()) {
2583 Json events = Json::object_t{};
2584 for (
auto const& [
event, type] : options.
events) {
2602 Json decl = Json::object_t{};
2603 detail::put(decl,
"kind", std::string{
"columns"});
2605 return addPoint(name, std::move(decl));
2614 Json decl = Json::object_t{};
2615 detail::put(decl,
"kind", std::string{
"arrange"});
2617 return addPoint(name, std::move(decl));
2626 std::string
const& fallback = {}) {
2627 Json decl = Json::object_t{};
2628 detail::put(decl,
"kind", std::string{
"choice"});
2629 detail::put(decl,
"options", Json::array_t(options.begin(), options.end()));
2630 if (!fallback.empty()) {
2631 detail::put(decl,
"default", fallback);
2633 return addPoint(name, std::move(decl));
2641 template <
typename T>
2643 Json decl = Json::object_t{};
2644 detail::put(decl,
"kind", std::string{
"value"});
2645 detail::put(decl,
"type", detail::typeName<T>());
2646 detail::put(decl,
"default", valueToJson(fallback));
2647 return addPoint(name, std::move(decl));
2655 Json decl = Json::object_t{};
2657 return addPoint(name, std::move(decl));
2665 Json out = Json::object_t{};
2668 for (
auto const& [
key, value] : std::get<Json::object_t>(_head.data)) {
2673 for (
auto const& [
key, value] : std::get<Json::object_t>(_lifecycle.data)) {
2677 if (!_showErrors.empty()) {
2680 if (!_prefetch.empty()) {
2690 Point addPoint(std::string
const& name,
Json decl) {
2695 Json _head = Json::object_t{};
2696 Json _params = Json::object_t{};
2697 Json _lifecycle = Json::object_t{};
2698 Json _keys = Json::object_t{};
2699 std::string _showErrors;
2700 Json::array_t _prefetch;
2701 Json _components = Json::object_t{};
2702 Json _customize = Json::object_t{};
2725 Json json = Json::object_t{};
2728 return Command{std::move(json)};
2754template <
typename Action>
2756 RunOptions
const& options = {}) {
2757 Json json = Json::object_t{};
2758 detail::put(json,
"run", mutation.
name());
2759 if (!body.empty()) {
2760 detail::put(json,
"body", body.json());
2762 if (options.each.present()) {
2765 if (!options.as.empty()) {
2768 return Command{std::move(json)};
2790 static_cast<void>(current);
2827 static constexpr std::array<char const*, 5> kModes = {
"",
"tab",
"dialog",
"drawer",
"window"};
2832 detail::put(json,
"as", std::string{kModes.at(
static_cast<std::size_t
>(options.
as))});
2840 return Command{std::move(json)};
2850 return detail::navigation(detail::single(
"navigate", screenId), options);
2858 return detail::navigation(detail::single(
"navigate", detail::single(
"route", route.
json())), options);
2871 Json json = Json::object_t{};
2872 detail::put(json,
"host", name);
2873 if (args.present()) {
2874 detail::put(json,
"args", args.json());
2876 return Command{std::move(json)};
2911 static constexpr std::array<char const*, 4> kRoles = {
"info",
"success",
"warning",
"error"};
2912 Json json = Json::object_t{};
2915 detail::put(json,
"role", std::string{kRoles.at(
static_cast<std::size_t
>(role))});
2917 return Command{std::move(json)};
2936 Json::array_t parts{condition.
json(), then.
json()};
2937 if (!otherwise.empty()) {
2938 parts.push_back(otherwise.json());
2940 return detail::command(
"if", std::move(parts));
2977 Json json = Json::object_t{};
2978 detail::put(json,
"upload", file.
json());
2979 detail::put(json,
"to", mutation.
name());
2980 if (!options.as.empty()) {
2981 detail::put(json,
"as", options.as);
2983 if (!options.transfer.empty()) {
2984 detail::put(json,
"id", options.transfer);
2986 return Command{std::move(json)};
2994 Json json = Json::object_t{};
2995 detail::put(json,
"download", file.
json());
2996 detail::put(json,
"save", std::string{
"dialog"});
2997 if (name.present()) {
2998 detail::put(json,
"name", name.json());
3000 return Command{std::move(json)};
3009 Json json = Json::object_t{};
3013 return Command{std::move(json)};
3043 if (!commands.
empty()) {
3054 if (!nodes.
empty()) {
3056 _facts.merge(nodes.
facts());
3066 if (!value.empty()) {
3085 _facts.merge(
scope.facts());
3086 if (!
scope.empty()) {
3087 Json decls = Json::object_t{};
3088 scope.writeTo(decls);
3098 _facts.merge(
facts);
3107 Json _json = Json::object_t{};
3159 writer.
put(
"emphasis", std::string{
"primary"});
3161 writer.
put(
"emphasis", std::string{
"danger"});
3163 writer.
put(
"emphasis", std::string{
"ghost"});
3165 if (options.
style) {
3168 return std::move(writer)
3211 static constexpr std::array<char const*, 3> kCommits = {
"change",
"blur",
"enter"};
3219 node.
text(
"commitOn", kCommits.at(
static_cast<std::size_t
>(*options.
commitOn)));
3315 Json json = Json::object_t{};
3316 auto put = [&json](
char const* key,
AnyHandle const& value) {
3317 if (value.present()) {
3323 if (!item.
keys.empty()) {
3331 if (!item.
items.empty()) {
3332 Json::array_t items;
3333 items.reserve(item.
items.size());
3347[[nodiscard]]
inline Node menu(std::vector<MenuItem>
const& items) {
3348 Json::array_t array;
3349 array.reserve(items.size());
3350 for (
MenuItem const& item : items) {
3378 node.
put(
"children", children).
put(
"gap", options.
gap);
3379 if (options.
align) {
3395 return detail::stackNode(
"column", children, options);
3403 return detail::stackNode(
"row", children, options);
3411 detail::NodeWriter node{
"grid"};
3412 node.put(
"children", children).put(
"gap", options.gap).put(
"columns", options.columns);
3413 if (options.align) {
3414 node.text(
"align", detail::alignName(*options.align));
3416 if (options.justify) {
3417 node.text(
"justify", detail::justifyName(*options.justify));
3447 return detail::NodeWriter{
"panel"}
3448 .put(
"title", options.title)
3449 .put(
"header", options.header)
3450 .put(
"padding", options.padding)
3451 .put(
"collapsible", options.collapsible)
3452 .put(
"collapsed", options.collapsed)
3453 .put(
"onToggle", options.onToggle)
3454 .put(
"children", children)
3463 return detail::NodeWriter{
"scroll"}.put(
"axis", axis).put(
"children", children).done();
3485 Json json = Json::object_t{};
3488 facts.
merge(_children.facts());
3489 if (!_scope.empty()) {
3490 Json decls = Json::object_t{};
3491 _scope.writeTo(decls);
3494 if (!_children.empty()) {
3497 return Node{std::move(json), std::move(facts)};
3512 Nodes const& fallback = {}) {
3513 detail::NodeWriter node{
"switch"};
3514 node.put(
"on", value);
3515 Json::array_t parts;
3516 parts.reserve(cases.size());
3517 for (Case
const& item : cases) {
3518 Node
const written = item.part();
3519 parts.push_back(written.json());
3520 node.facts(written.facts());
3522 return node.raw(
"cases", std::move(parts)).put(
"default", fallback).done();
3538 Json::array_t parts;
3539 parts.reserve(
tabs.size());
3541 Json part = Json::object_t{};
3543 detail::put(part,
"children", tab.children.json());
3544 node.
facts(tab.children.facts());
3545 parts.push_back(std::move(part));
3547 return node.
raw(
"tabs", std::move(parts)).
done();
3581 Nodes const children = builder(scope);
3587 if (!options.
actions.empty()) {
3588 Json::array_t actions;
3589 actions.reserve(options.
actions.size());
3593 node.
raw(
"actions", std::move(actions));
3595 return node.
put(
"side", options.
side)
3597 .
put(
"children", children)
3612 return detail::dialogNode(
"dialog", element, options, [&children](DeclScope&) {
return children; });
3630 return detail::dialogNode(
"drawer", element, options, [&children](DeclScope&) {
return children; });
3638 return detail::NodeWriter{
"busy"}.put(
"active", active).put(
"label", label).done();
3658 Nodes const children = builder(scope);
3665 .
put(
"children", children)
3686[[nodiscard]]
inline std::vector<TableColumn>
cells(std::vector<Node>
const&
cells) {
3687 std::vector<TableColumn> columns;
3688 columns.reserve(
cells.size());
3690 columns.push_back(
TableColumn{.name = {}, .label = {}, .width = {}, .cell =
Nodes{cell}});
3750 std::vector<TableColumn>
const& columns) {
3751 static constexpr std::array<char const*, 3> kSelections = {
"none",
"single",
"multiple"};
3752 node.
put(
"key", options.
key);
3753 if (!columns.empty()) {
3754 Json::array_t parts;
3755 parts.reserve(columns.size());
3757 Json part = Json::object_t{};
3758 if (!
column.name.empty()) {
3761 if (
column.label.present()) {
3764 if (
column.width.present()) {
3767 if (
column.role.present()) {
3770 if (!
column.cell.empty()) {
3774 parts.push_back(std::move(part));
3776 node.
raw(
"columns", std::move(parts));
3779 node.
raw(
"selectionMode", std::string{kSelections.at(
static_cast<std::size_t
>(*options.
selection))});
3782 node.
raw(
"showHeader",
false);
3790 return node.
put(
"sort", options.
sort)
3808 node.
put(
"rows", rows);
3818 std::function<std::vector<TableColumn>(
DeclScope&)>
const& builder) {
3820 std::vector<TableColumn>
const columns = builder(scope);
3822 node.
put(
"rows", rows);
3834template <
typename Action,
typename Model>
3835 requires Runs<Model, Action>
3839 facts.action<Model,
Action>();
3840 detail::NodeWriter node{
"table"};
3842 if (!body.
empty()) {
3843 node.raw(
"body", body.
json());
3858template <
typename Action,
typename Model>
3861 return dataTable<Action>(model, Body<Action>{}, options);
3870 return detail::NodeWriter{
"dateTimeInput"}.put(
"value", value).put(
"label", label).put(
"onCommit", commit).done();
3977 Json part = Json::object_t{};
3978 if (action->label.present()) {
3981 if (!action->onClick.empty()) {
3982 detail::put(part,
"onClick", action->onClick.json());
3984 node.
raw(
"action", std::move(part));
4019 return detail::NodeWriter{
"badge"}.put(
"tone", tone).put(
"text", text).put(
"icon", icon).done();
4027 return detail::NodeWriter{
"progress"}.put(
"value", value).put(
"label", label).done();
4035 return detail::NodeWriter{
"steps"}.put(
"items", items).put(
"current", current).done();
4073 AnyHandle
const& sizes = {}) {
4074 return detail::NodeWriter{
"splitter"}
4075 .put(
"orientation", orientation)
4076 .put(
"sizes", sizes)
4077 .put(
"children", children)
4098 return detail::NodeWriter{
"collapsible"}
4099 .put(
"title", options.title)
4100 .put(
"open", options.open)
4101 .put(
"header", options.header)
4102 .put(
"children", children)
4103 .put(
"onToggle", options.onToggle)
4122 return detail::NodeWriter{
"dropZone"}
4123 .put(
"accept", options.accept)
4124 .put(
"multiple", options.multiple)
4125 .put(
"children", children)
4126 .put(
"onDrop", options.onDrop)
4143 return detail::NodeWriter{
"boundary"}.put(
"loading", loading).put(
"error", error).put(
"children", children).done();
4166template <
typename Action>
4173 template <
typename Member>
4175 : _name{detail::memberName(member)}, _settings{std::move(settings)} {}
4179 [[nodiscard]] std::pair<std::string, Json>
json()
const {
4180 Json settings = Json::object_t{};
4181 auto put = [&settings](
char const* key,
AnyHandle const& value) {
4186 put(
"omit", _settings.omit);
4187 put(
"hidden", _settings.hidden);
4188 put(
"label", _settings.label);
4189 put(
"unit", _settings.unit);
4190 put(
"decimals", _settings.decimals);
4191 put(
"widget", _settings.widget);
4192 put(
"blankAs", _settings.blankAs);
4193 return {_name, std::move(settings)};
4222 node.
put(
"title", presentation.title).
put(
"submitLabel", presentation.submitLabel);
4223 if (presentation.showStatus) {
4224 node.
raw(
"showStatus",
AnyHandle{*presentation.showStatus}.json());
4226 if (presentation.columns) {
4227 node.
raw(
"columns",
AnyHandle{std::int64_t{*presentation.columns}}.json());
4229 if (presentation.fieldLayout) {
4230 node.
text(
"fieldLayout",
4239template <
typename Action>
4269template <
typename Action,
typename Model>
4270 requires Runs<Model, Action>
4273 facts.action<Model,
Action>();
4274 detail::NodeWriter node{
"form"};
4275 node.text(
"model", model.alias())
4277 .put(
"prefill", options.prefill)
4278 .put(
"key", options.
key)
4279 .text(
"submit", options.submit);
4280 if (options.showErrors) {
4281 node.text(
"showErrors", showErrorsName(*options.showErrors));
4283 if (!options.fields.
empty()) {
4284 Json overlay = Json::object_t{};
4286 auto [name, settings] = field.json();
4289 node.raw(
"fields", std::move(overlay));
4292 return node.put(
"onSubmit", options.onSubmit)
4293 .put(
"onSuccess", options.onSuccess)
4294 .put(
"onError", options.onError)
4305template <
typename Action,
typename Model>
4309 facts.action<Model,
Action>();
4310 detail::NodeWriter node{
"collection"};
4312 detail::present(node, presentation);
4313 return node.facts(facts).done();
4322template <
typename Action,
typename Model>
4326 facts.action<Model,
Action>();
4327 detail::NodeWriter node{
"wizard"};
4329 detail::present(node, presentation);
4330 return node.facts(facts).done();
4336 std::vector<std::pair<std::string, AnyHandle>>
props;
4338 std::vector<std::pair<std::string, Commands>>
on;
4346 detail::NodeWriter node{
"custom"};
4347 node.text(
"name", component.
name());
4348 if (!options.props.
empty()) {
4349 Json props = Json::object_t{};
4350 for (
auto const& [name, value] : options.props) {
4351 detail::put(props, name, value.
json());
4353 node.raw(
"props", std::move(props));
4355 if (!options.on.
empty()) {
4356 Json events = Json::object_t{};
4357 for (
auto const& [name, commands] : options.on) {
4360 node.raw(
"on", std::move(events));
4373[[nodiscard]]
inline bool names(
DocNode const& node, std::string_view key) {
4375 if (value ==
nullptr) {
4381 std::string
const*
const text = value->literal.asString();
4382 return !value->literal.isNull() && (
text ==
nullptr || !
text->empty());
4389 auto const parts = node.
partList(
"a11y");
4390 return !parts.empty() &&
names(*parts.front(),
"name");
4397 static constexpr std::array<std::string_view, 6> kInputs = {
"textInput",
"dateTimeInput",
"slider",
4398 "select",
"checkbox",
"filePicker"};
4399 std::string_view
const kind = node.
kind;
4400 if (std::ranges::find(kInputs, kind) != kInputs.end()) {
4404 return "accessibility: a `" + node.
kind +
"` has a `label` or an `a11y.name`";
4406 if (kind ==
"button") {
4410 return "accessibility: a button without text has an `a11y.name`; an icon alone does not name it";
4412 if (kind ==
"dialog" || kind ==
"drawer") {
4416 return "accessibility: a `" + node.
kind +
"` has a `title` or an `a11y.name`";
4418 if ((kind ==
"table" || kind ==
"custom") && !
hasA11yName(node)) {
4419 return "accessibility: a `" + node.
kind +
"` has an `a11y.name`";
4430[[nodiscard]]
inline std::optional<Refusal> accessibilityOf(
DocNode const& node, std::string
const& document);
4436[[nodiscard]]
inline std::optional<Refusal>
accessibilityOf(std::span<DocNodePtr const> nodes,
4437 std::string
const& document) {
4443 return std::nullopt;
4447 if (std::string rule =
brokenRule(node); !rule.empty()) {
4448 return Refusal{.document = document, .path = node.
path, .reason = std::move(rule)};
4450 for (
auto const& [key, nodes] : node.
children) {
4451 if (
auto refusal =
accessibilityOf(std::span<DocNodePtr const>{nodes}, document)) {
4455 for (
auto const& [key, parts] : node.
parts) {
4457 for (
auto const& [childKey, nodes] : part->children) {
4458 if (
auto refusal =
accessibilityOf(std::span<DocNodePtr const>{nodes}, document)) {
4464 return std::nullopt;
4481 if (document.
view) {
4486 for (
auto const& [name, component] : document.
components) {
4487 if (component.fallback) {
4493 return std::nullopt;
4517 Screen(std::string screenId,
Builder builder) : _id{std::move(screenId)}, _builder{std::move(builder)} {}
4521 [[nodiscard]] std::string
const&
id() const noexcept {
return _id; }
4525 [[nodiscard]] std::expected<Emitted, Refusal>
write()
const {
4528 Node const view = _builder(scope);
4534 out.
text = glz::write_json(out.
json).value_or(std::string{});
4536 }
catch (std::exception
const& error) {
4537 return std::unexpected(
Refusal{.document = _id, .path =
"", .reason = error.what()});
4539 return std::unexpected(
Refusal{.document = _id,
4541 .reason =
"the screen's builder threw an exception that is not a "
4555template <morph::detail::FixedString Id>
4557 return Screen{std::string{Id.view()}, std::move(builder)};
4565 return Screen{std::move(screenId), std::move(builder)};
4591 return std::unexpected(
4592 Refusal{.document =
screen.id(), .path =
"/id", .reason =
"a screen with this id is registered"});
4594 auto emitted =
screen.write();
4596 return std::unexpected(std::move(emitted.error()));
4599 facts.
merge(_facts);
4602 return std::unexpected(std::move(loaded.error()));
4605 return std::unexpected(std::move(*refusal));
4607 for (
LoadWarning const& warning : loaded->warnings) {
4608 morph::log::logWarn(warning.
message(loaded->id));
4610 _facts = std::move(facts);
4611 _screens.push_back(std::make_unique<RegisteredScreen>(
4613 return _screens.back().get();
4625 template <std::invocable Make>
4630 _refusals.push_back(std::move(added.error()));
4645 [[nodiscard]] std::size_t
lost() const noexcept {
return _lost; }
4651 for (
auto const& registered : _screens) {
4652 if (registered->id == screenId) {
4653 return registered.get();
4661 [[nodiscard]] std::size_t
size() const noexcept {
return _screens.size(); }
4665 [[nodiscard]] std::vector<Refusal>
const&
refusals() const noexcept {
return _refusals; }
4672 std::vector<std::unique_ptr<RegisteredScreen>> _screens;
4673 std::vector<Refusal> _refusals;
4674 std::size_t _lost = 0;
4689template <std::invocable Make>
4701#define MORPH_UI_AUTHOR_CAT_(a, b) a##b
4703#define MORPH_UI_AUTHOR_CAT(a, b) MORPH_UI_AUTHOR_CAT_(a, b)
4713#define MORPH_REGISTER_SCREEN(SCREEN) \
4715 [[maybe_unused]] bool const MORPH_UI_AUTHOR_CAT(morphUiScreen_, \
4716 __COUNTER__) = ::morph::ui::author::enrollScreen([] { \
morph::ui::Align and morph::ui::Justify: where a container places its children across its axis and al...
Justify
Where a stack places its spare length along its axis, while no shown child stretches.
Align
Where a child is placed across its container's axis: a column's horizontally, a row's vertically,...
std::string typeName()
A document type name for a C++ type, as params and state declare it.
Node text(TextOptions const &options)
A text node.
Handle< detail::ArithmeticT< L, R > > operator+(L const &lhs, R const &rhs)
add.
Handle< R > call(std::string_view function, std::vector< AnyHandle > const &args)
A call of any expr/1 library function, for the functions without a builder of their own.
Mode
What a mutation run does while another is in flight.
@ Exclusive
The run is refused (the default).
bool hasA11yName(DocNode const &node)
Whether a node has an a11y.name that names it.
Command signOut()
signOut.
Element element(std::string name)
An element of the view.
Node spacer()
A spacer node.
Node dateTimeInput(AnyHandle const &value, AnyHandle const &label, Commands const &commit={})
A dateTimeInput node.
AnyHandle list(std::vector< AnyHandle > const &items)
{"list": [...]}: a list built from expressions.
LiteralOf< std::remove_cvref_t< L > >::type LiteralT
The value type a constant stands for.
Json actionButton(ActionButtonOptions const &action)
An actionButton part's JSON.
Handle< T > ref(std::string const &path)
A named read, {"ref": "name.path"}: a param, state, let, query value, element value or bound name.
std::string refPath(Json const &json, std::string_view what)
The name a named read reads.
Command resetPrefs(Point const &point)
resetPrefs: resets a customization point to the document's default.
Command notify(AnyHandle const &message, Role role=Role::Info)
notify: shows a notification.
Node scroll(Nodes const &children, AnyHandle const &axis={})
A scroll node.
OnStale
What a select does with a selection its new options no longer hold.
AnyHandle env(std::string const &name)
A client environment value, {"env": name}: widthClass, locale, platform, online, backend.
Node emptyState(EmptyStateOptions const &options)
An emptyState node.
Command command(std::string_view key, Json value)
A command with one key.
Node collapsible(Nodes const &children, CollapsibleOptions const &options={})
A collapsible node.
Handle< T > cond(Handle< bool > const &condition, Handle< T > const &then, Handle< T > const &otherwise)
{"if": [c, a, b]}: a choice between two values.
Command ifThen(AnyHandle const &condition, Commands const &then, Commands const &otherwise={})
if: commands chosen by a condition.
Command set(AnyHandle const &target, AnyHandle const &value)
set: writes a state, an input's value or an app name.
Node panel(Nodes const &children, PanelOptions const &options={})
A panel node.
Node column(Nodes const &children, ContainerOptions const &options={})
A column node.
AnyHandle sum(AnyHandle const &list)
sum of a list of numbers.
AnyHandle hostValue(std::string const &name)
A value the application registered with the client, {"host": name}.
Screen screen(Screen::Builder builder)
A screen with its id as a template argument, as spec 5 §11 writes it.
OpenAs
How navigate opens a screen.
@ Default
The host's choice.
Selection
How a table's selection works.
Command all(Commands const &steps)
all: steps started together.
void putPresent(Json &json, char const *key, AnyHandle const &value)
An expression key of a part, left out when absent.
Unwrapped< T >::type UnwrappedT
The type with one std::optional removed.
Node drawer(Element const &element, Nodes const &children, DialogOptions const &options={})
A drawer node, a scope of its own.
Handle< std::string > format(AnyHandle const &pattern, std::vector< AnyHandle > args)
format: a template with {0}, {1}, … placeholders.
consteval bool assignable()
Whether an expression of one value type can stand where a value of another is expected....
Command downloadToFile(AnyHandle const &file, AnyHandle const &name={})
download to a file the user picks.
Command navigate(std::string const &screenId, NavigateOptions const &options={})
navigate to a screen by id.
Command returnWith(AnyHandle const &value)
return: a dialog screen's result.
Source
Which changes a watch reacts to.
@ User
Only the user's own edits.
Json single(std::string_view key, Json value)
A one-key JSON object.
Node button(ButtonOptions const &options)
A button node.
Handle< T > row()
The innermost row, {"row": ""}.
Command host(std::string const &name, AnyHandle const &args={})
host: a command the application registered with the client.
bool enrollScreen(Make const &make) noexcept
Enrols a screen into the process's registry; what MORPH_REGISTER_SCREEN runs.
std::string showErrorsName(ShowErrors when)
The spelling of a ShowErrors.
Json menuItem(MenuItem const &item)
A menu item's JSON.
Json fieldOf(Json const &json, std::string_view field)
A field read below a named read.
TableMode
Where a model-backed table sorts, filters and pages.
@ Server
In the server, through the action's table query.
Command run(MutationHandle< Action > const &mutation, Body< Action > const &body={}, RunOptions const &options={})
run: runs a mutation.
Node select(SelectOptions const &options)
A select node.
Handle< std::string > quantity(AnyHandle const &value)
quantity(x): a quantity for display in its own unit.
Command seq(Commands const &steps)
seq: steps in order; a failed step stops the rest.
Node busy(AnyHandle const &active, AnyHandle const &label={})
A busy node.
Handle< bool > eq(L const &lhs, R const &rhs)
eq: equality of magnitude. A function, not ==: an == that returns an expression rather than a bool br...
Handle< detail::ArithmeticT< L, R > > operator-(L const &lhs, R const &rhs)
sub.
Handle< bool > operator||(L const &lhs, R const &rhs)
or, with Kleene's three-valued logic.
Command upload(AnyHandle const &file, MutationRef const &mutation, UploadOptions const &options={})
upload: sends a file through a mutation (files/1).
ShowErrors
When a scope's element errors show (spec 5 §6).
@ Touched
Once the element has been touched.
@ Submit
Once the form or dialog has been submitted.
Handle< bool > ne(L const &lhs, R const &rhs)
ne.
Command retry()
retry: refetches every failed query of a boundary, inside its error.
std::conditional_t< IsHandle< L >::value, typename ValueOf< L >::type, typename ValueOf< R >::type > ArithmeticT
The value type of an arithmetic expression: the left handle's, else the right's.
Command cancelClose()
cancelClose: keeps a screen open, inside onClose.
Handle< bool > engaged(AnyHandle const &value)
engaged: whether a value is not null.
std::optional< Refusal > checkAccessibility(Document const &document)
The accessibility rules of spec 5 §11, which registration runs after the load-time validation.
AnyHandle prop(std::string const &name)
A prop of a component, inside the component's fallback.
AnyHandle session(std::string const &name)
A session attribute, {"session": name}.
AnyHandle content()
The content sizing, "content": as much as the content needs.
Command switchBackend(std::string const &name)
switchBackend: switches to a named backend.
Handle< Any > AnyHandle
An expression whose value type is not tracked: the type of every node property.
std::function< Nodes(DeclScope &)> ScopeBuilder
A scope's builder: declares on the scope and returns the nodes that read it.
AnyHandle obj(std::vector< std::pair< std::string, AnyHandle > > const &fields)
{"obj": {...}}: a record built from expressions.
Handle< detail::ArithmeticT< L, R > > operator*(L const &lhs, R const &rhs)
mul.
DeclScope RowScope
The scope a row of a table or forEach declares its own state, queries and model aliases on.
bool names(DocNode const &node, std::string_view key)
Whether a node has a property that names it: present, and not the constant "" or null.
Handle< bool > isEmpty(AnyHandle const &value)
isEmpty: an empty string or list.
std::vector< TableColumn > cells(std::vector< Node > const &cells)
Columns from cells alone, without ids or labels.
Node dialog(Element const &element, Nodes const &children, DialogOptions const &options={})
A dialog node, a scope of its own; open and close name it by its element.
std::optional< Refusal > accessibilityOf(DocNode const &node, std::string const &document)
The first accessibility rule a node or a node under it breaks.
Value scalarValue(L const &literal)
A C++ scalar as a document value.
Role
The role of a notification.
Node collection(ModelHandle< Model > const &model, FormPresentation const &presentation={})
A collection node: the forms engine's list screen over an action.
Node switchOn(AnyHandle const &value, std::vector< Case > const &cases, Nodes const &fallback={})
A switch node: shows the first case whose when equals its value.
AnyHandle map(AnyHandle const &source, std::string const &name, std::type_identity_t< std::function< AnyHandle(Handle< E > const &)> > const &body)
A comprehension over a list, {"map": e, "as": "x", "to": e}.
Node customize(Point const &point)
A customize node: the editor of a customization point.
Command open(Element const &dialog)
open: opens a dialog or drawer.
Handle< bool > operator>=(L const &lhs, R const &rhs)
ge.
Node steps(AnyHandle const &items, AnyHandle const ¤t={})
A steps node.
Instance
How a model alias binds when no key expression is given (spec 5 §5).
@ Private
An instance of the scope's own.
@ Default
shared when the catalog marks the model stateless, private otherwise.
Command focus(Element const &target)
focus: moves the keyboard focus to an element.
Node dataTable(ModelHandle< Model > const &model, Body< Action > const &body, TableOptions const &options={})
A table declared from a model's list action (spec 7): the client derives its columns from the row typ...
std::optional< std::pair< std::string, std::string > > namedRead(Json const &json)
The dotted path a named read reads, when the expression is one.
Node badge(AnyHandle const &text, AnyHandle const &tone={}, AnyHandle const &icon={})
A badge node.
Node textInput(TextInputOptions const &options)
A textInput node.
Node banner(BannerOptions const &options)
A banner node.
AnyHandle parseDecimal(AnyHandle const &text)
parseDecimal(text): typed text, in the client's locale, as an exact decimal; null while it does not s...
std::optional< std::vector< ActionEffect > > effectsInfo(std::string_view actionId)
The catalog's form of an action's declared effects.
Command reset(AnyHandle const &target)
reset: a state to its init, or a dialog's scope.
Node filePicker(FilePickerOptions const &options)
A filePicker node.
Node progress(AnyHandle const &value, AnyHandle const &label={})
A progress node.
PropType
A prop type of a component: how a value crosses into QML (spec 5 §10).
@ Exact
An exact decimal or quantity, as text.
@ Int
An integer, as text.
@ Json
Structured data, exact values inside it as text.
Node boundary(Nodes const &children, Nodes const &loading={}, Nodes const &error={})
A boundary node: loading until its queries have values, error when one failed without one.
void put(Json &object, std::string_view key, Json value)
Sets a member of a JSON object, replacing one of the same name and keeping the order of the others.
Node keyValue(AnyHandle const &items)
A keyValue node.
Handle< Result > binary(std::string_view function, L const &lhs, R const &rhs)
A two-operand call.
Handle< bool > operator&&(L const &lhs, R const &rhs)
and, with Kleene's three-valued logic. Both operands are written: a document has no short circuit.
Json headBadge(HeadBadgeOptions const &badge)
A headBadge part's JSON.
std::string brokenRule(DocNode const &node)
The accessibility rule a node breaks, if any.
AnyHandle coalesce(std::vector< AnyHandle > const &values)
coalesce: the first value that is not null.
AnyHandle round(AnyHandle const &value, std::int64_t places)
round(x, dp), half to even.
Handle< std::int64_t > count(AnyHandle const &list)
count: a list's length.
std::vector< ActionRecordInfo > recordsInfo(std::string_view actionId)
The catalog's form of the records an action's result holds.
std::string alignName(Align align)
An alignment's document name.
Json operandJson(X const &operand)
An operand's JSON.
Node grid(Nodes const &children, ContainerOptions const &options={})
A grid node.
std::string justifyName(Justify justify)
A justification's document name.
AnyHandle event()
The payload of the event whose command list runs.
void actionPart(NodeWriter &node, std::optional< ActionPart > const &action)
Writes an action part.
consteval bool scalarAssignable()
Whether a scalar value of one type can stand where a value of another is expected.
Node tabs(std::vector< Tab > const &tabs)
A tabs node.
ScreenRegistry & screenRegistry() noexcept
The process's screen registry, which MORPH_REGISTER_SCREEN enrols into.
std::optional< Json > literalOf(Json const &expression)
The value of a constant expression, as constant writes one.
AnyHandle concatLists(std::vector< AnyHandle > const &lists)
concatLists: the lists, one after another; null while any of them is null.
Command refetch(QueryRef const &query)
refetch: refetches a query.
Node slider(SliderOptions const &options)
A slider node.
Node wizard(ModelHandle< Model > const &model, FormPresentation const &presentation={})
A wizard node: the forms engine's wizard over an action.
AnyHandle stretch(int weight=1)
A stretch sizing, {"lit": {"stretch": weight}}: a share of the leftover space.
Node menu(std::vector< MenuItem > const &items)
A menu node.
Node tableNode(NodeWriter &node, TableOptions const &options, DeclScope const &scope, std::vector< TableColumn > const &columns)
Writes a table's options after its source.
Handle< std::string > t(std::string const &key, std::string const &fallback, std::vector< std::pair< std::string, AnyHandle > > const &args={})
t(key, fallback, args): a translated MessageFormat 2 message.
Value literalValue(L const &literal)
A C++ constant as a document value.
Command close(Element const &dialog)
close: closes a dialog or drawer.
Node dialogNode(std::string_view kind, Element const &element, DialogOptions const &options, ScopeBuilder const &builder)
A dialog or drawer.
Node custom(ComponentRef const &component, CustomOptions const &options={})
A custom node: a declared component with its props and events.
void putSection(Json &object, std::string_view key, Json const §ion)
Sets a member of a JSON object to another object, unless that one is empty.
Command resetAllPrefs()
resetPrefs of every point of the screen.
Handle< std::string > concat(std::vector< AnyHandle > const &parts)
concat.
Command navigateRoute(AnyHandle const &route, NavigateOptions const &options={})
navigate to a route.
Handle< bool > operator!(Handle< T > const &operand)
not.
std::size_t members(Json const &object)
How many members a JSON object has.
Command downloadToPreview(AnyHandle const &file, AnyHandle const &state)
download to a preview URL, bound to a state.
AnyHandle fixed(int units)
A fixed sizing, {"lit": {"fixed": units}}.
Json call(std::string_view function, std::vector< Json > args)
A call of an expr/1 library function.
Handle< bool > operator>(L const &lhs, R const &rhs)
gt.
constexpr Self self
The dialog or drawer a command list runs in.
AnyHandle filter(AnyHandle const &source, std::string const &name, std::type_identity_t< std::function< Handle< bool >(Handle< E > const &)> > const &where)
A filter over a list, {"filter": e, "as": "x", "where": e}.
Node splitter(Nodes const &children, AnyHandle const &orientation={}, AnyHandle const &sizes={})
A splitter node.
Command setLocale(std::string const &locale)
setLocale: switches the client's locale.
Node table(AnyHandle const &rows, TableOptions const &options)
A table over a list, with the columns in its options.
std::optional< std::vector< std::string > > listFields()
The members of an action result that hold a list, when the result is a record.
Handle< bool > includes(AnyHandle const &list, AnyHandle const &value)
includes: whether a list holds a value.
Node form(ModelHandle< Model > const &model, FormOptions< Action > const &options={})
A form node: a schema form of the forms engine over an action (spec 2); each field takes its label fr...
Node stackNode(char const *kind, Nodes const &children, ContainerOptions const &options)
A row or column with its options.
Handle< detail::ArithmeticT< L, R > > operator/(L const &lhs, R const &rhs)
div.
Command navigation(Json json, NavigateOptions const &options)
Writes navigate's options.
Commit
When a text input commits its draft.
Node dropZone(Nodes const &children, DropZoneOptions const &options={})
A dropZone node.
Json constant(Value const &value)
A document value as a constant expression: a scalar as itself, a list, record, decimal or quantity wr...
void append(Json &json, char const *key, Json item)
Appends item to the array at key of json, making the array on first use.
Node checkbox(CheckboxOptions const &options)
A checkbox node.
void present(NodeWriter &node, FormPresentation const &presentation)
Writes presentation's keys on node.
std::string memberName(Member Class::*member)
The reflected name of a data member, found by its address in a value-initialised object of its class.
Node forEach(AnyHandle const &rows, ForEachOptions const &options, ScopeBuilder const &builder)
A forEach node: its children once per row, each row a scope of its own.
Command signIn(MutationRef const &mutation)
signIn: runs a sign-in mutation and installs the token it returns.
void addDerivedChanges(Resolved const &resolved, std::string const &source, Changes &changes)
Adds a whole-type change for every record derived, directly or through others, from source.
The document's typed value (spec 5 §3): what every expression evaluates to.
What a builder throws when it is given something a document cannot say, such as a field read from a v...
One field of an action body: a member of the action, and the expression that fills it.
Json const & json() const noexcept
The field's expression.
std::string const & name() const noexcept
The field's JSON name.
BodyField(Member Action::*member, V const &value)
A field: a member pointer of the action, so a renamed field or another action's field is a compile er...
An action body: the action's members with expressions in place of values (spec 5 §11)....
Json json() const
The body's expression.
Body(std::initializer_list< BodyField< Action > > fields)
A body.
bool empty() const noexcept
Whether the body has no fields.
Body()=default
An empty body; the query or mutation sends an empty object.
Node part() const
The case's JSON and the facts it names.
Case(AnyHandle when, ScopeBuilder const &builder)
A case that declares on its own scope.
Case(AnyHandle when, Nodes children)
A case.
One command of ui/1 (spec 5 §8, spec 6).
Json const & json() const noexcept
The command's JSON.
Command(Json json)
A command over its JSON, for a form no builder spells.
A command list, run as one batch.
bool empty() const noexcept
Whether the list is empty.
Commands()=default
No commands.
Json json() const
The list's JSON.
A declared component, for custom nodes.
ComponentRef(std::string name)
A component by name.
std::string const & name() const noexcept
The component's name.
A scope's declarations (spec 5 §3): model aliases, state, let, queries, mutations,...
MutationHandle< Action > mutation(std::string const &name, ModelHandle< Model > const &model, MutationOptions< Action > const &options={})
A mutation (spec 5 §5).
void writeTo(Json &out) const
Writes the declarations into a document or a "scope" object, in the document's order.
ModelHandle< Model > model(std::string const &alias, ModelOptions const &options={})
A model alias.
QueryHandle< Action > query(std::string const &name, ModelHandle< Model > const &model, Body< Action > const &body={}, QueryOptions const &options={})
A query (spec 5 §5).
Handle< T > let(std::string const &name, Handle< T > const &value)
A derived value (let).
Handle< T > state(std::string const &name)
State with a declared type and no initial value; it starts null.
void watch(AnyHandle const &watched, Commands const &commands, Source source=Source::Any)
A cascade: commands that run when a value changes (spec 5 §6).
CatalogFacts & recordedFacts() noexcept
The catalog facts, for a scope that records more.
DeclScope()=default
An empty scope.
bool empty() const
Whether the scope declares nothing.
void check(CheckOptions const &options)
A validation across elements (spec 5 §6).
CatalogFacts const & facts() const noexcept
The catalog facts of the models and actions the scope names.
QueryHandle< Action, Action > evaluate(std::string const &name, ModelHandle< Model > const &model, Body< Action > const &body={}, QueryOptions const &options={})
A query that evaluates an action without running it: the server returns its calculated fields and fie...
Handle< detail::LiteralT< L > > state(std::string const &name, L const &init)
State initialised from a constant; its type is the constant's.
Handle< T > state(std::string const &name, Handle< T > const &init)
State initialised from an expression; its type is T's.
An element of the view, named by its id, whose live values relations read (spec 5 §6).
Handle< bool > checked() const
A checkbox's state.
AnyHandle get(std::string const &value) const
One of the element's values.
AnyHandle selected() const
A select's chosen key.
Handle< bool > open() const
Whether a dialog or drawer is open.
Element(std::string name)
An element.
AnyHandle value() const
An input's committed value.
Handle< bool > valid() const
Whether a form, dialog or element is valid.
std::string const & name() const noexcept
The element's id.
Handle< bool > ready() const
Whether a form or dialog is ready to submit.
AnyHandle draft() const
An input's text while it is edited.
AnyHandle selection() const
A table's selection.
One field of a form's fields overlay, by member pointer.
std::pair< std::string, Json > json() const
The overlay's JSON.
FieldOverlay(Member Action::*member, FieldSettings settings)
An overlay.
An expression of a document, typed by the C++ value it evaluates to (spec 5 §11).
Handle(L const &literal)
A constant.
static Handle raw(Json json)
A handle over an expression's JSON, for a form no builder spells.
Handle< Member > at(Member Class::*member) const
A field of the value, by member pointer: a renamed field is a compile error.
Handle()=default
An absent handle.
Json const & json() const
Handle orElse(Handle const &fallback) const
The value, or a fallback when it is null.
bool present() const noexcept
Whether the handle holds an expression.
T ValueType
The C++ type of the value.
Handle< Any > at(std::string_view field) const
A field of the value, by name.
Handle(Handle< U > const &other)
A handle of another value type that can stand for this one.
A model alias's instance: a binding mode, a key expression, or a borrowed binding.
InstanceSpec(Borrow borrow)
A borrowed binding.
InstanceSpec(Handle< T > const &key)
A key expression: the shared instance the key selects. The model must be keyed.
std::optional< Json > json() const
The instance JSON.
InstanceSpec()=default
The default binding.
InstanceSpec(Instance mode)
A binding mode.
A model alias of a scope.
ModelHandle(std::string alias)
An alias.
std::string const & alias() const noexcept
The alias.
A mutation declared by a scope.
Handle< bool > can() const
Whether the signed-in principal may run the mutation.
std::string const & name() const noexcept
The mutation's name.
Handle< bool > pending() const
Whether a run is in flight.
MutationHandle(std::string name)
A mutation by name.
Handle< std::string > error() const
The last run's error text.
Handle< std::string > errorKind() const
The kind of the last run's error.
ResultHandle< Result > result() const
The last run's result; bind it with let to read its fields.
::morph::model::ActionTraits< Action >::Result Result
The result's C++ type.
A mutation of a scope, without its action type: what signIn, upload and can name.
std::string const & name() const noexcept
The mutation's name.
MutationRef(std::string name)
A mutation by name.
A view node (spec 5 §9). The kind's own properties come from its builder's options; the properties ev...
Node withText(std::string const &key, std::string const &value) const
A property set to a plain string.
Node readonly(AnyHandle const &condition) const
Whether an input or text is read-only.
Node drag(AnyHandle const &payload) const
Makes the node draggable.
Json const & json() const noexcept
The node's JSON.
Node stale(AnyHandle const &condition) const
Whether an input or text shows a value that is being recalculated.
CatalogFacts const & facts() const noexcept
The catalog facts of the models and actions the node names.
Node span(AnyHandle const &columns) const
The grid columns the node spans.
Node tooltip(AnyHandle const &text) const
A tooltip.
Node autofocus(AnyHandle const &focus) const
Whether the node takes focus when it mounts.
Node alignSelf(Align align) const
Where the node sits in its row, column or grid, in place of the container's align.
Node surface(std::string const &name) const
A named token set for the subtree.
Node testId(std::string const &name) const
A stable identifier for automated tests.
Node visible(AnyHandle const &condition) const
Whether the node shows.
Node a11y(AnyHandle const &name, std::string const &role={}) const
The accessible name and role.
Node errors(AnyHandle const &errors) const
Errors shown on an input or text.
Node maxWidth(int units) const
The most width, in backend units.
Node id(Element const &element) const
The element id, by which relations read its values.
Node required(AnyHandle const &condition) const
Whether an input or text is required.
Node key(std::string const &chord, Commands const &commands) const
A key binding.
Node(Json json, CatalogFacts facts={})
A node over its JSON, for a kind no builder spells.
Node sizing(AnyHandle const &width, AnyHandle const &height) const
The sizing of both dimensions, as a {width, height} record; an absent one is content....
Node minHeight(int units) const
The least height, in backend units.
Node maxHeight(int units) const
The most height, in backend units.
Node minWidth(int units) const
The least width, in backend units.
Node drop(AnyHandle const &accepts, Commands const &onDrop) const
Makes the node a drop target.
Node enabled(AnyHandle const &condition) const
Whether the node is enabled.
Node sizing(AnyHandle const &sizing) const
The width's sizing: "content", {"fixed": n} or {"stretch": w}, such as content(), fixed(n) or stretch...
Node with(std::string const &key, AnyHandle const &value) const
A property set to an expression; an absent expression leaves the node as it is.
Json json() const
The list's JSON.
CatalogFacts const & facts() const noexcept
The catalog facts of the models and actions the nodes name.
bool empty() const noexcept
Whether the list is empty.
A customization point (spec 5 §12b).
std::string const & name() const noexcept
The point's name.
Point(std::string name)
A point by name.
AnyHandle pref(std::string const &path={}) const
The user's choice at the point.
A query declared by a scope.
Handle< bool > can() const
Whether the signed-in principal may run the query.
Handle< Result > value() const
The query's value; the last one is kept while a refetch is in flight.
Value Result
The value's C++ type.
Handle< bool > pending() const
Whether the query is in flight.
std::string const & name() const noexcept
The query's name.
Handle< Member > at(Member Class::*member) const
A field of the query's value.
Handle< std::string > errorKind() const
The kind of the query's error.
Handle< std::string > error() const
The query's error text.
QueryHandle(std::string name)
A query by name.
A query of a scope, without its action type: what refetch, prefetch and optimistic name.
std::string const & name() const noexcept
The query's name.
QueryRef(std::string name)
A query by name.
A mutation's result: a Handle with no field reads.
ResultHandle(Handle< T > const &handle)
The result read.
Handle< Member > at(Member Class::*member) const =delete
Not available: bind the result with let first, auto r = screen.let("r", m.result());,...
Handle< Any > at(std::string_view field) const =delete
Not available: bind the result with let first, auto r = screen.let("r", m.result());,...
The screen's own scope: its declarations, params, lifecycle keys, components and customization points...
Point choicePoint(std::string const &name, std::vector< std::string > const &options, std::string const &fallback={})
A choice point: one of a list of options.
void title(AnyHandle const &title)
The screen's title.
void onUnmount(Commands const &commands)
Commands run when the screen unmounts.
Point valuePoint(std::string const &name, Value const &fallback={})
A value point: one typed setting.
void onMount(Commands const &commands)
Commands run when the screen mounts.
Json document(std::string const &documentId, Node const &view) const
The document's JSON with a view.
Handle< T > param(std::string const &name)
A typed param, set by navigation.
void status(HeadBadgeOptions const &badge)
Appends a status badge to the head.
Point columnsPoint(std::string const &name, Element const &table)
A columns point: the user reorders, hides and resizes a table's columns.
void action(ActionButtonOptions const &action)
Appends an action to the head, run in the screen scope.
void identity(AnyHandle const &identity)
The screen's identity: two screens with equal identities are one.
void onBackendChange(Commands const &commands)
Commands run after a backend switch.
void key(std::string const &chord, Commands const &commands)
A screen-wide key binding.
void onSave(Commands const &commands)
Commands run when the host asks the screen to save.
ComponentRef component(std::string const &name, ComponentOptions const &options)
A custom or host component.
Point point(std::string const &name, std::string const &kind)
A layout or saved point.
void onClose(Commands const &commands)
Commands run when the screen is asked to close; cancelClose() keeps it open.
void subtitle(AnyHandle const &subtitle)
The head's line under the title.
void dirty(AnyHandle const &dirty)
Whether the screen has unsaved changes.
void showErrors(ShowErrors when)
When the screen's element errors show.
Point arrangePoint(std::string const &name, Element const &container)
An arrange point: the user reorders and hides the children of a column or row, each of which has an i...
void prefetch(QueryRef const &query)
A query started before the screen mounts; its body reads only params.
The screens of an application, each validated once when it is registered.
RegisteredScreen const * find(std::string_view screenId) const
A registered screen by id.
std::size_t lost() const noexcept
How many enrolments failed without a refusal to keep, because something threw outside the builder.
Catalog catalog() const
The catalog of every model and action the registered screens name, for a local-mode client.
std::vector< Refusal > const & refusals() const noexcept
The refusals enroll kept.
std::expected< RegisteredScreen const *, Refusal > add(Screen const &screen)
Registers a screen.
std::size_t size() const noexcept
How many screens are registered.
void enroll(Make const &make) noexcept
Registers a screen and keeps its refusal rather than returning it, for registration at static initial...
void enroll(Screen const &screen) noexcept
Registers a screen and keeps its refusal rather than returning it.
A screen definition: an id and the builder that writes the document.
Screen(std::string screenId, Builder builder)
A screen.
std::function< Node(Scope &)> Builder
Declares on the screen's scope and returns the view.
std::expected< Emitted, Refusal > write() const
Runs the builder and writes the document.
std::string const & id() const noexcept
The document id.
Builds a node's JSON key by key, leaving out what is absent.
NodeWriter & scope(DeclScope const &scope)
The declarations of the scope the node creates.
Node done() const
The node.
NodeWriter & raw(char const *key, Json value)
Any JSON.
NodeWriter & put(char const *key, Commands const &commands)
A command list.
NodeWriter & facts(CatalogFacts const &facts)
Adds catalog facts.
NodeWriter & text(char const *key, std::string const &value)
A plain string.
NodeWriter & put(char const *key, Nodes const &nodes)
A node list.
NodeWriter(std::string_view kind)
A node of a kind.
NodeWriter & put(char const *key, AnyHandle const &value)
An expression property.
Satisfied when Model runs Action: the model has an execute that takes it.
Satisfied when two operands are operands and at least one is a handle, so the builders' operators nev...
Satisfied by a C++ value a builder writes as a document constant: a scalar, a list of scalars,...
Satisfied by an operand of an expression operator: a handle or a constant.
Satisfied by a C++ scalar a builder writes as a document constant.
UTC timestamps for morph actions.
The UI document (spec 5 §3): its model — declarations, commands, view nodes, components and customiza...
constexpr std::string_view kVocabulary
The vocabulary a document declares; the only one this client reads.
std::function< std::optional< std::string >(std::string_view)> documentLookup(std::map< std::string, std::string, std::less<> > documents)
A Catalog::view or Catalog::wizard lookup over documents by action id.
std::expected< Document, Refusal > loadDocumentText(std::string_view text, LoadOptions const &options={})
Parses JSON text as a UI document and runs its load-time validation.
std::shared_ptr< DocNode const > DocNodePtr
A shared, immutable view node.
Resolved const & resolved()
The process registries, matched and validated on the first call.
std::optional< std::vector< Effect > > effectsOf(std::string_view actionId)
The effects an action declared.
@ ChangesInstance
changesInstance
std::vector< ActionRecord > recordsOf(std::string_view actionId)
The records an action's result holds, with the paths to each.
@ Literal
A JSON scalar, or {"lit": v}.
std::shared_ptr< Expr const > ExprPtr
A shared, immutable expression node.
morph::forms::FieldLayout: where a form's cell puts a field's caption. The forms engine and the autho...
@ Inline
The label beside the control, at a fixed width, centred on it; the help under the control.
Shared compile-time, NTTP-capable fixed string.
Exact rational arithmetic for morph model/action values.
The value glaze reads and writes under a reflected key.
constexpr decltype(auto) reflectedMember(T &object)
The value glaze reads and writes under glz::reflect<T>::keys[I].
What one successful execute changed.
std::vector< RecordChange > records
Record topics, one entry per record type.
Traits specialisation that maps an action type to its id, JSON codec, and result type.
Traits specialisation that maps a model type to its string type-id.
One effect an action declares, as the catalog states it.
What the catalog says about an action.
A record an action's result holds, as the catalog states it.
The facts load-time validation needs from the application's catalog. Local mode answers them from the...
std::function< std::optional< std::string >(std::string_view actionId)> wizard
The w-* wizard document whose first step is an action, as flows::wizardSchemaJson<W>() emits it: what...
std::function< std::optional< ModelInfo >(std::string_view typeId)> model
Looks up a model type by its registered type id.
std::function< std::optional< std::string >(std::string_view actionId)> view
The v-* view document whose v-query is an action, as views::viewSchemaJson<V>() emits it: what a coll...
std::function< std::optional< ActionInfo >(std::string_view actionId)> action
Looks up an action by its id.
An exact decimal: a math::Rational whose DecimalPlaces are the value's declared places.
A modal overlay; its content is mounted only while it is open.
A view node of the document (spec 5 §9), or a structured part of one (a table column,...
std::map< std::string, std::vector< DocNodePtr >, std::less<> > children
Child node lists, by key (children, header, loading, error, default, cell, fallback).
std::string path
The node's JSON Pointer.
std::map< std::string, std::vector< DocNodePtr >, std::less<> > parts
Structured parts, by key (columns, tabs, cases, items, a11y, action, fields, …).
std::string kind
The node kind (text, table, …), or for a part its part kind (column, tab, case, …).
std::span< DocNodePtr const > partList(std::string_view key) const
A list of parts.
ExprPtr prop(std::string_view key) const
An expression property.
A loaded and validated UI document: a screen, or the app document (id app, spec 6 §5).
std::string id
The document id, such as lims.results.
std::map< std::string, Component, std::less<> > components
Custom components, by name.
DocNodePtr view
The view; nullptr for the app document.
A panel that slides in from one edge over the content: a Dialog with a side, and the same rules.
A finding of load-time validation that does not refuse the document.
std::string message(std::string_view document) const
The warning as one line of text.
What the catalog says about a model type.
Why a document, an expression or a value was refused: where, and the reason.
Catalog facts of an action that its C++ type does not show. Specialise it for an action whose reply r...
static std::vector< std::string > secretFields()
The reply fields the server marks secret.
static std::vector< std::string > calculatedFields()
The reply-row fields the server calculates; an optimistic write may not name them.
static constexpr bool credential
Whether the action sends a credential.
The action of a banner or emptyState.
AnyHandle label
The label.
Commands onClick
Commands run when it is chosen.
The value type of a handle whose value the screen cannot know when it is written: a query result read...
AnyHandle dismissible
Whether the user may dismiss it.
std::optional< ActionPart > action
Its action.
Commands onDismiss
Commands run when it is dismissed.
The opener's binding of an alias, for a screen opened as a dialog ({"from": alias}...
std::string alias
The opener's alias.
The catalog facts the builders gathered from the model and action types a screen names.
Catalog catalog() const
The facts as a ui::Catalog.
std::map< std::string, ActionInfo, std::less<> > actions
Action facts by action id.
std::map< std::string, std::string, std::less<> > wizards
The w-* wizard documents wizard nodes build from, by the action of their first step.
void model()
Records a model type.
std::map< std::string, std::string, std::less<> > views
The v-* view documents collection nodes build from, by the action of their v-query.
std::map< std::string, ModelInfo, std::less<> > models
Model facts by registered type id.
void merge(CatalogFacts const &other)
Adds another set of facts; a fact already present is kept.
void action()
Records an action as one of a model type's.
A validation across elements (spec 5 §6).
AnyHandle message
The message added to each target's errors.
std::string name
The check's id.
std::vector< Element > targets
The elements the message attaches to.
bool blocking
Whether a failure blocks ready.
Handle< bool > assertion
The condition; false fails the check.
Commands onChange
Commands run on a change.
Commands onToggle
Commands run on a toggle.
AnyHandle checked
Whether it is checked.
AnyHandle label
The label.
Nodes header
Nodes shown beside the title.
Commands onToggle
Commands run on a toggle.
AnyHandle title
The title.
AnyHandle open
Whether it is open.
A custom or host component (spec 5 §10).
std::string host
The name of a host component the client carries; empty for a delivered one.
std::vector< std::pair< std::string, PropType > > props
The props and their types.
std::vector< std::pair< std::string, std::string > > events
The events and their payload types.
std::optional< Node > fallback
The built-in subtree a renderer shows when it cannot load the component; it reads the props through p...
std::string qml
The content hash of a delivered component, sha256:<64 hex digits>; empty for a host component.
std::int64_t version
The host component's version.
Options of column, row and grid.
std::optional< Align > align
Where the children sit across the axis; absent stretches them.
AnyHandle gap
The gap between children: a spacing step, 0 to kMaxSpacingStep.
AnyHandle columns
A grid's columns; adaptive layouts read env("widthClass").
std::optional< Justify > justify
A row's or column's spare length along its axis; absent packs at the start. Not on a grid,...
std::vector< std::pair< std::string, Commands > > on
The component's events and the commands they run.
std::vector< std::pair< std::string, AnyHandle > > props
The props, by name.
Options of dialog and drawer.
std::optional< ShowErrors > showErrors
When its element errors show.
AnyHandle title
The title: the accessible name.
AnyHandle side
A drawer's side, start or end.
AnyHandle open
Whether it is open; absent for open and close commands alone.
Commands defaultAction
Commands run on Enter.
Commands onDismiss
Commands run when the user dismisses it.
std::vector< ActionButtonOptions > actions
The buttons of its footer, run in its scope; each id once.
Commands cancel
Commands run on Escape.
AnyHandle multiple
Whether several files may be dropped.
Commands onDrop
Commands run with the dropped files as event.
AnyHandle accept
The accepted types.
A screen's emitted document: its JSON, the text that is served, and the catalog facts of its types.
std::string text
The document as compact JSON text.
CatalogFacts facts
The catalog facts of the models and actions it names.
std::optional< ActionPart > action
Its action.
AnyHandle title
The title.
An overlay on one field of a form (spec 6 §10), applied to the parsed form model.
AnyHandle label
The label.
AnyHandle unit
The display unit.
AnyHandle omit
Leaves the field out.
AnyHandle hidden
Hides the field.
AnyHandle widget
The widget.
AnyHandle decimals
The display places.
AnyHandle blankAs
What an empty input is sent as.
AnyHandle multiple
Whether several files may be picked.
AnyHandle mode
open or save.
AnyHandle label
The label: the accessible name.
AnyHandle accept
The accepted types.
Commands onPick
Commands run with the picked files as event.
AnyHandle key
The row key, read in the row scope.
AnyHandle gap
The gap between rows: a spacing step, 0 to kMaxSpacingStep.
A status badge of the screen's head (screen.status).
AnyHandle text
The badge's text.
AnyHandle icon
The name of an icon.
AnyHandle tone
Its tone: neutral, info, ok, warn or err.
AnyHandle visible
Whether it is shown; absent shows it.
Catalog facts of a model type that its C++ type does not show. Specialise it for a model whose shared...
static constexpr bool stateless
Whether a shared singleton is equivalent to a private instance.
Options of a model alias.
InstanceSpec instance
How the alias binds its instance.
Options of a mutation (spec 5 §5).
std::optional< Optimistic > optimistic
The row a run predicts before its reply.
Commands onSuccess
Commands run after a success.
bool offlineQueue
Whether a run that cannot reach the server is queued offline.
Commands onError
Commands run after a failure.
AnyHandle serialKey
The queue key of serial runs.
Body< Action > body
The body; empty sends the body each run gives.
Mode concurrency
What a run does while another is in flight.
OpenAs as
How the screen opens.
Commands onResult
Commands run with the value a dialog returns, as event.
Commands onCancel
Commands run when a dialog is cancelled.
AnyHandle params
The screen's params, a record.
An optimistic write: the row a mutation run predicts before its reply.
AnyHandle key
The row key, read against the run's body bound as body.
AnyHandle with
The predicted row, read against the run's body bound as body.
std::string at
The member of the query's record value that holds the list; empty when the value is the list itself.
std::optional< QueryRef > query
The query whose cached list it writes.
Commands onToggle
Commands run on a collapse or expand.
Nodes header
Nodes shown beside the title.
AnyHandle collapsible
Whether the user may collapse it.
AnyHandle padding
The padding.
AnyHandle title
The title.
AnyHandle collapsed
Whether it is collapsed.
Options of a query (spec 5 §5).
AnyHandle when
The gate; absent is always on.
std::chrono::milliseconds debounce
How long the body must be stable before a fetch.
std::chrono::milliseconds refreshEvery
The polling interval; zero does not poll.
std::chrono::milliseconds staleTime
How long a cached value counts as fresh.
A registered screen: the text that is served and the document it loads as.
std::string id
The document id.
std::string text
The document as compact JSON text.
Document document
The loaded document.
std::string as
With each, the element's name inside the body; without it, the name the reply is bound to for the lat...
AnyHandle each
A list to run the mutation once per element of, as one batch.
Commands onChoose
Commands run with the chosen key.
std::optional< OnStale > onStale
What happens to a selection the options no longer hold.
AnyHandle selected
The selected key.
Commands onChange
Commands run on a change.
AnyHandle label
The label: the accessible name.
AnyHandle options
The options: a list of {key, label} records.
AnyHandle style
The presentation, such as radio.
The dialog or drawer a command list runs in, for close.
AnyHandle min
The minimum.
Commands onCommit
Commands run on a commit.
AnyHandle value
The value.
AnyHandle label
The label: the accessible name.
Commands onChange
Commands run on a change.
AnyHandle max
The maximum.
Nodes children
The page's nodes.
AnyHandle label
The label.
AnyHandle label
The header label.
AnyHandle role
The column's text role; absent writes no key, Normal.
std::string name
The column's id, which a columns customization point names.
Nodes cell
The cell, read in the row scope.
AnyHandle width
The width.
Options of table and dataTable.
bool showHeader
Whether the header row is drawn. Several tables sharing one visual header set this false on every ins...
std::size_t virtualizeThreshold
Below this many rows, every row's widget stays realized regardless of scroll position; a backend that...
std::vector< TableColumn > columns
The columns; a model-backed table derives them from the row type when none are given.
AnyHandle rowTone
A row's tone, read in the row scope.
std::optional< Selection > selection
How the selection works.
std::optional< TableMode > mode
Where a model-backed table works; ignored by a table over rows.
Commands onActivate
Commands run when a row is activated, in its row scope.
std::size_t virtualizeOverscan
How many rows beyond the visible range, on each side, keep a realized widget.
AnyHandle filters
The filters.
AnyHandle key
The row key, read in the row scope.
AnyHandle highlightKey
The key a linked chart highlights.
Nodes empty
Shown instead of the table while its rows are an empty list, in the table's scope; empty shows the ta...
AnyHandle placeholder
Placeholder text.
Commands onSubmit
Commands run on Enter in a single-line input.
AnyHandle icon
An icon of the theme's icon map, drawn at the input's leading edge; empty shows none....
Commands onCommit
Commands run on a commit.
Commands onChange
Commands run on every edit.
AnyHandle value
The value.
AnyHandle mode
The input mode, such as multiline or password.
std::optional< Commit > commitOn
When the draft commits.
AnyHandle label
The label: the accessible name.
AnyHandle stale
Whether the shown value is being recalculated.
AnyHandle role
The typographic role, such as heading.
std::string transfer
The transfer id, whose progress, pending and error are element values.
std::string as
The name the reply is bound to for later steps.
Whether a type is a Handle.
Whether a type is a std::optional.
Whether a type is a std::vector.
std::string type
std::string.
std::string type
std::string.
std::string type
std::string.
The value type a constant stands for.
L type
The constant's own type.
A type with one std::optional removed.
T type
The handle's value type.
The value type of an operand: a handle's value type, or the type its constant stands for.
LiteralT< X > type
The constant's value type.
HandlerResult< T >::type HandlerResultT
The action result a handler returning T produces.
The UI document's typed values (ui::Value), its types (ui::Type), their JSON form,...
glz::generic_u64 Json
The JSON tree the document layer reads and writes: glaze's generic value in its integer-preserving mo...
Json valueToJson(Value const &value)
Writes a value in its JSON form: the inverse of valueFromJson, with a timestamp as ISO-8601 text.
std::vector< Value > ValueList
A list value.
std::map< std::string, Value, std::less<> > ValueRecord
A record value: field name to value, ordered by name.
std::function< void()> Action
A widget event handler taking no arguments.