Source code for runtimepy.net.html.bootstrap.elements
"""
A module for creating various bootstrap-related elements.
"""
# built-in
from io import StringIO
# third-party
from svgen.element import Element
from svgen.element.html import div
from vcorelib.io.file_writer import IndentedFileWriter
# internal
from runtimepy.net.html.bootstrap import icon_str
TEXT = "font-monospace"
BOOTSTRAP_BUTTON = f"rounded-0 {TEXT} text-start text-nowrap"
[docs]
def flex(kind: str = "row", **kwargs) -> Element:
"""Get a flexbox row container."""
container = div(**kwargs)
container["class"] = f"d-flex flex-{kind}"
return container
DEFAULT_PLACEMENT = "right"
[docs]
def set_tooltip(
element: Element, data: str, placement: str = DEFAULT_PLACEMENT
) -> None:
"""Set a tooltip on an element."""
element["data-bs-title"] = data
element["data-bs-placement"] = placement
element.add_class("has-tooltip")
BUTTON_COLOR = "secondary"
[docs]
def bootstrap_button(
text: str,
tooltip: str = None,
color: str = BUTTON_COLOR,
placement: str = DEFAULT_PLACEMENT,
**kwargs,
) -> Element:
"""Create a bootstrap button."""
button = div(
tag="button",
type="button",
text=text,
**kwargs,
class_str=f"btn btn-{color} " + BOOTSTRAP_BUTTON,
)
if tooltip:
set_tooltip(button, tooltip, placement=placement)
return button
[docs]
def collapse_button(
target: str,
tooltip: str = None,
icon: str = "arrows-collapse-vertical",
toggle: str = "collapse",
**kwargs,
) -> Element:
"""Create a collapse button."""
collapse = bootstrap_button(icon_str(icon), tooltip=tooltip, **kwargs)
if target:
collapse["data-bs-toggle"] = toggle
collapse["data-bs-target"] = target
return collapse
[docs]
def toggle_button(
parent: Element,
icon: str = "toggles",
title: str = None,
icon_classes: list[str] = None,
tooltip: str = None,
placement: str = "top",
**kwargs,
) -> Element:
"""Add a boolean-toggle button."""
# if title and not tooltip:
if not title and tooltip:
kwargs["title"] = "see tooltip"
elif title:
kwargs["title"] = title
button = div(
tag="button",
type="button",
text=icon_str(icon, classes=icon_classes),
parent=parent,
class_str=f"btn {BOOTSTRAP_BUTTON}",
**kwargs,
)
if tooltip:
set_tooltip(button, tooltip, placement=placement)
return button
[docs]
def input_box(
parent: Element,
label: str = "",
pattern: str = ".*",
description: str = None,
placement: str = "top",
icon: str = "",
**kwargs,
) -> tuple[Element, Element, Element]:
"""Create command input box."""
container = div(parent=parent, class_str="input-group")
label_elem = div(tag="span", parent=container)
label_elem.add_class("input-group-text", "rounded-0", TEXT)
if description:
set_tooltip(label_elem, description, placement=placement)
if icon:
div(text=icon_str(icon), parent=label_elem)
box = div(
tag="input",
type="text",
placeholder=pattern,
parent=container,
name=label,
title=label + " input",
**kwargs,
)
box.add_class("form-control", "rounded-0", TEXT)
return container, label_elem, box
[docs]
def slider(
min_val: int | float, max_val: int | float, steps: int, **kwargs
) -> Element:
"""Create a phase-control slider element."""
elem = div(
tag="input",
type="range",
class_str="m-auto form-range slider",
**kwargs,
)
assert min_val < max_val, (min_val, max_val)
elem["min"] = min_val
elem["max"] = max_val
step = (max_val - min_val) / steps
if isinstance(min_val, int) and isinstance(max_val, int):
step = int(step)
elem["step"] = step
# add tick marks - didn't seem to work (browser didn't render anything)
# list_name = f"{name}-datalist"
# elem["list"] = list_name
# markers = div(tag="datalist", id=list_name, parent=container, front=True)
# start = float(elem["min"])
# num_steps = 8
# step = (float(elem["max"]) - float(elem["min"])) / num_steps
# for idx in range(num_steps):
# div(tag="option", value=start + (idx * step), parent=markers)
return elem
TABLE_CLASSES = ["table", "table-hover", "table-striped", "table-bordered"]
[docs]
def centered_markdown(
parent: Element,
markdown: str,
*container_classes: str,
table_classes: list[str] = None,
) -> Element:
"""Add centered markdown."""
container = div(parent=parent)
container.add_class(
"flex-grow-1",
"d-flex",
"flex-row",
"justify-content-between",
*container_classes,
)
div(parent=container, class_str="flex-grow-1")
horiz_container = div(parent=container)
horiz_container.add_class(
"d-flex", "flex-column", "justify-content-between"
)
div(parent=horiz_container, class_str="flex-grow-1")
with StringIO() as stream:
writer = IndentedFileWriter(stream)
if table_classes is None:
table_classes = TABLE_CLASSES
def render_hook(data: str) -> str:
"""Make some adjustments to various element declarations."""
if table_classes:
data = data.replace(
"<table>", f'<table class="{' '.join(table_classes)}">'
)
return data
writer.write_markdown(markdown, hook=render_hook)
div(
text=stream.getvalue(),
parent=horiz_container,
class_str="text-body p-3 pb-0",
preformatted=True,
)
div(parent=horiz_container, class_str="flex-grow-1")
div(parent=container, class_str="flex-grow-1")
return container