visibleRows method

List<TreeRow<T>> visibleRows()

The visible rows in render order. While searching, only matches and their ancestors are shown, and ancestor folders are force-expanded so the hits are revealed.

Implementation

List<TreeRow<T>> visibleRows() {
  final q = _query.trim().toLowerCase();
  final filter = q.isNotEmpty;

  final matched = <TreeNodeId>{};
  final onPath = <TreeNodeId>{}; // ancestors of a match
  if (filter) {
    void rec(List<TreeNode<T>> nodes, List<TreeNodeId> path) {
      for (final n in nodes) {
        if (n.label.toLowerCase().contains(q)) {
          matched.add(n.id);
          onPath.addAll(path);
        }
        if (n.children.isNotEmpty) rec(n.children, [...path, n.id]);
      }
    }

    rec(_roots, const []);
  }

  bool visible(TreeNode<T> n) => !filter || matched.contains(n.id) || onPath.contains(n.id);
  bool expandedFor(TreeNode<T> n) => filter ? onPath.contains(n.id) : _expanded.contains(n.id);

  final rows = <TreeRow<T>>[];
  void emit(List<TreeNode<T>> nodes, int depth, List<bool> ancestorHasNext) {
    final shown = filter ? nodes.where(visible).toList() : nodes;
    for (var i = 0; i < shown.length; i++) {
      final n = shown[i];
      final isLast = i == shown.length - 1;
      final expanded = expandedFor(n);
      rows.add(TreeRow<T>(
        node: n,
        depth: depth,
        expanded: expanded,
        hasChildren: n.hasChildren,
        isLast: isLast,
        ancestorHasNext: List<bool>.of(ancestorHasNext),
      ));
      if (n.hasChildren && expanded) {
        emit(n.children, depth + 1, [...ancestorHasNext, !isLast]);
      }
    }
  }

  emit(_roots, 0, const []);
  return rows;
}