Skip to content

Commit 8076a92

Browse files
committed
feat: update version to 0.2.0, enhance README with new layout and border utilities, and improve style handling in the codebase
1 parent f0205a3 commit 8076a92

File tree

7 files changed

+446
-11
lines changed

7 files changed

+446
-11
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,14 @@ layout = x.vstack(
7979
- **Typography:** `text_xs/_sm/_base/_lg/_xl/_2xl/_3xl`, `bold`, `italic`, `mono`
8080
- **Text colors:** `text_red`, `text_green`, `text_blue`, `text_orange`, `text_purple`, `text_black`, `text_gray`
8181
- **Backgrounds:** `bg_red`, `bg_primary`, `bg_muted`, `bg_success`, `bg_warning`, `bg_info`
82-
- **Layout & alignment:** `text_left`, `text_center`, `text_right`, `align_top/middle/bottom`, `wrap`
82+
- **Layout & alignment:** `text_left`, `text_center`, `text_right`, `align_top/middle/bottom`, `wrap`, `nowrap`, `wrap_shrink`, `allow_overflow`
83+
- Use `allow_overflow` when you want to keep a column narrow and let the text spill into adjacent empty cells.
84+
- **Borders:** `border_all`, `border_top`, `border_bottom`, `border_left`, `border_right`, `border_x`, `border_y`, `border_red`, `border_green`, `border_blue`, `border_orange`, `border_purple`, `border_black`, `border_gray`, `border_white`, `border_muted`, `border_primary`, `border_thin`, `border_medium`, `border_thick`, `border_dashed`, `border_dotted`, `border_double`, `border_none`
8385
- **Tables:** `table_bordered`, `table_banded`, `table_compact`
8486
- **Number/date formats:** `number_comma`, `number_precision`, `percent`, `currency_usd`, `currency_eur`, `date_short`, `datetime_short`, `time_short`
8587

88+
Border utilities can sit on individual cells or be applied at the row/column level for fast outlines.
89+
8690
Mix and match utilities freely—what you see is what you get.
8791

8892
## Layout helpers

examples/border_styles_demo.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""Showcase workbook for the border utility classes."""
2+
3+
from pathlib import Path
4+
5+
import xpyxl as x
6+
7+
8+
def cell_border_section() -> x.Node:
9+
title = x.row(style=[x.text_lg, x.bold])["Cell utilities", "", ""]
10+
header = x.row(style=[x.text_sm, x.text_gray])["Utility", "Preview", "Notes"]
11+
12+
variants = [
13+
("x.border_all", [x.border_all, x.border_primary], "All sides + brand color"),
14+
("x.border_top", [x.border_top, x.border_thick], "Top rule"),
15+
("x.border_x", [x.border_x, x.border_green], "Left + right"),
16+
("x.border_y", [x.border_y, x.border_dashed], "Top + bottom dashed"),
17+
("x.border_bottom", [x.border_bottom, x.border_orange], "Underline style"),
18+
]
19+
20+
rows: list[x.Node] = []
21+
for label, styles, note in variants:
22+
preview = x.cell(style=[*styles, x.text_center, x.text_sm])["Sample"]
23+
rows.append(x.row()[label, preview, note])
24+
25+
return x.vstack(title, header, *rows)
26+
27+
28+
def row_border_section() -> x.Node:
29+
title = x.row(style=[x.text_lg, x.bold])["Row-level borders", "", ""]
30+
header = x.row(style=[x.text_sm, x.text_gray])["Utility", "Preview", "Notes"]
31+
32+
variants = [
33+
("x.border_y + x.border_muted", [x.border_y, x.border_muted], "Soft banded rows"),
34+
(
35+
"x.border_top + x.border_bottom + x.border_thick",
36+
[x.border_top, x.border_bottom, x.border_thick],
37+
"Strong divider",
38+
),
39+
("x.border_y + x.border_dotted", [x.border_y, x.border_dotted], "Subtle dotted grid"),
40+
]
41+
42+
rows: list[x.Node] = []
43+
for label, styles, note in variants:
44+
rows.append(x.row(style=styles)[label, "Row preview", note])
45+
46+
return x.vstack(title, header, *rows)
47+
48+
49+
def column_border_section() -> x.Node:
50+
header = x.row(style=[x.text_lg, x.bold])["Column-level borders"]
51+
details = x.row(style=[x.text_sm, x.text_gray])["Style the column container once and every cell inherits."]
52+
53+
columns = x.hstack(
54+
x.col(style=[x.border_x, x.border_blue])[
55+
x.cell(style=[x.bold])["x.border_x"],
56+
"keeps", "left & right", "outlined",
57+
],
58+
x.col(style=[x.border_y, x.border_red])[
59+
x.cell(style=[x.bold])["x.border_y"],
60+
"adds", "top & bottom", "per cell",
61+
],
62+
x.col(style=[x.border_all, x.border_muted, x.border_thin])[
63+
x.cell(style=[x.bold])["x.border_all"],
64+
"acts like", "inline cards", "for stacks",
65+
],
66+
gap=2,
67+
)
68+
69+
return x.vstack(header, details, columns)
70+
71+
72+
def build_workbook() -> x.Workbook:
73+
border_sheet = x.sheet("Borders")[
74+
x.row(style=[x.text_2xl, x.bold])["Border Styles Demo"],
75+
x.row(style=[x.text_sm, x.text_gray])["Cell, row, and column outlines using utility classes."],
76+
x.space(),
77+
cell_border_section(),
78+
x.space(),
79+
row_border_section(),
80+
x.space(),
81+
column_border_section(),
82+
x.space(),
83+
x.row(style=[x.text_sm, x.text_gray])["Generated with xpyxl"],
84+
]
85+
return x.workbook()[border_sheet]
86+
87+
88+
def main() -> None:
89+
workbook = build_workbook()
90+
output_path = Path("border-styles-demo-output.xlsx")
91+
workbook.save(output_path)
92+
print(f"Saved {output_path.resolve()}")
93+
94+
95+
if __name__ == "__main__":
96+
main()

examples/wrap_styles_demo.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
"""Showcase workbook for the wrapping utilities."""
2+
3+
from __future__ import annotations
4+
5+
from pathlib import Path
6+
7+
import xpyxl as x
8+
9+
10+
LONG_TEXT = (
11+
"This description is intentionally long so you can resize the column and observe "
12+
"how wrapping and shrink-to-fit behave in Excel."
13+
)
14+
15+
16+
def wrap_variants_gallery() -> x.Node:
17+
entries = [
18+
("Default", [], "Excel auto wrapping based on column width"),
19+
("x.wrap", [x.wrap], "Always wrap text to the next line"),
20+
(
21+
"x.nowrap",
22+
[x.nowrap],
23+
"Keep the text on one line even if parent containers set wrapping",
24+
),
25+
(
26+
"x.wrap_shrink",
27+
[x.wrap_shrink],
28+
"Wrap text but also shrink the font to keep headings tidy",
29+
),
30+
(
31+
"x.allow_overflow",
32+
[x.nowrap, x.allow_overflow],
33+
"Hold the column width and let text overflow",
34+
),
35+
]
36+
37+
cards: list[x.Node] = []
38+
for label, styles, note in entries:
39+
cards.append(
40+
x.table(
41+
header=[label],
42+
header_style=[x.text_sm, x.text_gray],
43+
style=[x.table_bordered, x.table_compact],
44+
)[
45+
[x.cell(style=styles)[LONG_TEXT]],
46+
[x.cell(style=[x.text_sm, x.text_gray])[note]],
47+
]
48+
)
49+
50+
return x.hstack(*cards, gap=2)
51+
52+
53+
def mix_and_match_section() -> x.Node:
54+
instructions = x.row(style=[x.text_sm, x.text_gray])["Stack wrapping utilities at the row/column level too."]
55+
56+
wrap_stack = x.col(style=[x.wrap])[
57+
x.cell(style=[x.bold])["Row wrap"],
58+
LONG_TEXT,
59+
x.cell(style=[x.text_sm, x.text_gray])["Row style enforces wrapping on every cell."],
60+
]
61+
nowrap_stack = x.col(style=[x.nowrap])[
62+
x.cell(style=[x.bold])["Row nowrap"],
63+
LONG_TEXT,
64+
x.cell(style=[x.text_sm, x.text_gray])["Keeps rows to a single line."],
65+
]
66+
shrink_stack = x.col(style=[x.wrap_shrink])[
67+
x.cell(style=[x.bold])["Wrap & shrink"],
68+
LONG_TEXT,
69+
x.cell(style=[x.text_sm, x.text_gray])["Great for skinny annotation columns."],
70+
]
71+
overflow_stack = x.col(style=[x.allow_overflow])[
72+
x.cell(style=[x.bold])["Allow overflow"],
73+
LONG_TEXT,
74+
x.cell(style=[x.text_sm, x.text_gray])["Column width stays fixed; Excel shows spillover."],
75+
]
76+
77+
return x.vstack(
78+
instructions,
79+
x.hstack(wrap_stack, nowrap_stack, shrink_stack, overflow_stack, gap=2),
80+
)
81+
82+
83+
def build_workbook() -> x.Workbook:
84+
sheet = x.sheet("Wrapping")[
85+
x.row(style=[x.text_2xl, x.bold])["Wrapping Utilities"],
86+
x.row(style=[x.text_sm, x.text_gray])["Resize the sample columns in Excel to see differences."],
87+
x.space(),
88+
wrap_variants_gallery(),
89+
x.space(),
90+
mix_and_match_section(),
91+
x.space(),
92+
x.row(style=[x.text_sm, x.text_gray])["Generated with xpyxl"],
93+
]
94+
return x.workbook()[sheet]
95+
96+
97+
def main() -> None:
98+
wb = build_workbook()
99+
output_path = Path("wrap-styles-demo-output.xlsx")
100+
wb.save(output_path)
101+
print(f"Saved {output_path.resolve()}")
102+
103+
104+
if __name__ == "__main__":
105+
main()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "xpyxl"
3-
version = "0.1.1"
3+
version = "0.2.0"
44
description = "Create styled excel reports with declarative python."
55
readme = "README.md"
66
authors = [

src/xpyxl/__init__.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,30 @@
2222
align_bottom,
2323
align_middle,
2424
align_top,
25+
border_all,
26+
border_black,
27+
border_blue,
28+
border_bottom,
29+
border_dashed,
30+
border_dotted,
31+
border_double,
32+
border_gray,
33+
border_green,
34+
border_left,
35+
border_medium,
36+
border_muted,
37+
border_none,
38+
border_orange,
39+
border_primary,
40+
border_purple,
41+
border_red,
42+
border_right,
43+
border_thick,
44+
border_thin,
45+
border_top,
46+
border_white,
47+
border_x,
48+
border_y,
2549
bg_info,
2650
bg_muted,
2751
bg_primary,
@@ -67,6 +91,11 @@
6791
time_short,
6892
to_argb,
6993
wrap,
94+
nowrap,
95+
wrap_shrink,
96+
allow_overflow,
97+
nowrap,
98+
wrap_shrink,
7099
)
71100

72101
__all__ = [
@@ -116,6 +145,35 @@
116145
"align_middle",
117146
"align_bottom",
118147
"wrap",
148+
"nowrap",
149+
"wrap_shrink",
150+
"allow_overflow",
151+
"nowrap",
152+
"wrap_shrink",
153+
"border_all",
154+
"border_top",
155+
"border_bottom",
156+
"border_left",
157+
"border_right",
158+
"border_x",
159+
"border_y",
160+
"border_red",
161+
"border_green",
162+
"border_blue",
163+
"border_orange",
164+
"border_purple",
165+
"border_black",
166+
"border_gray",
167+
"border_white",
168+
"border_muted",
169+
"border_primary",
170+
"border_thin",
171+
"border_medium",
172+
"border_thick",
173+
"border_dashed",
174+
"border_dotted",
175+
"border_double",
176+
"border_none",
119177
"bg_red",
120178
"bg_primary",
121179
"bg_muted",

0 commit comments

Comments
 (0)