Skip to content

Commit

Permalink
Migrate to pkg:web (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevmoo authored Jul 25, 2024
1 parent 87e5d05 commit ca67a42
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 48 deletions.
108 changes: 61 additions & 47 deletions example/web_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
library;

import 'dart:convert' show LineSplitter, htmlEscape;
import 'dart:html';
import 'dart:svg' as svg;
import 'dart:js_interop';

import 'package:js/js.dart';
import 'package:web/web.dart';

final zoomBtn = querySelector('#zoomBtn') as ButtonElement;
final zoomBtn = document.querySelector('#zoomBtn') as HTMLButtonElement;

svg.SvgElement? __root;
SVGElement? __root;

svg.SvgElement get _root => __root!;
SVGElement get _root => __root!;

final List<String> _dotContentLines = List.unmodifiable(
LineSplitter.split(
(querySelector('#dot') as ScriptElement).innerHtml!.trim(),
((document.querySelector('#dot') as HTMLScriptElement).innerHTML
as JSString)
.toDart
.trim(),
),
);

Expand All @@ -27,7 +29,7 @@ void main() {
_process();

zoomBtn.onClick.listen((_) {
__root?.classes.toggle('zoom');
__root?.classList.toggle('zoom');
});
}

Expand Down Expand Up @@ -98,7 +100,7 @@ void _process() {
_updateBody(output);
} catch (e) {
final output = '<pre>${htmlEscape.convert(e.toString())}</pre>';
document.body!.appendHtml(output);
document.body!.append(output.toJS);
} finally {
print('Total time generating graph: ${watch.elapsed}');
}
Expand All @@ -116,22 +118,22 @@ void _updateBody(String output) {
)
.join('\n');

document.body!.appendHtml(output, treeSanitizer: NodeTreeSanitizer.trusted);
document.body!.insertAdjacentHTML('beforeend', output.toJS);
zoomBtn.style.display = 'block';

__root = querySelector('svg') as svg.SvgElement;
__root = document.querySelector('svg') as SVGElement;

for (var element in _root.querySelectorAll('g.node')) {
final title = element.querySelector('title')!.text!;
for (var element in _root.querySelectorAll('g.node').elements) {
final title = element.querySelector('title')!.textContent!;
element.id = title;
}

for (var node in _root.querySelectorAll('g.node')) {
for (var node in _root.querySelectorAll('g.node').elements) {
// NOTE: we are assuming the shape of the generated SVG here – be careful!
final polygonBorder = node.querySelector('polygon')?.getAttribute('stroke');
if (polygonBorder != null &&
polygonBorder.toLowerCase().startsWith('#ff')) {
node.classes.add('outdated');
node.classList.add('outdated');
}

node.onClick.listen((MouseEvent event) {
Expand All @@ -145,8 +147,8 @@ void _updateBody(String output) {
});
}

for (var node in _root.querySelectorAll('g.edge')) {
final title = node.querySelector('title')!.text!;
for (var node in _root.querySelectorAll('g.edge').elements) {
final title = node.querySelector('title')!.textContent!;
final things = title.split('->');
node
..setAttribute('x-from', things[0])
Expand All @@ -158,67 +160,69 @@ void _updateBody(String output) {
assert(textFill.startsWith('#'));
if (textFill.toLowerCase().startsWith('#ff')) {
// This is an outdated dependency
node.classes.add('outdated');
node.classList.add('outdated');
}
}
}

final nodesOfInterest = _root.querySelectorAll('g.edge, g.node');

nodesOfInterest.onMouseEnter.listen((MouseEvent event) {
_updateOver(event.currentTarget as svg.GElement);
});
for (var interest in nodesOfInterest.elements) {
interest.onMouseEnter.listen((MouseEvent event) {
_updateOver(event.currentTarget as SVGGElement);
});

nodesOfInterest.onMouseLeave.listen((MouseEvent event) {
_updateOver(null);
});
interest.onMouseLeave.listen((MouseEvent event) {
_updateOver(null);
});
}
}

void _updateOver(svg.GElement? element) {
void _updateOver(SVGGElement? element) {
final targetPkg = <String?>[];
if (element != null) {
if (element.classes.contains('edge')) {
if (element.classList.contains('edge')) {
targetPkg
.addAll([element.attributes['x-to'], element.attributes['x-from']]);
} else {
assert(element.classes.contains('node'));
assert(element.classList.contains('node'));
targetPkg.add(element.id);
}
}

for (var node in _root.querySelectorAll('g.node')) {
for (var node in _root.querySelectorAll('g.node').elements) {
if (targetPkg.contains(node.id)) {
node.classes.add('active');
node.classList.add('active');
} else {
node.classes.remove('active');
node.classList.remove('active');
}
}

final fromNodes = <String>[];
final toNodes = <String>[];
for (var node in _root.querySelectorAll('g.edge')) {
for (var node in _root.querySelectorAll('g.edge').elements) {
final nodeXTo = node.attributes['x-to']!;
final nodeXFrom = node.attributes['x-from']!;
if (targetPkg.length == 2) {
// then the hover-over is on a line!
if (targetPkg.contains(node.attributes['x-to']) &&
targetPkg.contains(node.attributes['x-from'])) {
node.classes.add('active');
if (targetPkg.contains(nodeXTo) && targetPkg.contains(nodeXFrom)) {
node.classList.add('active');
} else {
node.classes.remove('active');
node.classList.remove('active');
}
} else {
if (targetPkg.contains(node.attributes['x-to']) ||
targetPkg.contains(node.attributes['x-from'])) {
if (targetPkg.contains(node.attributes['x-to'])) {
fromNodes.add(node.attributes['x-from']!);
if (targetPkg.contains(nodeXTo) || targetPkg.contains(nodeXFrom)) {
if (targetPkg.contains(nodeXTo)) {
fromNodes.add(nodeXFrom);
}

if (targetPkg.contains(node.attributes['x-from'])) {
toNodes.add(node.attributes['x-to']!);
if (targetPkg.contains(nodeXFrom)) {
toNodes.add(nodeXTo);
}

node.classes.add('active');
node.classList.add('active');
} else {
node.classes.remove('active');
node.classList.remove('active');
}
}
}
Expand All @@ -239,11 +243,21 @@ void _updateOver(svg.GElement? element) {
// ignore: non_constant_identifier_names
external String Viz(String src, [VizOptions options]);

@JS()
@anonymous
class VizOptions {
extension type VizOptions._(JSObject _) implements JSObject {
external VizOptions({String format, int totalMemory});

external String format;
external int totalMemory;
}

extension on NodeList {
Iterable<Element> get elements sync* {
for (var i = 0; i < length; i++) {
yield item(i)! as Element;
}
}
}

external factory VizOptions({String format, int totalMemory});
extension on NamedNodeMap {
String? operator [](String key) => getNamedItem(key)?.value;
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ dev_dependencies:
build_version: ^2.0.3
build_web_compilers: ^4.0.9
dart_flutter_team_lints: ^3.0.0
js: ^0.7.0
test: ^1.25.1
test_descriptor: ^2.0.0
test_process: ^2.0.0
web: ^1.0.0

executables:
pubviz: null

0 comments on commit ca67a42

Please sign in to comment.