initial commit
This commit is contained in:
parent
d02c23e9fb
commit
4a0dae531e
2348
pyscript-bootstrap-templates/HTML.py
Normal file
2348
pyscript-bootstrap-templates/HTML.py
Normal file
File diff suppressed because it is too large
Load Diff
1
pyscript-bootstrap-templates/__init__.py
Normal file
1
pyscript-bootstrap-templates/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = "0.1.0"
|
1470
pyscript-bootstrap-templates/bootstrap_HTML.py
Normal file
1470
pyscript-bootstrap-templates/bootstrap_HTML.py
Normal file
File diff suppressed because it is too large
Load Diff
2300
pyscript-bootstrap-templates/bootstrap_HTML_container.py
Normal file
2300
pyscript-bootstrap-templates/bootstrap_HTML_container.py
Normal file
File diff suppressed because it is too large
Load Diff
646
pyscript-bootstrap-templates/bootstrap_inputs.py
Normal file
646
pyscript-bootstrap-templates/bootstrap_inputs.py
Normal file
@ -0,0 +1,646 @@
|
||||
import datetime as dt
|
||||
import uuid
|
||||
|
||||
from bootstrap_HTML import *
|
||||
from js import document, FileReader, btoa # type: ignore
|
||||
from pyodide import create_proxy # type: ignore
|
||||
import io
|
||||
import base64
|
||||
|
||||
|
||||
class InputLabel(HTML.Label, BootstrapContainer):
|
||||
_default_class_name: str = "form-label"
|
||||
|
||||
|
||||
class InputLabelCheckbox(InputLabel):
|
||||
_default_class_name: str = "form-check-label"
|
||||
|
||||
|
||||
class InputHelp(BootstrapContainer):
|
||||
_default_class_name: str = "form-text"
|
||||
|
||||
|
||||
class InputFormControl(HTML.Input, BootstrapContainer):
|
||||
_default_class_name: str = "form-control"
|
||||
|
||||
@property
|
||||
def value(self) -> str:
|
||||
return self._parse_value()
|
||||
|
||||
@value.setter
|
||||
def value(self, value: str):
|
||||
self._set_value(value)
|
||||
|
||||
|
||||
class InputFormControlColor(InputFormControl):
|
||||
_default_class_name: str = "form-control form-control-color"
|
||||
|
||||
|
||||
class InputFormControlNumber(InputFormControl):
|
||||
_default_class_name: str = "form-control"
|
||||
|
||||
@property
|
||||
def min(self):
|
||||
return self.element.getAttribute("min")
|
||||
|
||||
@min.setter
|
||||
def min(self, value):
|
||||
self.element.setAttribute("min", str(value))
|
||||
|
||||
@property
|
||||
def max(self):
|
||||
return self.element.getAttribute("max")
|
||||
|
||||
@max.setter
|
||||
def max(self, value):
|
||||
self.element.setAttribute("max", str(value))
|
||||
|
||||
|
||||
class InputFormControlRange(InputFormControl):
|
||||
_default_class_name: str = "form-range"
|
||||
|
||||
|
||||
class InputFormControlSelect(InputFormControl):
|
||||
_default_class_name: str = "form-select"
|
||||
_tag_type: str = "select"
|
||||
|
||||
|
||||
class InputFormControlCheckbox(InputFormControl):
|
||||
_default_class_name: str = "form-check-input"
|
||||
|
||||
|
||||
class InputElement(BootstrapContainer):
|
||||
|
||||
_default_input_type = "text"
|
||||
_default_input_class = InputFormControl
|
||||
_default_label_class = InputLabel
|
||||
|
||||
def __init__(self,
|
||||
label_text: str = None,
|
||||
help_text: str = None,
|
||||
floating_label: bool = False,
|
||||
placeholder: str = None,
|
||||
input_type=None,
|
||||
id=None,
|
||||
parent=None):
|
||||
super().__init__(parent=parent, id=id)
|
||||
|
||||
if floating_label and placeholder is None:
|
||||
placeholder = " " # create a dummy placeholder
|
||||
|
||||
self._input = self.__class__._default_input_class(
|
||||
parent=None,
|
||||
id=self.id+"_input",
|
||||
type=input_type if input_type is not None else self._default_input_type,
|
||||
placeholder=placeholder
|
||||
)
|
||||
|
||||
self._label = self.__class__._default_label_class(
|
||||
parent=None, id=self.id+"_label", inner_html=label_text)
|
||||
|
||||
# if floating_label is True, add the input first, then the label
|
||||
|
||||
if floating_label:
|
||||
self.append_child(self._input)
|
||||
self.append_child(self._label)
|
||||
|
||||
else:
|
||||
self.append_child(self._label)
|
||||
self.append_child(self._input)
|
||||
|
||||
self._help_text = None
|
||||
if help_text is not None:
|
||||
self._help_text = InputHelp(
|
||||
parent=self, id=self.id+"_help_text", inner_html=help_text)
|
||||
|
||||
self.floating_label = floating_label
|
||||
|
||||
@property
|
||||
def is_small(self) -> bool:
|
||||
return self._input.has_class("form-control-sm")
|
||||
|
||||
@is_small.setter
|
||||
def is_small(self, value: bool):
|
||||
self._input.set_class_name("form-control-sm", value)
|
||||
|
||||
@property
|
||||
def is_large(self) -> bool:
|
||||
return self._input.has_class("form-control-lg")
|
||||
|
||||
@is_large.setter
|
||||
def is_large(self, value: bool):
|
||||
self._input.set_class_name("form-control-lg", value)
|
||||
|
||||
@property
|
||||
def readonly(self) -> bool:
|
||||
return self._input.has_attribute("readonly")
|
||||
|
||||
@readonly.setter
|
||||
def readonly(self, value: bool):
|
||||
self._input.set_attribute("readonly", value, is_boolean_attribute=True)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._input.value
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self._input.value = value
|
||||
|
||||
@property
|
||||
def onchange(self):
|
||||
return self._input.onchange
|
||||
|
||||
@onchange.setter
|
||||
def onchange(self, value):
|
||||
self._input.onchange = value
|
||||
|
||||
@property
|
||||
def floating_label(self) -> bool:
|
||||
return self.has_class("form-floating")
|
||||
|
||||
@floating_label.setter
|
||||
def floating_label(self, value: bool):
|
||||
self.set_class("form-floating", value)
|
||||
|
||||
|
||||
class InputEmail(InputElement):
|
||||
_default_input_type = "email"
|
||||
|
||||
|
||||
class InputPassword(InputElement):
|
||||
_default_input_type = "password"
|
||||
|
||||
|
||||
class InputText(InputElement):
|
||||
_default_input_type = "text"
|
||||
|
||||
|
||||
class InputFloat(InputElement):
|
||||
_default_input_type = "number"
|
||||
_default_number_class = float
|
||||
|
||||
def __init__(self,
|
||||
label_text: str = None,
|
||||
help_text: str = None,
|
||||
floating_label: bool = False,
|
||||
placeholder: str = None,
|
||||
min: _default_number_class = None,
|
||||
max: _default_number_class = None,
|
||||
step: _default_number_class = None,
|
||||
input_type=None,
|
||||
id=None,
|
||||
parent=None):
|
||||
super().__init__(label_text=label_text,
|
||||
help_text=help_text,
|
||||
floating_label=floating_label,
|
||||
placeholder=placeholder,
|
||||
input_type=input_type,
|
||||
id=id,
|
||||
parent=parent)
|
||||
|
||||
if min is not None:
|
||||
self.min = min
|
||||
|
||||
if max is not None:
|
||||
self.max = max
|
||||
|
||||
if step is not None:
|
||||
self.step = step
|
||||
|
||||
@property
|
||||
def value(self) -> _default_number_class:
|
||||
val = self._input.value
|
||||
if val == "":
|
||||
return None
|
||||
return self.__class__._default_number_class(self._input.value)
|
||||
|
||||
@value.setter
|
||||
def value(self, value: _default_number_class):
|
||||
self._input.value = str(value) if value is not None else ""
|
||||
|
||||
@property
|
||||
def min(self) -> _default_number_class:
|
||||
return self.__class__._default_number_class(self._input.get_attribute("min"))
|
||||
|
||||
@min.setter
|
||||
def min(self, value: _default_number_class):
|
||||
self._input.set_attribute("min", str(value))
|
||||
|
||||
@property
|
||||
def max(self) -> _default_number_class:
|
||||
return self.__class__._default_number_class(self._input.get_attribute("max"))
|
||||
|
||||
@max.setter
|
||||
def max(self, value: _default_number_class):
|
||||
self._input.set_attribute("max", str(value))
|
||||
|
||||
@property
|
||||
def step(self) -> _default_number_class:
|
||||
return self.__class__._default_number_class(self._input.get_attribute("step"))
|
||||
|
||||
@step.setter
|
||||
def step(self, value: _default_number_class):
|
||||
self._input.set_attribute("step", str(value))
|
||||
|
||||
|
||||
class InputRangeFloat(InputFloat):
|
||||
_default_input_type = "range"
|
||||
_default_input_class = InputFormControlRange
|
||||
|
||||
|
||||
class InputInt(InputFloat):
|
||||
|
||||
_default_number_class = int
|
||||
|
||||
|
||||
class InputRangeInt(InputInt):
|
||||
_default_input_type = "range"
|
||||
_default_input_class = InputFormControlRange
|
||||
|
||||
|
||||
class InputDate(InputElement):
|
||||
_default_input_type = "date"
|
||||
|
||||
@property
|
||||
def value(self) -> dt.datetime:
|
||||
if self._input.value == "":
|
||||
return None
|
||||
return dt.datetime.strptime(self._input.value, "%Y-%m-%d").date()
|
||||
|
||||
@value.setter
|
||||
def value(self, value: Union[dt.datetime, dt.date, str, None]):
|
||||
if isinstance(value, str):
|
||||
self._input.value = value
|
||||
self._input.value = value.strftime(
|
||||
"%Y-%m-%d") if value is not None else ""
|
||||
|
||||
|
||||
class InputTime(InputElement):
|
||||
_default_input_type = "time"
|
||||
|
||||
@property
|
||||
def value(self) -> dt.datetime:
|
||||
if self._input.value == "":
|
||||
return None
|
||||
return dt.datetime.strptime(self._input.value, "%H:%M").time()
|
||||
|
||||
@value.setter
|
||||
def value(self, value: Union[dt.datetime, dt.time, str, None]):
|
||||
if isinstance(value, str):
|
||||
self._input.value = value
|
||||
self._input.value = value.strftime(
|
||||
"%H:%M") if value is not None else ""
|
||||
|
||||
|
||||
class InputRange(InputElement):
|
||||
_default_input_type = "range"
|
||||
|
||||
|
||||
class InputFile(InputElement):
|
||||
_default_input_type = "file"
|
||||
|
||||
def __init__(self,
|
||||
label_text: str = None,
|
||||
help_text: str = None,
|
||||
floating_label: bool = False,
|
||||
placeholder: str = None,
|
||||
input_type=None,
|
||||
id=None,
|
||||
parent=None):
|
||||
|
||||
super().__init__(label_text=label_text,
|
||||
help_text=help_text,
|
||||
floating_label=floating_label,
|
||||
placeholder=placeholder,
|
||||
input_type=input_type,
|
||||
id=id,
|
||||
parent=parent)
|
||||
|
||||
self._files = {}
|
||||
self._input.onchange = self._load_file
|
||||
self._on_file_change = None
|
||||
|
||||
|
||||
def _load_file(self, *args):
|
||||
def read_file(event, name):
|
||||
buffer = io.BytesIO()
|
||||
|
||||
# FIXME: way too much decoding and encoding between formats here
|
||||
# but bytearray conversation between js and python drove me crazy!!!
|
||||
|
||||
|
||||
f = btoa(event.target.result)
|
||||
buffer.write(base64.decodebytes(bytes(f, "utf-8")))
|
||||
print(btoa(event.target.result))
|
||||
self._files[name] = buffer
|
||||
buffer.seek(0)
|
||||
buffer.name = name # not standardized, but works here
|
||||
if self._on_file_change is not None:
|
||||
self._on_file_change(buffer)
|
||||
|
||||
fileList = self._input.element.files
|
||||
|
||||
self._files = {}
|
||||
|
||||
for f in fileList:
|
||||
# reader is a pyodide.JsProxy
|
||||
reader = FileReader.new()
|
||||
|
||||
# Create a Python proxy for the callback function
|
||||
onload_event = create_proxy(lambda event, name=f.name: read_file(event, name))
|
||||
|
||||
reader.onload = onload_event
|
||||
|
||||
reader.readAsBinaryString(f)
|
||||
|
||||
|
||||
|
||||
return
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._files
|
||||
|
||||
@property
|
||||
def onchange(self):
|
||||
return self._on_file_change
|
||||
|
||||
@onchange.setter
|
||||
def onchange(self, value):
|
||||
self._on_file_change = value
|
||||
|
||||
|
||||
class InputMultiFile(InputFile):
|
||||
|
||||
def __init__(self,
|
||||
label_text: str = None,
|
||||
help_text: str = None,
|
||||
floating_label: bool = False,
|
||||
placeholder: str = None,
|
||||
input_type=None,
|
||||
id=None,
|
||||
parent=None):
|
||||
super().__init__(label_text=label_text,
|
||||
help_text=help_text,
|
||||
floating_label=floating_label,
|
||||
placeholder=placeholder,
|
||||
input_type=input_type,
|
||||
id=id,
|
||||
parent=parent)
|
||||
self._input.set_attribute("multiple", True, is_boolean_attribute=True)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
files = []
|
||||
for i in range(self._input.element.files.length):
|
||||
files.append(self._input.element.files.item(i).name)
|
||||
return files
|
||||
|
||||
|
||||
class InputColor(InputElement):
|
||||
_default_input_type = "color"
|
||||
_default_input_class = InputFormControlColor
|
||||
|
||||
|
||||
class InputDatalist(InputElement):
|
||||
|
||||
def __init__(self,
|
||||
options: List[str],
|
||||
label_text: str = None,
|
||||
help_text: str = None,
|
||||
floating_label: bool = False,
|
||||
placeholder: str = None,
|
||||
input_type=None,
|
||||
id=None,
|
||||
parent=None):
|
||||
super().__init__(label_text=label_text,
|
||||
help_text=help_text,
|
||||
floating_label=floating_label,
|
||||
placeholder=placeholder,
|
||||
input_type=input_type,
|
||||
id=id,
|
||||
parent=parent)
|
||||
|
||||
self._input.type = None
|
||||
|
||||
self._options = options
|
||||
|
||||
self._datalist = HTML.DataList(parent=self, id=self.id+"_datalist")
|
||||
|
||||
for option in options:
|
||||
HTML.Option(inner_html=option, parent=self._datalist)
|
||||
|
||||
self._input.set_attribute("list", self._datalist.id)
|
||||
|
||||
@property
|
||||
def options(self):
|
||||
return self._options
|
||||
|
||||
@options.setter
|
||||
def options(self, value):
|
||||
self._options = value
|
||||
for c in self._datalist.children:
|
||||
c.destroy()
|
||||
for option in self._options:
|
||||
HTML.Option(inner_html=option, parent=self._datalist)
|
||||
|
||||
|
||||
class InputSelect(InputElement):
|
||||
|
||||
_default_input_class = InputFormControlSelect
|
||||
|
||||
def __init__(self,
|
||||
options: List[str],
|
||||
label_text: str = None,
|
||||
help_text: str = None,
|
||||
floating_label: bool = False,
|
||||
placeholder: str = None,
|
||||
multiple: bool = False,
|
||||
input_type=None,
|
||||
id=None,
|
||||
parent=None):
|
||||
super().__init__(label_text=label_text,
|
||||
help_text=help_text,
|
||||
floating_label=floating_label,
|
||||
placeholder=placeholder,
|
||||
input_type=input_type,
|
||||
id=id,
|
||||
parent=parent)
|
||||
|
||||
self._input.type = None
|
||||
|
||||
self._options = options
|
||||
|
||||
self.multiple = multiple
|
||||
|
||||
for option in options:
|
||||
HTML.Option(inner_html=option, parent=self._input)
|
||||
|
||||
@property
|
||||
def options(self):
|
||||
return self._options
|
||||
|
||||
@options.setter
|
||||
def options(self, value):
|
||||
self._options = value
|
||||
for c in self._input.children:
|
||||
c.destroy()
|
||||
for option in self._options:
|
||||
HTML.Option(inner_html=option, parent=self._input)
|
||||
|
||||
@property
|
||||
def multiple(self):
|
||||
return self._input.has_attribute("multiple")
|
||||
|
||||
@multiple.setter
|
||||
def multiple(self, value):
|
||||
self._input.set_attribute("multiple", value, is_boolean_attribute=True)
|
||||
|
||||
|
||||
class InputCheckboxSingle(InputElement):
|
||||
|
||||
_default_class_name: str = "form-check"
|
||||
_default_input_type = "checkbox"
|
||||
_default_input_class = InputFormControlCheckbox
|
||||
_default_label_class = InputLabelCheckbox
|
||||
|
||||
def __init__(self,
|
||||
label_text: str = None,
|
||||
help_text: str = None,
|
||||
floating_label: bool = False,
|
||||
placeholder: str = None,
|
||||
name: str = None,
|
||||
input_type=None,
|
||||
id=None,
|
||||
parent=None):
|
||||
super().__init__(label_text=label_text,
|
||||
help_text=help_text,
|
||||
floating_label=floating_label,
|
||||
placeholder=placeholder,
|
||||
input_type=input_type,
|
||||
id=id,
|
||||
parent=parent)
|
||||
|
||||
if name is not None:
|
||||
self._input.set_attribute("name", name)
|
||||
|
||||
@property
|
||||
def checked(self) -> bool:
|
||||
return self._input.element.checked
|
||||
|
||||
@checked.setter
|
||||
def checked(self, value: bool):
|
||||
self._input.element.checked = value
|
||||
|
||||
@property
|
||||
def value(self) -> bool:
|
||||
return self.checked
|
||||
|
||||
@value.setter
|
||||
def value(self, value: bool):
|
||||
self.checked = value
|
||||
|
||||
@property
|
||||
def inline(self) -> bool:
|
||||
return self.has_class("inline")
|
||||
|
||||
@inline.setter
|
||||
def inline(self, value: bool):
|
||||
self.set_class("inline", value)
|
||||
|
||||
|
||||
class InputRadioSingle(InputCheckboxSingle):
|
||||
_default_input_type = "radio"
|
||||
|
||||
|
||||
class InputSwitchSingle(InputCheckboxSingle):
|
||||
_default_class_name: str = "form-check form-switch"
|
||||
|
||||
|
||||
class InputCheckboxGroup(BootstrapContainer):
|
||||
|
||||
_default_input_class = InputCheckboxSingle
|
||||
|
||||
def __init__(self,
|
||||
options: List[str],
|
||||
inline: bool = False,
|
||||
id: str = None,
|
||||
label_text: Union[str, HTML.Element] = None,
|
||||
group_name: str = None,
|
||||
class_name: str = None,
|
||||
parent: HTML.Element = None,
|
||||
inner_html: str = None,) -> None:
|
||||
super().__init__(inner_html=inner_html,
|
||||
id=id,
|
||||
class_name=class_name,
|
||||
parent=parent)
|
||||
|
||||
if group_name is None:
|
||||
group_name = "id-" + str(uuid.uuid4())
|
||||
if label_text is not None:
|
||||
if isinstance(label_text, str):
|
||||
label_text = HTML.Label(inner_html=label_text, parent=self)
|
||||
else:
|
||||
self.append_child(label_text)
|
||||
|
||||
self._options = options
|
||||
self._option_checkboxes = {}
|
||||
self._group_name = group_name
|
||||
|
||||
for option in options:
|
||||
cb = self.__class__._default_input_class(
|
||||
option, name=group_name, parent=self)
|
||||
cb.inline = inline
|
||||
self._option_checkboxes[option] = cb
|
||||
|
||||
@property
|
||||
def options(self):
|
||||
return self._options
|
||||
|
||||
@options.setter
|
||||
def options(self, value):
|
||||
self._options = value
|
||||
for c in self._option_checkboxes.values():
|
||||
c.destroy()
|
||||
self._option_checkboxes = {}
|
||||
for option in self._options:
|
||||
cb = self.__class__._default_input_class(
|
||||
option, name=self._group_name, parent=self)
|
||||
self._option_checkboxes[option] = cb
|
||||
|
||||
@property
|
||||
def value(self) -> List[str]:
|
||||
"""
|
||||
returns a list of checked options
|
||||
"""
|
||||
|
||||
values = []
|
||||
for option in self._options:
|
||||
if self._option_checkboxes[option].checked:
|
||||
values.append(option)
|
||||
return values
|
||||
|
||||
@value.setter
|
||||
def value(self, value: List[str]):
|
||||
for option in self._options:
|
||||
self._option_checkboxes[option].checked = option in value
|
||||
|
||||
@property
|
||||
def option_checkboxes(self) -> Dict[str, InputCheckboxSingle]:
|
||||
"""
|
||||
returns the form elements for each option as dictionary
|
||||
"""
|
||||
return self._option_checkboxes
|
||||
|
||||
|
||||
class InputRadioGroup(InputCheckboxGroup):
|
||||
_default_input_class = InputRadioSingle
|
||||
|
||||
|
||||
class InputSwitchGroup(InputCheckboxGroup):
|
||||
_default_input_class = InputSwitchSingle
|
||||
|
||||
class Form(HTML.Form, BootstrapContainer):
|
||||
pass
|
||||
# TODO: implement more Form and form validation methods
|
90
pyscript-bootstrap-templates/bootstrap_templates.py
Normal file
90
pyscript-bootstrap-templates/bootstrap_templates.py
Normal file
@ -0,0 +1,90 @@
|
||||
import HTML
|
||||
import bootstrap_HTML as bHTML
|
||||
from js import document # type: ignore
|
||||
|
||||
class PyScriptBootstrapApp(object):
|
||||
def __init__(self, parent_element:str = "pyscript_app"):
|
||||
self._main_div = bHTML.ContainerFluid(id="main")
|
||||
|
||||
self.main_div.w = 100
|
||||
self.main_div.h = 100
|
||||
|
||||
self._parent_element = document.getElementById(parent_element)
|
||||
self._parent_element.appendChild(self.main_div.element)
|
||||
|
||||
@property
|
||||
def main_div(self) -> HTML.Div:
|
||||
return self._main_div
|
||||
|
||||
class PyScriptBootstrapDashboard(PyScriptBootstrapApp):
|
||||
def __init__(self, parent_element:str = "pyscript_app", brand_name = "Dashboard"):
|
||||
|
||||
super().__init__(parent_element)
|
||||
|
||||
row = bHTML.Row(parent=self.main_div)
|
||||
row.height = "100%"
|
||||
row.width = "100%"
|
||||
row.mt = 5
|
||||
row.display_property = bHTML.DisplayProperty.INLINE_FLEX
|
||||
#row.set_attribute("style", "flex-shrink: 0;")
|
||||
|
||||
self._sidebar = bHTML.Col(id="sidebar", col_sm=5, col_md=4, col_lg=3, col_xl=3, parent=row)
|
||||
|
||||
self._navbar = bHTML.NavbarDark(
|
||||
parent=self.main_div,
|
||||
brand = brand_name,
|
||||
nav_fill=True,
|
||||
toggle_button_for_target=self.sidebar
|
||||
)
|
||||
|
||||
self._navbar.position = bHTML.Position.FIXED_TOP
|
||||
self._navbar.p = 2
|
||||
|
||||
|
||||
row.append_child(self.sidebar)
|
||||
self._sidebar.add_classes("sidebar")
|
||||
self._sidebar.background_color = bHTML.BackgroundColor.LIGHT
|
||||
self._sidebar.p = 4
|
||||
self._sidebar.g = 2
|
||||
self._sidebar.position = bHTML.Position.STATIC
|
||||
self._sidebar.collapsable = True
|
||||
self._sidebar.height = "100%"
|
||||
self._sidebar.mw = 100
|
||||
self._sidebar.shadow = bHTML.Shadow.LARGE
|
||||
self._sidebar.overflow = bHTML.Overflow.SCROLL
|
||||
|
||||
|
||||
|
||||
self._modal = bHTML.Modal(parent=self.main_div, title="Modal")
|
||||
|
||||
self._main_area = bHTML.Col(id="main_area", parent=row, col_lg=9, col_xl=9)
|
||||
self._main_area.p = 4
|
||||
self._main_area.overflow = bHTML.Overflow.SCROLL
|
||||
self._main_area.height = "100%"
|
||||
self._main_area.mw = 100
|
||||
|
||||
def show_modal(self):
|
||||
self.modal.show()
|
||||
|
||||
def hide_modal(self):
|
||||
self.modal.hide()
|
||||
|
||||
def toggle_modal(self):
|
||||
self.modal.toggle()
|
||||
|
||||
|
||||
@property
|
||||
def header(self) -> HTML.Header:
|
||||
return self._header
|
||||
|
||||
@property
|
||||
def sidebar(self) -> HTML.Div:
|
||||
return self._sidebar
|
||||
|
||||
@property
|
||||
def modal(self) -> HTML.Div:
|
||||
return self._modal
|
||||
|
||||
@property
|
||||
def main_area(self) -> HTML.Div:
|
||||
return self._main_area
|
95
pyscript-bootstrap-templates/create.py
Normal file
95
pyscript-bootstrap-templates/create.py
Normal file
@ -0,0 +1,95 @@
|
||||
|
||||
import pathlib
|
||||
from typing import List
|
||||
import argparse
|
||||
|
||||
|
||||
# create a new pyscript project
|
||||
|
||||
def create_project(root_folder: pathlib.Path,
|
||||
title: str,
|
||||
packages: List[str],
|
||||
paths: List[str] = None,
|
||||
pyscript_css_url: str = "https://pyscript.net/alpha/pyscript.css",
|
||||
pyscript_js_url: str = "https://pyscript.net/alpha/pyscript.js"):
|
||||
|
||||
pyenv = "- git+https://the-cake-is-a-lie.net/gogs/jonas/pyscript-bootstrap-templates.git"
|
||||
for package in packages:
|
||||
pyenv += f"\n - {package}"
|
||||
|
||||
if paths is not None:
|
||||
pyenv += "\n - paths:"
|
||||
for path in paths:
|
||||
pyenv += f"\n - {path}"
|
||||
|
||||
pyenv += "\n"
|
||||
|
||||
html = f"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" style="width: 100%;height: 100%">
|
||||
<head id="head">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="{pyscript_css_url}" />
|
||||
<script defer src="{pyscript_js_url}"></script>
|
||||
|
||||
<title>{title}</title>
|
||||
|
||||
<py-env>
|
||||
{pyenv}
|
||||
</py-env>
|
||||
</head>
|
||||
<body style="width: 100%; height: 100%">
|
||||
<div id="simple_app" style="height: 100%; min-height: 100%"></div>
|
||||
<py-script src="./main.py"></py-script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
py = f"""
|
||||
|
||||
"""
|
||||
|
||||
if not root_folder.exists():
|
||||
root_folder.mkdir(parents=True)
|
||||
|
||||
(root_folder / "index.html").write_text(html, encoding="utf-8")
|
||||
(root_folder / "main.py").write_text("", encoding="utf-8")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
argument_parser = argparse.ArgumentParser(
|
||||
description="create a new pyscript project")
|
||||
argument_parser.add_argument(
|
||||
"root_folder", type=pathlib.Path, help="the root folder of the new project")
|
||||
argument_parser.add_argument(
|
||||
"title", type=str, help="the title of the new project")
|
||||
argument_parser.add_argument(
|
||||
"packages", type=str, nargs="+", help="the packages to include in the new project")
|
||||
argument_parser.add_argument("--paths", type=str, nargs="+",
|
||||
help="additional local python files to include in the new project")
|
||||
argument_parser.add_argument("--pyscript_css_url", type=str,
|
||||
help="the url of the pyscript css file", default="https://pyscript.net/alpha/pyscript.css")
|
||||
argument_parser.add_argument("--pyscript_js_url", type=str,
|
||||
help="the url of the pyscript js file", default="https://pyscript.net/alpha/pyscript.js")
|
||||
arguments = argument_parser.parse_args()
|
||||
|
||||
create_project(arguments.root_folder,
|
||||
arguments.title,
|
||||
arguments.packages,
|
||||
arguments.paths,
|
||||
arguments.pyscript_css_url,
|
||||
arguments.pyscript_js_url)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
exit(0)
|
56
setup.py
Normal file
56
setup.py
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
import pathlib
|
||||
|
||||
here = pathlib.Path(__file__).parent.resolve()
|
||||
|
||||
|
||||
def get_version(rel_path):
|
||||
for line in (here / rel_path).read_text(encoding="utf-8").splitlines():
|
||||
if line.startswith('__version__'):
|
||||
delim = '"' if '"' in line else "'"
|
||||
return line.split(delim)[1]
|
||||
else:
|
||||
raise RuntimeError("Unable to find version string.")
|
||||
|
||||
|
||||
|
||||
# Get the long description from the README file
|
||||
long_description = (here / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
setup(
|
||||
|
||||
name="pyscript-bootstrap-templates",
|
||||
version=get_version("pyscript-bootstrap-templates/__init__.py"),
|
||||
description="templates and basic python/pyscript wrappers for bootstrap 5",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
url="https://github.com/pypa/sampleproject",
|
||||
|
||||
author="Jonas Weinz",
|
||||
|
||||
author_email="author@example.com",
|
||||
|
||||
keywords="sample, setuptools, development",
|
||||
|
||||
package_dir={"": "pyscript-bootstrap-templates"},
|
||||
packages=find_packages(where="pyscript-bootstrap-templates"),
|
||||
|
||||
|
||||
python_requires=">=3.7, <4",
|
||||
|
||||
install_requires=[
|
||||
"pillow",
|
||||
"parse",
|
||||
],
|
||||
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"create_pyscript_bootstrap_app=create:main",
|
||||
],
|
||||
},
|
||||
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user