Skip to content

Commit 6f599a3

Browse files
authored
Add Stepper directive (#658)
1 parent d31f1c7 commit 6f599a3

File tree

7 files changed

+205
-1
lines changed

7 files changed

+205
-1
lines changed

docs/build.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"schemaVersion": 1,
33
"label": "docs build",
4-
"message": "2.1.2"
4+
"message": "2.1.5"
55
}

docs/myst/stepper.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Stepper
2+
3+
::::{stepper}
4+
:::{step} First step
5+
You can add any content in a step.
6+
:::
7+
8+
:::{step} Next step
9+
Some step details
10+
:::
11+
12+
:::{step} Last step
13+
Last step details
14+
:::
15+
::::

src/crate/theme/rtd/conf/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ def apply_html_context_custom(app_inited):
338338
app.connect("builder-inited", set_proxied_static_path)
339339
app.connect("builder-inited", apply_html_context_custom)
340340

341+
# Register stepper directive
342+
from crate.theme.rtd.crate.directives import stepper
343+
stepper.setup(app)
344+
341345
return {
342346
"parallel_read_safe": True,
343347
"parallel_write_safe": True,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# -*- coding: utf-8; -*-
2+
# Directives for crate-docs-theme
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# -*- coding: utf-8; -*-
2+
"""
3+
Stepper directive for step-by-step documentation.
4+
"""
5+
6+
from docutils import nodes
7+
from sphinx.util.docutils import SphinxDirective
8+
9+
10+
# Define custom nodes
11+
class stepper_node(nodes.General, nodes.Element):
12+
pass
13+
14+
class step_node(nodes.General, nodes.Element):
15+
pass
16+
17+
class StepperDirective(SphinxDirective):
18+
"""Container for step-by-step instructions."""
19+
20+
has_content = True
21+
option_spec = {
22+
'class': str, # directives.class_option is for multiple classes
23+
}
24+
25+
def run(self):
26+
stepper = stepper_node()
27+
stepper['classes'] = self.options.get('class', '').split() # Add custom classes if provided
28+
self_content = nodes.paragraph()
29+
self.state.nested_parse(self.content, self.content_offset, self_content)
30+
31+
step_number = 1
32+
for child in self_content.children:
33+
if isinstance(child, step_node):
34+
child['step-number'] = step_number
35+
step_number += 1
36+
stepper += child # Add all children from self_content to stepper
37+
38+
return [stepper]
39+
40+
41+
class StepDirective(SphinxDirective):
42+
"""Individual step in a stepper."""
43+
44+
has_content = True
45+
required_arguments = 1 # Step title
46+
optional_arguments = 0
47+
final_argument_whitespace = True # Allow spaces in title
48+
option_spec = {
49+
'class': str,
50+
}
51+
52+
def run(self):
53+
step = step_node()
54+
step['classes'] = ['stepper-step']
55+
step['classes'].extend(self.options.get('class', '').split())
56+
57+
# Set title
58+
step['step-title'] = self.arguments[0]
59+
60+
# Parse content
61+
self.state.nested_parse(
62+
self.content,
63+
self.content_offset,
64+
step
65+
)
66+
return [step]
67+
68+
# HTML Translator functions
69+
def html_visit_stepper_node(self, node):
70+
self.context.append('')
71+
self.body.append(self.starttag(node, 'div', CLASS='stepper'))
72+
73+
def html_depart_stepper_node(self, node):
74+
self.body.append('</div>\n')
75+
self.context.pop()
76+
77+
def html_visit_step_node(self, node):
78+
self.context.append('')
79+
self.body.append(self.starttag(node, 'div', CLASS='stepper-step'))
80+
self.body.append(f'<div class="stepper-header">')
81+
self.body.append(f'<div class="stepper-number">{node.get("step-number", "")}</div>')
82+
self.body.append(f'<div class="stepper-title">{node.get("step-title", "")}</div>')
83+
self.body.append(f'</div>')
84+
self.body.append(f'<div class="stepper-content">')
85+
86+
def html_depart_step_node(self, node):
87+
self.body.append('</div>\n') # Close stepper-content
88+
self.body.append('</div>\n') # Close stepper-step
89+
self.context.pop()
90+
91+
92+
def setup(app):
93+
"""Register the stepper directives and nodes."""
94+
app.add_node(stepper_node, html=(html_visit_stepper_node, html_depart_stepper_node))
95+
app.add_node(step_node, html=(html_visit_step_node, html_depart_step_node))
96+
97+
app.add_directive('stepper', StepperDirective)
98+
app.add_directive('step', StepDirective)
99+
100+
return {
101+
'version': '0.1',
102+
'parallel_read_safe': True,
103+
'parallel_write_safe': True,
104+
}

src/crate/theme/rtd/crate/static/css/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
@import "ng/index.scss";
44
@import "crateio-rtd.css";
55
@import "custom.css";
6+
@import "stepper.css";
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* Stepper component styling */
2+
3+
:root {
4+
--stepper-number-bg: var(--primary-color);
5+
--stepper-number-color: #ffffff;
6+
--stepper-line-color: #dee2e6;
7+
--stepper-spacing: 2rem;
8+
--stepper-number-size: 2rem;
9+
}
10+
11+
html[data-theme="dark"] {
12+
--stepper-line-color: #495057;
13+
}
14+
15+
.stepper {
16+
margin: 2rem 0;
17+
}
18+
19+
.stepper-step {
20+
position: relative;
21+
padding-left: 3rem; /* Space for number + line */
22+
padding-bottom: var(--stepper-spacing);
23+
}
24+
25+
.stepper-step:last-child {
26+
padding-bottom: 0;
27+
}
28+
29+
/* Number badge */
30+
.stepper-number {
31+
position: absolute;
32+
left: 0;
33+
top: 0;
34+
width: var(--stepper-number-size);
35+
height: var(--stepper-number-size);
36+
border-radius: 50%;
37+
background-color: var(--stepper-number-bg);
38+
color: var(--stepper-number-color);
39+
display: flex;
40+
align-items: center;
41+
justify-content: center;
42+
font-weight: 600;
43+
font-size: 0.9rem;
44+
z-index: 2;
45+
}
46+
47+
/* Connecting line */
48+
.stepper-step:not(:last-child)::before {
49+
content: '';
50+
position: absolute;
51+
left: calc(var(--stepper-number-size) / 2 - 1px);
52+
top: var(--stepper-number-size);
53+
bottom: 0;
54+
width: 2px;
55+
background-color: var(--stepper-line-color);
56+
z-index: 1;
57+
}
58+
59+
/* Header (number + title on same line) */
60+
.stepper-header {
61+
display: flex;
62+
align-items: center;
63+
margin-bottom: 0.75rem;
64+
}
65+
66+
.stepper-title {
67+
font-weight: 600;
68+
font-size: 1.1rem;
69+
}
70+
71+
/* Content */
72+
.stepper-content > *:first-child {
73+
margin-top: 0;
74+
}
75+
76+
.stepper-content > *:last-child {
77+
margin-bottom: 0;
78+
}

0 commit comments

Comments
 (0)