Repository navigation
Expand file tree
/
Copy pathreact.html
More file actions
99 lines (92 loc) · 4.6 KB
/
Copy pathreact.html
File metadata and controls
99 lines (92 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 32 32%27%3E%3Ccircle cx=%2711%27 cy=%2712%27 r=%277%27 fill=%27%232f6fe0%27/%3E%3Ccircle cx=%2722%27 cy=%2721%27 r=%275%27 fill=%27%23e2403a%27/%3E%3Ccircle cx=%2722%27 cy=%278%27 r=%273%27 fill=%27%231c2130%27/%3E%3C/svg%3E">
<title>ipyspeck 0.8.4 · React example</title>
<script crossorigin src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
<style>
body { margin: 0; background: #f5f6f8; color: #1c2130;
font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
main { max-width: 1100px; margin: 0 auto; padding: 24px 20px 40px; }
h1 { font-size: 22px; margin: 0 0 4px; }
p { color: #636b7a; margin: 0 0 14px; }
a { color: #2f6fe0; }
.viewer { height: 520px; background: #fff; border: 1px solid #e3e6eb; border-radius: 10px; overflow: hidden; }
.bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 12px 0; }
button { font: inherit; padding: 6px 11px; border: 1px solid #e3e6eb; border-radius: 6px; background: #fff; cursor: pointer; }
button.on { background: #e8effc; border-color: #2f6fe0; color: #2f6fe0; }
pre { background: #fff; border: 1px solid #e3e6eb; border-radius: 10px; padding: 14px; overflow-x: auto; font-size: 12.5px; }
</style>
</head>
<body>
<main>
<h1>Speck viewer in React</h1>
<p>A small component around <code>SpeckViewer</code>, the viewer shared by ipyspeck and stspeck.
<a href="index.html">Back to the demo</a>.</p>
<div id="root"></div>
<pre id="source"></pre>
</main>
<script src="speck.58e6a57b59.js"></script>
<script id="example">
const { useEffect, useRef, useState } = React;
// <SpeckView data="..." cartoon surface ... />: every prop is a viewer setting.
function SpeckView(props) {
const el = useRef(null);
const viewer = useRef(null);
const settings = useRef({ ...SpeckView.defaults, ...props });
useEffect(() => {
viewer.current = new window.Speck.SpeckViewer(el.current, {
get: (trait) => settings.current[trait],
set: (changes) => { // toolbar buttons
Object.assign(settings.current, changes);
for (const k in changes) viewer.current.setTrait(k, changes[k]);
if (props.onChange) props.onChange(changes);
},
});
return () => viewer.current.destroy();
}, []);
useEffect(() => {
const v = viewer.current;
const next = { ...SpeckView.defaults, ...props };
const dataChanged = next.data !== settings.current.data;
for (const k of window.Speck.VIEW_TRAITS) {
if (JSON.stringify(next[k]) !== JSON.stringify(settings.current[k])) {
settings.current[k] = next[k];
if (v) v.setTrait(k, next[k]);
}
}
settings.current.data = next.data;
if (v && dataChanged) v.loadStructure();
if (v) v.updateToolbar();
});
return React.createElement("div", { ref: el, className: props.className, style: { width: "100%", height: "100%" } });
}
SpeckView.defaults = { data: "", toolbar: true, camera: {}, ...window.Speck.VIEW_DEFAULTS };
function App() {
const [pdb, setPdb] = useState("");
const [look, setLook] = useState({ cartoon: true, surface: false, surfaceOpacity: 0.35, shadows: 0.5 });
useEffect(() => {
fetch("https://files.rcsb.org/download/4HHB.pdb").then((r) => r.text()).then(setPdb);
}, []);
const toggle = (k) => setLook({ ...look, [k]: !look[k] });
const h = React.createElement;
return h("div", null,
h("div", { className: "bar" },
h("button", { className: look.cartoon ? "on" : "", onClick: () => toggle("cartoon") }, "Cartoon"),
h("button", { className: look.surface ? "on" : "", onClick: () => toggle("surface") }, "Glass surface"),
h("button", { className: look.shadows ? "on" : "", onClick: () => setLook({ ...look, shadows: look.shadows ? 0 : 0.5 }) }, "Shadows")),
h("div", { className: "viewer" },
h(SpeckView, { data: pdb, cartoonColor: "chain", surfaceColor: "#eef2f8", specular: 0.5, gloss: 0.7,
highlight: { resName: "HEM" }, highlightScale: 1.3, ...look,
onChange: (changes) => setLook((l) => ({ ...l, ...changes })) })));
}
ReactDOM.createRoot(document.getElementById("root")).render(React.createElement(App));
</script>
<script>
document.getElementById("source").textContent = document.getElementById("example").textContent.trim();
</script>
</body>
</html>