triggersOutsideAny function

Stream<Event> triggersOutsideAny(
  1. Predicate<Node> checkNodeInside
)

A stream of click, mouseup or focus events of any node none of whose parents pass the check inside function.

Implementation

Stream<Event> triggersOutsideAny(Predicate<Node> checkNodeInside) {
  StreamController<Event>? controller;
  StreamSubscription<MouseEvent>? clickListener;
  StreamSubscription<MouseEvent>? mouseDownListener;
  StreamSubscription<MouseEvent>? mouseUpListener;
  EventListener? listener;

  controller = StreamController.broadcast(
      sync: true,
      onListen: () {
        Event? lastEvent;
        Event? lastDownEvent;

        listener = (Event e) {
          lastEvent = e;
          var node = e.target as Node?;
          while (node != null) {
            if (checkNodeInside(node)) {
              return;
            } else {
              node = node.parent;
            }
          }
          controller!.add(e);
        };

        mouseDownListener = document.onMouseDown.listen((MouseEvent e) {
          lastDownEvent = e;
        });

        mouseUpListener = document.onMouseUp.listen((MouseEvent e) {
          if (lastDownEvent == null || e.target == lastDownEvent!.target) {
            listener!(e);
          }
          lastEvent = e;
        });

        clickListener = document.onClick.listen((MouseEvent e) {
          if (lastEvent?.type == 'mouseup' && e.target == lastEvent?.target) {
            return;
          }
          if (lastDownEvent == null || e.target == lastDownEvent!.target) {
            listener!(e);
          }
          lastDownEvent = null;
        });

        document.addEventListener('focus', listener!, true);

        document.addEventListener('touchend', listener!);
      },
      onCancel: () {
        clickListener?.cancel();
        clickListener = null;
        mouseDownListener?.cancel();
        mouseDownListener = null;
        mouseUpListener?.cancel();
        mouseUpListener = null;
        document.removeEventListener('focus', listener!, true);
        document.removeEventListener('touchend', listener!);
      });
  return controller!.stream;
}