Skip to content

Commit 2383202

Browse files
Enhancements for dropdowns: animations, open at initial state, and chevron indicators (#675)
This PR modifies the `dropdown` shortcode to add three new features, in order to establish feature parity with the dropdowns from the [PyData Sphinx Theme](https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/web-components.html#dropdowns) and the [sphinx-design](https://sphinx-design.readthedocs.io/en/pydata-theme/dropdowns.html) projects. This was also discussed in #477 (comment), and should resolve that part of the conversation. Also, this PR is an aftermath of #471. In particular, the features added here are as follows: - An "open" dropdown: the option to leave the dropdown open at initial load, i.e., with the contents expanded instead of collapsed - Chevron indicators: these are now displayed at the end of the dropdown's title container when the dropdown has been expanded or is in its collapsed state, and are animated, and there is also a choice between - Animations for the chevron indicators and for how the content is displayed (fade-in, fade-in-slide-down) have been added, based on the corresponding option for the directive In addition to this, I've added several more examples to the shortcode's docs: a success dropdown, a warning one, a danger one, a multi-line example, one with code, and lastly, a nested dropdown. I've checked for any accessibility issues/implications, and I think this should be fine; I've closely followed the PST/SD implementation(s), and the chevrons here work similarly.
1 parent 23ae21c commit 2383202

File tree

2 files changed

+266
-26
lines changed

2 files changed

+266
-26
lines changed

assets/theme-css/sphinx-design/_dropdown.scss

Lines changed: 123 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,35 @@ details.sd-dropdown {
55
font-weight: 700;
66
// don't overlap the chevron
77
padding-right: 3em !important;
8-
-moz-user-select: none;
9-
-ms-user-select: none;
10-
-webkit-user-select: none;
118
user-select: none;
9+
display: inline-flex;
10+
justify-content: space-between;
11+
align-items: center;
12+
width: 100%;
13+
14+
.sd-summary-text {
15+
flex-grow: 1;
16+
line-height: 1.5;
17+
padding-right: 0.5rem;
18+
}
19+
20+
.sd-summary-state-marker {
21+
position: absolute;
22+
right: 1em;
23+
top: 50%;
24+
transform: translateY(-50%);
25+
pointer-events: none;
26+
display: inline-flex;
27+
align-items: center;
28+
justify-content: center;
29+
width: 0.75em;
30+
}
31+
32+
// The hover effect should only change opacity, not transform.
33+
// We are transforming the chevron elements instead, see below.
34+
&:hover .sd-summary-state-marker svg {
35+
opacity: 1;
36+
}
1237
}
1338

1439
&:hover {
@@ -54,7 +79,6 @@ details.sd-dropdown {
5479
summary:hover .sd-summary-up svg,
5580
summary:hover .sd-summary-down svg {
5681
opacity: 1;
57-
transform: scale(1.1);
5882
}
5983

6084
.sd-summary-up svg,
@@ -79,6 +103,61 @@ details.sd-dropdown {
79103
visibility: hidden;
80104
}
81105

106+
// Chevron transitions
107+
.sd-summary-chevron-right i,
108+
.sd-summary-chevron-down i {
109+
display: inline-block;
110+
transform-origin: center;
111+
opacity: 0.6;
112+
}
113+
114+
// The chevron rotation animations are applied to
115+
// the icon inside the dropdown title div
116+
&[open] > .sd-summary-title .sd-summary-chevron-right i {
117+
transform: rotate(90deg);
118+
animation: rotate-to-90 0.25s ease-in-out;
119+
}
120+
121+
&[open] > .sd-summary-title .sd-summary-chevron-down i {
122+
transform: rotate(-180deg);
123+
animation: rotate-to-negative-180 0.25s ease-in-out;
124+
}
125+
126+
&:not([open]) > .sd-summary-title .sd-summary-chevron-right i {
127+
transform: rotate(0deg);
128+
animation: rotate-to-0-from-90 0.25s ease-in-out;
129+
}
130+
131+
&:not([open]) > .sd-summary-title .sd-summary-chevron-down i {
132+
transform: rotate(0deg);
133+
animation: rotate-to-0-from-negative-180 0.25s ease-in-out;
134+
}
135+
136+
> .sd-summary-title:hover .sd-summary-chevron-right i,
137+
> .sd-summary-title:hover .sd-summary-chevron-down i {
138+
opacity: 1;
139+
}
140+
141+
// Combined transforms for each state with hover. These cover
142+
// the cases where the chevron is rotated, say, when the dropdown
143+
// is open or if the chevron starts rotated (e.g. in the down-up
144+
// state).
145+
&:not([open]) > .sd-summary-title:hover .sd-summary-chevron-right i {
146+
transform: scale(1.1);
147+
}
148+
149+
&:not([open]) > .sd-summary-title:hover .sd-summary-chevron-down i {
150+
transform: scale(1.1);
151+
}
152+
153+
&[open] > .sd-summary-title:hover .sd-summary-chevron-right i {
154+
transform: rotate(90deg) scale(1.1);
155+
}
156+
157+
&[open] > .sd-summary-title:hover .sd-summary-chevron-down i {
158+
transform: rotate(-180deg) scale(1.1);
159+
}
160+
82161
// Hide the card body border when not open
83162
&:not([open]).sd-card {
84163
border: none;
@@ -90,18 +169,10 @@ details.sd-dropdown {
90169

91170
// Transition animation
92171
&.sd-fade-in[open] summary ~ * {
93-
-moz-animation: sd-fade-in 0.5s ease-in-out;
94-
-webkit-animation: sd-fade-in 0.5s ease-in-out;
95172
animation: sd-fade-in 0.5s ease-in-out;
96173
}
97174

98175
&.sd-fade-in-slide-down[open] summary ~ * {
99-
-moz-animation:
100-
sd-fade-in 0.5s ease-in-out,
101-
sd-slide-down 0.5s ease-in-out;
102-
-webkit-animation:
103-
sd-fade-in 0.5s ease-in-out,
104-
sd-slide-down 0.5s ease-in-out;
105176
animation:
106177
sd-fade-in 0.5s ease-in-out,
107178
sd-slide-down 0.5s ease-in-out;
@@ -135,3 +206,43 @@ details.sd-dropdown {
135206
transform: translate(0, 0);
136207
}
137208
}
209+
210+
@keyframes rotate-to-90 {
211+
from {
212+
transform: rotate(0deg) scale(1.1);
213+
}
214+
to {
215+
transform: rotate(90deg) scale(1.1);
216+
}
217+
}
218+
219+
@keyframes rotate-to-0-from-90 {
220+
from {
221+
transform: rotate(90deg) scale(1.1);
222+
}
223+
to {
224+
transform: rotate(0deg) scale(1.1);
225+
}
226+
}
227+
228+
// TODO: These keyframes are disabled for now to maintain
229+
// parity with Sphinx Design upstream, which does not implement
230+
// the down-up chevron animation.
231+
232+
// @keyframes rotate-to-negative-180 {
233+
// from {
234+
// transform: rotate(0deg) scale(1.1);
235+
// }
236+
// to {
237+
// transform: rotate(-180deg) scale(1.1);
238+
// }
239+
// }
240+
241+
// @keyframes rotate-to-0-from-negative-180 {
242+
// from {
243+
// transform: rotate(-180deg) scale(1.1);
244+
// }
245+
// to {
246+
// transform: rotate(0deg) scale(1.1);
247+
// }
248+
// }

layouts/shortcodes/dropdown.html

Lines changed: 143 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,152 @@
3131
body = 'And some content and an icon!'
3232
{{< /dropdown >}}
3333

34+
{{< dropdown >}}
35+
title = 'A success color dropdown'
36+
icon = 'fa-solid fa-check'
37+
color = 'success'
38+
body = 'And some content and an icon!'
39+
{{< /dropdown >}}
40+
41+
{{< dropdown >}}
42+
title = 'A warning color dropdown'
43+
icon = 'fa-solid fa-exclamation'
44+
color = 'warning'
45+
body = 'And some content and an icon!'
46+
{{< /dropdown >}}
47+
48+
{{< dropdown >}}
49+
title = 'A danger color dropdown'
50+
icon = 'fa-solid fa-exclamation-triangle'
51+
color = 'danger'
52+
body = 'And some content and an icon!'
53+
{{< /dropdown >}}
54+
55+
{{< dropdown >}}
56+
title = 'Open dropdown by default'
57+
icon = 'fa-solid fa-eye'
58+
color = 'info'
59+
open = true
60+
body = 'This dropdown is open by default!'
61+
{{< /dropdown >}}
62+
63+
{{< dropdown >}}
64+
title = 'Fade in animation'
65+
icon = 'fa-solid fa-magic'
66+
animate = 'fade-in'
67+
body = 'This dropdown fades in when opened!'
68+
{{< /dropdown >}}
69+
70+
{{< dropdown >}}
71+
title = 'Fade in and slide down animation'
72+
icon = 'fa-solid fa-chart-line'
73+
animate = 'fade-in-slide-down'
74+
body = 'This dropdown fades in and slides down when opened!'
75+
{{< /dropdown >}}
76+
77+
{{< dropdown >}}
78+
title = 'Using chevron: down-up'
79+
icon = 'fa-solid fa-cog'
80+
chevron = 'down-up'
81+
body = 'Notice the different chevron direction when opened!'
82+
{{< /dropdown >}}
83+
84+
{{< dropdown >}}
85+
title = 'Custom chevron with a fade-in animation'
86+
icon = 'fa-solid fa-star'
87+
chevron = 'down-up'
88+
animate = 'fade-in'
89+
color = 'warning'
90+
body = 'Combining different features!'
91+
{{< /dropdown >}}
92+
93+
{{< dropdown >}}
94+
title = 'Multi-line content'
95+
icon = 'fa-solid fa-align-left'
96+
body = '''
97+
This is multi-line content.
98+
99+
It can include **markdown** and multiple paragraphs.
100+
And even lists:
101+
102+
- Item 1
103+
- Item 2
104+
- Item 3
105+
'''
106+
{{< /dropdown >}}
107+
108+
{{< dropdown >}}
109+
title = 'Dropdown with code'
110+
icon = 'fa-solid fa-code'
111+
color = 'success'
112+
body = '''
113+
Here's some code inside a dropdown:
114+
115+
```python
116+
def hello_world():
117+
print("Hello from a dropdown!")
118+
return 42
119+
```
120+
121+
And more content after the code.
122+
'''
123+
{{< /dropdown >}}
124+
125+
{{< dropdown >}}
126+
title = 'Nested dropdowns'
127+
icon = 'fa-solid fa-folder'
128+
body = '''
129+
This is a dropdown with a dropdown inside it.
130+
131+
{{< dropdown >}}
132+
title = 'Inner dropdown'
133+
icon = 'fa-solid fa-code'
134+
body = '''
135+
I'm inside a dropdown in a dropdown!
136+
'''
137+
{{< /dropdown >}}
138+
139+
And here is some more content after the inner dropdown.
140+
'''
141+
{{< /dropdown >}}
142+
34143
*/}}
35144

36145
{{- $data := .Inner | transform.Unmarshal -}}
37-
<details class="sd-card sd-dropdown sd-mb-3">
38-
{{- with $data.color }}
39-
<summary class="sd-summary-title sd-card-header sd-bg-{{ . }} sd-bg-text-{{ . }}">
40-
{{- else }}
41-
<summary class="sd-summary-title sd-card-header">
42-
{{- end }}
43-
{{- with $data.icon }}
44-
<span class="sd-summary-icon"><i class="fa {{ . }}"></i></span>
45-
{{- end }}
46-
{{- with $data.title }}
47-
{{- . }}
48-
{{- else }}
49-
<i class="fas fa-ellipsis-h"></i>&nbsp;
50-
{{- end }}
146+
147+
{{- $chevronClass := "sd-summary-chevron-right" -}}
148+
{{- $chevronIcon := "fa-chevron-right" -}}
149+
{{- if eq $data.chevron "down-up" -}}
150+
{{- $chevronClass = "sd-summary-chevron-down" -}}
151+
{{- $chevronIcon = "fa-chevron-down" -}}
152+
{{- end -}}
153+
154+
{{- $detailsClasses := slice "sd-sphinx-override" "sd-dropdown" "sd-card" "sd-mb-3" -}}
155+
{{- with $data.animate -}}
156+
{{- $detailsClasses = $detailsClasses | append (printf "sd-%s" .) -}}
157+
{{- end -}}
158+
159+
<details class="{{ delimit $detailsClasses " " }}"{{ if $data.open }} open="open"{{ end }}>
160+
{{- $summaryClasses := slice "sd-summary-title" "sd-card-header" -}}
161+
{{- with $data.color -}}
162+
{{- $summaryClasses = $summaryClasses | append (printf "sd-bg-%s" .) -}}
163+
{{- $summaryClasses = $summaryClasses | append (printf "sd-bg-text-%s" .) -}}
164+
{{- end -}}
165+
166+
<summary class="{{ delimit $summaryClasses " " }}">
167+
{{- with $data.icon }}
168+
<span class="sd-summary-icon"><i class="fa {{ . }}"></i></span>
169+
{{- end }}
170+
<span class="sd-summary-text">
171+
{{- with $data.title }}
172+
{{- . }}
173+
{{- else }}
174+
<i class="fas fa-ellipsis-h no-title sd-octicon"></i>
175+
{{- end }}
176+
</span>
177+
<span class="sd-summary-state-marker {{ $chevronClass }}">
178+
<i class="fas {{ $chevronIcon }}"></i>
179+
</span>
51180
</summary>
52181
<div class="sd-summary-content sd-card-body">
53182
{{- with (trim $data.body "\n") }}

0 commit comments

Comments
 (0)