Skip to content

Commit 2582889

Browse files
authored
Add a loading spinner for TryExamples directive. (#133)
* Remove unused config variable * Add a spinner * Add link for example css * Dynamically adjust spinner position * Adjust spinner position * Add spinners for other jupyterlite-sphinx directives
1 parent e084a6f commit 2582889

File tree

4 files changed

+63
-48
lines changed

4 files changed

+63
-48
lines changed

docs/directives/try_examples.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ Here's an example with some options set
137137
We've also added the ``blue-bottom`` class, the button should appear as blue,
138138
below the examples, and on the left side of the screen.
139139
140-
See ``try_examples.css`` in the Repository to see how we achieved this via
141-
custom css.
140+
See `try_examples.css <https://github.com/jupyterlite/jupyterlite-sphinx/blob/main/docs/_static/try_examples.css>`_
141+
to see how we achieved this via custom css.
142142
```
143143

144144
and here is the result
@@ -159,8 +159,8 @@ and here is the result
159159
We've also added the ``blue-bottom`` class, the button should appear as blue,
160160
below the examples, and on the left side of the screen.
161161
162-
See ``try_examples.css`` in the Repository to see how we achieved this via
163-
custom css.
162+
See `try_examples.css <https://github.com/jupyterlite/jupyterlite-sphinx/blob/main/docs/_static/try_examples.css>`_
163+
to see how we achieved this via custom css.
164164
```
165165

166166

jupyterlite_sphinx/jupyterlite_sphinx.css

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,47 +38,29 @@
3838
box-shadow: 0 0.2rem 0.5rem #d8d8d8;
3939
}
4040

41-
.jupyterlite_sphinx_iframe_container:hover .jupyterlite_sphinx_try_it_button_unclicked {
42-
-webkit-animation:grow 0.2s ease-in-out;
43-
animation:grow 0.2s ease-in-out;
44-
transform: translateY(-50%) translateX(-50%) scale(1.2);
45-
}
46-
47-
.jupyterlite_sphinx_try_it_button_clicked {
48-
-webkit-animation:grow 1s infinite alternate;
49-
animation:grow 1s infinite alternate;
50-
transform: translateY(-50%) translateX(-50%) scale(1.2);
51-
}
52-
53-
@keyframes grow {
54-
from {
55-
transform: translateY(-50%) translateX(-50%) scale(1);
56-
}
57-
to {
58-
transform: translateY(-50%) translateX(-50%) scale(1.2);
59-
}
60-
}
61-
62-
@-webkit-keyframes grow {
63-
from {
64-
transform: translateY(-50%) translateX(-50%) scale(1);
65-
}
66-
to {
67-
transform: translateY(-50%) translateX(-50%) scale(1.2);
68-
}
69-
}
70-
71-
.try_examples_iframe_container {
72-
position: relative;
73-
cursor: pointer;
74-
}
75-
76-
7741
.try_examples_outer_container {
7842
position: relative;
7943
}
8044

81-
8245
.hidden {
8346
display: none;
8447
}
48+
49+
.jupyterlite_sphinx_spinner {
50+
/* From https://css-loaders.com/spinner/ */
51+
position: absolute;
52+
z-index: 0;
53+
top: 50%;
54+
left: 50%;
55+
width: 50px;
56+
aspect-ratio: 1;
57+
border-radius: 50%;
58+
background:
59+
radial-gradient(farthest-side,#ffa516 94%,#0000) top/8px 8px no-repeat,
60+
conic-gradient(#0000 30%,#ffa516);
61+
-webkit-mask: radial-gradient(farthest-side,#0000 calc(100% - 8px),#000 0);
62+
animation: l13 1s infinite linear;
63+
}
64+
@keyframes l13{
65+
100%{transform: rotate(1turn)}
66+
}

jupyterlite_sphinx/jupyterlite_sphinx.js

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
window.jupyterliteShowIframe = (tryItButtonId, iframeSrc) => {
22
const tryItButton = document.getElementById(tryItButtonId);
33
const iframe = document.createElement('iframe');
4+
const buttonRect = tryItButton.getBoundingClientRect();
5+
6+
const spinner = document.createElement('div');
7+
// hardcoded spinner height and width needs to match what is in css.
8+
const spinnerHeight = 50; // px
9+
const spinnerWidth = 50; // px
10+
spinner.classList.add('jupyterlite_sphinx_spinner');
11+
spinner.style.display = 'none';
12+
// Add negative margins to center the spinner
13+
spinner.style.marginTop = `-${spinnerHeight/2}px`;
14+
spinner.style.marginLeft = `-${spinnerWidth/2}px`;
415

516
iframe.src = iframeSrc;
617
iframe.width = iframe.height = '100%';
718
iframe.classList.add('jupyterlite_sphinx_iframe');
819

20+
tryItButton.style.display = 'none';
21+
spinner.style.display = 'block';
22+
23+
tryItButton.parentNode.appendChild(spinner);
924
tryItButton.parentNode.appendChild(iframe);
10-
tryItButton.innerText = 'Loading ...';
11-
tryItButton.classList.remove('jupyterlite_sphinx_try_it_button_unclicked');
12-
tryItButton.classList.add('jupyterlite_sphinx_try_it_button_clicked');
1325
}
1426

1527
window.jupyterliteConcatSearchParams = (iframeSrc, params) => {
@@ -50,9 +62,17 @@ window.tryExamplesShowIframe = (
5062
const iframeContainer = document.getElementById(iframeContainerId);
5163
var height;
5264

53-
let iframe = iframeContainer.querySelector('iframe.jupyterlite_sphinx_raw_iframe');
65+
let iframe = iframeContainer.querySelector('iframe.jupyterlite_sphinx_iframe');
5466

5567
if (!iframe) {
68+
// Add spinner
69+
const spinner = document.createElement('div');
70+
// hardcoded spinner width needs to match what is in css.
71+
const spinnerHeight = 50; // px
72+
const spinnerWidth = 50; // px
73+
spinner.classList.add('jupyterlite_sphinx_spinner');
74+
iframeContainer.appendChild(spinner);
75+
5676
const examples = examplesContainer.querySelector('.try_examples_content');
5777
iframe = document.createElement('iframe');
5878
iframe.src = iframeSrc;
@@ -62,9 +82,23 @@ window.tryExamplesShowIframe = (
6282
} else {
6383
height = Math.max(tryExamplesGlobalMinHeight, examples.offsetHeight);
6484
}
85+
86+
/* Get spinner position. It will be centered in the iframe, unless the
87+
* iframe extends beyond the viewport, in which case it will be centered
88+
* between the top of the iframe and the bottom of the viewport.
89+
*/
90+
const examplesTop = examples.getBoundingClientRect().top;
91+
const viewportBottom = window.innerHeight;
92+
const spinnerTop = 0.5 * Math.min((viewportBottom - examplesTop), height);
93+
spinner.style.top = `${spinnerTop}px`;
94+
// Add negative margins to center the spinner
95+
spinner.style.marginTop = `-${spinnerHeight/2}px`;
96+
spinner.style.marginLeft = `-${spinnerWidth/2}px`;
97+
6598
iframe.style.height = `${height}px`;
66-
iframe.classList.add('jupyterlite_sphinx_raw_iframe');
99+
iframe.classList.add('jupyterlite_sphinx_iframe');
67100
examplesContainer.classList.add("hidden");
101+
68102
iframeContainer.appendChild(iframe);
69103
} else {
70104
examplesContainer.classList.add("hidden");

jupyterlite_sphinx/jupyterlite_sphinx.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ def run(self):
436436
iframe_parent_container_div_end = "</div>"
437437
iframe_container_div = (
438438
f'<div id="{iframe_div_id}" '
439-
f'class="try_examples_iframe_container">'
439+
f'class="jupyterlite_sphinx_iframe_container">'
440440
f"</div>"
441441
)
442442

@@ -498,7 +498,6 @@ def _process_autodoc_docstrings(app, what, name, obj, options, lines):
498498
try_examples_options = {
499499
"theme": app.config.try_examples_global_theme,
500500
"button_text": app.config.try_examples_global_button_text,
501-
"example_class": app.config.try_examples_global_example_class,
502501
}
503502
try_examples_options = {
504503
key: value for key, value in try_examples_options.items() if value is not None

0 commit comments

Comments
 (0)