109 lines
3.5 KiB
Dart
109 lines
3.5 KiB
Dart
|
import 'node.dart';
|
||
|
|
||
|
/// Helper class to build nodes.
|
||
|
class NodeBuilder {
|
||
|
final String tagName;
|
||
|
final Map<String, dynamic> attributes;
|
||
|
final Iterable<Node> children;
|
||
|
Node? _existing;
|
||
|
|
||
|
NodeBuilder(this.tagName,
|
||
|
{this.attributes = const {}, this.children = const []});
|
||
|
|
||
|
/// Creates a [NodeBuilder] that just spits out an already-existing [Node].
|
||
|
factory NodeBuilder.existing(Node existingNode) =>
|
||
|
NodeBuilder(existingNode.tagName).._existing = existingNode;
|
||
|
|
||
|
factory NodeBuilder.from(Node node) => NodeBuilder(node.tagName,
|
||
|
attributes: Map<String, dynamic>.from(node.attributes),
|
||
|
children: List<Node>.from(node.children));
|
||
|
|
||
|
/// Builds the node.
|
||
|
Node build({bool selfClosing = false}) =>
|
||
|
_existing ??
|
||
|
(selfClosing
|
||
|
? SelfClosingNode(tagName, attributes)
|
||
|
: Node(tagName, attributes, children));
|
||
|
|
||
|
/// Produce a modified copy of this builder.
|
||
|
NodeBuilder change(
|
||
|
{String? tagName,
|
||
|
Map<String, dynamic>? attributes,
|
||
|
Iterable<Node>? children}) {
|
||
|
return NodeBuilder(tagName ?? this.tagName,
|
||
|
attributes: attributes ?? this.attributes,
|
||
|
children: children ?? this.children);
|
||
|
}
|
||
|
|
||
|
NodeBuilder changeTagName(String tagName) => change(tagName: tagName);
|
||
|
|
||
|
NodeBuilder changeAttributes(Map<String, dynamic> attributes) =>
|
||
|
change(attributes: attributes);
|
||
|
|
||
|
NodeBuilder changeChildren(Iterable<Node> children) =>
|
||
|
change(children: children);
|
||
|
|
||
|
NodeBuilder changeAttributesMapped(
|
||
|
Map<String, dynamic> Function(Map<String, dynamic>) f) {
|
||
|
var map = Map<String, dynamic>.from(attributes);
|
||
|
return changeAttributes(f(map));
|
||
|
}
|
||
|
|
||
|
NodeBuilder changeChildrenMapped(Iterable<Node> Function(List<Node>) f) {
|
||
|
var list = List<Node>.from(children);
|
||
|
return changeChildren(f(list));
|
||
|
}
|
||
|
|
||
|
NodeBuilder mapChildren(Node Function(Node) f) =>
|
||
|
changeChildrenMapped((list) => list.map(f));
|
||
|
|
||
|
NodeBuilder mapAttributes(
|
||
|
MapEntry<String, dynamic> Function(String, dynamic) f) =>
|
||
|
changeAttributesMapped((map) => map.map(f));
|
||
|
|
||
|
NodeBuilder setAttribute(String name, dynamic value) =>
|
||
|
changeAttributesMapped((map) => map..[name] = value);
|
||
|
|
||
|
NodeBuilder addChild(Node child) =>
|
||
|
changeChildrenMapped((list) => list..add(child));
|
||
|
|
||
|
NodeBuilder removeChild(Node child) =>
|
||
|
changeChildrenMapped((list) => list..remove(child));
|
||
|
|
||
|
NodeBuilder removeAttribute(String name) =>
|
||
|
changeAttributesMapped((map) => map..remove(name));
|
||
|
|
||
|
NodeBuilder setId(String id) => setAttribute('id', id);
|
||
|
|
||
|
NodeBuilder setClassName(String className) =>
|
||
|
setAttribute('class', className);
|
||
|
|
||
|
NodeBuilder setClasses(Iterable<String> classes) =>
|
||
|
setClassName(classes.join(' '));
|
||
|
|
||
|
NodeBuilder setClassesMapped(Iterable<String> Function(List<String>) f) {
|
||
|
var clazz = attributes['class'];
|
||
|
var classes = <String>[];
|
||
|
|
||
|
if (clazz is String) {
|
||
|
classes.addAll(clazz.split(' '));
|
||
|
} else if (clazz is Iterable) {
|
||
|
classes.addAll(clazz.map((s) => s.toString()));
|
||
|
}
|
||
|
|
||
|
return setClasses(f(classes));
|
||
|
}
|
||
|
|
||
|
NodeBuilder addClass(String className) => setClassesMapped(
|
||
|
(classes) => classes.contains(className) ? classes : classes
|
||
|
..add(className));
|
||
|
|
||
|
NodeBuilder removeClass(String className) =>
|
||
|
setClassesMapped((classes) => classes..remove(className));
|
||
|
|
||
|
NodeBuilder toggleClass(String className) =>
|
||
|
setClassesMapped((classes) => classes.contains(className)
|
||
|
? (classes..remove(className))
|
||
|
: (classes..add(className)));
|
||
|
}
|