-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathstepper.dart
More file actions
97 lines (86 loc) · 3.14 KB
/
stepper.dart
File metadata and controls
97 lines (86 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Copyright 2025 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:jaspr/dom.dart';
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_content/jaspr_content.dart';
import '../common/button.dart';
import '../common/material_icon.dart';
class Stepper extends CustomComponent {
const Stepper() : super.base();
@override
Component? create(Node node, NodesBuilder builder) {
if (node case ElementNode(
tag: 'Stepper',
:final attributes,
:final children,
)) {
final levelStr = attributes['level'] ?? '1';
final level = int.tryParse(levelStr) ?? 1;
final collapsible = attributes['collapsible']?.toLowerCase() != 'false';
final showActions = attributes['actions']?.toLowerCase() != 'none';
final showFinish =
showActions &&
attributes['actions']?.toLowerCase() != 'continue-only';
assert(
level >= 1 && level <= 6,
'Stepper level must be between 1 and 6, got $level',
);
final steps = <({Node title, List<Node> content})>[];
if (children != null) {
for (final child in children) {
if (child case final ElementNode heading
when heading.tag == 'h$level') {
steps.add((title: child, content: []));
} else if (child case ElementNode(
tag: 'div',
attributes: {'class': 'header-wrapper'},
children: [final ElementNode heading, ..._],
) when heading.tag == 'h$level') {
steps.add((title: heading, content: []));
} else {
if (steps.isEmpty) {
throw Exception(
'Content found before first step title in Stepper. Make sure '
'your Stepper content starts with a heading of level $level.',
);
}
steps.last.content.add(child);
}
}
}
assert(steps.isNotEmpty, 'Stepper must have at least one step.');
final stepperClasses = collapsible
? 'stepper'
: 'stepper non-collapsible';
return div(classes: stepperClasses, [
for (final (index, step) in steps.indexed)
details(open: !collapsible || index == 0, [
summary([
span(
classes: 'step-number',
attributes: {'aria-label': 'Step ${index + 1}'},
[.text('${index + 1}')],
),
div(classes: 'step-title', [
builder.build([step.title]),
]),
const MaterialIcon('keyboard_arrow_up'),
]),
div(classes: 'step-content', [
builder.build(step.content),
]),
if (showActions && (index < steps.length - 1 || showFinish))
div(classes: 'step-actions', [
Button(
classes: ['next-step-button'],
style: ButtonStyle.filled,
content: index == steps.length - 1 ? 'Finish' : 'Continue',
),
]),
]),
]);
}
return null;
}
}