Skip to content

Commit 7c8c266

Browse files
authored
👌 IMPROVE: Make octicon's size variable (#9)
1 parent 694848d commit 7c8c266

16 files changed

+114
-88
lines changed

docs/badges_buttons.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Badges, Buttons & Icons
1+
# Badges, Buttons & Icons {octicon}`rocket`
22

33
(badges)=
44

@@ -152,29 +152,27 @@ class
152152

153153
Inline icon roles are available for both the [GitHub octicon](https://octicons-git-v2.primer.now.sh/octicons/) or [FontAwesome](https://fontawesome.com/icons?d=gallery&m=free) libraries.
154154

155-
Octicon icons are added as SVG's directly into the page, for either 16px (`octicon-16`) or 24px (`octicon-24`) sizes.
156-
Additional CSS classes can be added to the SVG after a semi-colon (`;`) delimiter.
155+
Octicon icons are added as SVG's directly into the page with the `octicon` role.
157156

158-
A coloured icon: {octicon-16}`report;sd-text-info`, some more text.
157+
By default the icon will be of height `1em` (i.e. the height of the font).
158+
A specific height can be set after a semi-colon (`;`) with units of either `px`, `em` or `rem`.
159+
Additional CSS classes can also be added to the SVG after a second semi-colon (`;`) delimiter.
160+
161+
A coloured icon: {octicon}`report;1em;sd-text-info`, some more text.
159162

160163
````{tab-set-code}
161-
```markdown
162-
A coloured icon: {octicon-16}`report;sd-text-info`, some more text.
164+
```{literalinclude} ./snippets/myst/icon-octicon.txt
165+
:language: markdown
163166
```
164-
```rst
165-
A coloured icon: :octicon-16:`report;sd-text-info`, some more text.
167+
```{literalinclude} ./snippets/rst/icon-octicon.txt
168+
:language: rst
166169
```
167170
````
168171

169-
````{dropdown} All Octicons (16px)
170-
```{_all-octicon}
171-
:size: 16
172-
```
173-
````
172+
````{dropdown} All Octicons
173+
:open:
174174
175-
````{dropdown} All Octicons (24px)
176175
```{_all-octicon}
177-
:size: 24
178176
```
179177
````
180178

docs/cards.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ Content
151151
Content
152152
:::
153153

154+
(cards:options)=
155+
154156
## `card` options
155157

156158
width

docs/grids.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,22 @@ text-align
287287

288288
class
289289
: Additional CSS classes for the grid item element.
290+
291+
## `grid-item-card` options
292+
293+
columns
294+
: The number of columns (out of 12) a grid-item will take up.
295+
One or four integers (for "xs sm md lg") between 1 and 12 (or `auto` to adapt to the content).
296+
297+
margin
298+
: Outer margin of grid item.
299+
One (all) or four (top bottom left right) values from: 0, 1, 2, 3, 4, 5, auto.
300+
301+
padding
302+
: Inner padding of grid item.
303+
One (all) or four (top bottom left right) values from: 0, 1, 2, 3, 4, 5.
304+
305+
class-item
306+
: Additional CSS classes for the grid item element.
307+
308+
Plus all options from [](cards:options).

docs/index.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,42 +57,42 @@ furo <https://sphinx-design.readthedocs.io/en/furo-theme>
5757
:margin: 4 4 0 0
5858
:gutter: 1
5959

60-
:::{grid-item-card} {octicon-16}`table` Grids
60+
:::{grid-item-card} {octicon}`table` Grids
6161
:link: grids
6262
:link-type: doc
6363

6464
Screen size adaptable grid layouts.
6565
:::
6666

67-
:::{grid-item-card} {octicon-16}`note` Cards
67+
:::{grid-item-card} {octicon}`note` Cards
6868
:link: cards
6969
:link-type: doc
7070

7171
Flexible and extensible content containers.
7272
:::
7373

74-
:::{grid-item-card} {octicon-16}`chevron-down` Dropdowns
74+
:::{grid-item-card} {octicon}`chevron-down` Dropdowns
7575
:link: dropdowns
7676
:link-type: doc
7777

7878
Hide content in expandable containers.
7979
:::
8080

81-
:::{grid-item-card} {octicon-16}`duplicate` Tabs
81+
:::{grid-item-card} {octicon}`duplicate` Tabs
8282
:link: tabs
8383
:link-type: doc
8484

8585
Synchronisable, tabbed content sets.
8686
:::
8787

88-
:::{grid-item-card} {octicon-16}`plus-circle` Badges, Buttons & Icons
88+
:::{grid-item-card} {octicon}`plus-circle` Badges, Buttons & Icons
8989
:link: badges_buttons
9090
:link-type: doc
9191

9292
Roles and directives for {bdg-primary}`badges` and other components.
9393
:::
9494

95-
:::{grid-item-card} {octicon-16}`image` CSS Styling
95+
:::{grid-item-card} {octicon}`image` CSS Styling
9696
:link: css_variables
9797
:link-type: doc
9898

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
A coloured icon: {octicon-16}`report;sd-text-info`, some more text.
1+
A coloured icon: {octicon}`report;1em;sd-text-info`, some more text.

docs/snippets/rst/icon-octicon.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
A coloured icon: :octicon-16:`report;sd-text-info`, some more text.
1+
A coloured icon: :octicon:`report;1em;sd-text-info`, some more text.

sphinx_design/article_info.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def run(self) -> List[nodes.Node]:
154154
self.set_source_info(date_column)
155155
date_icon = nodes.raw(
156156
"",
157-
nodes.Text(get_octicon("calendar", size=16)),
157+
nodes.Text(get_octicon("calendar", height="16px")),
158158
classes=["sd-pr-2"],
159159
format="html",
160160
)
@@ -171,7 +171,7 @@ def run(self) -> List[nodes.Node]:
171171
self.set_source_info(read_time_column)
172172
read_time_icon = nodes.raw(
173173
"",
174-
nodes.Text(get_octicon("clock", size=16)),
174+
nodes.Text(get_octicon("clock", height="16px")),
175175
classes=["sd-pr-2"],
176176
format="html",
177177
)

sphinx_design/compiled/style.min.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sphinx_design/dropdown.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def run(self):
126126

127127

128128
# Note the custom octicon here has thicker dots than:
129-
# get_octicon("kebab-horizontal", classes="no-title", size=24)
129+
# get_octicon("kebab-horizontal", classes=["no-title"])
130130
KEBAB = """\
131131
<svg viewBox="0 0 36 24" width="36" height="16" xmlns="http://www.w3.org/2000/svg"
132132
class="octicon no-title" aria-hidden="true">
@@ -165,7 +165,7 @@ def run(self):
165165
children=[
166166
nodes.raw(
167167
"",
168-
nodes.Text(get_octicon("chevron-up", size=24)),
168+
nodes.Text(get_octicon("chevron-up", height="24px")),
169169
format="html",
170170
)
171171
],
@@ -176,7 +176,7 @@ def run(self):
176176
children=[
177177
nodes.raw(
178178
"",
179-
nodes.Text(get_octicon("chevron-down", size=24)),
179+
nodes.Text(get_octicon("chevron-down", height="24px")),
180180
format="html",
181181
)
182182
],
@@ -206,7 +206,7 @@ def run(self):
206206
0,
207207
nodes.raw(
208208
"",
209-
nodes.Text(get_octicon(node["icon"], size=16)),
209+
nodes.Text(get_octicon(node["icon"], height="1em")),
210210
classes=["sd-summary-icon"],
211211
format="html",
212212
),

sphinx_design/icons.py

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
2+
import re
23
from functools import lru_cache
3-
from typing import Any, Dict, List, Optional, Tuple, Union
4+
from typing import Any, Dict, List, Optional, Sequence, Tuple
45

56
try:
67
import importlib.resources as resources
@@ -14,7 +15,6 @@
1415
from sphinx.util.docutils import SphinxDirective, SphinxRole
1516

1617
from . import compiled
17-
from .shared import make_choice
1818

1919
OCTICON_VERSION = "0.0.0-dd899ea"
2020

@@ -27,8 +27,7 @@
2727

2828

2929
def setup_icons(app: Sphinx) -> None:
30-
app.add_role("octicon-16", OcticonRole(16))
31-
app.add_role("octicon-24", OcticonRole(24))
30+
app.add_role("octicon", OcticonRole())
3231
app.add_directive("_all-octicon", AllOcticons)
3332
for style in ["fa", "fas", "fab"]:
3433
# note: fa is deprecated in v5, fas is the default and fab is the other free option
@@ -52,50 +51,57 @@ def get_octicon_data() -> Dict[str, Any]:
5251
return json.loads(content)
5352

5453

55-
def list_octicons(size: int = 16) -> List[str]:
54+
def list_octicons() -> List[str]:
5655
"""List available octicon names."""
57-
return [
58-
key
59-
for key, data in get_octicon_data().items()
60-
if str(size) in data.get("heights", [])
61-
]
56+
return list(get_octicon_data().keys())
57+
58+
59+
HEIGHT_REGEX = re.compile(r"^(?P<value>\d+)(?P<unit>px|em|rem)$")
6260

6361

6462
def get_octicon(
6563
name: str,
66-
classes: Optional[str] = None,
67-
width: Optional[Union[int, float]] = None,
68-
height: Optional[Union[int, float]] = None,
64+
height: str = "1em",
65+
classes: Sequence[str] = (),
6966
aria_label: Optional[str] = None,
70-
size: int = 16,
7167
) -> str:
72-
"""Return the HTML for an GitHub octicon SVG icon."""
73-
assert size in [16, 24], "size must be 16 or 24"
68+
"""Return the HTML for an GitHub octicon SVG icon.
69+
70+
:height: the height of the octicon, with suffix unit 'px', 'em' or 'rem'.
71+
"""
7472
try:
7573
data = get_octicon_data()[name]
7674
except KeyError:
7775
raise KeyError(f"Unrecognised octicon: {name}")
7876

79-
content = data["heights"][str(size)]["path"]
77+
match = HEIGHT_REGEX.match(height)
78+
if not match:
79+
raise ValueError(
80+
f"Invalid height: '{height}', must be format <integer><px|em|rem>"
81+
)
82+
height_value = int(match.group("value"))
83+
height_unit = match.group("unit")
84+
85+
original_height = 16
86+
if "16" not in data["heights"]:
87+
original_height = int(list(data["heights"].keys())[0])
88+
elif "24" in data["heights"]:
89+
if height_unit == "px":
90+
if height_value >= 24:
91+
original_height = 24
92+
elif height_value >= 1.5:
93+
original_height = 24
94+
original_width = data["heights"][str(original_height)]["width"]
95+
width_value = round(original_width * height_value / original_height, 2)
96+
content = data["heights"][str(original_height)]["path"]
8097
options = {
8198
"version": "1.1",
82-
"width": data["heights"][str(size)]["width"],
83-
"height": int(size),
84-
"class": f"sd-octicon sd-octicon-{name}",
99+
"width": f"{width_value}{height_unit}",
100+
"height": f"{height_value}{height_unit}",
101+
"class": " ".join(("sd-octicon", f"sd-octicon-{name}", *classes)),
85102
}
86103

87-
if width is not None or height is not None:
88-
if width is None and height is not None:
89-
width = round((int(height) * options["width"]) / options["height"], 2)
90-
if height is None and width is not None:
91-
height = round((int(width) * options["height"]) / options["width"], 2)
92-
options["width"] = width
93-
options["height"] = height
94-
95-
options["viewBox"] = f'0 0 {options["width"]} {options["height"]}'
96-
97-
if classes is not None:
98-
options["class"] += " " + classes.strip()
104+
options["viewBox"] = f"0 0 {original_width} {original_height}"
99105

100106
if aria_label is not None:
101107
options["aria-label"] = aria_label
@@ -113,19 +119,18 @@ class OcticonRole(SphinxRole):
113119
Additional classes can be added to the element after a semicolon.
114120
"""
115121

116-
def __init__(self, size: int) -> None:
117-
super().__init__()
118-
self.size = size
119-
120122
def run(self) -> Tuple[List[nodes.Node], List[nodes.system_message]]:
121123
"""Run the role."""
122-
icon, classes = self.text.split(";", 1) if ";" in self.text else [self.text, ""]
124+
values = self.text.split(";") if ";" in self.text else [self.text]
125+
icon = values[0]
126+
height = "1em" if len(values) < 2 else values[1]
127+
classes = "" if len(values) < 3 else values[2]
123128
icon = icon.strip()
124129
try:
125-
svg = get_octicon(icon, size=self.size, classes=classes)
126-
except KeyError:
130+
svg = get_octicon(icon, height=height, classes=classes.split())
131+
except Exception as exc:
127132
msg = self.inliner.reporter.error(
128-
f"Unknown octicon name: {icon}",
133+
f"Invalid octicon content: {exc}",
129134
line=self.lineno,
130135
)
131136
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
@@ -142,24 +147,22 @@ class AllOcticons(SphinxDirective):
142147
"""
143148

144149
option_spec = {
145-
"size": make_choice(["16", "24"]),
146150
"class": directives.class_option,
147151
}
148152

149153
def run(self) -> List[nodes.Node]:
150154
"""Run the directive."""
151-
size = int(self.options.get("size", "16"))
152-
classes = " ".join(self.options.get("class", []))
155+
classes = self.options.get("class", [])
153156
list_node = nodes.bullet_list()
154-
for icon in list_octicons(size):
157+
for icon in list_octicons():
155158
item_node = nodes.list_item()
156159
item_node.extend(
157160
(
158161
nodes.literal(icon, icon),
159162
nodes.Text(": "),
160163
nodes.raw(
161164
"",
162-
nodes.Text(get_octicon(icon, size=size, classes=classes)),
165+
nodes.Text(get_octicon(icon, classes=classes)),
163166
format="html",
164167
),
165168
)

0 commit comments

Comments
 (0)