feat: improve table rendering

This commit is contained in:
Tim Grelka
2025-11-09 10:33:56 +01:00
parent 169c81c5e0
commit f72879967c
3 changed files with 111 additions and 68 deletions
-5
View File
@@ -2,11 +2,6 @@
margin-bottom: 2ex; margin-bottom: 2ex;
} }
.table_inner {
padding: 1ex;
background: #ddd;
}
.table tr { .table tr {
border-bottom: 1px solid #aaa; border-bottom: 1px solid #aaa;
} }
+27 -19
View File
@@ -13,7 +13,7 @@ from .qso import Qso
from .question import Question from .question import Question
from .quote import Quote from .quote import Quote
from .references import References from .references import References
from .table import Table from .table import Table, TableBody, TableCell, TableHeader, TableRow
from .tag import Tag from .tag import Tag
from .underline import Underline from .underline import Underline
from .unit import Unit from .unit import Unit
@@ -46,6 +46,10 @@ class FiftyOhmHtmlRenderer(HtmlRenderer):
Picture, Picture,
Photo, Photo,
Table, Table,
TableHeader,
TableRow,
TableCell,
TableBody,
Qso, Qso,
Include, Include,
*extras, *extras,
@@ -104,7 +108,7 @@ class FiftyOhmHtmlRenderer(HtmlRenderer):
for char in morse_code: for char in morse_code:
result += '<span class="morse_char">\n' result += '<span class="morse_char">\n'
for symbol in char: for symbol in char:
result += '<span class="morse_char">\n' result += '<span class="morse_symbol">\n'
if symbol == 1: if symbol == 1:
result += "▄" result += "▄"
elif symbol == 2: elif symbol == 2:
@@ -217,28 +221,32 @@ class FiftyOhmHtmlRenderer(HtmlRenderer):
self.photo_handler(token.id) self.photo_handler(token.id)
return self.render_photo_helper(token.id, token.ref, token.text, token.number) return self.render_photo_helper(token.id, token.ref, token.text, token.number)
@staticmethod def render_table(self, token: Table):
def render_cell_helper(cell, alignment, type): table = f'<table class="table table-hover">\n{self.render_inner(token)}'
style = ""
if alignment in table_alignment:
style = f' style="text-align: {table_alignment[alignment]};"'
return f"<{type}{style}>{cell}</{type}>\n"
def render_table(self, token):
alignment = token.alignment
table = "<table>\n"
for i, row in enumerate(token.children):
content = ""
for j, cell in enumerate(row.children):
content += self.render_cell_helper(self.render_inner(cell), alignment[j], ("th" if i == 0 else "td"))
table += f"<tr>\n{content}</tr>\n"
if token.caption != "": if token.caption != "":
table += f"<caption>{token.caption}</caption>" table += f"<caption>{token.caption}</caption>\n"
table += "</table>" table += "</table>"
return table return table
def render_table_row(self, token: TableRow):
return f"<tr>\n{self.render_inner(token)}</tr>\n"
def render_table_header(self, token: TableHeader):
return self.render_table_row(token)
def render_table_cell(self, token: TableCell):
type = "th" if token.header else "td"
style = ""
if token.alignment in table_alignment:
style = f' style="text-align: {table_alignment[token.alignment]};"'
return f"<{type}{style}>{self.render_inner(token)}</{type}>\n"
def render_table_body(self, token: TableBody):
type = "thead" if token.header else "tbody"
return f"<{type}>\n{self.render_inner(token)}</{type}>\n"
def render_include(self, token): def render_include(self, token):
return self.include_handler(token.ident) return self.include_handler(token.ident)
+84 -44
View File
@@ -1,37 +1,99 @@
import re import re
from enum import Enum
from mistletoe import span_token from mistletoe import span_token
from mistletoe.block_token import BlockToken from mistletoe.block_token import BlockToken
class CellAlignment(Enum):
LEFT = "l"
CENTER = "c"
RIGHT = "r"
EXPAND = "X"
class TableCell(BlockToken):
alignment: CellAlignment
header: bool
@staticmethod
def start(_):
return False
def __init__(self, match, alignment: CellAlignment, header=False):
super().__init__(match, span_token.tokenize_inner)
self.alignment, self.header = alignment, header
class TableRow(BlockToken):
@staticmethod
def start(_):
return False
@classmethod
def parse(cls, line) -> list[str]:
line = line.strip().strip("|")
return [col.strip() for col in line.split("|")]
def __init__(self, match, alignment: list[CellAlignment]):
self.children = [TableCell(cell, alignment[i]) for i, cell in enumerate(self.parse(match))]
class TableHeader(TableRow):
alignment: list[CellAlignment]
@staticmethod
def start(_):
return False
@classmethod
def parse(cls, line) -> list[str]:
alignment: list[CellAlignment] = []
columns: list[str] = []
for column in super().parse(line):
attr: CellAlignment
value: str
attr, value = re.match(r"^ ?(?:([lcrX]):)? ?(.*)", column).group(1, 2)
alignment.append(attr)
columns.append(value)
return columns, alignment
def __init__(self, match):
columns, self.alignment = self.parse(match)
self.children = [TableCell(cell, self.alignment[i], True) for i, cell in enumerate(columns)]
class TableBody(BlockToken):
header: bool
@staticmethod
def start(_):
return False
def __init__(self, rows: TableRow, header=False):
self.children, self.header = rows, header
class Table(BlockToken): class Table(BlockToken):
name: str
caption: str
header: list[TableHeader]
rows: list[TableRow]
@staticmethod @staticmethod
def start(line): def start(line):
return "|" in line return "|" in line
@classmethod
def parse_header(cls, line):
# Parse header, extract alignment and make an ordinary row out of it
line = line.strip().strip("|") # Remove leading/trailing whitespace and pipes
columns = [col.strip() for col in line.split("|")] # Split by pipe |
alignment = []
header = "| "
for column in columns:
attr, val = re.match(r"^ ?(?:([lcrX]):)?(.*)", column).group(1, 2)
alignment.append(attr)
header += val + " | "
return header, alignment
@classmethod @classmethod
def read(cls, lines): def read(cls, lines):
header = next(lines) header = TableHeader(next(lines))
header, alignment = cls.parse_header(header) alignment = header.alignment
name = "" name = ""
caption = "" caption = ""
rows = [TableRow(header)] children = [TableBody([header], True)]
# Read table until the end: No more column definitions, caption, or empty line. # Read table until the end: No more column definitions, caption, or empty line.
line_buffer = [] line_buffer = []
@@ -48,31 +110,9 @@ class Table(BlockToken):
line_buffer.append(next(lines)) line_buffer.append(next(lines))
next_line = lines.peek() next_line = lines.peek()
rows.extend([TableRow(line) for line in line_buffer]) children.append(TableBody([TableRow(line, alignment) for line in line_buffer]))
return alignment, rows, name, caption return children, name, caption
def __init__(self, match): def __init__(self, match):
self.alignment, self.children, self.name, self.caption = match self.children, self.name, self.caption = match
class TableRow(BlockToken):
@classmethod
def parse_row(cls, line):
line = line.strip().strip("|")
columns = [col.strip() for col in line.split("|")]
data = []
for column in columns:
data.append(TableCell(column))
return data
def __init__(self, match):
self.children = self.parse_row(match)
class TableCell(BlockToken):
def __init__(self, match):
super().__init__(match, span_token.tokenize_inner)