GameStatusSelector: custom element + typed contract (retire Alpine)

The Game status dropdown is now a <game-status-selector> light-DOM custom
element: the Python builder emits the tag + kebab attrs htpy-style, behavior
lives in ts/elements/{dropdown,game-status-selector}.ts wired by the native
connectedCallback, and GameStatusSelectorProps is the codegen'd contract. The
~70-line inline-Alpine f-string is gone.

Also fix SimpleTable to collect and re-attach the media of its row/header
nodes: it stringifies cells into the table markup, which silently dropped each
cell component's declared Media — so a <game-status-selector> in a cell never
got its <script> emitted. Now Page() emits it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 21:09:52 +02:00
parent 0f0dfc48fb
commit 04552aa8f6
6 changed files with 184 additions and 51 deletions
+16 -3
View File
@@ -25,6 +25,7 @@ from common.components.core import (
Safe,
as_attributes,
as_children,
collect_media,
randomid,
)
from common.icons import get_icon
@@ -988,15 +989,26 @@ def SimpleTable(
columns = columns or []
rows = rows or []
# Rows/header are stringified into the table markup, so their components'
# declared Media would be lost; collect it from the nodes first and attach
# it to the returned node so Page() still emits each cell component's JS
# (e.g. a <game-status-selector> in a cell).
media = Media()
header_html = ""
if header_action:
header_html = str(TableHeader(children=[header_action]))
header_node = TableHeader(children=[header_action])
header_html = str(header_node)
media = media + collect_media(header_node)
columns_html = "".join(
f'<th scope="col" class="px-6 py-3">{conditional_escape(col)}</th>'
for col in columns
)
rows_html = "".join(str(TableRow(data=row)) for row in rows)
row_nodes = [TableRow(data=row) for row in rows]
rows_html = "".join(str(node) for node in row_nodes)
for node in row_nodes:
media = media + collect_media(node)
pagination_html = ""
if page_obj and elided_page_range:
@@ -1012,7 +1024,8 @@ def SimpleTable(
f"<tr>{columns_html}</tr></thead>"
'<tbody class="dark:divide-y max-sm:[&_td:not(:first-child):not(:last-child)]:hidden">'
f"{rows_html}</tbody></table></div>"
f"{pagination_html}</div>"
f"{pagination_html}</div>",
media=media,
)