23namespace morph::forms {
52namespace detail::engine {
60 WizardSession
const*
const wizard = &session;
63 std::string
const named = isSet(heading) ? heading.evaluate() : wizard->title();
64 return named +
" (" + std::to_string(wizard->current() + 1) +
" / " +
65 std::to_string(wizard->stepCount()) +
")";
67 .role = ui::TextRole::Heading};
72 std::vector<ui::Step> items;
73 std::size_t
const here =
wizard->current();
74 for (std::size_t i = 0; i <
wizard->stepCount(); ++i) {
75 ui::StepState state = ui::StepState::Pending;
77 state = ui::StepState::Active;
78 }
else if (
wizard->stepDone(i)) {
79 state = ui::StepState::Done;
81 items.push_back(ui::Step{.label =
wizard->stepTitle(i), .state = state});
85 .current = [
wizard] {
return std::optional<std::size_t>{
wizard->current()}; },
88 return ui::column(ui::Column{.children = {ui::text(std::move(title)), ui::steps(std::move(steps))}, .gap = 4});
95[[nodiscard]]
inline ui::Node wizardNav(WizardSession& session, WizardViewOptions
const& options) {
97 ui::Button back{.label = options.backLabel, .onClick = [
wizard] {
wizard->back(); }};
98 back.common.enabled = [
wizard] {
return wizard->canBack(); };
99 back.common.testId =
"wizardBack";
100 ui::Button next{.label = options.nextLabel, .onClick = [
wizard] {
wizard->next(); }};
101 next.common.enabled = [
wizard] {
return wizard->canNext(); };
102 next.common.testId =
"wizardNext";
103 ui::Text last{.text = options.lastStepText, .role = ui::TextRole::Muted};
104 last.common.visible = [
wizard] {
return wizard->onLastStep(); };
105 last.common.testId =
"wizard_last";
106 return ui::row(ui::Row{
107 .children = {ui::button(std::move(back)), ui::button(std::move(next)), ui::text(std::move(last))}, .gap = 4});
124 WizardSession
const*
const wizard = &session;
125 std::vector<ui::SwitchCase> cases;
126 for (std::size_t i = 0; i < session.stepCount(); ++i) {
127 ui::Column step{.children = {formView(session.step(i), options.form)}};
128 step.
common.
testId =
"wizard_step_" + std::to_string(i);
130 ui::SwitchCase{.key = ui::Key{
static_cast<std::int64_t
>(i)}, .node = ui::column(std::move(step))});
132 ui::Node body = ui::switchOf(ui::Switch{
133 .selector = [
wizard] {
return ui::Key{
static_cast<std::int64_t
>(
wizard->current())}; },
134 .cases = std::move(cases),
136 return ui::column(ui::Column{
137 .children = {options.chrome.header ? options.chrome.header(session)
138 : detail::engine::wizardHeader(session, options.title),
140 options.chrome.nav ? options.chrome.nav(session) : detail::engine::wizardNav(session, options)},
AnyHandle session(std::string const &name)
A session attribute, {"session": name}.
Node steps(AnyHandle const &items, AnyHandle const ¤t={})
A steps node.
Node wizard(ModelHandle< Model > const &model, FormPresentation const &presentation={})
A wizard node: the forms engine's wizard over an action.
A node property: a constant, or a slot — a reactive read, with a stable id when it was given one.
Node testId(std::string const &name) const
A stable identifier for automated tests.
Children stacked top to bottom.
Common common
Visibility, enablement, layout, drag-and-drop.
std::string testId
The stable identifier an automated test finds the widget by; empty means none. Set once.
Common common
Visibility, enablement, layout, drag-and-drop.
The toolkit-agnostic view tree: props, node kinds and their builders.
std::shared_ptr< NodeData const > Node
A view-tree node: immutable, shared data.
Wizards, headless: a w-* document as one FormSession per step, sequenced by successful submissions,...
ui::Node wizardView(WizardSession &session, WizardViewOptions const &options={})
Renders session: the header (title with the step's position, a step indicator), the current step's fo...