@@ -19,10 +19,27 @@ def __init__(self) -> None:
19
19
nvl_entry_point = resource_folder / "nvl_entrypoint"
20
20
21
21
js_path = nvl_entry_point / "base.js"
22
-
23
22
with js_path .open ("r" , encoding = "utf-8" ) as file :
24
23
self .library_code = file .read ()
25
24
25
+ styles_path = nvl_entry_point / "styles.css"
26
+ with styles_path .open ("r" , encoding = "utf-8" ) as file :
27
+ self .styles = file .read ()
28
+
29
+ icons = resource_folder / "icons"
30
+
31
+ zoom_in_path = icons / "zoom-in.svg"
32
+ with zoom_in_path .open ("r" , encoding = "utf-8" ) as file :
33
+ self .zoom_in_svg = file .read ()
34
+
35
+ zoom_out_path = icons / "zoom-out.svg"
36
+ with zoom_out_path .open ("r" , encoding = "utf-8" ) as file :
37
+ self .zoom_out_svg = file .read ()
38
+
39
+ screenshot_path = icons / "screenshot.svg"
40
+ with screenshot_path .open ("r" , encoding = "utf-8" ) as file :
41
+ self .screenshot_svg = file .read ()
42
+
26
43
def unsupported_field_type_error (self , e : TypeError , entity : str ) -> Exception :
27
44
if "not JSON serializable" in str (e ):
28
45
return ValueError (f"A field of a { entity } object is not supported: { str (e )} " )
@@ -51,13 +68,19 @@ def render(
51
68
52
69
if show_hover_tooltip :
53
70
hover_element = f"document.getElementById('{ container_id } -tooltip')"
54
- hover_div = f'<div id="{ container_id } -tooltip" style="width: 20%; min-width: 100px; max-width: 600px; max-height: 80%; position: absolute; z-index: 2147483647; right: 0; bottom: 0; background: white; display: none; border: solid; border-color: #BBBEC3; border-width: 0.5px; padding: 0.8rem; border-radius: 8px; margin-bottom: 1rem; margin-right: 0.5rem; filter: drop-shadow(0 4px 8px rgba(26,27,29,0.12)); font-family: PublicSans; color: #4D5157; font-size: 14px "></div>'
71
+ hover_div = f'<div id="{ container_id } -tooltip" class="tooltip" style=" display: none;"></div>'
55
72
else :
56
73
hover_element = "null"
57
74
hover_div = ""
58
75
76
+ # Using a different varname for every instance, so that a notebook
77
+ # can use several instances without unwanted interactions.
78
+ # The first part of the UUID should be "unique enough" in this context.
79
+ nvl_varname = "graph_" + container_id .split ("-" )[0 ]
80
+ download_name = nvl_varname + ".png"
81
+
59
82
js_code = f"""
60
- var myNvl = new NVLBase.NVL(
83
+ var { nvl_varname } = new NVLBase.NVL(
61
84
document.getElementById('{ container_id } '),
62
85
{ hover_element } ,
63
86
{ nodes_json } ,
@@ -66,12 +89,37 @@ def render(
66
89
);
67
90
"""
68
91
full_code = self .library_code + js_code
92
+
69
93
html_output = f"""
94
+ <style>
95
+ { self .styles }
96
+ </style>
70
97
<div id="{ container_id } " style="width: { width } ; height: { height } ; position: relative;">
98
+ <div style="position: absolute; z-index: 2147483647; right: 0; top: 0; padding: 1rem">
99
+ <button type="button" title="Save as PNG" onclick="{ nvl_varname } .nvl.saveToFile({{ filename: '{ download_name } ' }})" class="icon">
100
+ { self .screenshot_svg }
101
+ </button>
102
+ <button type="button" title="Zoom in" onclick="{ nvl_varname } .nvl.setZoom({ nvl_varname } .nvl.getScale() + 0.5)" class="icon">
103
+ { self .zoom_in_svg }
104
+ </button>
105
+ <button type="button" title="Zoom out" onclick="{ nvl_varname } .nvl.setZoom({ nvl_varname } .nvl.getScale() - 0.5)" class="icon">
106
+ { self .zoom_out_svg }
107
+ </button>
108
+ </div>
71
109
{ hover_div }
72
110
</div>
111
+
73
112
<script>
113
+ getTheme = () => {{
114
+ const backgroundColorString = window.getComputedStyle(document.body, null).getPropertyValue('background-color')
115
+ const colorsArray = backgroundColorString.match(/\d+/g);
116
+ const brightness = Number(colorsArray[0]) * 0.2126 + Number(colorsArray[1]) * 0.7152 + Number(colorsArray[2]) * 0.0722
117
+ return brightness < 128 ? "dark" : "light"
118
+ }}
119
+ document.documentElement.className = getTheme()
120
+
74
121
{ full_code }
75
122
</script>
76
123
"""
124
+
77
125
return HTML (html_output ) # type: ignore[no-untyped-call]
0 commit comments