Spaces:
Sleeping
Sleeping
Upload 81 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +2 -0
- MonsterUI/.cursorrules +1 -0
- MonsterUI/.gitattributes +1 -0
- MonsterUI/.gitconfig +11 -0
- MonsterUI/.gitignore +167 -0
- MonsterUI/CHANGELOG.bak +222 -0
- MonsterUI/CHANGELOG.md +229 -0
- MonsterUI/CONTRIBUTING.md +27 -0
- MonsterUI/LICENSE +201 -0
- MonsterUI/MANIFEST.in +5 -0
- MonsterUI/README.md +99 -0
- MonsterUI/__pycache__/testnavbar.cpython-312.pyc +0 -0
- MonsterUI/docs/.plash +1 -0
- MonsterUI/docs/MonsterUI.jpg +3 -0
- MonsterUI/docs/api_reference/api_reference.py +1211 -0
- MonsterUI/docs/api_reference/logo.svg +32 -0
- MonsterUI/docs/apilist.txt +520 -0
- MonsterUI/docs/cf_addns.py +18 -0
- MonsterUI/docs/createllms.sh +48 -0
- MonsterUI/docs/custom_theme.css +56 -0
- MonsterUI/docs/data_/mail.json +206 -0
- MonsterUI/docs/data_/status_list.json +1 -0
- MonsterUI/docs/data_/statuses.json +22 -0
- MonsterUI/docs/examples/auth.py +40 -0
- MonsterUI/docs/examples/cards.py +141 -0
- MonsterUI/docs/examples/dashboard.py +106 -0
- MonsterUI/docs/examples/data/mail.json +206 -0
- MonsterUI/docs/examples/data/status_list.json +1 -0
- MonsterUI/docs/examples/data/statuses.json +22 -0
- MonsterUI/docs/examples/forms.py +150 -0
- MonsterUI/docs/examples/mail.py +111 -0
- MonsterUI/docs/examples/music.py +138 -0
- MonsterUI/docs/examples/playground.py +69 -0
- MonsterUI/docs/examples/scrollspy.py +125 -0
- MonsterUI/docs/examples/tasks.py +118 -0
- MonsterUI/docs/examples/ticket.py +95 -0
- MonsterUI/docs/favicon.ico +3 -0
- MonsterUI/docs/getting_started/GettingStarted.md +98 -0
- MonsterUI/docs/getting_started/StylingRulesOfThumb.py +110 -0
- MonsterUI/docs/getting_started/app_product_gallery.py +96 -0
- MonsterUI/docs/guides/Layout.ipynb +591 -0
- MonsterUI/docs/guides/Spacing.ipynb +482 -0
- MonsterUI/docs/htmxindicator.py +25 -0
- MonsterUI/docs/llms-ctx-full.txt +0 -0
- MonsterUI/docs/llms-ctx.txt +1603 -0
- MonsterUI/docs/llms.txt +44 -0
- MonsterUI/docs/main.py +266 -0
- MonsterUI/docs/requirements.txt +13 -0
- MonsterUI/docs/updatellms.sh +5 -0
- MonsterUI/docs/utils.py +60 -0
.gitattributes
CHANGED
|
@@ -35,3 +35,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
MonsterUI/content/MonsterUI/docs/favicon.ico filter=lfs diff=lfs merge=lfs -text
|
| 37 |
MonsterUI/content/MonsterUI/docs/MonsterUI.jpg filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
MonsterUI/content/MonsterUI/docs/favicon.ico filter=lfs diff=lfs merge=lfs -text
|
| 37 |
MonsterUI/content/MonsterUI/docs/MonsterUI.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
MonsterUI/docs/favicon.ico filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
MonsterUI/docs/MonsterUI.jpg filter=lfs diff=lfs merge=lfs -text
|
MonsterUI/.cursorrules
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
I am trying to make the python code as consistent with tailwind as possible. For example `large` should be abbreviated `lg` like in tailwind.
|
MonsterUI/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*.ipynb merge=nbdev-merge
|
MonsterUI/.gitconfig
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by nbdev_install_hooks
|
| 2 |
+
#
|
| 3 |
+
# If you need to disable this instrumentation do:
|
| 4 |
+
# git config --local --unset include.path
|
| 5 |
+
#
|
| 6 |
+
# To restore:
|
| 7 |
+
# git config --local include.path ../.gitconfig
|
| 8 |
+
#
|
| 9 |
+
[merge "nbdev-merge"]
|
| 10 |
+
name = resolve conflicts with nbdev_fix
|
| 11 |
+
driver = nbdev_merge %O %A %B %P
|
MonsterUI/.gitignore
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tags
|
| 2 |
+
.sesskey
|
| 3 |
+
docs/static
|
| 4 |
+
# Byte-compiled / optimized / DLL files
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.py[cod]
|
| 7 |
+
*$py.class
|
| 8 |
+
_docs/*
|
| 9 |
+
_proc/*
|
| 10 |
+
# C extensions
|
| 11 |
+
*.so
|
| 12 |
+
|
| 13 |
+
# Distribution / packaging
|
| 14 |
+
.Python
|
| 15 |
+
build/
|
| 16 |
+
develop-eggs/
|
| 17 |
+
dist/
|
| 18 |
+
downloads/
|
| 19 |
+
eggs/
|
| 20 |
+
.eggs/
|
| 21 |
+
lib/
|
| 22 |
+
lib64/
|
| 23 |
+
parts/
|
| 24 |
+
sdist/
|
| 25 |
+
var/
|
| 26 |
+
wheels/
|
| 27 |
+
share/python-wheels/
|
| 28 |
+
*.egg-info/
|
| 29 |
+
.installed.cfg
|
| 30 |
+
*.egg
|
| 31 |
+
MANIFEST
|
| 32 |
+
|
| 33 |
+
# PyInstaller
|
| 34 |
+
# Usually these files are written by a python script from a template
|
| 35 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 36 |
+
*.manifest
|
| 37 |
+
*.spec
|
| 38 |
+
|
| 39 |
+
# Installer logs
|
| 40 |
+
pip-log.txt
|
| 41 |
+
pip-delete-this-directory.txt
|
| 42 |
+
|
| 43 |
+
# Unit test / coverage reports
|
| 44 |
+
htmlcov/
|
| 45 |
+
.tox/
|
| 46 |
+
.nox/
|
| 47 |
+
.coverage
|
| 48 |
+
.coverage.*
|
| 49 |
+
.cache
|
| 50 |
+
nosetests.xml
|
| 51 |
+
coverage.xml
|
| 52 |
+
*.cover
|
| 53 |
+
*.py,cover
|
| 54 |
+
.hypothesis/
|
| 55 |
+
.pytest_cache/
|
| 56 |
+
cover/
|
| 57 |
+
|
| 58 |
+
# Translations
|
| 59 |
+
*.mo
|
| 60 |
+
*.pot
|
| 61 |
+
|
| 62 |
+
# Django stuff:
|
| 63 |
+
*.log
|
| 64 |
+
local_settings.py
|
| 65 |
+
db.sqlite3
|
| 66 |
+
db.sqlite3-journal
|
| 67 |
+
|
| 68 |
+
# Flask stuff:
|
| 69 |
+
instance/
|
| 70 |
+
.webassets-cache
|
| 71 |
+
|
| 72 |
+
# Scrapy stuff:
|
| 73 |
+
.scrapy
|
| 74 |
+
|
| 75 |
+
# Sphinx documentation
|
| 76 |
+
docs/_build/
|
| 77 |
+
|
| 78 |
+
# PyBuilder
|
| 79 |
+
.pybuilder/
|
| 80 |
+
target/
|
| 81 |
+
|
| 82 |
+
# Jupyter Notebook
|
| 83 |
+
.ipynb_checkpoints
|
| 84 |
+
|
| 85 |
+
# IPython
|
| 86 |
+
profile_default/
|
| 87 |
+
ipython_config.py
|
| 88 |
+
|
| 89 |
+
# pyenv
|
| 90 |
+
# For a library or package, you might want to ignore these files since the code is
|
| 91 |
+
# intended to run in multiple environments; otherwise, check them in:
|
| 92 |
+
# .python-version
|
| 93 |
+
|
| 94 |
+
# pipenv
|
| 95 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 96 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 97 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 98 |
+
# install all needed dependencies.
|
| 99 |
+
#Pipfile.lock
|
| 100 |
+
|
| 101 |
+
# poetry
|
| 102 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
| 103 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 104 |
+
# commonly ignored for libraries.
|
| 105 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
| 106 |
+
#poetry.lock
|
| 107 |
+
|
| 108 |
+
# pdm
|
| 109 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
| 110 |
+
#pdm.lock
|
| 111 |
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
| 112 |
+
# in version control.
|
| 113 |
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
| 114 |
+
.pdm.toml
|
| 115 |
+
.pdm-python
|
| 116 |
+
.pdm-build/
|
| 117 |
+
|
| 118 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 119 |
+
__pypackages__/
|
| 120 |
+
|
| 121 |
+
# Celery stuff
|
| 122 |
+
celerybeat-schedule
|
| 123 |
+
celerybeat.pid
|
| 124 |
+
|
| 125 |
+
# SageMath parsed files
|
| 126 |
+
*.sage.py
|
| 127 |
+
|
| 128 |
+
# Environments
|
| 129 |
+
.env
|
| 130 |
+
.venv
|
| 131 |
+
env/
|
| 132 |
+
venv/
|
| 133 |
+
ENV/
|
| 134 |
+
env.bak/
|
| 135 |
+
venv.bak/
|
| 136 |
+
uv.lock
|
| 137 |
+
|
| 138 |
+
# Spyder project settings
|
| 139 |
+
.spyderproject
|
| 140 |
+
.spyproject
|
| 141 |
+
|
| 142 |
+
# Rope project settings
|
| 143 |
+
.ropeproject
|
| 144 |
+
|
| 145 |
+
# mkdocs documentation
|
| 146 |
+
/site
|
| 147 |
+
|
| 148 |
+
# mypy
|
| 149 |
+
.mypy_cache/
|
| 150 |
+
.dmypy.json
|
| 151 |
+
dmypy.json
|
| 152 |
+
|
| 153 |
+
# Pyre type checker
|
| 154 |
+
.pyre/
|
| 155 |
+
|
| 156 |
+
# pytype static type analyzer
|
| 157 |
+
.pytype/
|
| 158 |
+
|
| 159 |
+
# Cython debug symbols
|
| 160 |
+
cython_debug/
|
| 161 |
+
|
| 162 |
+
# PyCharm
|
| 163 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
| 164 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
| 165 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
| 166 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
| 167 |
+
.idea/
|
MonsterUI/CHANGELOG.bak
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Release notes
|
| 2 |
+
|
| 3 |
+
<!-- do not remove -->
|
| 4 |
+
|
| 5 |
+
## 1.0.28
|
| 6 |
+
|
| 7 |
+
### New Features
|
| 8 |
+
|
| 9 |
+
- allow optional renderer ([#134](https://github.com/AnswerDotAI/MonsterUI/pull/134))
|
| 10 |
+
|
| 11 |
+
### Bugs Squashed
|
| 12 |
+
|
| 13 |
+
- `render_md` doesn't render images with data uris when `img_dir` is set ([#132](https://github.com/AnswerDotAI/MonsterUI/issues/132))
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
## 1.0.26
|
| 17 |
+
|
| 18 |
+
### Bugs Squashed
|
| 19 |
+
|
| 20 |
+
- fix `render_md` non http/s urls ([#133](https://github.com/AnswerDotAI/MonsterUI/pull/133)), thanks to [@comhar](https://github.com/comhar)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
## 1.0.25
|
| 24 |
+
|
| 25 |
+
### Bugs Squashed
|
| 26 |
+
|
| 27 |
+
- remove modal on close ([#130](https://github.com/AnswerDotAI/MonsterUI/pull/130)), thanks to [@comhar](https://github.com/comhar)
|
| 28 |
+
- Buttons not working in Modal added to the DOM by HTMX. ([#126](https://github.com/AnswerDotAI/MonsterUI/issues/126))
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
## 1.0.24
|
| 32 |
+
|
| 33 |
+
### New Features
|
| 34 |
+
|
| 35 |
+
- use uikit to initialise and open modals inserted with htmx ([#127](https://github.com/AnswerDotAI/MonsterUI/pull/127)), thanks to [@comhar](https://github.com/comhar)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
## 1.0.23
|
| 39 |
+
|
| 40 |
+
### Bugs Squashed
|
| 41 |
+
|
| 42 |
+
- fragment md parsing does not work ([#124](https://github.com/AnswerDotAI/MonsterUI/issues/124))
|
| 43 |
+
- llms.txt showing "internal server" errors ([#122](https://github.com/AnswerDotAI/MonsterUI/issues/122))
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
## 1.0.22
|
| 47 |
+
|
| 48 |
+
- Create custom themes
|
| 49 |
+
|
| 50 |
+
### Bugs Squashed
|
| 51 |
+
|
| 52 |
+
- Unicode strings with encoding declaration are not supported ([#123](https://github.com/AnswerDotAI/MonsterUI/issues/123))
|
| 53 |
+
- Update DropDownNavContainer class to remove unnecessary width class ([#114](https://github.com/AnswerDotAI/MonsterUI/pull/114)), thanks to [@ndendic](https://github.com/ndendic)
|
| 54 |
+
- fix font weight ordering ([#113](https://github.com/AnswerDotAI/MonsterUI/pull/113)), thanks to [@comhar](https://github.com/comhar)
|
| 55 |
+
- FieldSet inputs in Form are misaligned when monsterui is active ([#87](https://github.com/AnswerDotAI/MonsterUI/issues/87))
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
## 1.0.21
|
| 59 |
+
|
| 60 |
+
### New Features
|
| 61 |
+
|
| 62 |
+
- The new `ApexChart` component can create line charts, pie charts and [more](https://monsterui.answer.ai/api_ref/docs_charts). ([#110](https://github.com/AnswerDotAI/MonsterUI/pull/110)), thanks to [@ndendic](https://github.com/ndendic)
|
| 63 |
+
- Toasts now disappear automatically after 5 seconds. You can adjust the duration by using the `dur` field (e.g. `Toast('My Toast', dur=10.0)`). ([#109](https://github.com/AnswerDotAI/MonsterUI/pull/109)), thanks to [@comhar](https://github.com/comhar)
|
| 64 |
+
|
| 65 |
+
### Breaking Change
|
| 66 |
+
|
| 67 |
+
- Katex is no longer included in the default headers. To include it set `katex=True` when calling `.headers`. (i.e. `Theme.slate.headers(katex=True)`). ([#105](https://github.com/AnswerDotAI/MonsterUI/pull/105)), thanks to [@comhar](https://github.com/comhar)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
## 1.0.20
|
| 71 |
+
|
| 72 |
+
### New Features
|
| 73 |
+
|
| 74 |
+
- Add `icon` header param to optionally not bring in icons js lib ([#99](https://github.com/AnswerDotAI/MonsterUI/issues/99))
|
| 75 |
+
|
| 76 |
+
### Bugs Squashed
|
| 77 |
+
|
| 78 |
+
- added fh. to unqid ([#98](https://github.com/AnswerDotAI/MonsterUI/pull/98)), thanks to [@MorsCerta-crypto](https://github.com/MorsCerta-crypto)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
## 1.0.19
|
| 82 |
+
|
| 83 |
+
### New Features
|
| 84 |
+
|
| 85 |
+
- Add accordion component ([#94](https://github.com/AnswerDotAI/MonsterUI/pull/94)), thanks to [@MichlF](https://github.com/MichlF)
|
| 86 |
+
- Fix theme logic ([#93](https://github.com/AnswerDotAI/MonsterUI/pull/93)), thanks to [@curtis-allan](https://github.com/curtis-allan)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
## 1.0.18
|
| 90 |
+
|
| 91 |
+
- Hotfix accidental deletion of label select :(
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
## 1.0.16
|
| 95 |
+
|
| 96 |
+
### Bugs Squashed
|
| 97 |
+
|
| 98 |
+
- Select not properly checking presence of hx-trigger
|
| 99 |
+
|
| 100 |
+
## 1.0.14
|
| 101 |
+
|
| 102 |
+
### New Features
|
| 103 |
+
|
| 104 |
+
- Move CDN's to jsdelivr ([#88](https://github.com/AnswerDotAI/MonsterUI/pull/88)), thanks to [@curtis-allan](https://github.com/curtis-allan)
|
| 105 |
+
|
| 106 |
+
### Bugs Squashed
|
| 107 |
+
|
| 108 |
+
- Select htmx compatibility bug
|
| 109 |
+
- SVG Path Name Collision Issue in MonsterUI ([#85](https://github.com/AnswerDotAI/MonsterUI/issues/85))
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
## 1.0.13
|
| 113 |
+
|
| 114 |
+
### Bugs Squashed
|
| 115 |
+
|
| 116 |
+
- LabelCheckboxX ignores id set manually ([#80](https://github.com/AnswerDotAI/MonsterUI/issues/80))
|
| 117 |
+
- Select sending multiple values to HTMX
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
## 1.0.10
|
| 121 |
+
|
| 122 |
+
### New Features
|
| 123 |
+
|
| 124 |
+
- Cusom Themes support in ThemePicker ([#71](https://github.com/AnswerDotAI/MonsterUI/pull/71)), thanks to [@ndendic](https://github.com/ndendic)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
## 1.0.8
|
| 128 |
+
|
| 129 |
+
- Add lightbox and Insertable select
|
| 130 |
+
|
| 131 |
+
## 1.0.7
|
| 132 |
+
|
| 133 |
+
### New Features
|
| 134 |
+
|
| 135 |
+
- Add select kwargs to Select and LabelSelect ([#70](https://github.com/AnswerDotAI/MonsterUI/issues/70))
|
| 136 |
+
|
| 137 |
+
### Bugs Squashed
|
| 138 |
+
|
| 139 |
+
- Update component classes to align with Franken UI v2.0 ([#67](https://github.com/AnswerDotAI/MonsterUI/pull/67)), thanks to [@Zaseem-BIsquared](https://github.com/Zaseem-BIsquared)
|
| 140 |
+
- Updated ContainerT, CardT, and TableT class names to match the documented patterns from Franken UI v2.0 docs
|
| 141 |
+
|
| 142 |
+
## 1.0.6
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
### Bugs Squashed
|
| 146 |
+
|
| 147 |
+
- Update component classes to align with Franken UI v2.0 ([#67](https://github.com/AnswerDotAI/MonsterUI/pull/67)), thanks to [@Zaseem-BIsquared](https://github.com/Zaseem-BIsquared)
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
## 1.0.5
|
| 151 |
+
|
| 152 |
+
- Add Center component
|
| 153 |
+
|
| 154 |
+
## 1.0.4
|
| 155 |
+
|
| 156 |
+
### New Features
|
| 157 |
+
|
| 158 |
+
- Bug fix to correct theming conflict in theme initialization
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
## 1.0.2
|
| 162 |
+
|
| 163 |
+
- Bug fix to add dark mode theme selector to TW config, thanks to [@curtis-allan](https://github.com/curtis-allan)
|
| 164 |
+
|
| 165 |
+
## 1.0.1
|
| 166 |
+
|
| 167 |
+
- Theme bug fix not allowing theme changes to stick, thanks to [@zaseem-bisquared](https://github.com/Zaseem-BIsquared)
|
| 168 |
+
- Documentation bug fix on tutorial app, thanks to [@decherd](https://github.com/decherd)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
## 1.0.0
|
| 172 |
+
|
| 173 |
+
### New Features
|
| 174 |
+
|
| 175 |
+
- Migrated to use FrankenUI version 2.0 version
|
| 176 |
+
|
| 177 |
+
- Brand new simplified NavBar api, including enhanced sticky and scrollspy options, thanks to [@ohmeow](https://github.com/ohmeow) and [@curtis-allan](https://github.com/curtis-allan)
|
| 178 |
+
|
| 179 |
+
- Latex rendering enabled via katex ([#58](https://github.com/AnswerDotAI/MonsterUI/pull/58)), thanks to [@algal](https://github.com/algal)
|
| 180 |
+
|
| 181 |
+
- Add API References and Guides to llms.txt ([#54](https://github.com/AnswerDotAI/MonsterUI/issues/54))
|
| 182 |
+
|
| 183 |
+
- New Range component, which includes min/max range
|
| 184 |
+
|
| 185 |
+
- New center component thanks to inspiration from Carson of HTMX ([#52](https://github.com/AnswerDotAI/MonsterUI/issues/52))
|
| 186 |
+
|
| 187 |
+
- Better Theme Picker ([#51](https://github.com/AnswerDotAI/MonsterUI/issues/51))
|
| 188 |
+
|
| 189 |
+
- Upload Zone and Upload Button Components added ([#50](https://github.com/AnswerDotAI/MonsterUI/issues/50))
|
| 190 |
+
|
| 191 |
+
### Bugs Squashed
|
| 192 |
+
|
| 193 |
+
- fix: correct PaddingT class naming for padding variants ([#55](https://github.com/AnswerDotAI/MonsterUI/pull/55)), thanks to [@Zaseem-BIsquared](https://github.com/Zaseem-BIsquared)
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
## 0.0.34
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
### Bugs Squashed
|
| 200 |
+
|
| 201 |
+
- Table markdown rendering bug fix ([#46](https://github.com/AnswerDotAI/MonsterUI/issues/46))
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
## 0.0.33
|
| 205 |
+
|
| 206 |
+
### New Features
|
| 207 |
+
|
| 208 |
+
- Add subtitle function for common semantic styling option ([#44](https://github.com/AnswerDotAI/MonsterUI/pull/44))
|
| 209 |
+
|
| 210 |
+
- Add semantic text styling to docs examples ([#42](https://github.com/AnswerDotAI/MonsterUI/pull/42))
|
| 211 |
+
|
| 212 |
+
- Created higher level Navbars function for auto-responsive collapse to mobile and easier interface ([#33](https://github.com/AnswerDotAI/MonsterUI/issues/33)), thanks to @curtis-allan
|
| 213 |
+
|
| 214 |
+
- Added Scrollspy to Nav and NavBar and example that demonstrates it ([#31](https://github.com/AnswerDotAI/MonsterUI/issues/31)), thanks to @99ch
|
| 215 |
+
|
| 216 |
+
- Added Highlight JS integration to headers ([#28](https://github.com/AnswerDotAI/MonsterUI/issues/28)), thanks to @99ch
|
| 217 |
+
|
| 218 |
+
### Bugs Squashed
|
| 219 |
+
|
| 220 |
+
- Improve defaults for Navbar when components are passed so it doesn't override styling specified toby user ([#43](https://github.com/AnswerDotAI/MonsterUI/pull/43))
|
| 221 |
+
|
| 222 |
+
|
MonsterUI/CHANGELOG.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Release notes
|
| 2 |
+
|
| 3 |
+
<!-- do not remove -->
|
| 4 |
+
|
| 5 |
+
## 1.0.29
|
| 6 |
+
|
| 7 |
+
### Bugs Squashed
|
| 8 |
+
|
| 9 |
+
- `apply_classes` wraps some elements in html/body/head tags ([#135](https://github.com/AnswerDotAI/MonsterUI/issues/135))
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
## 1.0.28
|
| 13 |
+
|
| 14 |
+
### New Features
|
| 15 |
+
|
| 16 |
+
- allow optional renderer ([#134](https://github.com/AnswerDotAI/MonsterUI/pull/134))
|
| 17 |
+
|
| 18 |
+
### Bugs Squashed
|
| 19 |
+
|
| 20 |
+
- `render_md` doesn't render images with data uris when `img_dir` is set ([#132](https://github.com/AnswerDotAI/MonsterUI/issues/132))
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
## 1.0.26
|
| 24 |
+
|
| 25 |
+
### Bugs Squashed
|
| 26 |
+
|
| 27 |
+
- fix `render_md` non http/s urls ([#133](https://github.com/AnswerDotAI/MonsterUI/pull/133)), thanks to [@comhar](https://github.com/comhar)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
## 1.0.25
|
| 31 |
+
|
| 32 |
+
### Bugs Squashed
|
| 33 |
+
|
| 34 |
+
- remove modal on close ([#130](https://github.com/AnswerDotAI/MonsterUI/pull/130)), thanks to [@comhar](https://github.com/comhar)
|
| 35 |
+
- Buttons not working in Modal added to the DOM by HTMX. ([#126](https://github.com/AnswerDotAI/MonsterUI/issues/126))
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
## 1.0.24
|
| 39 |
+
|
| 40 |
+
### New Features
|
| 41 |
+
|
| 42 |
+
- use uikit to initialise and open modals inserted with htmx ([#127](https://github.com/AnswerDotAI/MonsterUI/pull/127)), thanks to [@comhar](https://github.com/comhar)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
## 1.0.23
|
| 46 |
+
|
| 47 |
+
### Bugs Squashed
|
| 48 |
+
|
| 49 |
+
- fragment md parsing does not work ([#124](https://github.com/AnswerDotAI/MonsterUI/issues/124))
|
| 50 |
+
- llms.txt showing "internal server" errors ([#122](https://github.com/AnswerDotAI/MonsterUI/issues/122))
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
## 1.0.22
|
| 54 |
+
|
| 55 |
+
- Create custom themes
|
| 56 |
+
|
| 57 |
+
### Bugs Squashed
|
| 58 |
+
|
| 59 |
+
- Unicode strings with encoding declaration are not supported ([#123](https://github.com/AnswerDotAI/MonsterUI/issues/123))
|
| 60 |
+
- Update DropDownNavContainer class to remove unnecessary width class ([#114](https://github.com/AnswerDotAI/MonsterUI/pull/114)), thanks to [@ndendic](https://github.com/ndendic)
|
| 61 |
+
- fix font weight ordering ([#113](https://github.com/AnswerDotAI/MonsterUI/pull/113)), thanks to [@comhar](https://github.com/comhar)
|
| 62 |
+
- FieldSet inputs in Form are misaligned when monsterui is active ([#87](https://github.com/AnswerDotAI/MonsterUI/issues/87))
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
## 1.0.21
|
| 66 |
+
|
| 67 |
+
### New Features
|
| 68 |
+
|
| 69 |
+
- The new `ApexChart` component can create line charts, pie charts and [more](https://monsterui.answer.ai/api_ref/docs_charts). ([#110](https://github.com/AnswerDotAI/MonsterUI/pull/110)), thanks to [@ndendic](https://github.com/ndendic)
|
| 70 |
+
- Toasts now disappear automatically after 5 seconds. You can adjust the duration by using the `dur` field (e.g. `Toast('My Toast', dur=10.0)`). ([#109](https://github.com/AnswerDotAI/MonsterUI/pull/109)), thanks to [@comhar](https://github.com/comhar)
|
| 71 |
+
|
| 72 |
+
### Breaking Change
|
| 73 |
+
|
| 74 |
+
- Katex is no longer included in the default headers. To include it set `katex=True` when calling `.headers`. (i.e. `Theme.slate.headers(katex=True)`). ([#105](https://github.com/AnswerDotAI/MonsterUI/pull/105)), thanks to [@comhar](https://github.com/comhar)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
## 1.0.20
|
| 78 |
+
|
| 79 |
+
### New Features
|
| 80 |
+
|
| 81 |
+
- Add `icon` header param to optionally not bring in icons js lib ([#99](https://github.com/AnswerDotAI/MonsterUI/issues/99))
|
| 82 |
+
|
| 83 |
+
### Bugs Squashed
|
| 84 |
+
|
| 85 |
+
- added fh. to unqid ([#98](https://github.com/AnswerDotAI/MonsterUI/pull/98)), thanks to [@MorsCerta-crypto](https://github.com/MorsCerta-crypto)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
## 1.0.19
|
| 89 |
+
|
| 90 |
+
### New Features
|
| 91 |
+
|
| 92 |
+
- Add accordion component ([#94](https://github.com/AnswerDotAI/MonsterUI/pull/94)), thanks to [@MichlF](https://github.com/MichlF)
|
| 93 |
+
- Fix theme logic ([#93](https://github.com/AnswerDotAI/MonsterUI/pull/93)), thanks to [@curtis-allan](https://github.com/curtis-allan)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
## 1.0.18
|
| 97 |
+
|
| 98 |
+
- Hotfix accidental deletion of label select :(
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
## 1.0.16
|
| 102 |
+
|
| 103 |
+
### Bugs Squashed
|
| 104 |
+
|
| 105 |
+
- Select not properly checking presence of hx-trigger
|
| 106 |
+
|
| 107 |
+
## 1.0.14
|
| 108 |
+
|
| 109 |
+
### New Features
|
| 110 |
+
|
| 111 |
+
- Move CDN's to jsdelivr ([#88](https://github.com/AnswerDotAI/MonsterUI/pull/88)), thanks to [@curtis-allan](https://github.com/curtis-allan)
|
| 112 |
+
|
| 113 |
+
### Bugs Squashed
|
| 114 |
+
|
| 115 |
+
- Select htmx compatibility bug
|
| 116 |
+
- SVG Path Name Collision Issue in MonsterUI ([#85](https://github.com/AnswerDotAI/MonsterUI/issues/85))
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
## 1.0.13
|
| 120 |
+
|
| 121 |
+
### Bugs Squashed
|
| 122 |
+
|
| 123 |
+
- LabelCheckboxX ignores id set manually ([#80](https://github.com/AnswerDotAI/MonsterUI/issues/80))
|
| 124 |
+
- Select sending multiple values to HTMX
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
## 1.0.10
|
| 128 |
+
|
| 129 |
+
### New Features
|
| 130 |
+
|
| 131 |
+
- Cusom Themes support in ThemePicker ([#71](https://github.com/AnswerDotAI/MonsterUI/pull/71)), thanks to [@ndendic](https://github.com/ndendic)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
## 1.0.8
|
| 135 |
+
|
| 136 |
+
- Add lightbox and Insertable select
|
| 137 |
+
|
| 138 |
+
## 1.0.7
|
| 139 |
+
|
| 140 |
+
### New Features
|
| 141 |
+
|
| 142 |
+
- Add select kwargs to Select and LabelSelect ([#70](https://github.com/AnswerDotAI/MonsterUI/issues/70))
|
| 143 |
+
|
| 144 |
+
### Bugs Squashed
|
| 145 |
+
|
| 146 |
+
- Update component classes to align with Franken UI v2.0 ([#67](https://github.com/AnswerDotAI/MonsterUI/pull/67)), thanks to [@Zaseem-BIsquared](https://github.com/Zaseem-BIsquared)
|
| 147 |
+
- Updated ContainerT, CardT, and TableT class names to match the documented patterns from Franken UI v2.0 docs
|
| 148 |
+
|
| 149 |
+
## 1.0.6
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
### Bugs Squashed
|
| 153 |
+
|
| 154 |
+
- Update component classes to align with Franken UI v2.0 ([#67](https://github.com/AnswerDotAI/MonsterUI/pull/67)), thanks to [@Zaseem-BIsquared](https://github.com/Zaseem-BIsquared)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
## 1.0.5
|
| 158 |
+
|
| 159 |
+
- Add Center component
|
| 160 |
+
|
| 161 |
+
## 1.0.4
|
| 162 |
+
|
| 163 |
+
### New Features
|
| 164 |
+
|
| 165 |
+
- Bug fix to correct theming conflict in theme initialization
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
## 1.0.2
|
| 169 |
+
|
| 170 |
+
- Bug fix to add dark mode theme selector to TW config, thanks to [@curtis-allan](https://github.com/curtis-allan)
|
| 171 |
+
|
| 172 |
+
## 1.0.1
|
| 173 |
+
|
| 174 |
+
- Theme bug fix not allowing theme changes to stick, thanks to [@zaseem-bisquared](https://github.com/Zaseem-BIsquared)
|
| 175 |
+
- Documentation bug fix on tutorial app, thanks to [@decherd](https://github.com/decherd)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
## 1.0.0
|
| 179 |
+
|
| 180 |
+
### New Features
|
| 181 |
+
|
| 182 |
+
- Migrated to use FrankenUI version 2.0 version
|
| 183 |
+
|
| 184 |
+
- Brand new simplified NavBar api, including enhanced sticky and scrollspy options, thanks to [@ohmeow](https://github.com/ohmeow) and [@curtis-allan](https://github.com/curtis-allan)
|
| 185 |
+
|
| 186 |
+
- Latex rendering enabled via katex ([#58](https://github.com/AnswerDotAI/MonsterUI/pull/58)), thanks to [@algal](https://github.com/algal)
|
| 187 |
+
|
| 188 |
+
- Add API References and Guides to llms.txt ([#54](https://github.com/AnswerDotAI/MonsterUI/issues/54))
|
| 189 |
+
|
| 190 |
+
- New Range component, which includes min/max range
|
| 191 |
+
|
| 192 |
+
- New center component thanks to inspiration from Carson of HTMX ([#52](https://github.com/AnswerDotAI/MonsterUI/issues/52))
|
| 193 |
+
|
| 194 |
+
- Better Theme Picker ([#51](https://github.com/AnswerDotAI/MonsterUI/issues/51))
|
| 195 |
+
|
| 196 |
+
- Upload Zone and Upload Button Components added ([#50](https://github.com/AnswerDotAI/MonsterUI/issues/50))
|
| 197 |
+
|
| 198 |
+
### Bugs Squashed
|
| 199 |
+
|
| 200 |
+
- fix: correct PaddingT class naming for padding variants ([#55](https://github.com/AnswerDotAI/MonsterUI/pull/55)), thanks to [@Zaseem-BIsquared](https://github.com/Zaseem-BIsquared)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
## 0.0.34
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
### Bugs Squashed
|
| 207 |
+
|
| 208 |
+
- Table markdown rendering bug fix ([#46](https://github.com/AnswerDotAI/MonsterUI/issues/46))
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
## 0.0.33
|
| 212 |
+
|
| 213 |
+
### New Features
|
| 214 |
+
|
| 215 |
+
- Add subtitle function for common semantic styling option ([#44](https://github.com/AnswerDotAI/MonsterUI/pull/44))
|
| 216 |
+
|
| 217 |
+
- Add semantic text styling to docs examples ([#42](https://github.com/AnswerDotAI/MonsterUI/pull/42))
|
| 218 |
+
|
| 219 |
+
- Created higher level Navbars function for auto-responsive collapse to mobile and easier interface ([#33](https://github.com/AnswerDotAI/MonsterUI/issues/33)), thanks to @curtis-allan
|
| 220 |
+
|
| 221 |
+
- Added Scrollspy to Nav and NavBar and example that demonstrates it ([#31](https://github.com/AnswerDotAI/MonsterUI/issues/31)), thanks to @99ch
|
| 222 |
+
|
| 223 |
+
- Added Highlight JS integration to headers ([#28](https://github.com/AnswerDotAI/MonsterUI/issues/28)), thanks to @99ch
|
| 224 |
+
|
| 225 |
+
### Bugs Squashed
|
| 226 |
+
|
| 227 |
+
- Improve defaults for Navbar when components are passed so it doesn't override styling specified toby user ([#43](https://github.com/AnswerDotAI/MonsterUI/pull/43))
|
| 228 |
+
|
| 229 |
+
|
MonsterUI/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CONTRIBUTING
|
| 2 |
+
|
| 3 |
+
## Library Contributions
|
| 4 |
+
|
| 5 |
+
> The code for all components in the library in the Jupyter notebooks (.ipynb files). Edit those, not the .py files.
|
| 6 |
+
|
| 7 |
+
This is an [nbdev](https://nbdev.fast.ai/) library. The notebooks are located in `nbs`.
|
| 8 |
+
|
| 9 |
+
### Exporting the Modules
|
| 10 |
+
|
| 11 |
+
You can use `nbdev_export` to export the notebooks to the library directory, `monsterui`.
|
| 12 |
+
|
| 13 |
+
### Cleaning NB metadad
|
| 14 |
+
|
| 15 |
+
You can use `nbdev_clean` to clean the nb metadata from the .py files.
|
| 16 |
+
|
| 17 |
+
## Docs Contributions
|
| 18 |
+
|
| 19 |
+
The docs are run using [FastHTML](https://fastht.ml/) and can be run locally with:
|
| 20 |
+
|
| 21 |
+
```bash
|
| 22 |
+
cd docs
|
| 23 |
+
pip install -r requirements.txt
|
| 24 |
+
python main.py
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
This will start up the MonsterUI documentation site, which is a FastHTML app. Then you can see the site locally at http://localhost:5001/
|
MonsterUI/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright 2022, fastai
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
MonsterUI/MANIFEST.in
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
include settings.ini
|
| 2 |
+
include LICENSE
|
| 3 |
+
include CONTRIBUTING.md
|
| 4 |
+
include README.md
|
| 5 |
+
recursive-exclude * __pycache__
|
MonsterUI/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MonsterUI
|
| 2 |
+
|
| 3 |
+
MonsterUI is a UI framework for FastHTML for building beautiful web interfaces with minimal code. It combines the simplicity of Python with the power of Tailwind. Perfect for data scientists, ML engineers, and developers who want to quickly turn their Python code into polished web apps without the complexity of traditional UI frameworks. Follows semantic HTML patterns when possible.
|
| 4 |
+
|
| 5 |
+
MonsterUI adds the following Tailwind-based libraries [Franken UI](https://franken-ui.dev/) and [DaisyUI](https://daisyui.com/) to FastHTML, as well as Python's [Mistletoe](https://github.com/miyuchina/mistletoe) for Markdown, [HighlightJS](https://highlightjs.org/) for code highlighting, and [Katex](https://katex.org/) for latex support.
|
| 6 |
+
|
| 7 |
+
# Getting Started
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
## Installation
|
| 11 |
+
|
| 12 |
+
To install this library, uses
|
| 13 |
+
|
| 14 |
+
`pip install MonsterUI`
|
| 15 |
+
|
| 16 |
+
## Getting Started
|
| 17 |
+
|
| 18 |
+
### TLDR
|
| 19 |
+
|
| 20 |
+
Run `python file.py` on this to start:
|
| 21 |
+
|
| 22 |
+
``` python
|
| 23 |
+
from fasthtml.common import *
|
| 24 |
+
from monsterui.all import *
|
| 25 |
+
|
| 26 |
+
# Choose a theme color (blue, green, red, etc)
|
| 27 |
+
hdrs = Theme.blue.headers()
|
| 28 |
+
|
| 29 |
+
# Create your app with the theme
|
| 30 |
+
app, rt = fast_app(hdrs=hdrs)
|
| 31 |
+
|
| 32 |
+
@rt
|
| 33 |
+
def index():
|
| 34 |
+
socials = (('github','https://github.com/AnswerDotAI/MonsterUI'),
|
| 35 |
+
('twitter','https://twitter.com/isaac_flath/'),
|
| 36 |
+
('linkedin','https://www.linkedin.com/in/isaacflath/'))
|
| 37 |
+
return Titled("Your First App",
|
| 38 |
+
Card(
|
| 39 |
+
H1("Welcome!"),
|
| 40 |
+
P("Your first MonsterUI app", cls=TextPresets.muted_sm),
|
| 41 |
+
P("I'm excited to see what you build with MonsterUI!"),
|
| 42 |
+
footer=DivLAligned(*[UkIconLink(icon,href=url) for icon,url in socials])))
|
| 43 |
+
|
| 44 |
+
serve()
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
## LLM context files
|
| 48 |
+
|
| 49 |
+
Using LLMs for development is a best practice way to get started and
|
| 50 |
+
explore. While LLMs cannot code for you, they can be helpful assistants.
|
| 51 |
+
You must check, refactor, test, and vet any code any LLM generates for
|
| 52 |
+
you - but they are helpful productivity tools. Take a look inside the
|
| 53 |
+
`llms.txt` file to see links to particularly useful context files!
|
| 54 |
+
|
| 55 |
+
- [llms.txt](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/llms.txt): Links to what is included
|
| 56 |
+
- [llms-ctx.txt](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/llms-ctx.txt): MonsterUI Documentation Pages
|
| 57 |
+
- [API list](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/apilist.txt): API list for MonsterUI (included in llms-ctx.txt)
|
| 58 |
+
- [llms-ctx-full.txt](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/llms-ctx-full.txt): Full context that includes all api reference pages as markdown
|
| 59 |
+
|
| 60 |
+
In addition you can add `/md` (for markdown) to a url to get a markdown representation and `/rmd` for rendered markdown representation (nice for looking to see what would be put into context.
|
| 61 |
+
|
| 62 |
+
### Step by Step
|
| 63 |
+
|
| 64 |
+
To get started, check out:
|
| 65 |
+
|
| 66 |
+
1. Start by importing the modules as follows:
|
| 67 |
+
|
| 68 |
+
``` python
|
| 69 |
+
from fasthtml.common import *
|
| 70 |
+
from monsterui.all import *
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
2. Instantiate the app with the MonsterUI headers
|
| 74 |
+
|
| 75 |
+
``` python
|
| 76 |
+
app = FastHTML(hdrs=Theme.blue.headers())
|
| 77 |
+
|
| 78 |
+
# Alternatively, using the fast_app method
|
| 79 |
+
app, rt = fast_app(hdrs=Theme.slate.headers())
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
> *The color option can be any of the theme options available out of the
|
| 83 |
+
> box*
|
| 84 |
+
|
| 85 |
+
> `katex` and `highlightjs` are not included by default. To include them set `katex=True` or `highlightjs=True` when calling `.headers`. (i.e. `Theme.slate.headers(katex=True)`)*
|
| 86 |
+
|
| 87 |
+
From here, you can explore the API Reference & examples to see how to
|
| 88 |
+
implement the components. You can also check out these demo videos to as
|
| 89 |
+
a quick start guide:
|
| 90 |
+
|
| 91 |
+
- MonsterUI [documentation page and Tutorial
|
| 92 |
+
app](https://monsterui.answer.ai/tutorial_app)
|
| 93 |
+
- Isaac & Hamel : [Building his website’s team
|
| 94 |
+
page](https://youtu.be/22Jn46-mmM0)
|
| 95 |
+
- Isaac & Audrey : [Building a blog](https://youtu.be/gVWAsywxLXE)
|
| 96 |
+
- Isaac : [Building a blog](https://youtu.be/22NJgfAqgko)
|
| 97 |
+
|
| 98 |
+
More resources and improvements to the documentation will be added here
|
| 99 |
+
soon!
|
MonsterUI/__pycache__/testnavbar.cpython-312.pyc
ADDED
|
Binary file (989 Bytes). View file
|
|
|
MonsterUI/docs/.plash
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
export PLASH_APP_NAME=monsterui.answer.ai
|
MonsterUI/docs/MonsterUI.jpg
ADDED
|
Git LFS Details
|
MonsterUI/docs/api_reference/api_reference.py
ADDED
|
@@ -0,0 +1,1211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Reference to all FrankenUI Components"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
from monsterui.all import *
|
| 5 |
+
# from nbdev.showdoc import *
|
| 6 |
+
from utils import create_flippable_card, fn2code_string
|
| 7 |
+
from enum import EnumType
|
| 8 |
+
from collections.abc import Callable
|
| 9 |
+
|
| 10 |
+
'''
|
| 11 |
+
Any variable starting with docs_ is a function that generates a section of the API reference.
|
| 12 |
+
|
| 13 |
+
These are automatically added to the docs sidebar so you don't have to do anything other than add the function using create_doc_section.
|
| 14 |
+
'''
|
| 15 |
+
from inspect import signature, getdoc, getsourcefile, getsourcelines
|
| 16 |
+
|
| 17 |
+
# Utilities
|
| 18 |
+
def get_github_url(func):
|
| 19 |
+
"Create GitHub URL for function, assuming AnswerDotAI/MonsterUI repo"
|
| 20 |
+
file = getsourcefile(func).split('MonsterUI/')[-1]
|
| 21 |
+
line = getsourcelines(func)[-1]
|
| 22 |
+
file = file.replace('/opt/venv/lib/python3.12/site-packages/','')
|
| 23 |
+
return f"https://github.com/AnswerDotAI/MonsterUI/blob/main/{file}#L{line}"
|
| 24 |
+
|
| 25 |
+
def get_github_url(func):
|
| 26 |
+
"Create GitHub URL for function, assuming AnswerDotAI/MonsterUI repo"
|
| 27 |
+
try:
|
| 28 |
+
file = getsourcefile(func)
|
| 29 |
+
line = getsourcelines(func)[-1]
|
| 30 |
+
|
| 31 |
+
# Get path after monsterui/ (case insensitive)
|
| 32 |
+
file = file.lower()
|
| 33 |
+
if 'monsterui/' in file:
|
| 34 |
+
file = file.split('monsterui/')[-1]
|
| 35 |
+
elif 'site-packages/' in file:
|
| 36 |
+
file = file.split('site-packages/')[-1]
|
| 37 |
+
|
| 38 |
+
return f"https://github.com/AnswerDotAI/MonsterUI/blob/main/monsterui/{file}#L{line}"
|
| 39 |
+
except:
|
| 40 |
+
return None
|
| 41 |
+
|
| 42 |
+
from fastcore.docments import docments, docstring, get_name
|
| 43 |
+
def show_doc(func) -> str:
|
| 44 |
+
"Convert a Google-style docstring to markdown"
|
| 45 |
+
params = docments(func, args_kwargs=True)
|
| 46 |
+
funcname = get_name(func)
|
| 47 |
+
doc = docstring(func)
|
| 48 |
+
par, ret = None, None
|
| 49 |
+
if params:
|
| 50 |
+
par = Div(Strong('Params'),
|
| 51 |
+
Ul(*[Li(render_md(f"`{name}` {desc if desc else ''}",class_map_mods={'p':'leading-relaxed'}), cls='') for name, desc in params.items() if name != 'return'], cls='uk-list-disc space-y-2 mb-6 ml-6'))
|
| 52 |
+
if 'return' in params and params['return']: ret = render_md(f"**Returns:** {params['return']}")
|
| 53 |
+
return Div(
|
| 54 |
+
DivFullySpaced(
|
| 55 |
+
render_md(f"### {funcname}"),
|
| 56 |
+
A("Source", href=get_github_url(func), cls='text-primary hover:text-primary-focus underline')),
|
| 57 |
+
Pre(Code(f"{funcname}{signature(func)}",
|
| 58 |
+
cls='hljs language-python px-1 block overflow-x-auto'),
|
| 59 |
+
cls='bg-base-200 rounded-lg p-4 mb-6'),
|
| 60 |
+
Div(Blockquote(render_md(doc), cls='pl-4 border-l-4 border-primary mb-6'), par, ret, cls='ml-10'))
|
| 61 |
+
|
| 62 |
+
def enum_to_html_table(enum_class):
|
| 63 |
+
"Creates a compact multi-column table display for enum documentation"
|
| 64 |
+
items = list(enum_class.__members__.items())
|
| 65 |
+
n_cols = min(4, max(2, round((len(items) ** 0.5))))
|
| 66 |
+
|
| 67 |
+
# Create header/cell pairs with borders
|
| 68 |
+
def make_pair(opt, val, i):
|
| 69 |
+
border = 'border-l border-base-300 pl-4' if i > 0 else ''
|
| 70 |
+
return [Th('Option', cls=border), Th('Value')] if opt == 'header' else [Td(opt, cls=border), Td(val)]
|
| 71 |
+
|
| 72 |
+
# Build rows with padding for incomplete final row
|
| 73 |
+
rows = []
|
| 74 |
+
for i in range(0, len(items), n_cols):
|
| 75 |
+
cells = []
|
| 76 |
+
for j in range(n_cols):
|
| 77 |
+
name, val = items[i + j] if i + j < len(items) else ('', '')
|
| 78 |
+
cells.extend(make_pair(name, val.value if val else '', j))
|
| 79 |
+
rows.append(Tr(*cells))
|
| 80 |
+
|
| 81 |
+
return Div(
|
| 82 |
+
Hr(cls='uk-divider-icon my-2'),
|
| 83 |
+
DivFullySpaced(H3(enum_class.__name__, cls='my-2'), P(I(enum_class.__doc__), cls='text-sm')),
|
| 84 |
+
Table(
|
| 85 |
+
Thead(Tr(*make_pair('header', '', 0) * n_cols)),
|
| 86 |
+
Tbody(*rows),
|
| 87 |
+
cls=(TableT.hover, 'uk-table-small uk-table-justify uk-table-middle')))
|
| 88 |
+
|
| 89 |
+
def render_content(c):
|
| 90 |
+
"Renders content by type"
|
| 91 |
+
if isinstance(c, str): return render_md(c) # Strings are rendered as markdown
|
| 92 |
+
elif isinstance(c, EnumType): return enum_to_html_table(c) # Enums are rendered as tables
|
| 93 |
+
elif isinstance(c, FT): return c # FastHTML tags are rendered as themselves
|
| 94 |
+
elif isinstance(c, tuple): # Tuples are rendered as cards with source and output that can be flipped
|
| 95 |
+
extra_cls = c[2] if len(tuple(c)) == 3 else None
|
| 96 |
+
return create_flippable_card(c[0], c[1], extra_cls)
|
| 97 |
+
elif isinstance(c, Callable): # Callables are rendered as documentation via show_doc
|
| 98 |
+
return show_doc(c)
|
| 99 |
+
# _html = show_doc(c, renderer=BasicHtmlRenderer)._repr_html_()
|
| 100 |
+
# return NotStr(apply_classes(_html, class_map_mods={"table":'uk-table uk-table-hover uk-table-small'}))
|
| 101 |
+
else: return c
|
| 102 |
+
|
| 103 |
+
def create_doc_section(*content, title):
|
| 104 |
+
return lambda: Div(Container(*map(render_content, content)))
|
| 105 |
+
|
| 106 |
+
def string2code_string(code: str) -> tuple: return eval(code), code
|
| 107 |
+
|
| 108 |
+
# Sliders
|
| 109 |
+
|
| 110 |
+
def ex_sliders_1():
|
| 111 |
+
return Slider(*[Img(src=f'https://picsum.photos/200/200?random={i}') for i in range(10)])
|
| 112 |
+
|
| 113 |
+
def ex_sliders_2():
|
| 114 |
+
def _card(i): return Card(H3(f'Card {i}'), P(f'Card {i} content'))
|
| 115 |
+
return Slider(*[_card(i) for i in range(10)])
|
| 116 |
+
|
| 117 |
+
def ex_sliders_3():
|
| 118 |
+
def _card(i): return Card(H3(f'Card {i}'), P(f'Card {i} content'))
|
| 119 |
+
return Slider(*[_card(i) for i in range(10)], items_cls='gap-10', uk_slider='autoplay: true; autoplay-interval: 1000')
|
| 120 |
+
|
| 121 |
+
docs_sliders = create_doc_section(
|
| 122 |
+
H1("Carousel Sliders API Reference"),
|
| 123 |
+
"Here is a simple example of a slider:",
|
| 124 |
+
fn2code_string(ex_sliders_1),
|
| 125 |
+
"Here is a slider with cards:",
|
| 126 |
+
fn2code_string(ex_sliders_2),
|
| 127 |
+
"Here is a slider with cards and autoplay:",
|
| 128 |
+
fn2code_string(ex_sliders_3),
|
| 129 |
+
"Typically you want to use the `Slider` component, but if you need more control you can use the `SliderContainer`, `SliderItems`, and `SliderNav` components.",
|
| 130 |
+
Slider,
|
| 131 |
+
SliderContainer,
|
| 132 |
+
SliderItems,
|
| 133 |
+
SliderNav,
|
| 134 |
+
title="Sliders")
|
| 135 |
+
|
| 136 |
+
# Accordions
|
| 137 |
+
|
| 138 |
+
def ex_accordion_1():
|
| 139 |
+
return Div(
|
| 140 |
+
H2("Accordion Header"),
|
| 141 |
+
Accordion(
|
| 142 |
+
AccordionItem(
|
| 143 |
+
"Section 1",
|
| 144 |
+
P("Content for the first section."),
|
| 145 |
+
P("More content here."),
|
| 146 |
+
),
|
| 147 |
+
AccordionItem(
|
| 148 |
+
"Section 2",
|
| 149 |
+
P("Content for the second section."),
|
| 150 |
+
Label("A label inside!"),
|
| 151 |
+
li_kwargs={"id": "section-2"},
|
| 152 |
+
),
|
| 153 |
+
AccordionItem(
|
| 154 |
+
"Section 3 - The last one!", P("Content for the third section.")
|
| 155 |
+
),
|
| 156 |
+
multiple=False,
|
| 157 |
+
animation=True,
|
| 158 |
+
),
|
| 159 |
+
),
|
| 160 |
+
|
| 161 |
+
def ex_accordion_2():
|
| 162 |
+
return Div(
|
| 163 |
+
H2("Accordion Header"),
|
| 164 |
+
Accordion(
|
| 165 |
+
AccordionItem(
|
| 166 |
+
"Section 1",
|
| 167 |
+
P("Content for the first section."),
|
| 168 |
+
P("More content here."),
|
| 169 |
+
open=True,
|
| 170 |
+
),
|
| 171 |
+
AccordionItem(
|
| 172 |
+
"Section 2",
|
| 173 |
+
P("Content for the second section."),
|
| 174 |
+
Label("A label inside!"),
|
| 175 |
+
li_kwargs={"id": "section-2"},
|
| 176 |
+
),
|
| 177 |
+
AccordionItem(
|
| 178 |
+
"Section 3 - The last one!", P("Content for the third section.")
|
| 179 |
+
),
|
| 180 |
+
multiple=True,
|
| 181 |
+
animation=True,
|
| 182 |
+
),
|
| 183 |
+
),
|
| 184 |
+
|
| 185 |
+
def ex_accordion_3():
|
| 186 |
+
return Div(
|
| 187 |
+
H2("Accordion Header"),
|
| 188 |
+
Accordion(
|
| 189 |
+
AccordionItem(
|
| 190 |
+
"Section 1",
|
| 191 |
+
P("Content for the first section."),
|
| 192 |
+
P("More content here."),
|
| 193 |
+
),
|
| 194 |
+
AccordionItem(
|
| 195 |
+
"Section 2",
|
| 196 |
+
P("Content for the second section."),
|
| 197 |
+
Label("A label inside!"),
|
| 198 |
+
li_kwargs={"id": "section-2"},
|
| 199 |
+
),
|
| 200 |
+
AccordionItem(
|
| 201 |
+
"Section 3 - The last one!", P("Content for the third section.")
|
| 202 |
+
),
|
| 203 |
+
multiple=False,
|
| 204 |
+
animation=False,
|
| 205 |
+
),
|
| 206 |
+
),
|
| 207 |
+
|
| 208 |
+
docs_accordion_link = create_doc_section(
|
| 209 |
+
H1("Accordion API Reference"),
|
| 210 |
+
Div(id='accordion'), # for linking to in release post
|
| 211 |
+
H3("Example Accordions"),
|
| 212 |
+
P("A simple accordion with fluid collapsing and expanding animation where only a single Section can be exanded at any time."),
|
| 213 |
+
fn2code_string(ex_accordion_1),
|
| 214 |
+
P("An accordion with fluid collapsing and expanding animation where one section is already expanded at startup and multiple section can be expanded at any time."),
|
| 215 |
+
fn2code_string(ex_accordion_2),
|
| 216 |
+
P("An accordion with no collapsing and expanding animation where only a single Section can be exanded at any time."),
|
| 217 |
+
fn2code_string(ex_accordion_3),
|
| 218 |
+
H3("API Reference"),
|
| 219 |
+
Accordion,
|
| 220 |
+
AccordionItem,
|
| 221 |
+
title="Accordion")
|
| 222 |
+
|
| 223 |
+
# Buttons
|
| 224 |
+
|
| 225 |
+
def ex_buttons():
|
| 226 |
+
return Grid(
|
| 227 |
+
Button("Default"),
|
| 228 |
+
Button("Primary", cls=ButtonT.primary),
|
| 229 |
+
Button("Secondary", cls=ButtonT.secondary),
|
| 230 |
+
Button("Danger", cls=ButtonT.destructive),
|
| 231 |
+
Button("Text", cls=ButtonT.text),
|
| 232 |
+
Button("Link", cls=ButtonT.link),
|
| 233 |
+
Button("Ghost", cls=ButtonT.ghost),
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
def ex_links():
|
| 237 |
+
return Div(cls='space-x-4')(
|
| 238 |
+
A('Default Link'),
|
| 239 |
+
A('Muted Link', cls=AT.muted),
|
| 240 |
+
A('Text Link', cls=AT.text),
|
| 241 |
+
A('Reset Link', cls=AT.reset),
|
| 242 |
+
A('Primary Link', cls=AT.primary),
|
| 243 |
+
A('Classic Link', cls=AT.classic),)
|
| 244 |
+
|
| 245 |
+
docs_button_link = create_doc_section(
|
| 246 |
+
H1("Buttons & Links API Reference"),
|
| 247 |
+
Div(id='button'), # for linking to in release post
|
| 248 |
+
fn2code_string(ex_buttons),
|
| 249 |
+
fn2code_string(ex_links),
|
| 250 |
+
Button,
|
| 251 |
+
ButtonT,
|
| 252 |
+
AT,
|
| 253 |
+
title="Buttons & Links")
|
| 254 |
+
|
| 255 |
+
# Theme
|
| 256 |
+
|
| 257 |
+
def ex_theme_switcher():
|
| 258 |
+
return ThemePicker()
|
| 259 |
+
|
| 260 |
+
docs_theme_headers = create_doc_section(
|
| 261 |
+
H1("Theme and Headers API Reference"),
|
| 262 |
+
"""
|
| 263 |
+
To get headers with a default theme use `hdrs=Theme.<color>.headers()`. For example for the blue theme you would use `hdrs=Theme.blue.headers()`. The theme integrated together different frameworks and allows tailwind, FrankenUI, HighlighJS, and DaisyUI components to work well together.
|
| 264 |
+
|
| 265 |
+
Tailwind, FrankenUI and DaisyUI are imported by default. You must use DaisyUI headers to use anything in the `daisy` module, and FrankenUI headers to use anything in the `franken` module.
|
| 266 |
+
|
| 267 |
+
HighlightJS is not added by default, but you can add it by setting `highlightjs=True` in the headers function. The `render_md` function will use HighlightJS for code blocks.
|
| 268 |
+
|
| 269 |
+
Theme options are:""",
|
| 270 |
+
Card(Grid(map(P,Theme)),cls='mb-8'),
|
| 271 |
+
H3("Theme Picker", id='theme'),
|
| 272 |
+
fn2code_string(ex_theme_switcher),
|
| 273 |
+
ThemePicker,
|
| 274 |
+
H3("Custom Themes"),
|
| 275 |
+
render_md("""
|
| 276 |
+
1. You can use [this theme](https://github.com/AnswerDotAI/MonsterUI/blob/main/docs/custom_theme.css) as a starting point.
|
| 277 |
+
2. Add the theme to your headers as a link like this `Link(rel="stylesheet", href="/custom_theme.css", type="text/css")`
|
| 278 |
+
3. Then add the theme to the `ThemePicker` component. For example `ThemePicker(custom_themes=[('Grass', '#10b981')])`
|
| 279 |
+
"""),
|
| 280 |
+
"Themes are controlled with `bg-background text-foreground` classes on the `Body` tag. `fast_app` and `FastHTML` will do this for you automatically so you typically do not have to do anything",
|
| 281 |
+
fast_app,
|
| 282 |
+
FastHTML,
|
| 283 |
+
|
| 284 |
+
Blockquote(P("Users have said ", A("this site", href="https://ui.jln.dev/"), " is helpful in creating your own themes.")),
|
| 285 |
+
|
| 286 |
+
title="Headers")
|
| 287 |
+
|
| 288 |
+
# Typography
|
| 289 |
+
|
| 290 |
+
def ex_headings():
|
| 291 |
+
return Div(
|
| 292 |
+
Titled("Titled"),
|
| 293 |
+
H1("Level 1 Heading (H1)"),
|
| 294 |
+
H2("Level 2 Heading (H2)"),
|
| 295 |
+
H3("Level 3 Heading (H3)"),
|
| 296 |
+
H4("Level 4 Heading (H4)"),
|
| 297 |
+
H5("Level 5 Heading (H5)"),
|
| 298 |
+
H6("Level 6 Heading (H6)"),
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def ex_semantic_elements():
|
| 303 |
+
return Div(
|
| 304 |
+
H2("Semantic HTML Elements Demo"),
|
| 305 |
+
# Text formatting examples
|
| 306 |
+
P("Here's an example of ", Em("emphasized (Em)"), " and ", Strong("strong (Strong)"), " text."),
|
| 307 |
+
P("Some ", I("italic text (I)"), " and ", Small("smaller text (Small)"), " in a paragraph."),
|
| 308 |
+
P("You can ", Mark("highlight (Mark)"), " text, show ", Del("deleted (Del)"), " and ",
|
| 309 |
+
Ins("inserted (Ins)"), " content."),
|
| 310 |
+
P("Chemical formulas use ", Sub("subscripts (Sub)"), " and ", Sup("superscripts (Sup)"),
|
| 311 |
+
" like H", Sub("2"), "O."),
|
| 312 |
+
# Quote examples
|
| 313 |
+
Blockquote(
|
| 314 |
+
P("The only way to do great work is to love what you do."),
|
| 315 |
+
Cite("Steve Jobs (Cite)")),
|
| 316 |
+
P("As Shakespeare wrote, ", Q("All the world's a stage (Q)"), "."),
|
| 317 |
+
# Time and Address
|
| 318 |
+
P("Posted on ", Time("2024-01-29", datetime="2024-01-29")),
|
| 319 |
+
Address(
|
| 320 |
+
"Mozilla Foundation (Address)",
|
| 321 |
+
Br(),
|
| 322 |
+
"331 E Evelyn Ave (Address)",
|
| 323 |
+
Br(),
|
| 324 |
+
"Mountain View, CA 94041 (Address)",
|
| 325 |
+
Br(),
|
| 326 |
+
"USA (Address)"),
|
| 327 |
+
# Technical and definition examples
|
| 328 |
+
P(
|
| 329 |
+
Dfn("HTML (Dfn)"), " (",
|
| 330 |
+
Abbr("HyperText Markup Language (Abbr)", title="HyperText Markup Language"),
|
| 331 |
+
") is the standard markup language for documents designed to be displayed in a web browser."),
|
| 332 |
+
P("Press ", Kbd("Ctrl (Kbd)"), " + ", Kbd("C (Kbd)"), " to copy."),
|
| 333 |
+
P("The command returned: ", Samp("Hello, World! (Samp)")),
|
| 334 |
+
P("Let ", Var("x (Var)"), " be the variable in the equation."),
|
| 335 |
+
# Figure with caption
|
| 336 |
+
Figure(
|
| 337 |
+
PicSumImg(),
|
| 338 |
+
Caption("Figure 1: An example image with caption (Caption)")),
|
| 339 |
+
# Interactive elements
|
| 340 |
+
Details(
|
| 341 |
+
Summary("Click to show more information (Summary)"),
|
| 342 |
+
P("This is the detailed content that is initially hidden (P)")),
|
| 343 |
+
# Data representation
|
| 344 |
+
P(
|
| 345 |
+
Data("123 (Data)", value="123"), " is a number, and here's a Meter showing progress: ",
|
| 346 |
+
Meter(value=0.6, min=0, max=1)),
|
| 347 |
+
P(
|
| 348 |
+
"Temperature: ",
|
| 349 |
+
Meter(value=-1, min=-10, max=40, low=0, high=30, optimum=21),
|
| 350 |
+
" (with low/high/optimum values)"),
|
| 351 |
+
P(
|
| 352 |
+
Data("€42.00", value="42"),
|
| 353 |
+
" - price example with semantic value"),
|
| 354 |
+
# Output example
|
| 355 |
+
P("Form calculation result: ", Output("The sum is 42 (Output)", form="calc-form", for_="num1 num2")),
|
| 356 |
+
# Meta information example
|
| 357 |
+
Section(
|
| 358 |
+
H3("Blog Post Title (H3)"),
|
| 359 |
+
Small("By John Doe • 5 min read (Small)"),
|
| 360 |
+
P("Article content here...")),
|
| 361 |
+
# Text decoration examples
|
| 362 |
+
P("This text has ",U("proper name annotation (U)"), " and this is ",S("outdated information (S)"), " that's been superseded."),
|
| 363 |
+
cls='space-y-4'
|
| 364 |
+
)
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def ex_textpresets():
|
| 368 |
+
return Grid(*[Div(P(f"This is {preset.name} text", cls=preset.value)) for preset in TextPresets])
|
| 369 |
+
|
| 370 |
+
def ex_textt():
|
| 371 |
+
return Grid(*[Div(P(f"This is {s.name} text", cls=s.value)) for s in TextT])
|
| 372 |
+
|
| 373 |
+
def ex_other():
|
| 374 |
+
return Div(
|
| 375 |
+
CodeSpan("This is a CodeSpan element"),
|
| 376 |
+
Blockquote("This is a blockquote element"),
|
| 377 |
+
CodeBlock("#This is a CodeBlock element\n\ndef add(a,b): return a+b"))
|
| 378 |
+
|
| 379 |
+
docs_typography = create_doc_section(
|
| 380 |
+
H1("Typography API Reference"),
|
| 381 |
+
P("Ready to go semantic options that cover most of what you need based on the HTML spec"),
|
| 382 |
+
fn2code_string(ex_headings),
|
| 383 |
+
fn2code_string(ex_semantic_elements),
|
| 384 |
+
fn2code_string(ex_other),
|
| 385 |
+
P("Styling text is possibly the most common style thing to do, so we have a couple of helpers for discoverability inside python. `TextPresets` is intended to be combinations are are widely applicable and used often, where `TextT` is intended to be more flexible options for you to combine together yourself."),
|
| 386 |
+
H5("TextPresets.*"),
|
| 387 |
+
fn2code_string(ex_textpresets),
|
| 388 |
+
H5("TextT.*"),
|
| 389 |
+
fn2code_string(ex_textt),
|
| 390 |
+
H3("API Reference"),
|
| 391 |
+
TextPresets,
|
| 392 |
+
TextT,
|
| 393 |
+
H1, H2, H3, H4, H5, H6,
|
| 394 |
+
CodeSpan, Blockquote, CodeBlock,
|
| 395 |
+
Em, Strong, I, Small, Mark, Sub, Sup, Del, Ins,
|
| 396 |
+
Dfn, Abbr, Q, Kbd, Samp, Var,
|
| 397 |
+
Figure, Caption,
|
| 398 |
+
Details, Summary,
|
| 399 |
+
Meter, Data, Output,
|
| 400 |
+
Address, Time,
|
| 401 |
+
title="Text Style")
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
# Notifications
|
| 405 |
+
def ex_alerts1(): return Alert("This is a plain alert")
|
| 406 |
+
|
| 407 |
+
def ex_alerts2(): return Alert("Your purchase has been confirmed!", cls=AlertT.success)
|
| 408 |
+
|
| 409 |
+
def ex_alerts3():
|
| 410 |
+
return Alert(
|
| 411 |
+
DivLAligned(UkIcon('triangle-alert'),
|
| 412 |
+
P("Please enter a valid email.")),
|
| 413 |
+
cls=AlertT.error)
|
| 414 |
+
|
| 415 |
+
def ex_toasts1():
|
| 416 |
+
return Toast("First Example Toast", cls=(ToastHT.start, ToastVT.bottom), dur=300)
|
| 417 |
+
|
| 418 |
+
def ex_toasts2():
|
| 419 |
+
return Toast("Second Example Toast", alert_cls=AlertT.info, dur=300)
|
| 420 |
+
|
| 421 |
+
docs_notifications = create_doc_section(
|
| 422 |
+
H1("Alerts & Toasts API Reference"),
|
| 423 |
+
H3("Alerts"),
|
| 424 |
+
P("The simplest alert is a div wrapped with a span:"),
|
| 425 |
+
fn2code_string(ex_alerts1),
|
| 426 |
+
P("Alert colors are defined by the alert styles:"),
|
| 427 |
+
fn2code_string(ex_alerts2),
|
| 428 |
+
P("It often looks nice to use icons in alerts: "),
|
| 429 |
+
fn2code_string(ex_alerts3),
|
| 430 |
+
Alert, AlertT,
|
| 431 |
+
DividerLine(),
|
| 432 |
+
H3("Toasts"),
|
| 433 |
+
P("To define a toast with a particular location, add horizontal or vertical toast type classes:"),
|
| 434 |
+
fn2code_string(ex_toasts1),
|
| 435 |
+
P("To define toast colors, set the class of the alert wrapped by the toast:"),
|
| 436 |
+
fn2code_string(ex_toasts2),
|
| 437 |
+
P("Toasts will disappear automatically after 5 seconds. To change the duration of the toast set the `dur` param like this `Toast('Content', dur=10)`."),
|
| 438 |
+
P("Here's a demo ", A("app", href="https://gallery.fastht.ml/split/dynamic_user_interface_(htmx)/toast", target="_blank", rel="noopener noreferrer", cls='underline'), " showing how to trigger a toast.", cls='mt-4'),
|
| 439 |
+
Toast, ToastHT, ToastVT,
|
| 440 |
+
title="Alerts & Toasts")
|
| 441 |
+
|
| 442 |
+
# Containers
|
| 443 |
+
|
| 444 |
+
def ex_articles():
|
| 445 |
+
return Article(
|
| 446 |
+
ArticleTitle("Sample Article Title"),
|
| 447 |
+
Subtitle("By: John Doe"),
|
| 448 |
+
P('lorem ipsum dolor sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'))
|
| 449 |
+
|
| 450 |
+
def ex_containers():
|
| 451 |
+
return Container(
|
| 452 |
+
"This is a sample container with custom styling.",
|
| 453 |
+
cls=ContainerT.xs,
|
| 454 |
+
style="background-color: #FFA500; color: #000000")
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
docs_containers = create_doc_section(
|
| 458 |
+
H1("Articles, Containers & Sections API Reference"),
|
| 459 |
+
ArticleMeta,
|
| 460 |
+
ArticleTitle,
|
| 461 |
+
Article,
|
| 462 |
+
fn2code_string(ex_articles),
|
| 463 |
+
Container,
|
| 464 |
+
ContainerT,
|
| 465 |
+
fn2code_string(ex_containers),
|
| 466 |
+
Section,
|
| 467 |
+
SectionT,
|
| 468 |
+
title="Articles, Containers & Sections"
|
| 469 |
+
)
|
| 470 |
+
|
| 471 |
+
# Cards
|
| 472 |
+
|
| 473 |
+
def ex_card():
|
| 474 |
+
return Card(
|
| 475 |
+
Form(LabelInput("Input"),
|
| 476 |
+
LabelRange("Range")),
|
| 477 |
+
header=Div(
|
| 478 |
+
CardTitle("Header"),
|
| 479 |
+
P("A card with header and footer",cls=TextPresets.muted_sm)),
|
| 480 |
+
footer=DivLAligned(Button("Footer Submit Button")))
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
def Tags(cats): return Div(cls='space-x-2')(map(Label, cats))
|
| 485 |
+
|
| 486 |
+
def ex_card2_wide():
|
| 487 |
+
def Tags(cats): return DivLAligned(map(Label, cats))
|
| 488 |
+
|
| 489 |
+
return Card(
|
| 490 |
+
DivLAligned(
|
| 491 |
+
A(Img(src="https://picsum.photos/200/200?random=12", style="width:200px"),href="#"),
|
| 492 |
+
Div(cls='space-y-3 uk-width-expand')(
|
| 493 |
+
H4("Creating Custom FastHTML Tags for Markdown Rendering"),
|
| 494 |
+
P("A step by step tutorial to rendering markdown in FastHTML using zero-md inside of DaisyUI chat bubbles"),
|
| 495 |
+
DivFullySpaced(map(Small, ["Isaac Flath", "20-October-2024"]), cls=TextT.muted),
|
| 496 |
+
DivFullySpaced(
|
| 497 |
+
Tags(["FastHTML", "HTMX", "Web Apps"]),
|
| 498 |
+
Button("Read", cls=(ButtonT.primary,'h-6'))))),
|
| 499 |
+
cls=CardT.hover)
|
| 500 |
+
|
| 501 |
+
def ex_card2_tall():
|
| 502 |
+
def Tags(cats): return DivLAligned(map(Label, cats))
|
| 503 |
+
|
| 504 |
+
return Card(
|
| 505 |
+
Div(
|
| 506 |
+
A(Img(src="https://picsum.photos/400/200?random=14"), href="#"),
|
| 507 |
+
Div(cls='space-y-3 uk-width-expand')(
|
| 508 |
+
H4("Creating Custom FastHTML Tags for Markdown Rendering"),
|
| 509 |
+
P("A step by step tutorial to rendering markdown in FastHTML using zero-md inside of DaisyUI chat bubbles"),
|
| 510 |
+
DivFullySpaced(map(Small, ["Isaac Flath", "20-October-2024"]), cls=TextT.muted),
|
| 511 |
+
DivFullySpaced(
|
| 512 |
+
Tags(["FastHTML", "HTMX", "Web Apps"]),
|
| 513 |
+
Button("Read", cls=(ButtonT.primary,'h-6'))))),
|
| 514 |
+
cls=CardT.hover)
|
| 515 |
+
|
| 516 |
+
def ex_card3():
|
| 517 |
+
def team_member(name, role, location="Remote"):
|
| 518 |
+
return Card(
|
| 519 |
+
DivLAligned(
|
| 520 |
+
DiceBearAvatar(name, h=24, w=24),
|
| 521 |
+
Div(H3(name), P(role))),
|
| 522 |
+
footer=DivFullySpaced(
|
| 523 |
+
DivHStacked(UkIcon("map-pin", height=16), P(location)),
|
| 524 |
+
DivHStacked(*(UkIconLink(icon, height=16) for icon in ("mail", "linkedin", "github")))))
|
| 525 |
+
team = [
|
| 526 |
+
team_member("Sarah Chen", "Engineering Lead", "San Francisco"),
|
| 527 |
+
team_member("James Wilson", "Senior Developer", "New York"),
|
| 528 |
+
team_member("Maria Garcia", "UX Designer", "London"),
|
| 529 |
+
team_member("Alex Kumar", "Product Manager", "Singapore"),
|
| 530 |
+
team_member("Emma Brown", "DevOps Engineer", "Toronto"),
|
| 531 |
+
team_member("Marcus Johnson", "Frontend Developer", "Berlin")
|
| 532 |
+
]
|
| 533 |
+
return Grid(*team, cols_sm=1, cols_md=1, cols_lg=2, cols_xl=3)
|
| 534 |
+
|
| 535 |
+
docs_cards = create_doc_section(
|
| 536 |
+
H1("Cards API Reference"),
|
| 537 |
+
H3("Example Usage"),
|
| 538 |
+
fn2code_string(ex_card),
|
| 539 |
+
(*fn2code_string(ex_card2_wide),'sm:block'),
|
| 540 |
+
(*fn2code_string(ex_card2_tall),'sm:hidden'),
|
| 541 |
+
fn2code_string(ex_card3),
|
| 542 |
+
H3("API Reference"),
|
| 543 |
+
Card,
|
| 544 |
+
CardTitle,
|
| 545 |
+
CardT,
|
| 546 |
+
P("The remainder of these are only needed if you're doing something really special. They are used in the `Card` function to generate the boilerplate for you.", cls='my-6'),
|
| 547 |
+
CardContainer,
|
| 548 |
+
CardHeader,
|
| 549 |
+
CardBody,
|
| 550 |
+
CardFooter,
|
| 551 |
+
title="Cards"
|
| 552 |
+
)
|
| 553 |
+
|
| 554 |
+
# Lists
|
| 555 |
+
|
| 556 |
+
def ex_lists():
|
| 557 |
+
list_options = [(style,str(cls)) for style,cls in ListT.__members__.items()]
|
| 558 |
+
lists = [Div(H4(f"{style} List:"), Ul(Li("Item 1"), Li("Item 2"), cls=cls)) for style, cls in list_options]
|
| 559 |
+
return Grid(*lists)
|
| 560 |
+
|
| 561 |
+
docs_lists = create_doc_section(
|
| 562 |
+
H1("Lists API Reference"),
|
| 563 |
+
fn2code_string(ex_lists),
|
| 564 |
+
ListT,
|
| 565 |
+
title="Lists")
|
| 566 |
+
|
| 567 |
+
# Forms
|
| 568 |
+
|
| 569 |
+
def ex_formlabel():
|
| 570 |
+
return FormLabel("Form Label")
|
| 571 |
+
|
| 572 |
+
def ex_input():
|
| 573 |
+
return Div(
|
| 574 |
+
Input(placeholder="Enter text"),
|
| 575 |
+
LabelInput(label="Input", id='myid'))
|
| 576 |
+
|
| 577 |
+
def ex_checkbox():
|
| 578 |
+
return Div(
|
| 579 |
+
CheckboxX(),
|
| 580 |
+
LabelCheckboxX(label="Checkbox", id='checkbox1'))
|
| 581 |
+
|
| 582 |
+
def ex_range():
|
| 583 |
+
return Div(
|
| 584 |
+
Range(),
|
| 585 |
+
Range(label='kg', value="25,75", min=20, max=75),
|
| 586 |
+
LabelRange('Basic Range', value='50', min=0, max=100, step=1),
|
| 587 |
+
LabelRange('Range with Label', value='75', min=0, max=100, step=5, label_range=True),
|
| 588 |
+
LabelRange('Multiple Values', value='25,75', min=0, max=100, step=5, label_range=True),
|
| 589 |
+
LabelRange('Custom Range', value='500', min=0, max=1000, step=100, label_range=True)
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
def ex_switch():
|
| 593 |
+
return Div(
|
| 594 |
+
Switch(id="switch"),
|
| 595 |
+
LabelSwitch(label="Switch", id='switch'))
|
| 596 |
+
|
| 597 |
+
def ex_textarea():
|
| 598 |
+
return Div(
|
| 599 |
+
TextArea(placeholder="Enter multiple lines of text"),
|
| 600 |
+
LabelTextArea(label="TextArea", id='myid'))
|
| 601 |
+
|
| 602 |
+
def ex_radio():
|
| 603 |
+
return Div(
|
| 604 |
+
Radio(name="radio-group", id="radio1"),
|
| 605 |
+
LabelRadio(label="Radio", id='radio1',cls='flex items-center space-x-4'))
|
| 606 |
+
|
| 607 |
+
def ex_insertable_select1():
|
| 608 |
+
fruit_opts = ['apple', 'orange', 'banana', 'mango']
|
| 609 |
+
|
| 610 |
+
return Grid(
|
| 611 |
+
Select(Option('Apple', value='apple'),
|
| 612 |
+
Option('Orange', value='orange'),
|
| 613 |
+
Option('Banana', value='banana'),
|
| 614 |
+
Option('Mango', value='mango'),
|
| 615 |
+
id="fruit", icon=True, insertable=True, placeholder="Choose a fruit..."),
|
| 616 |
+
|
| 617 |
+
Select(Optgroup(label="Fruit")(
|
| 618 |
+
*map(lambda l: Option(l.capitalize(), value=l), sorted(fruit_opts))),
|
| 619 |
+
id="fruit", icon=True, insertable=True, placeholder="Choose a fruit...",
|
| 620 |
+
cls_custom="button: uk-input-fake justify-between w-full; dropdown: w-full"))
|
| 621 |
+
|
| 622 |
+
def ex_select():
|
| 623 |
+
return Div(
|
| 624 |
+
Select(map(Option, ["Option 1", "Option 2", "Option 3"])),
|
| 625 |
+
LabelSelect(map(Option, ["Option 1", "Option 2", "Option 3"]), label="Select", id='myid'))
|
| 626 |
+
|
| 627 |
+
def ex_progress():
|
| 628 |
+
return Progress(value=20, max=100)
|
| 629 |
+
|
| 630 |
+
def ex_form():
|
| 631 |
+
relationship = ["Parent",'Sibling', "Friend", "Spouse", "Significant Other", "Relative", "Child", "Other"]
|
| 632 |
+
return Div(cls='space-y-4')(
|
| 633 |
+
DivCentered(
|
| 634 |
+
H3("Emergency Contact Form"),
|
| 635 |
+
P("Please fill out the form completely", cls=TextPresets.muted_sm)),
|
| 636 |
+
Form(cls='space-y-4')(
|
| 637 |
+
Grid(LabelInput("First Name",id='fn'), LabelInput("Last Name",id='ln')),
|
| 638 |
+
Grid(LabelInput("Email", id='em'), LabelInput("Phone", id='ph')),
|
| 639 |
+
H3("Relationship to patient"),
|
| 640 |
+
Grid(*[LabelCheckboxX(o) for o in relationship], cols=4, cls='space-y-3'),
|
| 641 |
+
LabelInput("Address", id='ad'),
|
| 642 |
+
LabelInput("Address Line 2", id='ad2'),
|
| 643 |
+
Grid(LabelInput("City", id='ct'), LabelInput("State", id='st')),
|
| 644 |
+
LabelInput("Zip", id='zp'),
|
| 645 |
+
DivCentered(Button("Submit Form", cls=ButtonT.primary))))
|
| 646 |
+
|
| 647 |
+
def ex_upload():
|
| 648 |
+
return Div(Upload("Upload Button!", id='upload1'),
|
| 649 |
+
UploadZone(DivCentered(Span("Upload Zone"), UkIcon("upload")), id='upload2'),
|
| 650 |
+
cls='space-y-4')
|
| 651 |
+
|
| 652 |
+
docs_forms = create_doc_section(
|
| 653 |
+
H1("Forms and User Inputs API Reference"),
|
| 654 |
+
H3("Example Form"),
|
| 655 |
+
P(f"This form was live coded in a 5 minute video ",
|
| 656 |
+
A("here",href="https://www.loom.com/share/0916e8a95d524c43a4d100ee85157624?start_and_pause=1",
|
| 657 |
+
cls=AT.muted), cls=TextPresets.muted_sm),
|
| 658 |
+
fn2code_string(ex_form),
|
| 659 |
+
fn2code_string(ex_upload),
|
| 660 |
+
FormLabel,
|
| 661 |
+
fn2code_string(ex_formlabel),
|
| 662 |
+
Input,
|
| 663 |
+
fn2code_string(ex_input),
|
| 664 |
+
LabelInput,
|
| 665 |
+
LabelCheckboxX,
|
| 666 |
+
LabelSwitch,
|
| 667 |
+
LabelRange,
|
| 668 |
+
LabelTextArea,
|
| 669 |
+
LabelRadio,
|
| 670 |
+
LabelSelect,
|
| 671 |
+
Progress,
|
| 672 |
+
fn2code_string(ex_progress),
|
| 673 |
+
Radio,
|
| 674 |
+
fn2code_string(ex_radio),
|
| 675 |
+
CheckboxX,
|
| 676 |
+
fn2code_string(ex_checkbox),
|
| 677 |
+
Range,
|
| 678 |
+
fn2code_string(ex_range),
|
| 679 |
+
Switch,
|
| 680 |
+
fn2code_string(ex_switch),
|
| 681 |
+
TextArea,
|
| 682 |
+
fn2code_string(ex_textarea),
|
| 683 |
+
Select,
|
| 684 |
+
fn2code_string(ex_select),
|
| 685 |
+
H3("Example: Insertable Select"),
|
| 686 |
+
Caption("In a production app, the user-inserted option would be saved server-side (db, session etc.)"),
|
| 687 |
+
fn2code_string(ex_insertable_select1),
|
| 688 |
+
Legend,
|
| 689 |
+
Fieldset,
|
| 690 |
+
title="Forms")
|
| 691 |
+
|
| 692 |
+
|
| 693 |
+
# Lightbox
|
| 694 |
+
|
| 695 |
+
def ex_lightbox1():
|
| 696 |
+
return LightboxContainer(
|
| 697 |
+
LightboxItem(Button("Open"), href='https://picsum.photos/id/100/1280/720.webp', data_alt='A placeholder image to demonstrate the lightbox', data_caption='This is my super cool caption'),
|
| 698 |
+
)
|
| 699 |
+
def ex_lightbox2():
|
| 700 |
+
return LightboxContainer(
|
| 701 |
+
LightboxItem(Button("Open"), href='https://picsum.photos/id/100/1280/720.webp', data_alt='A placeholder image to demonstrate the lightbox', data_caption='Image 1'),
|
| 702 |
+
LightboxItem(href='https://picsum.photos/id/101/1280/720.webp', data_alt='A placeholder image to demonstrate the lightbox', data_caption='Image 2'),
|
| 703 |
+
LightboxItem(href='https://picsum.photos/id/102/1280/720.webp', data_alt='A placeholder image to demonstrate the lightbox', data_caption='Image 3'),
|
| 704 |
+
)
|
| 705 |
+
|
| 706 |
+
def ex_lightbox3():
|
| 707 |
+
return LightboxContainer(
|
| 708 |
+
LightboxItem(Button("mp4"), href='https://yootheme.com/site/images/media/yootheme-pro.mp4'),
|
| 709 |
+
LightboxItem(Button("Youtube"), href='https://www.youtube.com/watch?v=c2pz2mlSfXA'),
|
| 710 |
+
LightboxItem(Button("Vimeo"), href='https://vimeo.com/1084537'),
|
| 711 |
+
LightboxItem(Button("Iframe"), data_type='iframe', href='https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d4740.819266853735!2d9.99008871708242!3d53.550454675412404!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x3f9d24afe84a0263!2sRathaus!5e0!3m2!1sde!2sde!4v1499675200938'))
|
| 712 |
+
|
| 713 |
+
docs_lightbox = create_doc_section(
|
| 714 |
+
H1("Lightbox API Reference"),
|
| 715 |
+
fn2code_string(ex_lightbox1),
|
| 716 |
+
fn2code_string(ex_lightbox2),
|
| 717 |
+
fn2code_string(ex_lightbox3),
|
| 718 |
+
LightboxContainer,
|
| 719 |
+
LightboxItem,
|
| 720 |
+
title="Lightbox")
|
| 721 |
+
|
| 722 |
+
# Modals
|
| 723 |
+
|
| 724 |
+
def ex_modal():
|
| 725 |
+
return Div(
|
| 726 |
+
Button("Open Modal",data_uk_toggle="target: #my-modal" ),
|
| 727 |
+
Modal(ModalTitle("Simple Test Modal"),
|
| 728 |
+
P("With some somewhat brief content to show that it works!", cls=TextPresets.muted_sm),
|
| 729 |
+
footer=ModalCloseButton("Close", cls=ButtonT.primary),id='my-modal'))
|
| 730 |
+
|
| 731 |
+
docs_modals = create_doc_section(
|
| 732 |
+
H1("Modals API Reference"),
|
| 733 |
+
H3("Example Modal"),
|
| 734 |
+
fn2code_string(ex_modal),
|
| 735 |
+
Modal,
|
| 736 |
+
P("If you want to trigger a htmx event on close (like calling a route) you can use `hx_trigger='hidden'` to catch the close event."),
|
| 737 |
+
ModalCloseButton,
|
| 738 |
+
P("The remainder of the Modal functions below are used internally by the `Modal` function for you. You shouldn't need to use them unless you're doing something really special."),
|
| 739 |
+
ModalTitle,
|
| 740 |
+
ModalFooter,
|
| 741 |
+
ModalBody,
|
| 742 |
+
ModalHeader,
|
| 743 |
+
ModalDialog,
|
| 744 |
+
ModalContainer,
|
| 745 |
+
title="Modals")
|
| 746 |
+
|
| 747 |
+
# Layout
|
| 748 |
+
|
| 749 |
+
def ex_grid():
|
| 750 |
+
return Grid(
|
| 751 |
+
Div(
|
| 752 |
+
P("Column 1 Item 1"),
|
| 753 |
+
P("Column 1 Item 2"),
|
| 754 |
+
P("Column 1 Item 3")),
|
| 755 |
+
Div(
|
| 756 |
+
P("Column 2 Item 1"),
|
| 757 |
+
P("Column 2 Item 2"),
|
| 758 |
+
P("Column 2 Item 3")),
|
| 759 |
+
Div(
|
| 760 |
+
P("Column 3 Item 1"),
|
| 761 |
+
P("Column 3 Item 2"),
|
| 762 |
+
P("Column 3 Item 3")))
|
| 763 |
+
|
| 764 |
+
def ex_product_grid():
|
| 765 |
+
products = [
|
| 766 |
+
{"name": "Laptop", "price": "$999", "img": "https://picsum.photos/200/100?random=1"},
|
| 767 |
+
{"name": "Smartphone", "price": "$599", "img": "https://picsum.photos/200/100?random=2"},
|
| 768 |
+
{"name": "Headphones", "price": "$199", "img": "https://picsum.photos/200/100?random=3"},
|
| 769 |
+
{"name": "Smartwatch", "price": "$299", "img": "https://picsum.photos/200/100?random=4"},
|
| 770 |
+
{"name": "Tablet", "price": "$449", "img": "https://picsum.photos/200/100?random=5"},
|
| 771 |
+
{"name": "Camera", "price": "$799", "img": "https://picsum.photos/200/100?random=6"},
|
| 772 |
+
]
|
| 773 |
+
|
| 774 |
+
product_cards = [
|
| 775 |
+
Card(
|
| 776 |
+
Img(src=p["img"], alt=p["name"], style="width:100%; height:100px; object-fit:cover;"),
|
| 777 |
+
H4(p["name"], cls="mt-2"),
|
| 778 |
+
P(p["price"], cls=TextPresets.bold_sm),
|
| 779 |
+
Button("Add to Cart", cls=(ButtonT.primary, "mt-2"))
|
| 780 |
+
) for p in products
|
| 781 |
+
]
|
| 782 |
+
|
| 783 |
+
return Grid(*product_cards, cols_lg=3)
|
| 784 |
+
|
| 785 |
+
def ex_fully_spaced_div():
|
| 786 |
+
return DivFullySpaced(
|
| 787 |
+
Button("Left", cls=ButtonT.primary),
|
| 788 |
+
Button("Center", cls=ButtonT.secondary),
|
| 789 |
+
Button("Right", cls=ButtonT.destructive)
|
| 790 |
+
)
|
| 791 |
+
|
| 792 |
+
def ex_centered_div():
|
| 793 |
+
return DivCentered(
|
| 794 |
+
H3("Centered Title"),
|
| 795 |
+
P("This content is centered both horizontally and vertically.")
|
| 796 |
+
)
|
| 797 |
+
|
| 798 |
+
def ex_l_aligned_div():
|
| 799 |
+
return DivLAligned(
|
| 800 |
+
Img(src="https://picsum.photos/100/100?random=1", style="max-width: 100px;"),
|
| 801 |
+
H4("Left Aligned Title"),
|
| 802 |
+
P("Some text that's left-aligned with the title and image.")
|
| 803 |
+
)
|
| 804 |
+
|
| 805 |
+
def ex_r_aligned_div():
|
| 806 |
+
return DivRAligned(
|
| 807 |
+
Button("Action", cls=ButtonT.primary),
|
| 808 |
+
P("Right-aligned text"),
|
| 809 |
+
Img(src="https://picsum.photos/100/100?random=3", style="max-width: 100px;")
|
| 810 |
+
)
|
| 811 |
+
|
| 812 |
+
def ex_v_stacked_div():
|
| 813 |
+
return DivVStacked(
|
| 814 |
+
H2("Vertical Stack"),
|
| 815 |
+
P("First paragraph in the stack"),
|
| 816 |
+
P("Second paragraph in the stack"),
|
| 817 |
+
Button("Action Button", cls=ButtonT.secondary)
|
| 818 |
+
)
|
| 819 |
+
|
| 820 |
+
def ex_h_stacked_div():
|
| 821 |
+
return DivHStacked(
|
| 822 |
+
Div(H4("Column 1"), P("Content for column 1")),
|
| 823 |
+
Div(H4("Column 2"), P("Content for column 2")),
|
| 824 |
+
Div(H4("Column 3"), P("Content for column 3"))
|
| 825 |
+
)
|
| 826 |
+
|
| 827 |
+
docs_layout = create_doc_section(
|
| 828 |
+
H1("Layout (Flex and Grid) API Reference"),
|
| 829 |
+
P("This page covers `Grid`s, which are often used for general structure, `Flex` which is often used for layout of components that are not grid based, padding and positioning that can help you make your layout look good, and dividers that can help break up the page", cls=TextPresets.muted_sm),
|
| 830 |
+
H2("Grid"),
|
| 831 |
+
fn2code_string(ex_grid),
|
| 832 |
+
Grid,
|
| 833 |
+
H4("Practical Grid Example"),
|
| 834 |
+
fn2code_string(ex_product_grid),
|
| 835 |
+
H2("Flex"),
|
| 836 |
+
P("Play ",
|
| 837 |
+
A("Flex Box Froggy", href="https://flexboxfroggy.com/", cls=AT.muted),
|
| 838 |
+
" to get an understanding of flex box.",
|
| 839 |
+
cls=TextPresets.muted_sm),
|
| 840 |
+
DivFullySpaced,
|
| 841 |
+
fn2code_string(ex_fully_spaced_div),
|
| 842 |
+
DivCentered,
|
| 843 |
+
fn2code_string(ex_centered_div),
|
| 844 |
+
DivLAligned,
|
| 845 |
+
fn2code_string(ex_l_aligned_div),
|
| 846 |
+
DivRAligned,
|
| 847 |
+
fn2code_string(ex_r_aligned_div),
|
| 848 |
+
DivVStacked,
|
| 849 |
+
fn2code_string(ex_v_stacked_div),
|
| 850 |
+
DivHStacked,
|
| 851 |
+
fn2code_string(ex_h_stacked_div),
|
| 852 |
+
FlexT,
|
| 853 |
+
title="Layout")
|
| 854 |
+
|
| 855 |
+
# Dividers
|
| 856 |
+
|
| 857 |
+
def ex_dividers():
|
| 858 |
+
return Div(
|
| 859 |
+
P("Small Divider"),
|
| 860 |
+
Divider(cls=DividerT.sm),
|
| 861 |
+
DivCentered(
|
| 862 |
+
P("Vertical Divider"),
|
| 863 |
+
Divider(cls=DividerT.vertical)),
|
| 864 |
+
DivCentered("Icon Divider"),
|
| 865 |
+
Divider(cls=DividerT.icon))
|
| 866 |
+
|
| 867 |
+
def ex_dividersplit():
|
| 868 |
+
return DividerSplit(P("Or continue with", cls=TextPresets.muted_sm))
|
| 869 |
+
|
| 870 |
+
def ex_dividerline():
|
| 871 |
+
return DividerLine()
|
| 872 |
+
|
| 873 |
+
docs_dividers = create_doc_section(
|
| 874 |
+
H1("Dividers API Reference"),
|
| 875 |
+
Divider,
|
| 876 |
+
DividerT,
|
| 877 |
+
fn2code_string(ex_dividers),
|
| 878 |
+
DividerSplit,
|
| 879 |
+
fn2code_string(ex_dividersplit),
|
| 880 |
+
DividerLine,
|
| 881 |
+
fn2code_string(ex_dividerline),
|
| 882 |
+
title="Dividers")
|
| 883 |
+
|
| 884 |
+
# Navigation
|
| 885 |
+
|
| 886 |
+
def ex_nav1():
|
| 887 |
+
mbrs1 = [Li(A('Option 1'), cls='uk-active'), Li(A('Option 2')), Li(A('Option 3'))]
|
| 888 |
+
return NavContainer(*mbrs1)
|
| 889 |
+
|
| 890 |
+
def ex_nav2():
|
| 891 |
+
mbrs1 = [Li(A('Option 1'), cls='uk-active'), Li(A('Option 2')), Li(A('Option 3'))]
|
| 892 |
+
mbrs2 = [Li(A('Child 1')), Li(A('Child 2')),Li(A('Child 3'))]
|
| 893 |
+
|
| 894 |
+
return NavContainer(
|
| 895 |
+
NavHeaderLi("NavHeaderLi"),
|
| 896 |
+
*mbrs1,
|
| 897 |
+
Li(A(href='')(Div("Subtitle Ex",NavSubtitle("NavSubtitle text to be shown")))),
|
| 898 |
+
NavDividerLi(),
|
| 899 |
+
NavParentLi(
|
| 900 |
+
A('Parent Name'),
|
| 901 |
+
NavContainer(*mbrs2,parent=False),
|
| 902 |
+
),
|
| 903 |
+
)
|
| 904 |
+
|
| 905 |
+
def ex_navbar1():
|
| 906 |
+
return NavBar(A("Page1",href='/rt1'),
|
| 907 |
+
A("Page2",href='/rt2'),
|
| 908 |
+
A("Page3",href='/rt3'),
|
| 909 |
+
brand=H3('My Blog'))
|
| 910 |
+
|
| 911 |
+
def ex_navbar2():
|
| 912 |
+
return NavBar(
|
| 913 |
+
A(Input(placeholder='search')),
|
| 914 |
+
A(UkIcon("rocket")),
|
| 915 |
+
A('Page1',href='/rt1'),
|
| 916 |
+
A("Page2", href='/rt3'),
|
| 917 |
+
brand=DivLAligned(Img(src='/api_reference/logo.svg'),UkIcon('rocket',height=30,width=30)))
|
| 918 |
+
|
| 919 |
+
def ex_navdrop():
|
| 920 |
+
return Div(
|
| 921 |
+
Button("Open DropDown"),
|
| 922 |
+
DropDownNavContainer(Li(A("Item 1",href=''),Li(A("Item 2",href='')))))
|
| 923 |
+
|
| 924 |
+
def ex_tabs1():
|
| 925 |
+
return Container(
|
| 926 |
+
TabContainer(
|
| 927 |
+
Li(A("Active",href='#', cls='uk-active')),
|
| 928 |
+
Li(A("Item",href='#')),
|
| 929 |
+
Li(A("Item",href='#')),
|
| 930 |
+
Li(A("Disabled",href='#', cls='uk-disabled')),
|
| 931 |
+
uk_switcher='connect: #component-nav; animation: uk-animation-fade',
|
| 932 |
+
alt=True),
|
| 933 |
+
Ul(id="component-nav", cls="uk-switcher")(
|
| 934 |
+
Li(H1("Tab 1")),
|
| 935 |
+
Li(H1("Tab 2")),
|
| 936 |
+
Li(H1("Tab 3"))))
|
| 937 |
+
|
| 938 |
+
def ex_tabs2():
|
| 939 |
+
return Container(
|
| 940 |
+
TabContainer(
|
| 941 |
+
Li(A("Active",href='javascript:void(0);', cls='uk-active')),
|
| 942 |
+
Li(A("Item",href='javascript:void(0);')),
|
| 943 |
+
Li(A("Item",href='javascript:void(0);')),
|
| 944 |
+
Li(A("Disabled", cls='uk-disabled'))))
|
| 945 |
+
|
| 946 |
+
docs_navigation = create_doc_section(
|
| 947 |
+
H1("Navigation (Nav, NavBar, Tabs, etc.) API Reference"),
|
| 948 |
+
H1("Nav, NavBar, DowDownNav, and Tab examples"),
|
| 949 |
+
Divider(),
|
| 950 |
+
H2("Nav"),
|
| 951 |
+
fn2code_string(ex_nav1),
|
| 952 |
+
fn2code_string(ex_nav2),
|
| 953 |
+
H2("Navbars", id='navbars'),
|
| 954 |
+
"Fully responsive simple navbar using the high level API. This will collapse to a hamburger menu on mobile devices. See the Scrollspy example for a more complex navbar example.",
|
| 955 |
+
fn2code_string(ex_navbar1),
|
| 956 |
+
fn2code_string(ex_navbar2),
|
| 957 |
+
H2("Drop Down Navs"),
|
| 958 |
+
fn2code_string(ex_navdrop),
|
| 959 |
+
H2("Tabs"),
|
| 960 |
+
fn2code_string(ex_tabs2),
|
| 961 |
+
P("A tabs can use any method of navigation (htmx, or href). However, often these are use in conjunction with switchers do to this client side", cls=TextPresets.muted_sm),
|
| 962 |
+
fn2code_string(ex_tabs1),
|
| 963 |
+
H1("API Docs"),
|
| 964 |
+
NavBar,
|
| 965 |
+
TabContainer,
|
| 966 |
+
NavContainer,
|
| 967 |
+
NavT,
|
| 968 |
+
NavCloseLi,
|
| 969 |
+
NavSubtitle,
|
| 970 |
+
NavHeaderLi,
|
| 971 |
+
NavDividerLi,
|
| 972 |
+
NavParentLi,
|
| 973 |
+
DropDownNavContainer,
|
| 974 |
+
title="Navigation")
|
| 975 |
+
|
| 976 |
+
# Steps
|
| 977 |
+
|
| 978 |
+
|
| 979 |
+
def ex_steps2():
|
| 980 |
+
return Steps(
|
| 981 |
+
LiStep("Account Created", cls=StepT.primary),
|
| 982 |
+
LiStep("Profile Setup", cls=StepT.neutral),
|
| 983 |
+
LiStep("Verification", cls=StepT.neutral),
|
| 984 |
+
cls="w-full")
|
| 985 |
+
def ex_steps3():
|
| 986 |
+
return Steps(
|
| 987 |
+
LiStep("Project Planning", cls=StepT.success, data_content="📝"),
|
| 988 |
+
LiStep("Design Phase", cls=StepT.success, data_content="💡"),
|
| 989 |
+
LiStep("Development", cls=StepT.primary, data_content="🛠️"),
|
| 990 |
+
LiStep("Testing", cls=StepT.neutral, data_content="🔎"),
|
| 991 |
+
LiStep("Deployment", cls=StepT.neutral, data_content="🚀"),
|
| 992 |
+
cls=(StepsT.vertical, "min-h-[400px]"))
|
| 993 |
+
|
| 994 |
+
docs_steps = create_doc_section(
|
| 995 |
+
H1("Steps API Reference"),
|
| 996 |
+
fn2code_string(ex_steps2),
|
| 997 |
+
fn2code_string(ex_steps3),
|
| 998 |
+
H1("API Docs"),
|
| 999 |
+
Steps,
|
| 1000 |
+
StepsT,
|
| 1001 |
+
LiStep,
|
| 1002 |
+
StepT,
|
| 1003 |
+
title="Steps")
|
| 1004 |
+
# Tables
|
| 1005 |
+
|
| 1006 |
+
def ex_tables0():
|
| 1007 |
+
return Table(
|
| 1008 |
+
Thead(Tr(Th('Name'), Th('Age'), Th('City'))),
|
| 1009 |
+
Tbody(Tr(Td('Alice'), Td('25'), Td('New York')),
|
| 1010 |
+
Tr(Td('Bob'), Td('30'), Td('San Francisco')),
|
| 1011 |
+
Tr(Td('Charlie'), Td('35'), Td('London'))),
|
| 1012 |
+
Tfoot(Tr(Td('Total'), Td('90'))))
|
| 1013 |
+
|
| 1014 |
+
def ex_tables1():
|
| 1015 |
+
header = ['Name', 'Age', 'City']
|
| 1016 |
+
body = [['Alice', '25', 'New York'],
|
| 1017 |
+
['Bob', '30', 'San Francisco'],
|
| 1018 |
+
['Charlie', '35', 'London']]
|
| 1019 |
+
footer = ['Total', '90']
|
| 1020 |
+
return TableFromLists(header, body, footer)
|
| 1021 |
+
|
| 1022 |
+
def ex_tables2():
|
| 1023 |
+
def body_render(k, v):
|
| 1024 |
+
match k.lower():
|
| 1025 |
+
case 'name': return Td(v, cls='font-bold')
|
| 1026 |
+
case 'age': return Td(f"{v} years")
|
| 1027 |
+
case _: return Td(v)
|
| 1028 |
+
|
| 1029 |
+
header_data = ['Name', 'Age', 'City']
|
| 1030 |
+
body_data =[{'Name': 'Alice', 'Age': 30, 'City': 'New York'},
|
| 1031 |
+
{'Name': 'Bob', 'Age': 25, 'City': 'London'}]
|
| 1032 |
+
|
| 1033 |
+
return TableFromDicts(header_data, body_data,
|
| 1034 |
+
header_cell_render=lambda v: Th(v.upper()),
|
| 1035 |
+
body_cell_render=body_render)
|
| 1036 |
+
|
| 1037 |
+
docs_tables = create_doc_section(
|
| 1038 |
+
H1("Tables API Reference"),
|
| 1039 |
+
fn2code_string(ex_tables0),
|
| 1040 |
+
fn2code_string(ex_tables1),
|
| 1041 |
+
fn2code_string(ex_tables2),
|
| 1042 |
+
Table,
|
| 1043 |
+
TableFromLists,
|
| 1044 |
+
TableFromDicts,
|
| 1045 |
+
TableT,
|
| 1046 |
+
Tbody,
|
| 1047 |
+
Th,
|
| 1048 |
+
Td,
|
| 1049 |
+
title="Tables")
|
| 1050 |
+
|
| 1051 |
+
# Icons
|
| 1052 |
+
|
| 1053 |
+
def ex_dicebear():
|
| 1054 |
+
return DivLAligned(
|
| 1055 |
+
DiceBearAvatar('Isaac Flath',10,10),
|
| 1056 |
+
DiceBearAvatar('Aaliyah',10,10),
|
| 1057 |
+
DiceBearAvatar('Alyssa',10,10))
|
| 1058 |
+
|
| 1059 |
+
def ex_picsum():
|
| 1060 |
+
return Grid(PicSumImg(100,100), PicSumImg(100,100, blur=6),PicSumImg(100,100, grayscale=True))
|
| 1061 |
+
|
| 1062 |
+
def ex_icon():
|
| 1063 |
+
return Grid(
|
| 1064 |
+
UkIcon('chevrons-right', height=15, width=15),
|
| 1065 |
+
UkIcon('bug', height=15, width=15),
|
| 1066 |
+
UkIcon('phone-call', height=15, width=15),
|
| 1067 |
+
UkIcon('maximize-2', height=15, width=15),
|
| 1068 |
+
UkIcon('thumbs-up', height=15, width=15),)
|
| 1069 |
+
|
| 1070 |
+
def ex_iconlink():
|
| 1071 |
+
return DivLAligned(
|
| 1072 |
+
UkIconLink('chevrons-right'),
|
| 1073 |
+
UkIconLink('chevrons-right', button=True, cls=ButtonT.primary))
|
| 1074 |
+
|
| 1075 |
+
docs_icons_images = create_doc_section(
|
| 1076 |
+
H1("Icons & Images API Reference"),
|
| 1077 |
+
H1("Avatars"),
|
| 1078 |
+
fn2code_string(ex_dicebear),
|
| 1079 |
+
DiceBearAvatar,
|
| 1080 |
+
H1("PlaceHolder Images"),
|
| 1081 |
+
fn2code_string(ex_picsum),
|
| 1082 |
+
PicSumImg,
|
| 1083 |
+
H1("Icons"),
|
| 1084 |
+
P("Icons use Lucide icons - you can find a full list of icons in their docs.", cls=TextPresets.muted_sm),
|
| 1085 |
+
fn2code_string(ex_icon),
|
| 1086 |
+
UkIcon,
|
| 1087 |
+
fn2code_string(ex_iconlink),
|
| 1088 |
+
UkIconLink,
|
| 1089 |
+
title="Icons")
|
| 1090 |
+
|
| 1091 |
+
# Markdown
|
| 1092 |
+
|
| 1093 |
+
def ex_markdown():
|
| 1094 |
+
md = '''# Example Markdown
|
| 1095 |
+
|
| 1096 |
+
+ With **bold** and *italics*
|
| 1097 |
+
+ With a [link](https://github.com)
|
| 1098 |
+
|
| 1099 |
+
### And a subheading
|
| 1100 |
+
|
| 1101 |
+
> This is a blockquote
|
| 1102 |
+
|
| 1103 |
+
This supports inline latex: $e^{\\pi i} + 1 = 0$ as well as block latex thanks to Katex.
|
| 1104 |
+
|
| 1105 |
+
$$
|
| 1106 |
+
\\frac{1}{2\\pi i} \\oint_C \\frac{f(z)}{z-z_0} dz
|
| 1107 |
+
$$
|
| 1108 |
+
|
| 1109 |
+
And even syntax highlighting thanks to Highlight.js! (Just make sure you set `highlightjs=True` in the headers function)
|
| 1110 |
+
|
| 1111 |
+
```python
|
| 1112 |
+
def add(a, b):
|
| 1113 |
+
return a + b
|
| 1114 |
+
```
|
| 1115 |
+
'''
|
| 1116 |
+
return render_md(md)
|
| 1117 |
+
|
| 1118 |
+
def ex_markdown2():
|
| 1119 |
+
md = '''With custom **bold** style\n\n > But no extra quote style because class_map overrides all default styled'''
|
| 1120 |
+
return render_md(md, class_map={'b': 'text-red-500'})
|
| 1121 |
+
|
| 1122 |
+
def ex_markdown3():
|
| 1123 |
+
md = '''With custom **bold** style\n\n > But default quote style because class_map_mods replaces sepecified styles and leaves the rest as default'''
|
| 1124 |
+
return render_md(md, class_map_mods={'b': 'text-red-500'})
|
| 1125 |
+
|
| 1126 |
+
def ex_applyclasses():
|
| 1127 |
+
return apply_classes('<h1>Hello, World!</h1><p>This is a paragraph</p>')
|
| 1128 |
+
|
| 1129 |
+
def ex_applyclasses2():
|
| 1130 |
+
from mistletoe import markdown, HTMLRenderer
|
| 1131 |
+
md = markdown('# Hi\n[a link](www.google.com)', renderer=HTMLRenderer)
|
| 1132 |
+
return Safe(apply_classes(md))
|
| 1133 |
+
|
| 1134 |
+
docs_markdown = create_doc_section(
|
| 1135 |
+
H1("Markdown + automated HTML styling API Reference"),
|
| 1136 |
+
fn2code_string(ex_markdown),
|
| 1137 |
+
render_md("You can overwrite the default styling for markdown rendering with your own css classes with `class_map"),
|
| 1138 |
+
fn2code_string(ex_markdown2),
|
| 1139 |
+
render_md("You can modify the default styling for markdown rendering with your own css classes with `class_map_mods"),
|
| 1140 |
+
fn2code_string(ex_markdown3),
|
| 1141 |
+
render_md("This uses the `apply_classes` function, which can be used to apply classes to html strings. This is useful for applying styles to any html you get from an external source."),
|
| 1142 |
+
fn2code_string(ex_applyclasses),
|
| 1143 |
+
render_md("One common external source is a markdown renderer. MonsterUI uses tailwind css for styling so you don't get any styling without specifying classes, `apply_classes` can do that for you."),
|
| 1144 |
+
fn2code_string(ex_applyclasses2),
|
| 1145 |
+
apply_classes,
|
| 1146 |
+
title="Markdown + HTML Frankification")
|
| 1147 |
+
|
| 1148 |
+
|
| 1149 |
+
docs_html = create_doc_section(
|
| 1150 |
+
H1("HTML Styling API Reference"),
|
| 1151 |
+
fn2code_string(ex_applyclasses),
|
| 1152 |
+
title="HTML Styling")
|
| 1153 |
+
|
| 1154 |
+
def ex_loading1():
|
| 1155 |
+
return Loading()
|
| 1156 |
+
|
| 1157 |
+
def ex_loading2():
|
| 1158 |
+
types = [LoadingT.spinner, LoadingT.dots, LoadingT.ring, LoadingT.ball, LoadingT.bars, LoadingT.infinity]
|
| 1159 |
+
sizes = [LoadingT.xs, LoadingT.sm, LoadingT.md, LoadingT.lg]
|
| 1160 |
+
rows = [Div(*[Loading((t,s)) for s in sizes], cls='flex gap-4') for t in types]
|
| 1161 |
+
return Div(*rows, cls='flex flex-col gap-4')
|
| 1162 |
+
|
| 1163 |
+
docs_loading = create_doc_section(
|
| 1164 |
+
H1("Loading Indicators API Reference"),
|
| 1165 |
+
fn2code_string(ex_loading1),
|
| 1166 |
+
fn2code_string(ex_loading2),
|
| 1167 |
+
Loading,
|
| 1168 |
+
LoadingT,
|
| 1169 |
+
title="Loading")
|
| 1170 |
+
|
| 1171 |
+
|
| 1172 |
+
# Charts
|
| 1173 |
+
def ex_line_chart():
|
| 1174 |
+
return ApexChart(
|
| 1175 |
+
opts={
|
| 1176 |
+
"chart": {"type":"line", "zoom":{"enabled": False}, "toolbar":{"show":False}},
|
| 1177 |
+
"series": [{"name":"Desktops", "data": [186, 305, 237, 73, 209, 214, 355]}],
|
| 1178 |
+
"xaxis": {"categories":["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]}
|
| 1179 |
+
},
|
| 1180 |
+
cls='max-w-md max-h-md'
|
| 1181 |
+
)
|
| 1182 |
+
|
| 1183 |
+
def ex_pie_chart():
|
| 1184 |
+
return ApexChart(
|
| 1185 |
+
opts={
|
| 1186 |
+
"chart": {"type":"pie", "zoom":{"enabled": False}, "toolbar":{"show":False}},
|
| 1187 |
+
"series": [186, 305, 237, 73, 209, 214, 355],
|
| 1188 |
+
"labels": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]
|
| 1189 |
+
},
|
| 1190 |
+
cls='max-w-md max-h-md'
|
| 1191 |
+
)
|
| 1192 |
+
|
| 1193 |
+
docs_charts = create_doc_section(
|
| 1194 |
+
H1("Charts API Reference"),
|
| 1195 |
+
P(
|
| 1196 |
+
"MonsterUI supports ", A("ApexCharts", href="https://apexcharts.com/", cls='underline'),
|
| 1197 |
+
", a javascript library for rendering different charts like line and pie charts. ",
|
| 1198 |
+
"See the full list of chart types ", A("here.", href="https://apexcharts.com/javascript-chart-demos/", cls='underline'),
|
| 1199 |
+
),
|
| 1200 |
+
P("To render a chart you'll need to include the ApexChart js in your app headers like this"),
|
| 1201 |
+
CodeSpan("app, rt = fast_app(hdrs=Theme.blue.headers(apex_charts=True))"),
|
| 1202 |
+
P("Then create an " , CodeSpan("ApexChart"), " component as shown in the examples below."),
|
| 1203 |
+
P("Generally, you should be able to take any chart from the ApexChart docs, convert the chart's options var to a python dict and plug it straight into MonsterUI's ApexChart component."),
|
| 1204 |
+
H2("Example usage", cls="mt-4"),
|
| 1205 |
+
H4("Line chart", cls="mt-4"),
|
| 1206 |
+
fn2code_string(ex_line_chart),
|
| 1207 |
+
H4("Pie chart", cls="mt-4"),
|
| 1208 |
+
fn2code_string(ex_pie_chart),
|
| 1209 |
+
ApexChart,
|
| 1210 |
+
title="Charts"
|
| 1211 |
+
)
|
MonsterUI/docs/api_reference/logo.svg
ADDED
|
|
MonsterUI/docs/apilist.txt
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# monsterui Module Documentation
|
| 2 |
+
|
| 3 |
+
## monsterui.core
|
| 4 |
+
|
| 5 |
+
- `class ThemeRadii(Enum)`
|
| 6 |
+
Members: none, sm, md, lg
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
- `class ThemeShadows`
|
| 10 |
+
|
| 11 |
+
- `class ThemeFont`
|
| 12 |
+
|
| 13 |
+
- `class Theme(Enum)`
|
| 14 |
+
Selector to choose theme and get all headers needed for app. Includes frankenui + tailwind + daisyui + highlight.js options
|
| 15 |
+
Members: slate, stone, gray, neutral, red, rose, orange, green, blue, yellow, violet, zinc
|
| 16 |
+
|
| 17 |
+
- `headers(self, mode, icons, daisy, highlightjs, katex, apex_charts, radii, shadows, font)`
|
| 18 |
+
Create frankenui and tailwind cdns
|
| 19 |
+
|
| 20 |
+
- `local_headers(self, mode, static_dir, icons, daisy, highlightjs, katex, apex_charts, radii, shadows, font)`
|
| 21 |
+
Create headers using local files downloaded from CDNs
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
## monsterui.daisy
|
| 25 |
+
|
| 26 |
+
- `class AlertT(Enum)`
|
| 27 |
+
Alert styles from DaisyUI
|
| 28 |
+
Members: info, success, warning, error
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
- `def Alert(*c, **kwargs)`
|
| 32 |
+
Alert informs users about important events.
|
| 33 |
+
|
| 34 |
+
- `class StepsT(Enum)`
|
| 35 |
+
Options for Steps
|
| 36 |
+
Members: vertical, horizonal
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
- `class StepT(Enum)`
|
| 40 |
+
Step styles for LiStep
|
| 41 |
+
Members: primary, secondary, accent, info, success, warning, error, neutral
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
- `def Steps(*li, **kwargs)`
|
| 45 |
+
Creates a steps container
|
| 46 |
+
|
| 47 |
+
- `def LiStep(*c, **kwargs)`
|
| 48 |
+
Creates a step list item
|
| 49 |
+
|
| 50 |
+
- `class LoadingT(Enum)`
|
| 51 |
+
Members: spinner, dots, ring, ball, bars, infinity, xs, sm, md, lg
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
- `def Loading(cls, htmx_indicator, **kwargs)`
|
| 55 |
+
Creates a loading animation component
|
| 56 |
+
|
| 57 |
+
- `class ToastHT(Enum)`
|
| 58 |
+
Horizontal position for Toast
|
| 59 |
+
Members: start, center, end
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
- `class ToastVT(Enum)`
|
| 63 |
+
Vertical position for Toast
|
| 64 |
+
Members: top, middle, bottom
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
## monsterui.foundations
|
| 68 |
+
|
| 69 |
+
> Data Structures and Utilties
|
| 70 |
+
|
| 71 |
+
- `def stringify(o)`
|
| 72 |
+
Converts input types into strings that can be passed to FT components
|
| 73 |
+
|
| 74 |
+
- `class VEnum(Enum)`
|
| 75 |
+
Members:
|
| 76 |
+
|
| 77 |
+
- `__str__(self)`
|
| 78 |
+
- `__add__(self, other)`
|
| 79 |
+
- `__radd__(self, other)`
|
| 80 |
+
|
| 81 |
+
## monsterui.franken
|
| 82 |
+
|
| 83 |
+
- `class TextT(Enum)`
|
| 84 |
+
Text Styles from https://franken-ui.dev/docs/text
|
| 85 |
+
Members: paragraph, lead, meta, gray, italic, xs, sm, lg, xl, light, normal, medium, bold, extrabold, muted, primary, secondary, success, warning, error, info, left, right, center, justify, start, end, top, middle, bottom, truncate, break_, nowrap, underline, highlight
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
- `class TextPresets(Enum)`
|
| 89 |
+
Common Typography Presets
|
| 90 |
+
Members: muted_sm, muted_lg, bold_sm, bold_lg, md_weight_sm, md_weight_muted
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
- `def CodeSpan(*c, **kwargs)`
|
| 94 |
+
A CodeSpan with Styling
|
| 95 |
+
|
| 96 |
+
- `def CodeBlock(*c, **kwargs)`
|
| 97 |
+
CodeBlock with Styling
|
| 98 |
+
|
| 99 |
+
- `def H1(*c, **kwargs)`
|
| 100 |
+
H1 with styling and appropriate size
|
| 101 |
+
|
| 102 |
+
- `def H2(*c, **kwargs)`
|
| 103 |
+
H2 with styling and appropriate size
|
| 104 |
+
|
| 105 |
+
- `def H3(*c, **kwargs)`
|
| 106 |
+
H3 with styling and appropriate size
|
| 107 |
+
|
| 108 |
+
- `def H4(*c, **kwargs)`
|
| 109 |
+
H4 with styling and appropriate size
|
| 110 |
+
|
| 111 |
+
- `def H5(*c, **kwargs)`
|
| 112 |
+
H5 with styling and appropriate size
|
| 113 |
+
|
| 114 |
+
- `def H6(*c, **kwargs)`
|
| 115 |
+
H6 with styling and appropriate size
|
| 116 |
+
|
| 117 |
+
- `def Subtitle(*c, **kwargs)`
|
| 118 |
+
Styled muted_sm text designed to go under Headings and Titles
|
| 119 |
+
|
| 120 |
+
- `def Q(*c, **kwargs)`
|
| 121 |
+
Styled quotation mark
|
| 122 |
+
|
| 123 |
+
- `def Em(*c, **kwargs)`
|
| 124 |
+
Styled emphasis text
|
| 125 |
+
|
| 126 |
+
- `def Strong(*c, **kwargs)`
|
| 127 |
+
Styled strong text
|
| 128 |
+
|
| 129 |
+
- `def I(*c, **kwargs)`
|
| 130 |
+
Styled italic text
|
| 131 |
+
|
| 132 |
+
- `def Small(*c, **kwargs)`
|
| 133 |
+
Styled small text
|
| 134 |
+
|
| 135 |
+
- `def Mark(*c, **kwargs)`
|
| 136 |
+
Styled highlighted text
|
| 137 |
+
|
| 138 |
+
- `def Del(*c, **kwargs)`
|
| 139 |
+
Styled deleted text
|
| 140 |
+
|
| 141 |
+
- `def Ins(*c, **kwargs)`
|
| 142 |
+
Styled inserted text
|
| 143 |
+
|
| 144 |
+
- `def Sub(*c, **kwargs)`
|
| 145 |
+
Styled subscript text
|
| 146 |
+
|
| 147 |
+
- `def Sup(*c, **kwargs)`
|
| 148 |
+
Styled superscript text
|
| 149 |
+
|
| 150 |
+
- `def Blockquote(*c, **kwargs)`
|
| 151 |
+
Blockquote with Styling
|
| 152 |
+
|
| 153 |
+
- `def Caption(*c, **kwargs)`
|
| 154 |
+
Styled caption text
|
| 155 |
+
|
| 156 |
+
- `def Cite(*c, **kwargs)`
|
| 157 |
+
Styled citation text
|
| 158 |
+
|
| 159 |
+
- `def Time(*c, **kwargs)`
|
| 160 |
+
Styled time element
|
| 161 |
+
|
| 162 |
+
- `def Address(*c, **kwargs)`
|
| 163 |
+
Styled address element
|
| 164 |
+
|
| 165 |
+
- `def Abbr(*c, **kwargs)`
|
| 166 |
+
Styled abbreviation with dotted underline
|
| 167 |
+
|
| 168 |
+
- `def Dfn(*c, **kwargs)`
|
| 169 |
+
Styled definition term with italic and medium weight
|
| 170 |
+
|
| 171 |
+
- `def Kbd(*c, **kwargs)`
|
| 172 |
+
Styled keyboard input with subtle background
|
| 173 |
+
|
| 174 |
+
- `def Samp(*c, **kwargs)`
|
| 175 |
+
Styled sample output with subtle background
|
| 176 |
+
|
| 177 |
+
- `def Var(*c, **kwargs)`
|
| 178 |
+
Styled variable with italic monospace
|
| 179 |
+
|
| 180 |
+
- `def Figure(*c, **kwargs)`
|
| 181 |
+
Styled figure container with card-like appearance
|
| 182 |
+
|
| 183 |
+
- `def Details(*c, **kwargs)`
|
| 184 |
+
Styled details element
|
| 185 |
+
|
| 186 |
+
- `def Summary(*c, **kwargs)`
|
| 187 |
+
Styled summary element
|
| 188 |
+
|
| 189 |
+
- `def Data(*c, **kwargs)`
|
| 190 |
+
Styled data element
|
| 191 |
+
|
| 192 |
+
- `def Meter(*c, **kwargs)`
|
| 193 |
+
Styled meter element
|
| 194 |
+
|
| 195 |
+
- `def S(*c, **kwargs)`
|
| 196 |
+
Styled strikethrough text (different semantic meaning from Del)
|
| 197 |
+
|
| 198 |
+
- `def U(*c, **kwargs)`
|
| 199 |
+
Styled underline (for proper names in Chinese, proper spelling etc)
|
| 200 |
+
|
| 201 |
+
- `def Output(*c, **kwargs)`
|
| 202 |
+
Styled output element for form results
|
| 203 |
+
|
| 204 |
+
- `def PicSumImg(h, w, id, grayscale, blur, **kwargs)`
|
| 205 |
+
Creates a placeholder image using https://picsum.photos/
|
| 206 |
+
|
| 207 |
+
- `def AccordionItem(title, *c)`
|
| 208 |
+
Creates a single item for use within an Accordion component, handling title, content, and open state.
|
| 209 |
+
|
| 210 |
+
- `def Accordion(*c, **kwargs)`
|
| 211 |
+
Creates a styled Accordion container using accordion component.
|
| 212 |
+
|
| 213 |
+
- `class ButtonT(Enum)`
|
| 214 |
+
Options for styling Buttons
|
| 215 |
+
Members: default, ghost, primary, secondary, destructive, text, link, xs, sm, lg, xl, icon
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
- `def Button(*c, **kwargs)`
|
| 219 |
+
Button with Styling (defaults to `submit` for form submission)
|
| 220 |
+
|
| 221 |
+
- `class ContainerT(Enum)`
|
| 222 |
+
Max width container sizes from https://franken-ui.dev/docs/container
|
| 223 |
+
Members: xs, sm, lg, xl, expand
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
- `class BackgroundT(Enum)`
|
| 227 |
+
Members: muted, primary, secondary, default
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
- `def Container(*c, **kwargs)`
|
| 231 |
+
Div to be used as a container that often wraps large sections or a page of content
|
| 232 |
+
|
| 233 |
+
- `def Titled(title, *c, **kwargs)`
|
| 234 |
+
Creates a standard page structure for titled page. Main(Container(title, content))
|
| 235 |
+
|
| 236 |
+
- `class DividerT(Enum)`
|
| 237 |
+
Divider Styles from https://franken-ui.dev/docs/divider
|
| 238 |
+
Members: icon, sm, vertical
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
- `def Divider(*c, **kwargs)`
|
| 242 |
+
Divider with default styling and margin
|
| 243 |
+
|
| 244 |
+
- `def DividerSplit(*c)`
|
| 245 |
+
Creates a simple horizontal line divider with configurable thickness and vertical spacing
|
| 246 |
+
|
| 247 |
+
- `def Article(*c, **kwargs)`
|
| 248 |
+
A styled article container for blog posts or similar content
|
| 249 |
+
|
| 250 |
+
- `def ArticleTitle(*c, **kwargs)`
|
| 251 |
+
A title component for use within an Article
|
| 252 |
+
|
| 253 |
+
- `def ArticleMeta(*c, **kwargs)`
|
| 254 |
+
A metadata component for use within an Article showing things like date, author etc
|
| 255 |
+
|
| 256 |
+
- `class SectionT(Enum)`
|
| 257 |
+
Section styles from https://franken-ui.dev/docs/section
|
| 258 |
+
Members: default, muted, primary, secondary, xs, sm, lg, xl, remove_vertical
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
- `def Section(*c, **kwargs)`
|
| 262 |
+
Section with styling and margins
|
| 263 |
+
|
| 264 |
+
- `def Form(*c, **kwargs)`
|
| 265 |
+
A Form with default spacing between form elements
|
| 266 |
+
|
| 267 |
+
- `def Fieldset(*c, **kwargs)`
|
| 268 |
+
A Fieldset with default styling
|
| 269 |
+
|
| 270 |
+
- `def Legend(*c, **kwargs)`
|
| 271 |
+
A Legend with default styling
|
| 272 |
+
|
| 273 |
+
- `def Input(*c, **kwargs)`
|
| 274 |
+
An Input with default styling
|
| 275 |
+
|
| 276 |
+
- `def Radio(*c, **kwargs)`
|
| 277 |
+
A Radio with default styling
|
| 278 |
+
|
| 279 |
+
- `def CheckboxX(*c, **kwargs)`
|
| 280 |
+
A Checkbox with default styling
|
| 281 |
+
|
| 282 |
+
- `def Range(*c, **kwargs)`
|
| 283 |
+
A Range with default styling
|
| 284 |
+
|
| 285 |
+
- `def TextArea(*c, **kwargs)`
|
| 286 |
+
A Textarea with default styling
|
| 287 |
+
|
| 288 |
+
- `def Switch(*c, **kwargs)`
|
| 289 |
+
A Switch with default styling
|
| 290 |
+
|
| 291 |
+
- `def Upload(*c, **kwargs)`
|
| 292 |
+
A file upload component with default styling
|
| 293 |
+
|
| 294 |
+
- `def UploadZone(*c, **kwargs)`
|
| 295 |
+
A file drop zone component with default styling
|
| 296 |
+
|
| 297 |
+
- `def FormLabel(*c, **kwargs)`
|
| 298 |
+
A Label with default styling
|
| 299 |
+
|
| 300 |
+
- `class LabelT(Enum)`
|
| 301 |
+
Members: primary, secondary, destructive
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
- `def Label(*c, **kwargs)`
|
| 305 |
+
FrankenUI labels, which look like pills
|
| 306 |
+
|
| 307 |
+
- `def UkFormSection(title, description, *c)`
|
| 308 |
+
A form section with a title, description and optional button
|
| 309 |
+
|
| 310 |
+
- `def GenericLabelInput(label, lbl_cls, input_cls, container, cls, id, input_fn, **kwargs)`
|
| 311 |
+
`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `LabelInput` which is created for you in this library
|
| 312 |
+
|
| 313 |
+
- `def LabelInput(label, lbl_cls, input_cls, cls, id, **kwargs)`
|
| 314 |
+
A `FormLabel` and `Input` pair that provides default spacing and links/names them based on id
|
| 315 |
+
|
| 316 |
+
- `def LabelRadio(label, lbl_cls, input_cls, container, cls, id, **kwargs)`
|
| 317 |
+
A FormLabel and Radio pair that provides default spacing and links/names them based on id
|
| 318 |
+
|
| 319 |
+
- `def LabelCheckboxX(label, lbl_cls, input_cls, container, cls, id, **kwargs)`
|
| 320 |
+
A FormLabel and CheckboxX pair that provides default spacing and links/names them based on id
|
| 321 |
+
|
| 322 |
+
- `def Options(*c)`
|
| 323 |
+
Helper function to wrap things into `Option`s for use in `Select`
|
| 324 |
+
|
| 325 |
+
- `def Select(*option, **kwargs)`
|
| 326 |
+
Creates a select dropdown with uk styling and option for adding a search box
|
| 327 |
+
|
| 328 |
+
- `def LabelSelect(*option, **kwargs)`
|
| 329 |
+
A FormLabel and Select pair that provides default spacing and links/names them based on id
|
| 330 |
+
|
| 331 |
+
- `@delegates(GenericLabelInput, but=['input_fn', 'cls']) def LabelRange(label, lbl_cls, input_cls, cls, id, value, min, max, step, label_range, **kwargs)`
|
| 332 |
+
A FormLabel and Range pair that provides default spacing and links/names them based on id
|
| 333 |
+
|
| 334 |
+
- `class AT(Enum)`
|
| 335 |
+
Link styles from https://franken-ui.dev/docs/link
|
| 336 |
+
Members: muted, text, reset, primary, classic
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
- `class ListT(Enum)`
|
| 340 |
+
List styles using Tailwind CSS
|
| 341 |
+
Members: disc, circle, square, decimal, hyphen, bullet, divider, striped
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
- `def ModalContainer(*c, **kwargs)`
|
| 345 |
+
Creates a modal container that components go in
|
| 346 |
+
|
| 347 |
+
- `def ModalDialog(*c, **kwargs)`
|
| 348 |
+
Creates a modal dialog
|
| 349 |
+
|
| 350 |
+
- `def ModalHeader(*c, **kwargs)`
|
| 351 |
+
Creates a modal header
|
| 352 |
+
|
| 353 |
+
- `def ModalBody(*c, **kwargs)`
|
| 354 |
+
Creates a modal body
|
| 355 |
+
|
| 356 |
+
- `def ModalFooter(*c, **kwargs)`
|
| 357 |
+
Creates a modal footer
|
| 358 |
+
|
| 359 |
+
- `def ModalTitle(*c, **kwargs)`
|
| 360 |
+
Creates a modal title
|
| 361 |
+
|
| 362 |
+
- `def ModalCloseButton(*c, **kwargs)`
|
| 363 |
+
Creates a button that closes a modal with js
|
| 364 |
+
|
| 365 |
+
- `def Modal(*c, **kwargs)`
|
| 366 |
+
Creates a modal with the appropriate classes to put the boilerplate in the appropriate places for you
|
| 367 |
+
|
| 368 |
+
- `def Placeholder(*c, **kwargs)`
|
| 369 |
+
Creates a placeholder
|
| 370 |
+
|
| 371 |
+
- `def Progress(*c, **kwargs)`
|
| 372 |
+
Creates a progress bar
|
| 373 |
+
|
| 374 |
+
- `def UkIcon(icon, height, width, stroke_width, cls, **kwargs)`
|
| 375 |
+
Creates an icon using lucide icons
|
| 376 |
+
|
| 377 |
+
- `def UkIconLink(icon, height, width, stroke_width, cls, button, **kwargs)`
|
| 378 |
+
Creates an icon link using lucide icons
|
| 379 |
+
|
| 380 |
+
- `def DiceBearAvatar(seed_name, h, w)`
|
| 381 |
+
Creates an Avatar using https://dicebear.com/
|
| 382 |
+
|
| 383 |
+
- `def Center(*c, **kwargs)`
|
| 384 |
+
Centers contents both vertically and horizontally by default
|
| 385 |
+
|
| 386 |
+
- `class FlexT(Enum)`
|
| 387 |
+
Flexbox modifiers using Tailwind CSS
|
| 388 |
+
Members: block, inline, left, center, right, between, around, stretch, top, middle, bottom, row, row_reverse, column, column_reverse, nowrap, wrap, wrap_reverse
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
- `def Grid(*div, **kwargs)`
|
| 392 |
+
Creates a responsive grid layout with smart defaults based on content
|
| 393 |
+
|
| 394 |
+
- `def DivFullySpaced(*c, **kwargs)`
|
| 395 |
+
Creates a flex div with it's components having as much space between them as possible
|
| 396 |
+
|
| 397 |
+
- `def DivCentered(*c, **kwargs)`
|
| 398 |
+
Creates a flex div with it's components centered in it
|
| 399 |
+
|
| 400 |
+
- `def DivLAligned(*c, **kwargs)`
|
| 401 |
+
Creates a flex div with it's components aligned to the left
|
| 402 |
+
|
| 403 |
+
- `def DivRAligned(*c, **kwargs)`
|
| 404 |
+
Creates a flex div with it's components aligned to the right
|
| 405 |
+
|
| 406 |
+
- `def DivVStacked(*c, **kwargs)`
|
| 407 |
+
Creates a flex div with it's components stacked vertically
|
| 408 |
+
|
| 409 |
+
- `def DivHStacked(*c, **kwargs)`
|
| 410 |
+
Creates a flex div with it's components stacked horizontally
|
| 411 |
+
|
| 412 |
+
- `class NavT(Enum)`
|
| 413 |
+
Members: default, primary, secondary
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
- `def NavContainer(*li, **kwargs)`
|
| 417 |
+
Creates a navigation container (useful for creating a sidebar navigation). A Nav is a list (NavBar is something different)
|
| 418 |
+
|
| 419 |
+
- `def NavParentLi(*nav_container, **kwargs)`
|
| 420 |
+
Creates a navigation list item with a parent nav for nesting
|
| 421 |
+
|
| 422 |
+
- `def NavDividerLi(*c, **kwargs)`
|
| 423 |
+
Creates a navigation list item with a divider
|
| 424 |
+
|
| 425 |
+
- `def NavHeaderLi(*c, **kwargs)`
|
| 426 |
+
Creates a navigation list item with a header
|
| 427 |
+
|
| 428 |
+
- `def NavSubtitle(*c, **kwargs)`
|
| 429 |
+
Creates a navigation subtitle
|
| 430 |
+
|
| 431 |
+
- `def NavCloseLi(*c, **kwargs)`
|
| 432 |
+
Creates a navigation list item with a close button
|
| 433 |
+
|
| 434 |
+
- `class ScrollspyT(Enum)`
|
| 435 |
+
Members: underline, bold
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
- `def NavBar(*c)`
|
| 439 |
+
Creates a responsive navigation bar with mobile menu support
|
| 440 |
+
|
| 441 |
+
- `def SliderContainer(*c, **kwargs)`
|
| 442 |
+
Creates a slider container
|
| 443 |
+
|
| 444 |
+
- `def SliderItems(*c, **kwargs)`
|
| 445 |
+
Creates a slider items container
|
| 446 |
+
|
| 447 |
+
- `def SliderNav(cls, prev_cls, next_cls, **kwargs)`
|
| 448 |
+
Navigation arrows for Slider component
|
| 449 |
+
|
| 450 |
+
- `def Slider(*c, **kwargs)`
|
| 451 |
+
Creates a slider with optional navigation arrows
|
| 452 |
+
|
| 453 |
+
- `def DropDownNavContainer(*li, **kwargs)`
|
| 454 |
+
A Nav that is part of a DropDown
|
| 455 |
+
|
| 456 |
+
- `def TabContainer(*li, **kwargs)`
|
| 457 |
+
A TabContainer where children will be different tabs
|
| 458 |
+
|
| 459 |
+
- `class CardT(Enum)`
|
| 460 |
+
Card styles from UIkit
|
| 461 |
+
Members: default, primary, secondary, destructive, hover
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
- `def CardTitle(*c, **kwargs)`
|
| 465 |
+
Creates a card title
|
| 466 |
+
|
| 467 |
+
- `def CardHeader(*c, **kwargs)`
|
| 468 |
+
Creates a card header
|
| 469 |
+
|
| 470 |
+
- `def CardBody(*c, **kwargs)`
|
| 471 |
+
Creates a card body
|
| 472 |
+
|
| 473 |
+
- `def CardFooter(*c, **kwargs)`
|
| 474 |
+
Creates a card footer
|
| 475 |
+
|
| 476 |
+
- `def CardContainer(*c, **kwargs)`
|
| 477 |
+
Creates a card container
|
| 478 |
+
|
| 479 |
+
- `def Card(*c, **kwargs)`
|
| 480 |
+
Creates a Card with a header, body, and footer
|
| 481 |
+
|
| 482 |
+
- `class TableT(Enum)`
|
| 483 |
+
Members: divider, striped, hover, sm, lg, justify, middle, responsive
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
- `def Table(*c, **kwargs)`
|
| 487 |
+
Creates a table
|
| 488 |
+
|
| 489 |
+
- `def TableFromLists(header_data, body_data, footer_data, header_cell_render, body_cell_render, footer_cell_render, cls, sortable, **kwargs)`
|
| 490 |
+
Creates a Table from a list of header data and a list of lists of body data
|
| 491 |
+
|
| 492 |
+
- `def TableFromDicts(header_data, body_data, footer_data, header_cell_render, body_cell_render, footer_cell_render, cls, sortable, **kwargs)`
|
| 493 |
+
Creates a Table from a list of header data and a list of dicts of body data
|
| 494 |
+
|
| 495 |
+
- `def apply_classes(html_str, class_map, class_map_mods)`
|
| 496 |
+
Apply classes to html string
|
| 497 |
+
|
| 498 |
+
- `class FrankenRenderer`
|
| 499 |
+
Custom renderer for Franken UI that handles image paths
|
| 500 |
+
|
| 501 |
+
- `def __init__(self, *args, **kwargs)`
|
| 502 |
+
- `def render_image(self, token)`
|
| 503 |
+
Modify image paths if they're relative and self.img_dir is specified
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
- `def render_md(md_content, class_map, class_map_mods, img_dir, renderer)`
|
| 507 |
+
Renders markdown using mistletoe and lxml with custom image handling
|
| 508 |
+
|
| 509 |
+
- `def ThemePicker(color, radii, shadows, font, mode, cls, custom_themes)`
|
| 510 |
+
Theme picker component with configurable sections
|
| 511 |
+
|
| 512 |
+
- `def LightboxContainer(*lightboxitem, **kwargs)`
|
| 513 |
+
Lightbox container that will hold `LightboxItems`
|
| 514 |
+
|
| 515 |
+
- `def LightboxItem(*c, **kwargs)`
|
| 516 |
+
Anchor tag with appropriate structure to go inside a `LightBoxContainer`
|
| 517 |
+
|
| 518 |
+
- `def ApexChart(**kws)`
|
| 519 |
+
Apex chart component
|
| 520 |
+
|
MonsterUI/docs/cf_addns.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# https://github.com/cloudflare/cloudflare-python/blob/main/api.md
|
| 3 |
+
from fastcore.script import *
|
| 4 |
+
from cloudflare import Cloudflare
|
| 5 |
+
|
| 6 |
+
@call_parse
|
| 7 |
+
def add_dns_record(
|
| 8 |
+
record_type: str, # Type of DNS record (CNAME or A)
|
| 9 |
+
target: str, # Target IP address or domain name
|
| 10 |
+
record: str, # Record name (without the zone)
|
| 11 |
+
zone: str, # Zone name
|
| 12 |
+
proxied: bool_arg=True # Use CF proxy?
|
| 13 |
+
):
|
| 14 |
+
cf = Cloudflare()
|
| 15 |
+
zones = cf.zones.list(name=zone)
|
| 16 |
+
if not zones: raise ValueError(f"Zone '{zone}' not found")
|
| 17 |
+
zid = zones.result[0].id
|
| 18 |
+
cf.dns.records.create(zone_id=zid, type=record_type.upper(), name=f"{record}.{zone}", content=target, proxied=proxied)
|
MonsterUI/docs/createllms.sh
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
from fastcore.utils import *
|
| 4 |
+
import httpx
|
| 5 |
+
import api_reference.api_reference as api_reference
|
| 6 |
+
def fname2title(ref_fn_name): return ref_fn_name[5:].replace('_',' | ').title()
|
| 7 |
+
|
| 8 |
+
def create_llms_txt():
|
| 9 |
+
# Get examples
|
| 10 |
+
base = "https://monsterui.answer.ai"
|
| 11 |
+
examples = [f for f in Path('examples').glob('*.py') if not f.name.startswith('__') and f.name.endswith('.py')]
|
| 12 |
+
example_links = []
|
| 13 |
+
for f in examples:
|
| 14 |
+
content = httpx.get(f"{base}/{f.stem}/md").text
|
| 15 |
+
first_line = content.split('\n')[0].strip('# ').strip('"')
|
| 16 |
+
example_links.append(f"[{f.stem.title()}]({base}/{f.name[:-3]}/md): {first_line}")
|
| 17 |
+
|
| 18 |
+
# Get reference files with their H1 headings
|
| 19 |
+
reference_fns = L([o for o in dir(api_reference) if o.startswith('docs_')])
|
| 20 |
+
api_links = []
|
| 21 |
+
for f in reference_fns:
|
| 22 |
+
content = httpx.get(f"{base}/api_ref/{f}/md").text
|
| 23 |
+
first_heading = content.split('\n')[0].strip('# ')
|
| 24 |
+
api_links.append(f"[{fname2title(f)}]({base}/api_ref/{f}/md): {first_heading}")
|
| 25 |
+
|
| 26 |
+
# Create content
|
| 27 |
+
content = [
|
| 28 |
+
"# MonsterUI Documentation",
|
| 29 |
+
'''
|
| 30 |
+
> MonsterUI is a python library which brings styling to python for FastHTML apps.
|
| 31 |
+
|
| 32 |
+
'''
|
| 33 |
+
"## API Reference",
|
| 34 |
+
'- [API List](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/apilist.txt): Complete API Reference',
|
| 35 |
+
"",
|
| 36 |
+
"## Examples",
|
| 37 |
+
*[f'- {a}' for a in example_links],
|
| 38 |
+
"",
|
| 39 |
+
"## Optional",
|
| 40 |
+
*[f'- {a}' for a in api_links],
|
| 41 |
+
"- [Layout](https://monsterui.answer.ai/tutorial_layout/md): MonsterUI Page Layout Guide",
|
| 42 |
+
"- [Spacing](https://monsterui.answer.ai/tutorial_spacing/md): Padding & Margin & Spacing, Oh my! (MonsterUI Spacing Guide)",
|
| 43 |
+
]
|
| 44 |
+
|
| 45 |
+
# Write to file
|
| 46 |
+
Path('llms.txt').write_text('\n'.join(content))
|
| 47 |
+
|
| 48 |
+
create_llms_txt()
|
MonsterUI/docs/custom_theme.css
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* custom_theme.css */
|
| 2 |
+
|
| 3 |
+
.uk-theme-grass {
|
| 4 |
+
--background: 78 47% 99%;
|
| 5 |
+
--foreground: 78 51% 0%;
|
| 6 |
+
--muted: 78 12% 85%;
|
| 7 |
+
--muted-foreground: 78 8% 38%;
|
| 8 |
+
--popover: 78 47% 99%;
|
| 9 |
+
--popover-foreground: 78 51% 0%;
|
| 10 |
+
--card: 78 47% 99%;
|
| 11 |
+
--card-foreground: 78 51% 0%;
|
| 12 |
+
--border: 78 2% 93%;
|
| 13 |
+
--input: 78 2% 93%;
|
| 14 |
+
--primary: 78 28% 60%;
|
| 15 |
+
--primary-foreground: 0 0% 0%;
|
| 16 |
+
--secondary: 78 8% 81%;
|
| 17 |
+
--secondary-foreground: 78 8% 21%;
|
| 18 |
+
--accent: 78 8% 81%;
|
| 19 |
+
--accent-foreground: 78 8% 21%;
|
| 20 |
+
--destructive: 17 86% 32%;
|
| 21 |
+
--destructive-foreground: 17 86% 92%;
|
| 22 |
+
--ring: 78 28% 60%;
|
| 23 |
+
--chart-1: 78 28% 60%;
|
| 24 |
+
--chart-2: 78 8% 81%;
|
| 25 |
+
--chart-3: 78 8% 81%;
|
| 26 |
+
--chart-4: 78 8% 84%;
|
| 27 |
+
--chart-5: 78 31% 60%;
|
| 28 |
+
--radius: 0.5rem;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
.dark.uk-theme-grass {
|
| 32 |
+
--background: 78 48% 2%;
|
| 33 |
+
--foreground: 78 24% 99%;
|
| 34 |
+
--muted: 78 12% 15%;
|
| 35 |
+
--muted-foreground: 78 8% 62%;
|
| 36 |
+
--popover: 78 48% 2%;
|
| 37 |
+
--popover-foreground: 78 24% 99%;
|
| 38 |
+
--card: 78 48% 2%;
|
| 39 |
+
--card-foreground: 78 24% 99%;
|
| 40 |
+
--border: 78 2% 14%;
|
| 41 |
+
--input: 78 2% 14%;
|
| 42 |
+
--primary: 78 28% 60%;
|
| 43 |
+
--primary-foreground: 0 0% 0%;
|
| 44 |
+
--secondary: 78 8% 14%;
|
| 45 |
+
--secondary-foreground: 78 8% 74%;
|
| 46 |
+
--accent: 78 8% 14%;
|
| 47 |
+
--accent-foreground: 78 8% 74%;
|
| 48 |
+
--destructive: 17 86% 60%;
|
| 49 |
+
--destructive-foreground: 17 86% 0%;
|
| 50 |
+
--ring: 78 28% 60%;
|
| 51 |
+
--chart-1: 78 28% 60%;
|
| 52 |
+
--chart-2: 78 8% 14%;
|
| 53 |
+
--chart-3: 78 8% 14%;
|
| 54 |
+
--chart-4: 78 8% 17%;
|
| 55 |
+
--chart-5: 78 31% 60%;
|
| 56 |
+
}
|
MonsterUI/docs/data_/mail.json
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": "6c84fb90-12c4-11e1-840d-7b25c5ee775a",
|
| 4 |
+
"initial": "WS",
|
| 5 |
+
"name": "William Smith",
|
| 6 |
+
"email": "[email protected]",
|
| 7 |
+
"subject": "Meeting Tomorrow",
|
| 8 |
+
"text": "Hi, let's have a meeting tomorrow to discuss the project. I've been reviewing the project details and have some ideas I'd like to share. It's crucial that we align on our next steps to ensure the project's success.\n\nPlease come prepared with any questions or insights you may have. Looking forward to our meeting!\n\nBest regards, William",
|
| 9 |
+
"date": "2023-10-22T09:00:00",
|
| 10 |
+
"read": true,
|
| 11 |
+
"active": true,
|
| 12 |
+
"labels": ["meeting", "work", "important"]
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"id": "110e8400-e29b-11d4-a716-446655440000",
|
| 16 |
+
"initial": "AS",
|
| 17 |
+
"name": "Alice Smith",
|
| 18 |
+
"email": "[email protected]",
|
| 19 |
+
"subject": "Re: Project Update",
|
| 20 |
+
"text": "Thank you for the project update. It looks great! I've gone through the report, and the progress is impressive. The team has done a fantastic job, and I appreciate the hard work everyone has put in.\n\nI have a few minor suggestions that I'll include in the attached document.\n\nLet's discuss these during our next meeting. Keep up the excellent work!\n\nBest regards, Alice",
|
| 21 |
+
"date": "2023-10-22T10:30:00",
|
| 22 |
+
"read": true,
|
| 23 |
+
"active": false,
|
| 24 |
+
"labels": ["work", "important"]
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"id": "3e7c3f6d-bdf5-46ae-8d90-171300f27ae2",
|
| 28 |
+
"initial": "BJ",
|
| 29 |
+
"name": "Bob Johnson",
|
| 30 |
+
"email": "[email protected]",
|
| 31 |
+
"subject": "Weekend Plans",
|
| 32 |
+
"text": "Any plans for the weekend? I was thinking of going hiking in the nearby mountains. It's been a while since we had some outdoor fun.\n\nIf you're interested, let me know, and we can plan the details. It'll be a great way to unwind and enjoy nature.\n\nLooking forward to your response!\n\nBest, Bob",
|
| 33 |
+
"date": "2023-04-10T11:45:00",
|
| 34 |
+
"read": true,
|
| 35 |
+
"active": false,
|
| 36 |
+
"labels": ["personal"]
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"id": "61c35085-72d7-42b4-8d62-738f700d4b92",
|
| 40 |
+
"initial": "ED",
|
| 41 |
+
"name": "Emily Davis",
|
| 42 |
+
"email": "[email protected]",
|
| 43 |
+
"subject": "Re: Question about Budget",
|
| 44 |
+
"text": "I have a question about the budget for the upcoming project. It seems like there's a discrepancy in the allocation of resources.\n\nI've reviewed the budget report and identified a few areas where we might be able to optimize our spending without compromising the project's quality.\n\nI've attached a detailed analysis for your reference. Let's discuss this further in our next meeting.\n\nThanks, Emily",
|
| 45 |
+
"date": "2023-03-25T13:15:00",
|
| 46 |
+
"read": false,
|
| 47 |
+
"active": false,
|
| 48 |
+
"labels": ["work", "budget"]
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"id": "8f7b5db9-d935-4e42-8e05-1f1d0a3dfb97",
|
| 52 |
+
"initial": "MW",
|
| 53 |
+
"name": "Michael Wilson",
|
| 54 |
+
"email": "[email protected]",
|
| 55 |
+
"subject": "Important Announcement",
|
| 56 |
+
"text": "I have an important announcement to make during our team meeting. It pertains to a strategic shift in our approach to the upcoming product launch. We've received valuable feedback from our beta testers, and I believe it's time to make some adjustments to better meet our customers' needs.\n\nThis change is crucial to our success, and I look forward to discussing it with the team. Please be prepared to share your insights during the meeting.\n\nRegards, Michael",
|
| 57 |
+
"date": "2023-03-10T15:00:00",
|
| 58 |
+
"read": false,
|
| 59 |
+
"active": false,
|
| 60 |
+
"labels": ["meeting", "work", "important"]
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"id": "1f0f2c02-e299-40de-9b1d-86ef9e42126b",
|
| 64 |
+
"initial": "SB",
|
| 65 |
+
"name": "Sarah Brown",
|
| 66 |
+
"email": "[email protected]",
|
| 67 |
+
"subject": "Re: Feedback on Proposal",
|
| 68 |
+
"text": "Thank you for your feedback on the proposal. It looks great! I'm pleased to hear that you found it promising. The team worked diligently to address all the key points you raised, and I believe we now have a strong foundation for the project.\n\nI've attached the revised proposal for your review.\n\nPlease let me know if you have any further comments or suggestions. Looking forward to your response.\n\nBest regards, Sarah",
|
| 69 |
+
"date": "2023-02-15T16:30:00",
|
| 70 |
+
"read": true,
|
| 71 |
+
"active": false,
|
| 72 |
+
"labels": ["work"]
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"id": "17c0a96d-4415-42b1-8b4f-764efab57f66",
|
| 76 |
+
"initial": "DL",
|
| 77 |
+
"name": "David Lee",
|
| 78 |
+
"email": "[email protected]",
|
| 79 |
+
"subject": "New Project Idea",
|
| 80 |
+
"text": "I have an exciting new project idea to discuss with you. It involves expanding our services to target a niche market that has shown considerable growth in recent months.\n\nI've prepared a detailed proposal outlining the potential benefits and the strategy for execution.\n\nThis project has the potential to significantly impact our business positively. Let's set up a meeting to dive into the details and determine if it aligns with our current goals.\n\nBest regards, David",
|
| 81 |
+
"date": "2023-01-28T17:45:00",
|
| 82 |
+
"read": false,
|
| 83 |
+
"active": false,
|
| 84 |
+
"labels": ["meeting", "work", "important"]
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"id": "2f0130cb-39fc-44c4-bb3c-0a4337edaaab",
|
| 88 |
+
"initial": "OW",
|
| 89 |
+
"name": "Olivia Wilson",
|
| 90 |
+
"email": "[email protected]",
|
| 91 |
+
"subject": "Vacation Plans",
|
| 92 |
+
"text": "Let's plan our vacation for next month. What do you think? I've been thinking of visiting a tropical paradise, and I've put together some destination options.\n\nI believe it's time for us to unwind and recharge. Please take a look at the options and let me know your preferences.\n\nWe can start making arrangements to ensure a smooth and enjoyable trip.\n\nExcited to hear your thoughts! Olivia",
|
| 93 |
+
"date": "2022-12-20T18:30:00",
|
| 94 |
+
"read": true,
|
| 95 |
+
"active": false,
|
| 96 |
+
"labels": ["personal"]
|
| 97 |
+
},
|
| 98 |
+
{
|
| 99 |
+
"id": "de305d54-75b4-431b-adb2-eb6b9e546014",
|
| 100 |
+
"initial": "JM",
|
| 101 |
+
"name": "James Martin",
|
| 102 |
+
"email": "[email protected]",
|
| 103 |
+
"subject": "Re: Conference Registration",
|
| 104 |
+
"text": "I've completed the registration for the conference next month. The event promises to be a great networking opportunity, and I'm looking forward to attending the various sessions and connecting with industry experts.\n\nI've also attached the conference schedule for your reference.\n\nIf there are any specific topics or sessions you'd like me to explore, please let me know. It's an exciting event, and I'll make the most of it.\n\nBest regards, James",
|
| 105 |
+
"date": "2022-11-30T19:15:00",
|
| 106 |
+
"read": true,
|
| 107 |
+
"active": false,
|
| 108 |
+
"labels": ["work", "conference"]
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"id": "7dd90c63-00f6-40f3-bd87-5060a24e8ee7",
|
| 112 |
+
"initial": "SW",
|
| 113 |
+
"name": "Sophia White",
|
| 114 |
+
"email": "[email protected]",
|
| 115 |
+
"subject": "Team Dinner",
|
| 116 |
+
"text": "Let's have a team dinner next week to celebrate our success. We've achieved some significant milestones, and it's time to acknowledge our hard work and dedication.\n\nI've made reservations at a lovely restaurant, and I'm sure it'll be an enjoyable evening.\n\nPlease confirm your availability and any dietary preferences. Looking forward to a fun and memorable dinner with the team!\n\nBest, Sophia",
|
| 117 |
+
"date": "2022-11-05T20:30:00",
|
| 118 |
+
"read": false,
|
| 119 |
+
"active": false,
|
| 120 |
+
"labels": ["meeting", "work"]
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"id": "99a88f78-3eb4-4d87-87b7-7b15a49a0a05",
|
| 124 |
+
"initial": "DJ",
|
| 125 |
+
"name": "Daniel Johnson",
|
| 126 |
+
"email": "[email protected]",
|
| 127 |
+
"subject": "Feedback Request",
|
| 128 |
+
"text": "I'd like your feedback on the latest project deliverables. We've made significant progress, and I value your input to ensure we're on the right track.\n\nI've attached the deliverables for your review, and I'm particularly interested in any areas where you think we can further enhance the quality or efficiency.\n\nYour feedback is invaluable, and I appreciate your time and expertise. Let's work together to make this project a success.\n\nRegards, Daniel",
|
| 129 |
+
"date": "2022-10-22T09:30:00",
|
| 130 |
+
"read": false,
|
| 131 |
+
"active": false,
|
| 132 |
+
"labels": ["work"]
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
| 136 |
+
"initial": "AT",
|
| 137 |
+
"name": "Ava Taylor",
|
| 138 |
+
"email": "[email protected]",
|
| 139 |
+
"subject": "Re: Meeting Agenda",
|
| 140 |
+
"text": "Here's the agenda for our meeting next week. I've included all the topics we need to cover, as well as time allocations for each.\n\nIf you have any additional items to discuss or any specific points to address, please let me know, and we can integrate them into the agenda.\n\nIt's essential that our meeting is productive and addresses all relevant matters.\n\nLooking forward to our meeting! Ava",
|
| 141 |
+
"date": "2022-10-10T10:45:00",
|
| 142 |
+
"read": true,
|
| 143 |
+
"active": false,
|
| 144 |
+
"labels": ["meeting", "work"]
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"id": "c1a0ecb4-2540-49c5-86f8-21e5ce79e4e6",
|
| 148 |
+
"initial": "WA",
|
| 149 |
+
"name": "William Anderson",
|
| 150 |
+
"email": "[email protected]",
|
| 151 |
+
"subject": "Product Launch Update",
|
| 152 |
+
"text": "The product launch is on track. I'll provide an update during our call. We've made substantial progress in the development and marketing of our new product.\n\nI'm excited to share the latest updates with you during our upcoming call. It's crucial that we coordinate our efforts to ensure a successful launch. Please come prepared with any questions or insights you may have.\n\nLet's make this product launch a resounding success!\n\nBest regards, William",
|
| 153 |
+
"date": "2022-09-20T12:00:00",
|
| 154 |
+
"read": false,
|
| 155 |
+
"active": false,
|
| 156 |
+
"labels": ["meeting", "work", "important"]
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"id": "ba54eefd-4097-4949-99f2-2a9ae4d1a836",
|
| 160 |
+
"initial": "MH",
|
| 161 |
+
"name": "Mia Harris",
|
| 162 |
+
"email": "[email protected]",
|
| 163 |
+
"subject": "Re: Travel Itinerary",
|
| 164 |
+
"text": "I've received the travel itinerary. It looks great! Thank you for your prompt assistance in arranging the details. I've reviewed the schedule and the accommodations, and everything seems to be in order. I'm looking forward to the trip, and I'm confident it'll be a smooth and enjoyable experience.\n\nIf there are any specific activities or attractions you recommend at our destination, please feel free to share your suggestions.\n\nExcited for the trip! Mia",
|
| 165 |
+
"date": "2022-09-10T13:15:00",
|
| 166 |
+
"read": true,
|
| 167 |
+
"active": false,
|
| 168 |
+
"labels": ["personal", "travel"]
|
| 169 |
+
},
|
| 170 |
+
{
|
| 171 |
+
"id": "df09b6ed-28bd-4e0c-85a9-9320ec5179aa",
|
| 172 |
+
"initial": "EC",
|
| 173 |
+
"name": "Ethan Clark",
|
| 174 |
+
"email": "[email protected]",
|
| 175 |
+
"subject": "Team Building Event",
|
| 176 |
+
"text": "Let's plan a team-building event for our department. Team cohesion and morale are vital to our success, and I believe a well-organized team-building event can be incredibly beneficial. I've done some research and have a few ideas for fun and engaging activities.\n\nPlease let me know your thoughts and availability. We want this event to be both enjoyable and productive.\n\nTogether, we'll strengthen our team and boost our performance.\n\nRegards, Ethan",
|
| 177 |
+
"date": "2022-08-25T15:30:00",
|
| 178 |
+
"read": false,
|
| 179 |
+
"active": false,
|
| 180 |
+
"labels": ["meeting", "work"]
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"id": "d67c1842-7f8b-4b4b-9be1-1b3b1ab4611d",
|
| 184 |
+
"initial": "CH",
|
| 185 |
+
"name": "Chloe Hall",
|
| 186 |
+
"email": "[email protected]",
|
| 187 |
+
"subject": "Re: Budget Approval",
|
| 188 |
+
"text": "The budget has been approved. We can proceed with the project. I'm delighted to inform you that our budget proposal has received the green light from the finance department. This is a significant milestone, and it means we can move forward with the project as planned.\n\nI've attached the finalized budget for your reference. Let's ensure that we stay on track and deliver the project on time and within budget.\n\nIt's an exciting time for us! Chloe",
|
| 189 |
+
"date": "2022-08-10T16:45:00",
|
| 190 |
+
"read": true,
|
| 191 |
+
"active": false,
|
| 192 |
+
"labels": ["work", "budget"]
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"id": "6c9a7f94-8329-4d70-95d3-51f68c186ae1",
|
| 196 |
+
"initial": "ST",
|
| 197 |
+
"name": "Samuel Turner",
|
| 198 |
+
"email": "[email protected]",
|
| 199 |
+
"subject": "Weekend Hike",
|
| 200 |
+
"text": "Who's up for a weekend hike in the mountains? I've been craving some outdoor adventure, and a hike in the mountains sounds like the perfect escape. If you're up for the challenge, we can explore some scenic trails and enjoy the beauty of nature.\n\nI've done some research and have a few routes in mind.\n\nLet me know if you're interested, and we can plan the details.\n\nIt's sure to be a memorable experience! Samuel",
|
| 201 |
+
"date": "2022-07-28T17:30:00",
|
| 202 |
+
"read": false,
|
| 203 |
+
"active": false,
|
| 204 |
+
"labels": ["personal"]
|
| 205 |
+
}
|
| 206 |
+
]
|
MonsterUI/docs/data_/status_list.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[{"selected": true, "id": "TASK-8782", "title": "You can't compress the program without quantifying the open-source SSD pixel!", "status": "progress", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-7878", "title": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!", "status": "backlog", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-7839", "title": "We need to bypass the neural TCP card!", "status": "todo", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-5562", "title": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!", "status": "backlog", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-8686", "title": "I'll parse the wireless SSL protocol, that should driver the API panel!", "status": "cancelled", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-1280", "title": "Use the digital TLS panel, then you can transmit the haptic system!", "status": "done", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-7262", "title": "The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!", "status": "done", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-1138", "title": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-7184", "title": "We need to program the back-end THX pixel!", "status": "todo", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-5160", "title": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!", "status": "progress", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-5618", "title": "Generating the driver won't do anything, we need to index the online SSL application!", "status": "done", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-6699", "title": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!", "status": "backlog", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-2858", "title": "We need to override the online UDP bus!", "status": "backlog", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-9864", "title": "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!", "status": "done", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-8404", "title": "We need to generate the virtual HEX alarm!", "status": "progress", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-5365", "title": "Backing up the pixel won't do anything, we need to transmit the primary IB array!", "status": "progress", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1780", "title": "The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!", "status": "todo", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-6938", "title": "Use the redundant SCSI application, then you can hack the optical alarm!", "status": "todo", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-9885", "title": "We need to compress the auxiliary VGA driver!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-3216", "title": "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!", "status": "backlog", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-9285", "title": "The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!", "status": "todo", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-1024", "title": "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!", "status": "progress", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-7068", "title": "You can't generate the capacitor without indexing the wireless HEX pixel!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-6502", "title": "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!", "status": "todo", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-5326", "title": "We need to hack the redundant UTF8 transmitter!", "status": "todo", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-6274", "title": "Use the virtual PCI circuit, then you can parse the bluetooth alarm!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1571", "title": "I'll input the neural DRAM circuit, that should protocol the SMTP interface!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-9518", "title": "Compressing the interface won't do anything, we need to compress the online SDD matrix!", "status": "cancelled", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-5581", "title": "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!", "status": "backlog", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-2197", "title": "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!", "status": "todo", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-8484", "title": "We need to parse the solid state UDP firewall!", "status": "progress", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-9892", "title": "If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!", "status": "done", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-9616", "title": "We need to synthesize the cross-platform ASCII pixel!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-9744", "title": "Use the back-end IP card, then you can input the solid state hard drive!", "status": "done", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1376", "title": "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!", "status": "backlog", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-7382", "title": "If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!", "status": "todo", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-2290", "title": "I'll compress the virtual JSON panel, that should application the UTF8 bus!", "status": "cancelled", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-1533", "title": "You can't input the firewall without overriding the wireless TCP firewall!", "status": "done", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-4920", "title": "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!", "status": "progress", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-5168", "title": "If we synthesize the bus, we can get to the IP panel through the virtual TLS array!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-7103", "title": "We need to parse the multi-byte EXE bandwidth!", "status": "cancelled", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-4314", "title": "If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!", "status": "progress", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-3415", "title": "Use the cross-platform XML application, then you can quantify the solid state feed!", "status": "todo", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-8339", "title": "Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6995", "title": "Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!", "status": "todo", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-8053", "title": "If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!", "status": "todo", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-4336", "title": "If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!", "status": "todo", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-8790", "title": "I'll back up the optical COM alarm, that should alarm the RSS capacitor!", "status": "done", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-8980", "title": "Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-7342", "title": "Use the neural CLI card, then you can parse the online port!", "status": "backlog", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-5608", "title": "I'll hack the haptic SSL program, that should bus the UDP transmitter!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1606", "title": "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!", "status": "done", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-7872", "title": "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!", "status": "cancelled", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-4167", "title": "Use the cross-platform SMS circuit, then you can synthesize the optical feed!", "status": "cancelled", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-9581", "title": "You can't index the port without hacking the cross-platform XSS monitor!", "status": "backlog", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-8806", "title": "We need to bypass the back-end SSL panel!", "status": "done", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-6542", "title": "Try to quantify the RSS firewall, maybe it will quantify the open-source system!", "status": "done", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6806", "title": "The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-9549", "title": "You can't bypass the bus without connecting the neural JBOD bus!", "status": "todo", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-1075", "title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!", "status": "done", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-1427", "title": "Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!", "status": "done", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-1907", "title": "Hacking the circuit won't do anything, we need to back up the online DRAM system!", "status": "todo", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-4309", "title": "If we generate the system, we can get to the TCP sensor through the optical GB pixel!", "status": "backlog", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3973", "title": "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!", "status": "todo", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-7962", "title": "Use the wireless RAM program, then you can hack the cross-platform feed!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-3360", "title": "You can't quantify the program without synthesizing the neural OCR interface!", "status": "done", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-9887", "title": "Use the auxiliary ASCII sensor, then you can connect the solid state port!", "status": "backlog", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3649", "title": "I'll input the virtual USB system, that should circuit the DNS monitor!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-3586", "title": "If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!", "status": "progress", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-5150", "title": "I'll hack the wireless XSS port, that should transmitter the IP interface!", "status": "cancelled", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-3652", "title": "The SQL interface is down, override the optical bus so we can program the ASCII interface!", "status": "backlog", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6884", "title": "Use the digital PCI circuit, then you can synthesize the multi-byte microchip!", "status": "cancelled", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-1591", "title": "We need to connect the mobile XSS driver!", "status": "progress", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-3802", "title": "Try to override the ASCII protocol, maybe it will parse the virtual matrix!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-7253", "title": "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-9739", "title": "We need to hack the multi-byte HDD bus!", "status": "done", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-4424", "title": "Try to hack the HEX alarm, maybe it will connect the optical pixel!", "status": "progress", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-3922", "title": "You can't back up the capacitor without generating the wireless PCI program!", "status": "backlog", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-4921", "title": "I'll index the open-source IP feed, that should system the GB application!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-5814", "title": "We need to calculate the 1080p AGP feed!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-2645", "title": "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!", "status": "todo", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-4535", "title": "Try to copy the JSON circuit, maybe it will connect the wireless feed!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-4463", "title": "We need to copy the solid state AGP monitor!", "status": "done", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-9745", "title": "If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!", "status": "cancelled", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-2080", "title": "If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!", "status": "todo", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3838", "title": "I'll bypass the online TCP application, that should panel the AGP system!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-1340", "title": "We need to navigate the virtual PNG circuit!", "status": "todo", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-6665", "title": "If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!", "status": "cancelled", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-7585", "title": "If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!", "status": "backlog", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6319", "title": "We need to copy the multi-byte SCSI program!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-4369", "title": "Try to input the SCSI bus, maybe it will generate the 1080p pixel!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-9035", "title": "We need to override the solid state PNG array!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-3970", "title": "You can't index the transmitter without quantifying the haptic ASCII card!", "status": "todo", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-4473", "title": "You can't bypass the protocol without overriding the neural RSS program!", "status": "todo", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-4136", "title": "You can't hack the hard drive without hacking the primary JSON program!", "status": "cancelled", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3939", "title": "Use the back-end SQL firewall, then you can connect the neural hard drive!", "status": "done", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-2007", "title": "I'll input the back-end USB protocol, that should bandwidth the PCI system!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-7516", "title": "Use the primary SQL program, then you can generate the auxiliary transmitter!", "status": "done", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-6906", "title": "Try to back up the DRAM system, maybe it will reboot the online transmitter!", "status": "done", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-5207", "title": "The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!", "status": "progress", "label": "bug", "priority": "low"}]
|
MonsterUI/docs/data_/statuses.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"backlog": {
|
| 3 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M0.877075 7.49972C0.877075 3.84204 3.84222 0.876892 7.49991 0.876892C11.1576 0.876892 14.1227 3.84204 14.1227 7.49972C14.1227 11.1574 11.1576 14.1226 7.49991 14.1226C3.84222 14.1226 0.877075 11.1574 0.877075 7.49972ZM7.49991 1.82689C4.36689 1.82689 1.82708 4.36671 1.82708 7.49972C1.82708 10.6327 4.36689 13.1726 7.49991 13.1726C10.6329 13.1726 13.1727 10.6327 13.1727 7.49972C13.1727 4.36671 10.6329 1.82689 7.49991 1.82689ZM8.24993 10.5C8.24993 10.9142 7.91414 11.25 7.49993 11.25C7.08571 11.25 6.74993 10.9142 6.74993 10.5C6.74993 10.0858 7.08571 9.75 7.49993 9.75C7.91414 9.75 8.24993 10.0858 8.24993 10.5ZM6.05003 6.25C6.05003 5.57211 6.63511 4.925 7.50003 4.925C8.36496 4.925 8.95003 5.57211 8.95003 6.25C8.95003 6.74118 8.68002 6.99212 8.21447 7.27494C8.16251 7.30651 8.10258 7.34131 8.03847 7.37854L8.03841 7.37858C7.85521 7.48497 7.63788 7.61119 7.47449 7.73849C7.23214 7.92732 6.95003 8.23198 6.95003 8.7C6.95004 9.00376 7.19628 9.25 7.50004 9.25C7.8024 9.25 8.04778 9.00601 8.05002 8.70417L8.05056 8.7033C8.05924 8.6896 8.08493 8.65735 8.15058 8.6062C8.25207 8.52712 8.36508 8.46163 8.51567 8.37436L8.51571 8.37433C8.59422 8.32883 8.68296 8.27741 8.78559 8.21506C9.32004 7.89038 10.05 7.35382 10.05 6.25C10.05 4.92789 8.93511 3.825 7.50003 3.825C6.06496 3.825 4.95003 4.92789 4.95003 6.25C4.95003 6.55376 5.19628 6.8 5.50003 6.8C5.80379 6.8 6.05003 6.55376 6.05003 6.25Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 4 |
+
"text": "Backlog"
|
| 5 |
+
},
|
| 6 |
+
"cancelled": {
|
| 7 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M0.877075 7.49988C0.877075 3.84219 3.84222 0.877045 7.49991 0.877045C11.1576 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1576 14.1227 7.49991 14.1227C3.84222 14.1227 0.877075 11.1575 0.877075 7.49988ZM7.49991 1.82704C4.36689 1.82704 1.82708 4.36686 1.82708 7.49988C1.82708 10.6329 4.36689 13.1727 7.49991 13.1727C10.6329 13.1727 13.1727 10.6329 13.1727 7.49988C13.1727 4.36686 10.6329 1.82704 7.49991 1.82704ZM9.85358 5.14644C10.0488 5.3417 10.0488 5.65829 9.85358 5.85355L8.20713 7.49999L9.85358 9.14644C10.0488 9.3417 10.0488 9.65829 9.85358 9.85355C9.65832 10.0488 9.34173 10.0488 9.14647 9.85355L7.50002 8.2071L5.85358 9.85355C5.65832 10.0488 5.34173 10.0488 5.14647 9.85355C4.95121 9.65829 4.95121 9.3417 5.14647 9.14644L6.79292 7.49999L5.14647 5.85355C4.95121 5.65829 4.95121 5.3417 5.14647 5.14644C5.34173 4.95118 5.65832 4.95118 5.85358 5.14644L7.50002 6.79289L9.14647 5.14644C9.34173 4.95118 9.65832 4.95118 9.85358 5.14644Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 8 |
+
"text": "Cancelled"
|
| 9 |
+
},
|
| 10 |
+
"done": {
|
| 11 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M7.49991 0.877045C3.84222 0.877045 0.877075 3.84219 0.877075 7.49988C0.877075 11.1575 3.84222 14.1227 7.49991 14.1227C11.1576 14.1227 14.1227 11.1575 14.1227 7.49988C14.1227 3.84219 11.1576 0.877045 7.49991 0.877045ZM1.82708 7.49988C1.82708 4.36686 4.36689 1.82704 7.49991 1.82704C10.6329 1.82704 13.1727 4.36686 13.1727 7.49988C13.1727 10.6329 10.6329 13.1727 7.49991 13.1727C4.36689 13.1727 1.82708 10.6329 1.82708 7.49988ZM10.1589 5.53774C10.3178 5.31191 10.2636 5.00001 10.0378 4.84109C9.81194 4.68217 9.50004 4.73642 9.34112 4.96225L6.51977 8.97154L5.35681 7.78706C5.16334 7.59002 4.84677 7.58711 4.64973 7.78058C4.45268 7.97404 4.44978 8.29061 4.64325 8.48765L6.22658 10.1003C6.33054 10.2062 6.47617 10.2604 6.62407 10.2483C6.77197 10.2363 6.90686 10.1591 6.99226 10.0377L10.1589 5.53774Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 12 |
+
"text": "Done"
|
| 13 |
+
},
|
| 14 |
+
"progress": {
|
| 15 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M5.49998 0.5C5.49998 0.223858 5.72383 0 5.99998 0H7.49998H8.99998C9.27612 0 9.49998 0.223858 9.49998 0.5C9.49998 0.776142 9.27612 1 8.99998 1H7.99998V2.11922C9.09832 2.20409 10.119 2.56622 10.992 3.13572C11.0116 3.10851 11.0336 3.08252 11.058 3.05806L11.858 2.25806C12.1021 2.01398 12.4978 2.01398 12.7419 2.25806C12.986 2.50214 12.986 2.89786 12.7419 3.14194L11.967 3.91682C13.1595 5.07925 13.9 6.70314 13.9 8.49998C13.9 12.0346 11.0346 14.9 7.49998 14.9C3.96535 14.9 1.09998 12.0346 1.09998 8.49998C1.09998 5.13362 3.69904 2.3743 6.99998 2.11922V1H5.99998C5.72383 1 5.49998 0.776142 5.49998 0.5ZM2.09998 8.49998C2.09998 5.51764 4.51764 3.09998 7.49998 3.09998C10.4823 3.09998 12.9 5.51764 12.9 8.49998C12.9 11.4823 10.4823 13.9 7.49998 13.9C4.51764 13.9 2.09998 11.4823 2.09998 8.49998ZM7.99998 4.5C7.99998 4.22386 7.77612 4 7.49998 4C7.22383 4 6.99998 4.22386 6.99998 4.5V9.5C6.99998 9.77614 7.22383 10 7.49998 10C7.77612 10 7.99998 9.77614 7.99998 9.5V4.5Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 16 |
+
"text": "In Progress"
|
| 17 |
+
},
|
| 18 |
+
"todo": {
|
| 19 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M0.877075 7.49991C0.877075 3.84222 3.84222 0.877075 7.49991 0.877075C11.1576 0.877075 14.1227 3.84222 14.1227 7.49991C14.1227 11.1576 11.1576 14.1227 7.49991 14.1227C3.84222 14.1227 0.877075 11.1576 0.877075 7.49991ZM7.49991 1.82708C4.36689 1.82708 1.82708 4.36689 1.82708 7.49991C1.82708 10.6329 4.36689 13.1727 7.49991 13.1727C10.6329 13.1727 13.1727 10.6329 13.1727 7.49991C13.1727 4.36689 10.6329 1.82708 7.49991 1.82708Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 20 |
+
"text": "Todo"
|
| 21 |
+
}
|
| 22 |
+
}
|
MonsterUI/docs/examples/auth.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Auth Example built with MonsterUI (original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
from monsterui.all import *
|
| 5 |
+
from fasthtml.svg import *
|
| 6 |
+
|
| 7 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 8 |
+
|
| 9 |
+
@rt
|
| 10 |
+
def index():
|
| 11 |
+
left = Div(cls="col-span-1 hidden flex-col justify-between bg-zinc-900 p-8 text-white lg:flex")(
|
| 12 |
+
Div(cls=(TextT.bold))("Acme Inc"),
|
| 13 |
+
Blockquote(cls="space-y-2")(
|
| 14 |
+
P(cls=TextT.lg)('"This library has saved me countless hours of work and helped me deliver stunning designs to my clients faster than ever before."'),
|
| 15 |
+
Footer(cls=TextT.sm)("Sofia Davis")))
|
| 16 |
+
|
| 17 |
+
right = Div(cls="col-span-2 flex flex-col p-8 lg:col-span-1")(
|
| 18 |
+
DivRAligned(Button("Login", cls=ButtonT.ghost)),
|
| 19 |
+
DivCentered(cls='flex-1')(
|
| 20 |
+
Container(
|
| 21 |
+
DivVStacked(
|
| 22 |
+
H3("Create an account"),
|
| 23 |
+
Small("Enter your email below to create your account", cls=TextT.muted)),
|
| 24 |
+
Form(
|
| 25 |
+
Input(placeholder="[email protected]"),
|
| 26 |
+
Button(Span(cls="mr-2", uk_spinner="ratio: 0.54"), "Sign in with Email", cls=(ButtonT.primary, "w-full"), disabled=True),
|
| 27 |
+
DividerSplit(Small("Or continue with"),cls=TextT.muted),
|
| 28 |
+
Button(UkIcon('github',cls='mr-2'), "Github", cls=(ButtonT.default, "w-full")),
|
| 29 |
+
cls='space-y-6'),
|
| 30 |
+
DivVStacked(Small(
|
| 31 |
+
"By clicking continue, you agree to our ",
|
| 32 |
+
A(cls=AT.muted, href="#demo")("Terms of Service")," and ",
|
| 33 |
+
A(cls=AT.muted, href="#demo")("Privacy Policy"),".",
|
| 34 |
+
cls=(TextT.muted,"text-center"))),
|
| 35 |
+
cls="space-y-6")))
|
| 36 |
+
|
| 37 |
+
return Title("Auth Example"),Grid(left,right,cols=2, gap=0,cls='h-screen')
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
serve()
|
MonsterUI/docs/examples/cards.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Cards Example built with MonsterUI (original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
from fasthtml.components import Uk_input_tag
|
| 5 |
+
from fasthtml.svg import *
|
| 6 |
+
from monsterui.all import *
|
| 7 |
+
import calendar
|
| 8 |
+
from datetime import datetime
|
| 9 |
+
|
| 10 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 11 |
+
|
| 12 |
+
CreateAccount = Card(
|
| 13 |
+
Grid(Button(DivLAligned(UkIcon('github'),Div('Github'))),Button('Google')),
|
| 14 |
+
DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm),
|
| 15 |
+
LabelInput('Email', id='email', placeholder='[email protected]'),
|
| 16 |
+
LabelInput('Password', id='password',placeholder='Password', type='Password'),
|
| 17 |
+
header=(H3('Create an Account'),Subtitle('Enter your email below to create your account')),
|
| 18 |
+
footer=Button('Create Account',cls=(ButtonT.primary,'w-full')))
|
| 19 |
+
|
| 20 |
+
PaypalSVG_data = "M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z"
|
| 21 |
+
AppleSVG_data = "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
|
| 22 |
+
Card1Svg = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(Rect(width="20", height="14", x="2", y="5", rx="2"),Path(d="M2 10h20"))
|
| 23 |
+
PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor")),
|
| 24 |
+
AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor"))
|
| 25 |
+
|
| 26 |
+
PaymentMethod = Card(
|
| 27 |
+
Grid(Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary'),
|
| 28 |
+
Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20'),
|
| 29 |
+
Button(DivCentered(AppleSvg, "Apple"), cls='h-20')),
|
| 30 |
+
Form(LabelInput('Name', id='name', placeholder='John Doe'),
|
| 31 |
+
LabelInput('Card Number', id='card_number', placeholder='[email protected]'),
|
| 32 |
+
Grid(LabelSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'),
|
| 33 |
+
LabelSelect(*Options(*range(2024,2030),selected_idx=0), label='Year', id='expire_year'),
|
| 34 |
+
LabelInput('CVV', id='cvv',placeholder='CVV', cls='mt-0'))),
|
| 35 |
+
header=(H3('Payment Method'),Subtitle('Add a new payment method to your account.')))
|
| 36 |
+
|
| 37 |
+
area_opts = ('Team','Billing','Account','Deployment','Support')
|
| 38 |
+
severity_opts = ('Severity 1 (Highest)', 'Severity 2', 'Severity 3', 'Severity 4 (Lowest)')
|
| 39 |
+
ReportIssue = Card(
|
| 40 |
+
Grid(Div(LabelSelect(*Options(*area_opts), label='Area', id='area')),
|
| 41 |
+
Div(LabelSelect(*Options(*severity_opts),label='Severity',id='area'))),
|
| 42 |
+
LabelInput( label='Subject', id='subject', placeholder='I need help with'),
|
| 43 |
+
LabelTextArea( label='Description', id='description',placeholder='Please include all information relevant to your issue'),
|
| 44 |
+
Div(FormLabel('Tags', fr='#tags'),
|
| 45 |
+
Uk_input_tag(name="Tags",state="danger", value="Spam,Invalid", uk_cloak=True, id='tags')),
|
| 46 |
+
header=(H3('Report Issue'),Subtitle('What area are you having problems with?')),
|
| 47 |
+
footer = DivFullySpaced(Button('Cancel'), Button(cls=ButtonT.primary)('Submit')))
|
| 48 |
+
|
| 49 |
+
monster_desc ="Python-first beautifully designed components because you deserve to focus on features that matter and your app deserves to be beautiful from day one."
|
| 50 |
+
MonsterUI = Card(H4("Monster UI"),
|
| 51 |
+
Subtitle(monster_desc),
|
| 52 |
+
DivLAligned(
|
| 53 |
+
Div("Python"),
|
| 54 |
+
DivLAligned(UkIcon('star'),Div("20k"), cls='space-x-1'),
|
| 55 |
+
Div(datetime.now().strftime("%B %d, %Y")),
|
| 56 |
+
cls=('space-x-4',TextPresets.muted_sm)))
|
| 57 |
+
|
| 58 |
+
def CookieTableRow(heading, description, active=False):
|
| 59 |
+
return Tr(Td(H5(heading)),
|
| 60 |
+
Td(P(description, cls=TextPresets.muted_sm)),
|
| 61 |
+
Td(Switch(checked=active)))
|
| 62 |
+
|
| 63 |
+
CookieSettings = Card(
|
| 64 |
+
Table(Tbody(
|
| 65 |
+
CookieTableRow('Strictly Necessary', 'These cookies are essential in order to use the website and use its features.', True),
|
| 66 |
+
CookieTableRow('Functional Cookies', 'These cookies allow the website to provide personalized functionality.'),
|
| 67 |
+
CookieTableRow('Performance Cookies', 'These cookies help to improve the performance of the website.'))),
|
| 68 |
+
header=(H4('Cookie Settings'),Subtitle('Manage your cookie settings here.')),
|
| 69 |
+
footer=Button('Save Preferences', cls=(ButtonT.primary, 'w-full')))
|
| 70 |
+
|
| 71 |
+
team_members = [("Sofia Davis", "[email protected]", "Owner"),("Jackson Lee", "[email protected]", "Member"),]
|
| 72 |
+
def TeamMemberRow(name, email, role):
|
| 73 |
+
return DivFullySpaced(
|
| 74 |
+
DivLAligned(
|
| 75 |
+
DiceBearAvatar(name, 10,10),
|
| 76 |
+
Div(P(name, cls=(TextT.sm, TextT.medium)),
|
| 77 |
+
P(email, cls=TextPresets.muted_sm))),
|
| 78 |
+
Button(role, UkIcon('chevron-down', cls='ml-4')),
|
| 79 |
+
DropDownNavContainer(map(NavCloseLi, [
|
| 80 |
+
A(Div('Viewer', NavSubtitle('Can view and comment.'))),
|
| 81 |
+
A(Div('Developer', NavSubtitle('Can view, comment and edit.'))),
|
| 82 |
+
A(Div('Billing', NavSubtitle('Can view, comment and manage billing.'))),
|
| 83 |
+
A(Div('Owner', NavSubtitle('Admin-level access to all resources.')))])))
|
| 84 |
+
|
| 85 |
+
TeamMembers = Card(*[TeamMemberRow(*member) for member in team_members],
|
| 86 |
+
header = (H4('Team Members'),Subtitle('Invite your team members to collaborate.')))
|
| 87 |
+
|
| 88 |
+
access_roles = ("Read and write access", "Read-only access")
|
| 89 |
+
team_members = [("Olivia Martin", "[email protected]", "Read and write access"),
|
| 90 |
+
("Isabella Nguyen", "[email protected]", "Read-only access"),
|
| 91 |
+
("Sofia Davis", "[email protected]", "Read-only access")]
|
| 92 |
+
|
| 93 |
+
def TeamMemberRow(name, email, role):
|
| 94 |
+
return DivFullySpaced(
|
| 95 |
+
DivLAligned(DiceBearAvatar(name, 10,10),
|
| 96 |
+
Div(P(name, cls=(TextT.sm, TextT.medium)),
|
| 97 |
+
P(email, cls=TextPresets.muted_sm))),
|
| 98 |
+
Select(*Options(*access_roles, selected_idx=access_roles.index(role))))
|
| 99 |
+
|
| 100 |
+
ShareDocument = Card(
|
| 101 |
+
DivLAligned(Input(value='http://example.com/link/to/document'),Button('Copy link', cls='whitespace-nowrap')),
|
| 102 |
+
Divider(),
|
| 103 |
+
H4('People with access', cls=TextPresets.bold_sm),
|
| 104 |
+
*[TeamMemberRow(*member) for member in team_members],
|
| 105 |
+
header = (H4('Share this document'),Subtitle('Anyone with the link can view this document.')))
|
| 106 |
+
|
| 107 |
+
DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024'))
|
| 108 |
+
|
| 109 |
+
section_content =(('bell','Everything',"Email digest, mentions & all activity."),
|
| 110 |
+
('user',"Available","Only mentions and comments"),
|
| 111 |
+
('ban', "Ignoring","Turn of all notifications"))
|
| 112 |
+
|
| 113 |
+
def NotificationRow(icon, name, desc):
|
| 114 |
+
return Li(cls='-mx-1')(A(DivLAligned(UkIcon(icon),Div(P(name),P(desc, cls=TextPresets.muted_sm)))))
|
| 115 |
+
|
| 116 |
+
Notifications = Card(
|
| 117 |
+
NavContainer(
|
| 118 |
+
*[NotificationRow(*row) for row in section_content],
|
| 119 |
+
cls=NavT.secondary),
|
| 120 |
+
header = (H4('Notification'),Subtitle('Choose what you want to be notified about.')),
|
| 121 |
+
body_cls='pt-0')
|
| 122 |
+
|
| 123 |
+
TeamCard = Card(
|
| 124 |
+
DivLAligned(
|
| 125 |
+
DiceBearAvatar("Isaac Flath", h=24, w=24),
|
| 126 |
+
Div(H3("Isaac Flath"), P("Library Creator"))),
|
| 127 |
+
footer=DivFullySpaced(
|
| 128 |
+
DivHStacked(UkIcon("map-pin", height=16), P("Alexandria, VA")),
|
| 129 |
+
DivHStacked(*(UkIconLink(icon, height=16) for icon in ("mail", "linkedin", "github")))),
|
| 130 |
+
cls=CardT.hover)
|
| 131 |
+
|
| 132 |
+
@rt
|
| 133 |
+
def index():
|
| 134 |
+
return Title("Cards Example"),Container(Grid(
|
| 135 |
+
*map(Div,(
|
| 136 |
+
Div(PaymentMethod,CreateAccount, TeamCard, cls='space-y-4'),
|
| 137 |
+
Div(TeamMembers, ShareDocument,DateCard,Notifications, cls='space-y-4'),
|
| 138 |
+
Div(ReportIssue,MonsterUI,CookieSettings, cls='space-y-4'))),
|
| 139 |
+
cols_md=1, cols_lg=2, cols_xl=3))
|
| 140 |
+
|
| 141 |
+
serve()
|
MonsterUI/docs/examples/dashboard.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Dashboard Example built with MonsterUI (original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import * # Bring in all of fasthtml
|
| 4 |
+
import fasthtml.common as fh # Used to get unstyled components
|
| 5 |
+
from monsterui.all import * # Bring in all of monsterui, including shadowing fasthtml components with styled components
|
| 6 |
+
from fasthtml.svg import *
|
| 7 |
+
import numpy as np
|
| 8 |
+
import plotly.express as px
|
| 9 |
+
import pandas as pd
|
| 10 |
+
import numpy as np
|
| 11 |
+
|
| 12 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 13 |
+
|
| 14 |
+
def generate_chart(num_points=30):
|
| 15 |
+
df = pd.DataFrame({
|
| 16 |
+
'Date': pd.date_range('2024-01-01', periods=num_points),
|
| 17 |
+
'Revenue': np.random.normal(100, 10, num_points).cumsum(),
|
| 18 |
+
'Users': np.random.normal(80, 8, num_points).cumsum(),
|
| 19 |
+
'Growth': np.random.normal(60, 6, num_points).cumsum()})
|
| 20 |
+
|
| 21 |
+
fig = px.line(df, x='Date', y=['Revenue', 'Users', 'Growth'], template='plotly_white', line_shape='spline')
|
| 22 |
+
|
| 23 |
+
fig.update_traces(mode='lines+markers')
|
| 24 |
+
fig.update_layout(
|
| 25 |
+
margin=dict(l=20, r=20, t=20, b=20), hovermode='x unified',
|
| 26 |
+
showlegend=True, legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1),
|
| 27 |
+
plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
|
| 28 |
+
xaxis=dict(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,0,0.1)'),
|
| 29 |
+
yaxis=dict(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,0,0.1)'))
|
| 30 |
+
|
| 31 |
+
return fig.to_html(include_plotlyjs=True, full_html=False, config={'displayModeBar': False})
|
| 32 |
+
|
| 33 |
+
def InfoCard(title, value, change): return Card(H3(value),P(change, cls=TextPresets.muted_sm), header = H4(title))
|
| 34 |
+
|
| 35 |
+
rev = InfoCard("Total Revenue", "$45,231.89", "+20.1% from last month")
|
| 36 |
+
sub = InfoCard("Subscriptions", "+2350", "+180.1% from last month")
|
| 37 |
+
sal = InfoCard("Sales", "+12,234", "+19% from last month")
|
| 38 |
+
act = InfoCard("Active Now", "+573", "+201 since last hour")
|
| 39 |
+
|
| 40 |
+
info_card_data = [("Total Revenue", "$45,231.89", "+20.1% from last month"),
|
| 41 |
+
("Subscriptions", "+2350", "+180.1% from last month"),
|
| 42 |
+
("Sales", "+12,234", "+19% from last month"),
|
| 43 |
+
("Active Now", "+573", "+201 since last hour")]
|
| 44 |
+
|
| 45 |
+
top_info_row = Grid(*[InfoCard(*row) for row in info_card_data])
|
| 46 |
+
|
| 47 |
+
def AvatarItem(name, email, amount):
|
| 48 |
+
return DivFullySpaced(
|
| 49 |
+
DivLAligned(
|
| 50 |
+
DiceBearAvatar(name, 9,9),
|
| 51 |
+
Div(Strong(name, cls=TextT.sm),
|
| 52 |
+
Address(A(email,href=f'mailto:{email}')))),
|
| 53 |
+
fh.Data(amount, cls="ml-auto font-medium", value=amount[2:]))
|
| 54 |
+
|
| 55 |
+
recent_sales = Card(
|
| 56 |
+
Div(cls="space-y-8")(
|
| 57 |
+
*[AvatarItem(n,e,d) for (n,e,d) in (
|
| 58 |
+
("Olivia Martin", "[email protected]", "+$1,999.00"),
|
| 59 |
+
("Jackson Lee", "[email protected]", "+$39.00"),
|
| 60 |
+
("Isabella Nguyen", "[email protected]", "+$299.00"),
|
| 61 |
+
("William Kim", "[email protected]", "+$99.00"),
|
| 62 |
+
("Sofia Davis", "[email protected]", "+$39.00"))]),
|
| 63 |
+
header=Div(H3("Recent Sales"),Subtitle("You made 265 sales this month.")),
|
| 64 |
+
cls='col-span-3')
|
| 65 |
+
|
| 66 |
+
teams = [["Alicia Koch"],['Acme Inc', 'Monster Inc.'],['Create a Team']]
|
| 67 |
+
|
| 68 |
+
opt_hdrs = ["Personal", "Team", ""]
|
| 69 |
+
|
| 70 |
+
team_dropdown = Select(
|
| 71 |
+
Optgroup(Option(A("Alicia Koch")), label="Personal Account"),
|
| 72 |
+
Optgroup(Option(A("Acme Inc")), Option(A("Monster Inc.")), label="Teams"),
|
| 73 |
+
Option(A("Create a Team")),
|
| 74 |
+
cls='flex items-center')
|
| 75 |
+
|
| 76 |
+
hotkeys = [('Profile','⇧⌘P'),('Billing','⌘B'),('Settings','⌘S'),('New Team', ''), ('Logout', '')]
|
| 77 |
+
|
| 78 |
+
def NavSpacedLi(t,s): return NavCloseLi(A(DivFullySpaced(P(t),P(s,cls=TextPresets.muted_sm))))
|
| 79 |
+
|
| 80 |
+
avatar_dropdown = Div(
|
| 81 |
+
DiceBearAvatar('Alicia Koch',8,8),
|
| 82 |
+
DropDownNavContainer(
|
| 83 |
+
NavHeaderLi('sveltecult',NavSubtitle("[email protected]")),
|
| 84 |
+
*[NavSpacedLi(*hk) for hk in hotkeys],))
|
| 85 |
+
|
| 86 |
+
top_nav = NavBar(
|
| 87 |
+
team_dropdown, *map(A, ["Overview", "Customers", "Products", "Settings"]),
|
| 88 |
+
brand=DivLAligned(avatar_dropdown, Input(placeholder='Search')))
|
| 89 |
+
|
| 90 |
+
@rt
|
| 91 |
+
def index():
|
| 92 |
+
return Title("Dashboard Example"), Container(
|
| 93 |
+
top_nav,
|
| 94 |
+
H2('Dashboard'),
|
| 95 |
+
TabContainer(
|
| 96 |
+
Li(A("Overview"),cls='uk-active'),
|
| 97 |
+
*map(lambda x: Li(A(x)), ["Analytics", "Reports", "Notifications"]),
|
| 98 |
+
alt=True),
|
| 99 |
+
top_info_row,
|
| 100 |
+
Grid(
|
| 101 |
+
Card(Safe(generate_chart(100)), cls='col-span-4'),
|
| 102 |
+
recent_sales,
|
| 103 |
+
gap=4,cols_xl=7,cols_lg=7,cols_md=1,cols_sm=1,cols_xs=1),
|
| 104 |
+
cls=('space-y-4', ContainerT.xl))
|
| 105 |
+
|
| 106 |
+
serve()
|
MonsterUI/docs/examples/data/mail.json
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": "6c84fb90-12c4-11e1-840d-7b25c5ee775a",
|
| 4 |
+
"initial": "WS",
|
| 5 |
+
"name": "William Smith",
|
| 6 |
+
"email": "[email protected]",
|
| 7 |
+
"subject": "Meeting Tomorrow",
|
| 8 |
+
"text": "Hi, let's have a meeting tomorrow to discuss the project. I've been reviewing the project details and have some ideas I'd like to share. It's crucial that we align on our next steps to ensure the project's success.\n\nPlease come prepared with any questions or insights you may have. Looking forward to our meeting!\n\nBest regards, William",
|
| 9 |
+
"date": "2023-10-22T09:00:00",
|
| 10 |
+
"read": true,
|
| 11 |
+
"active": true,
|
| 12 |
+
"labels": ["meeting", "work", "important"]
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"id": "110e8400-e29b-11d4-a716-446655440000",
|
| 16 |
+
"initial": "AS",
|
| 17 |
+
"name": "Alice Smith",
|
| 18 |
+
"email": "[email protected]",
|
| 19 |
+
"subject": "Re: Project Update",
|
| 20 |
+
"text": "Thank you for the project update. It looks great! I've gone through the report, and the progress is impressive. The team has done a fantastic job, and I appreciate the hard work everyone has put in.\n\nI have a few minor suggestions that I'll include in the attached document.\n\nLet's discuss these during our next meeting. Keep up the excellent work!\n\nBest regards, Alice",
|
| 21 |
+
"date": "2023-10-22T10:30:00",
|
| 22 |
+
"read": true,
|
| 23 |
+
"active": false,
|
| 24 |
+
"labels": ["work", "important"]
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"id": "3e7c3f6d-bdf5-46ae-8d90-171300f27ae2",
|
| 28 |
+
"initial": "BJ",
|
| 29 |
+
"name": "Bob Johnson",
|
| 30 |
+
"email": "[email protected]",
|
| 31 |
+
"subject": "Weekend Plans",
|
| 32 |
+
"text": "Any plans for the weekend? I was thinking of going hiking in the nearby mountains. It's been a while since we had some outdoor fun.\n\nIf you're interested, let me know, and we can plan the details. It'll be a great way to unwind and enjoy nature.\n\nLooking forward to your response!\n\nBest, Bob",
|
| 33 |
+
"date": "2023-04-10T11:45:00",
|
| 34 |
+
"read": true,
|
| 35 |
+
"active": false,
|
| 36 |
+
"labels": ["personal"]
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"id": "61c35085-72d7-42b4-8d62-738f700d4b92",
|
| 40 |
+
"initial": "ED",
|
| 41 |
+
"name": "Emily Davis",
|
| 42 |
+
"email": "[email protected]",
|
| 43 |
+
"subject": "Re: Question about Budget",
|
| 44 |
+
"text": "I have a question about the budget for the upcoming project. It seems like there's a discrepancy in the allocation of resources.\n\nI've reviewed the budget report and identified a few areas where we might be able to optimize our spending without compromising the project's quality.\n\nI've attached a detailed analysis for your reference. Let's discuss this further in our next meeting.\n\nThanks, Emily",
|
| 45 |
+
"date": "2023-03-25T13:15:00",
|
| 46 |
+
"read": false,
|
| 47 |
+
"active": false,
|
| 48 |
+
"labels": ["work", "budget"]
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"id": "8f7b5db9-d935-4e42-8e05-1f1d0a3dfb97",
|
| 52 |
+
"initial": "MW",
|
| 53 |
+
"name": "Michael Wilson",
|
| 54 |
+
"email": "[email protected]",
|
| 55 |
+
"subject": "Important Announcement",
|
| 56 |
+
"text": "I have an important announcement to make during our team meeting. It pertains to a strategic shift in our approach to the upcoming product launch. We've received valuable feedback from our beta testers, and I believe it's time to make some adjustments to better meet our customers' needs.\n\nThis change is crucial to our success, and I look forward to discussing it with the team. Please be prepared to share your insights during the meeting.\n\nRegards, Michael",
|
| 57 |
+
"date": "2023-03-10T15:00:00",
|
| 58 |
+
"read": false,
|
| 59 |
+
"active": false,
|
| 60 |
+
"labels": ["meeting", "work", "important"]
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"id": "1f0f2c02-e299-40de-9b1d-86ef9e42126b",
|
| 64 |
+
"initial": "SB",
|
| 65 |
+
"name": "Sarah Brown",
|
| 66 |
+
"email": "[email protected]",
|
| 67 |
+
"subject": "Re: Feedback on Proposal",
|
| 68 |
+
"text": "Thank you for your feedback on the proposal. It looks great! I'm pleased to hear that you found it promising. The team worked diligently to address all the key points you raised, and I believe we now have a strong foundation for the project.\n\nI've attached the revised proposal for your review.\n\nPlease let me know if you have any further comments or suggestions. Looking forward to your response.\n\nBest regards, Sarah",
|
| 69 |
+
"date": "2023-02-15T16:30:00",
|
| 70 |
+
"read": true,
|
| 71 |
+
"active": false,
|
| 72 |
+
"labels": ["work"]
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"id": "17c0a96d-4415-42b1-8b4f-764efab57f66",
|
| 76 |
+
"initial": "DL",
|
| 77 |
+
"name": "David Lee",
|
| 78 |
+
"email": "[email protected]",
|
| 79 |
+
"subject": "New Project Idea",
|
| 80 |
+
"text": "I have an exciting new project idea to discuss with you. It involves expanding our services to target a niche market that has shown considerable growth in recent months.\n\nI've prepared a detailed proposal outlining the potential benefits and the strategy for execution.\n\nThis project has the potential to significantly impact our business positively. Let's set up a meeting to dive into the details and determine if it aligns with our current goals.\n\nBest regards, David",
|
| 81 |
+
"date": "2023-01-28T17:45:00",
|
| 82 |
+
"read": false,
|
| 83 |
+
"active": false,
|
| 84 |
+
"labels": ["meeting", "work", "important"]
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"id": "2f0130cb-39fc-44c4-bb3c-0a4337edaaab",
|
| 88 |
+
"initial": "OW",
|
| 89 |
+
"name": "Olivia Wilson",
|
| 90 |
+
"email": "[email protected]",
|
| 91 |
+
"subject": "Vacation Plans",
|
| 92 |
+
"text": "Let's plan our vacation for next month. What do you think? I've been thinking of visiting a tropical paradise, and I've put together some destination options.\n\nI believe it's time for us to unwind and recharge. Please take a look at the options and let me know your preferences.\n\nWe can start making arrangements to ensure a smooth and enjoyable trip.\n\nExcited to hear your thoughts! Olivia",
|
| 93 |
+
"date": "2022-12-20T18:30:00",
|
| 94 |
+
"read": true,
|
| 95 |
+
"active": false,
|
| 96 |
+
"labels": ["personal"]
|
| 97 |
+
},
|
| 98 |
+
{
|
| 99 |
+
"id": "de305d54-75b4-431b-adb2-eb6b9e546014",
|
| 100 |
+
"initial": "JM",
|
| 101 |
+
"name": "James Martin",
|
| 102 |
+
"email": "[email protected]",
|
| 103 |
+
"subject": "Re: Conference Registration",
|
| 104 |
+
"text": "I've completed the registration for the conference next month. The event promises to be a great networking opportunity, and I'm looking forward to attending the various sessions and connecting with industry experts.\n\nI've also attached the conference schedule for your reference.\n\nIf there are any specific topics or sessions you'd like me to explore, please let me know. It's an exciting event, and I'll make the most of it.\n\nBest regards, James",
|
| 105 |
+
"date": "2022-11-30T19:15:00",
|
| 106 |
+
"read": true,
|
| 107 |
+
"active": false,
|
| 108 |
+
"labels": ["work", "conference"]
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"id": "7dd90c63-00f6-40f3-bd87-5060a24e8ee7",
|
| 112 |
+
"initial": "SW",
|
| 113 |
+
"name": "Sophia White",
|
| 114 |
+
"email": "[email protected]",
|
| 115 |
+
"subject": "Team Dinner",
|
| 116 |
+
"text": "Let's have a team dinner next week to celebrate our success. We've achieved some significant milestones, and it's time to acknowledge our hard work and dedication.\n\nI've made reservations at a lovely restaurant, and I'm sure it'll be an enjoyable evening.\n\nPlease confirm your availability and any dietary preferences. Looking forward to a fun and memorable dinner with the team!\n\nBest, Sophia",
|
| 117 |
+
"date": "2022-11-05T20:30:00",
|
| 118 |
+
"read": false,
|
| 119 |
+
"active": false,
|
| 120 |
+
"labels": ["meeting", "work"]
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"id": "99a88f78-3eb4-4d87-87b7-7b15a49a0a05",
|
| 124 |
+
"initial": "DJ",
|
| 125 |
+
"name": "Daniel Johnson",
|
| 126 |
+
"email": "[email protected]",
|
| 127 |
+
"subject": "Feedback Request",
|
| 128 |
+
"text": "I'd like your feedback on the latest project deliverables. We've made significant progress, and I value your input to ensure we're on the right track.\n\nI've attached the deliverables for your review, and I'm particularly interested in any areas where you think we can further enhance the quality or efficiency.\n\nYour feedback is invaluable, and I appreciate your time and expertise. Let's work together to make this project a success.\n\nRegards, Daniel",
|
| 129 |
+
"date": "2022-10-22T09:30:00",
|
| 130 |
+
"read": false,
|
| 131 |
+
"active": false,
|
| 132 |
+
"labels": ["work"]
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
| 136 |
+
"initial": "AT",
|
| 137 |
+
"name": "Ava Taylor",
|
| 138 |
+
"email": "[email protected]",
|
| 139 |
+
"subject": "Re: Meeting Agenda",
|
| 140 |
+
"text": "Here's the agenda for our meeting next week. I've included all the topics we need to cover, as well as time allocations for each.\n\nIf you have any additional items to discuss or any specific points to address, please let me know, and we can integrate them into the agenda.\n\nIt's essential that our meeting is productive and addresses all relevant matters.\n\nLooking forward to our meeting! Ava",
|
| 141 |
+
"date": "2022-10-10T10:45:00",
|
| 142 |
+
"read": true,
|
| 143 |
+
"active": false,
|
| 144 |
+
"labels": ["meeting", "work"]
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"id": "c1a0ecb4-2540-49c5-86f8-21e5ce79e4e6",
|
| 148 |
+
"initial": "WA",
|
| 149 |
+
"name": "William Anderson",
|
| 150 |
+
"email": "[email protected]",
|
| 151 |
+
"subject": "Product Launch Update",
|
| 152 |
+
"text": "The product launch is on track. I'll provide an update during our call. We've made substantial progress in the development and marketing of our new product.\n\nI'm excited to share the latest updates with you during our upcoming call. It's crucial that we coordinate our efforts to ensure a successful launch. Please come prepared with any questions or insights you may have.\n\nLet's make this product launch a resounding success!\n\nBest regards, William",
|
| 153 |
+
"date": "2022-09-20T12:00:00",
|
| 154 |
+
"read": false,
|
| 155 |
+
"active": false,
|
| 156 |
+
"labels": ["meeting", "work", "important"]
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"id": "ba54eefd-4097-4949-99f2-2a9ae4d1a836",
|
| 160 |
+
"initial": "MH",
|
| 161 |
+
"name": "Mia Harris",
|
| 162 |
+
"email": "[email protected]",
|
| 163 |
+
"subject": "Re: Travel Itinerary",
|
| 164 |
+
"text": "I've received the travel itinerary. It looks great! Thank you for your prompt assistance in arranging the details. I've reviewed the schedule and the accommodations, and everything seems to be in order. I'm looking forward to the trip, and I'm confident it'll be a smooth and enjoyable experience.\n\nIf there are any specific activities or attractions you recommend at our destination, please feel free to share your suggestions.\n\nExcited for the trip! Mia",
|
| 165 |
+
"date": "2022-09-10T13:15:00",
|
| 166 |
+
"read": true,
|
| 167 |
+
"active": false,
|
| 168 |
+
"labels": ["personal", "travel"]
|
| 169 |
+
},
|
| 170 |
+
{
|
| 171 |
+
"id": "df09b6ed-28bd-4e0c-85a9-9320ec5179aa",
|
| 172 |
+
"initial": "EC",
|
| 173 |
+
"name": "Ethan Clark",
|
| 174 |
+
"email": "[email protected]",
|
| 175 |
+
"subject": "Team Building Event",
|
| 176 |
+
"text": "Let's plan a team-building event for our department. Team cohesion and morale are vital to our success, and I believe a well-organized team-building event can be incredibly beneficial. I've done some research and have a few ideas for fun and engaging activities.\n\nPlease let me know your thoughts and availability. We want this event to be both enjoyable and productive.\n\nTogether, we'll strengthen our team and boost our performance.\n\nRegards, Ethan",
|
| 177 |
+
"date": "2022-08-25T15:30:00",
|
| 178 |
+
"read": false,
|
| 179 |
+
"active": false,
|
| 180 |
+
"labels": ["meeting", "work"]
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"id": "d67c1842-7f8b-4b4b-9be1-1b3b1ab4611d",
|
| 184 |
+
"initial": "CH",
|
| 185 |
+
"name": "Chloe Hall",
|
| 186 |
+
"email": "[email protected]",
|
| 187 |
+
"subject": "Re: Budget Approval",
|
| 188 |
+
"text": "The budget has been approved. We can proceed with the project. I'm delighted to inform you that our budget proposal has received the green light from the finance department. This is a significant milestone, and it means we can move forward with the project as planned.\n\nI've attached the finalized budget for your reference. Let's ensure that we stay on track and deliver the project on time and within budget.\n\nIt's an exciting time for us! Chloe",
|
| 189 |
+
"date": "2022-08-10T16:45:00",
|
| 190 |
+
"read": true,
|
| 191 |
+
"active": false,
|
| 192 |
+
"labels": ["work", "budget"]
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"id": "6c9a7f94-8329-4d70-95d3-51f68c186ae1",
|
| 196 |
+
"initial": "ST",
|
| 197 |
+
"name": "Samuel Turner",
|
| 198 |
+
"email": "[email protected]",
|
| 199 |
+
"subject": "Weekend Hike",
|
| 200 |
+
"text": "Who's up for a weekend hike in the mountains? I've been craving some outdoor adventure, and a hike in the mountains sounds like the perfect escape. If you're up for the challenge, we can explore some scenic trails and enjoy the beauty of nature.\n\nI've done some research and have a few routes in mind.\n\nLet me know if you're interested, and we can plan the details.\n\nIt's sure to be a memorable experience! Samuel",
|
| 201 |
+
"date": "2022-07-28T17:30:00",
|
| 202 |
+
"read": false,
|
| 203 |
+
"active": false,
|
| 204 |
+
"labels": ["personal"]
|
| 205 |
+
}
|
| 206 |
+
]
|
MonsterUI/docs/examples/data/status_list.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[{"selected": true, "id": "TASK-8782", "title": "You can't compress the program without quantifying the open-source SSD pixel!", "status": "progress", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-7878", "title": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!", "status": "backlog", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-7839", "title": "We need to bypass the neural TCP card!", "status": "todo", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-5562", "title": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!", "status": "backlog", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-8686", "title": "I'll parse the wireless SSL protocol, that should driver the API panel!", "status": "cancelled", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-1280", "title": "Use the digital TLS panel, then you can transmit the haptic system!", "status": "done", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-7262", "title": "The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!", "status": "done", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-1138", "title": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-7184", "title": "We need to program the back-end THX pixel!", "status": "todo", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-5160", "title": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!", "status": "progress", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-5618", "title": "Generating the driver won't do anything, we need to index the online SSL application!", "status": "done", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-6699", "title": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!", "status": "backlog", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-2858", "title": "We need to override the online UDP bus!", "status": "backlog", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-9864", "title": "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!", "status": "done", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-8404", "title": "We need to generate the virtual HEX alarm!", "status": "progress", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-5365", "title": "Backing up the pixel won't do anything, we need to transmit the primary IB array!", "status": "progress", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1780", "title": "The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!", "status": "todo", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-6938", "title": "Use the redundant SCSI application, then you can hack the optical alarm!", "status": "todo", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-9885", "title": "We need to compress the auxiliary VGA driver!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-3216", "title": "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!", "status": "backlog", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-9285", "title": "The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!", "status": "todo", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-1024", "title": "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!", "status": "progress", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-7068", "title": "You can't generate the capacitor without indexing the wireless HEX pixel!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-6502", "title": "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!", "status": "todo", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-5326", "title": "We need to hack the redundant UTF8 transmitter!", "status": "todo", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-6274", "title": "Use the virtual PCI circuit, then you can parse the bluetooth alarm!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1571", "title": "I'll input the neural DRAM circuit, that should protocol the SMTP interface!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-9518", "title": "Compressing the interface won't do anything, we need to compress the online SDD matrix!", "status": "cancelled", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-5581", "title": "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!", "status": "backlog", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-2197", "title": "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!", "status": "todo", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-8484", "title": "We need to parse the solid state UDP firewall!", "status": "progress", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-9892", "title": "If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!", "status": "done", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-9616", "title": "We need to synthesize the cross-platform ASCII pixel!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-9744", "title": "Use the back-end IP card, then you can input the solid state hard drive!", "status": "done", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1376", "title": "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!", "status": "backlog", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-7382", "title": "If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!", "status": "todo", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-2290", "title": "I'll compress the virtual JSON panel, that should application the UTF8 bus!", "status": "cancelled", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-1533", "title": "You can't input the firewall without overriding the wireless TCP firewall!", "status": "done", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-4920", "title": "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!", "status": "progress", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-5168", "title": "If we synthesize the bus, we can get to the IP panel through the virtual TLS array!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-7103", "title": "We need to parse the multi-byte EXE bandwidth!", "status": "cancelled", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-4314", "title": "If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!", "status": "progress", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-3415", "title": "Use the cross-platform XML application, then you can quantify the solid state feed!", "status": "todo", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-8339", "title": "Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6995", "title": "Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!", "status": "todo", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-8053", "title": "If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!", "status": "todo", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-4336", "title": "If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!", "status": "todo", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-8790", "title": "I'll back up the optical COM alarm, that should alarm the RSS capacitor!", "status": "done", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-8980", "title": "Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-7342", "title": "Use the neural CLI card, then you can parse the online port!", "status": "backlog", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-5608", "title": "I'll hack the haptic SSL program, that should bus the UDP transmitter!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-1606", "title": "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!", "status": "done", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-7872", "title": "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!", "status": "cancelled", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-4167", "title": "Use the cross-platform SMS circuit, then you can synthesize the optical feed!", "status": "cancelled", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-9581", "title": "You can't index the port without hacking the cross-platform XSS monitor!", "status": "backlog", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-8806", "title": "We need to bypass the back-end SSL panel!", "status": "done", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-6542", "title": "Try to quantify the RSS firewall, maybe it will quantify the open-source system!", "status": "done", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6806", "title": "The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-9549", "title": "You can't bypass the bus without connecting the neural JBOD bus!", "status": "todo", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-1075", "title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!", "status": "done", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-1427", "title": "Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!", "status": "done", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-1907", "title": "Hacking the circuit won't do anything, we need to back up the online DRAM system!", "status": "todo", "label": "documentation", "priority": "high"}, {"selected": false, "id": "TASK-4309", "title": "If we generate the system, we can get to the TCP sensor through the optical GB pixel!", "status": "backlog", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3973", "title": "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!", "status": "todo", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-7962", "title": "Use the wireless RAM program, then you can hack the cross-platform feed!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-3360", "title": "You can't quantify the program without synthesizing the neural OCR interface!", "status": "done", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-9887", "title": "Use the auxiliary ASCII sensor, then you can connect the solid state port!", "status": "backlog", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3649", "title": "I'll input the virtual USB system, that should circuit the DNS monitor!", "status": "progress", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-3586", "title": "If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!", "status": "progress", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-5150", "title": "I'll hack the wireless XSS port, that should transmitter the IP interface!", "status": "cancelled", "label": "feature", "priority": "medium"}, {"selected": false, "id": "TASK-3652", "title": "The SQL interface is down, override the optical bus so we can program the ASCII interface!", "status": "backlog", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6884", "title": "Use the digital PCI circuit, then you can synthesize the multi-byte microchip!", "status": "cancelled", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-1591", "title": "We need to connect the mobile XSS driver!", "status": "progress", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-3802", "title": "Try to override the ASCII protocol, maybe it will parse the virtual matrix!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-7253", "title": "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-9739", "title": "We need to hack the multi-byte HDD bus!", "status": "done", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-4424", "title": "Try to hack the HEX alarm, maybe it will connect the optical pixel!", "status": "progress", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-3922", "title": "You can't back up the capacitor without generating the wireless PCI program!", "status": "backlog", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-4921", "title": "I'll index the open-source IP feed, that should system the GB application!", "status": "cancelled", "label": "bug", "priority": "low"}, {"selected": false, "id": "TASK-5814", "title": "We need to calculate the 1080p AGP feed!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-2645", "title": "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!", "status": "todo", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-4535", "title": "Try to copy the JSON circuit, maybe it will connect the wireless feed!", "status": "progress", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-4463", "title": "We need to copy the solid state AGP monitor!", "status": "done", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-9745", "title": "If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!", "status": "cancelled", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-2080", "title": "If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!", "status": "todo", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3838", "title": "I'll bypass the online TCP application, that should panel the AGP system!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-1340", "title": "We need to navigate the virtual PNG circuit!", "status": "todo", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-6665", "title": "If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!", "status": "cancelled", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-7585", "title": "If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!", "status": "backlog", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-6319", "title": "We need to copy the multi-byte SCSI program!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-4369", "title": "Try to input the SCSI bus, maybe it will generate the 1080p pixel!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-9035", "title": "We need to override the solid state PNG array!", "status": "cancelled", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-3970", "title": "You can't index the transmitter without quantifying the haptic ASCII card!", "status": "todo", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-4473", "title": "You can't bypass the protocol without overriding the neural RSS program!", "status": "todo", "label": "documentation", "priority": "low"}, {"selected": false, "id": "TASK-4136", "title": "You can't hack the hard drive without hacking the primary JSON program!", "status": "cancelled", "label": "bug", "priority": "medium"}, {"selected": false, "id": "TASK-3939", "title": "Use the back-end SQL firewall, then you can connect the neural hard drive!", "status": "done", "label": "feature", "priority": "low"}, {"selected": false, "id": "TASK-2007", "title": "I'll input the back-end USB protocol, that should bandwidth the PCI system!", "status": "backlog", "label": "bug", "priority": "high"}, {"selected": false, "id": "TASK-7516", "title": "Use the primary SQL program, then you can generate the auxiliary transmitter!", "status": "done", "label": "documentation", "priority": "medium"}, {"selected": false, "id": "TASK-6906", "title": "Try to back up the DRAM system, maybe it will reboot the online transmitter!", "status": "done", "label": "feature", "priority": "high"}, {"selected": false, "id": "TASK-5207", "title": "The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!", "status": "progress", "label": "bug", "priority": "low"}]
|
MonsterUI/docs/examples/data/statuses.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"backlog": {
|
| 3 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M0.877075 7.49972C0.877075 3.84204 3.84222 0.876892 7.49991 0.876892C11.1576 0.876892 14.1227 3.84204 14.1227 7.49972C14.1227 11.1574 11.1576 14.1226 7.49991 14.1226C3.84222 14.1226 0.877075 11.1574 0.877075 7.49972ZM7.49991 1.82689C4.36689 1.82689 1.82708 4.36671 1.82708 7.49972C1.82708 10.6327 4.36689 13.1726 7.49991 13.1726C10.6329 13.1726 13.1727 10.6327 13.1727 7.49972C13.1727 4.36671 10.6329 1.82689 7.49991 1.82689ZM8.24993 10.5C8.24993 10.9142 7.91414 11.25 7.49993 11.25C7.08571 11.25 6.74993 10.9142 6.74993 10.5C6.74993 10.0858 7.08571 9.75 7.49993 9.75C7.91414 9.75 8.24993 10.0858 8.24993 10.5ZM6.05003 6.25C6.05003 5.57211 6.63511 4.925 7.50003 4.925C8.36496 4.925 8.95003 5.57211 8.95003 6.25C8.95003 6.74118 8.68002 6.99212 8.21447 7.27494C8.16251 7.30651 8.10258 7.34131 8.03847 7.37854L8.03841 7.37858C7.85521 7.48497 7.63788 7.61119 7.47449 7.73849C7.23214 7.92732 6.95003 8.23198 6.95003 8.7C6.95004 9.00376 7.19628 9.25 7.50004 9.25C7.8024 9.25 8.04778 9.00601 8.05002 8.70417L8.05056 8.7033C8.05924 8.6896 8.08493 8.65735 8.15058 8.6062C8.25207 8.52712 8.36508 8.46163 8.51567 8.37436L8.51571 8.37433C8.59422 8.32883 8.68296 8.27741 8.78559 8.21506C9.32004 7.89038 10.05 7.35382 10.05 6.25C10.05 4.92789 8.93511 3.825 7.50003 3.825C6.06496 3.825 4.95003 4.92789 4.95003 6.25C4.95003 6.55376 5.19628 6.8 5.50003 6.8C5.80379 6.8 6.05003 6.55376 6.05003 6.25Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 4 |
+
"text": "Backlog"
|
| 5 |
+
},
|
| 6 |
+
"cancelled": {
|
| 7 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M0.877075 7.49988C0.877075 3.84219 3.84222 0.877045 7.49991 0.877045C11.1576 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1576 14.1227 7.49991 14.1227C3.84222 14.1227 0.877075 11.1575 0.877075 7.49988ZM7.49991 1.82704C4.36689 1.82704 1.82708 4.36686 1.82708 7.49988C1.82708 10.6329 4.36689 13.1727 7.49991 13.1727C10.6329 13.1727 13.1727 10.6329 13.1727 7.49988C13.1727 4.36686 10.6329 1.82704 7.49991 1.82704ZM9.85358 5.14644C10.0488 5.3417 10.0488 5.65829 9.85358 5.85355L8.20713 7.49999L9.85358 9.14644C10.0488 9.3417 10.0488 9.65829 9.85358 9.85355C9.65832 10.0488 9.34173 10.0488 9.14647 9.85355L7.50002 8.2071L5.85358 9.85355C5.65832 10.0488 5.34173 10.0488 5.14647 9.85355C4.95121 9.65829 4.95121 9.3417 5.14647 9.14644L6.79292 7.49999L5.14647 5.85355C4.95121 5.65829 4.95121 5.3417 5.14647 5.14644C5.34173 4.95118 5.65832 4.95118 5.85358 5.14644L7.50002 6.79289L9.14647 5.14644C9.34173 4.95118 9.65832 4.95118 9.85358 5.14644Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 8 |
+
"text": "Cancelled"
|
| 9 |
+
},
|
| 10 |
+
"done": {
|
| 11 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M7.49991 0.877045C3.84222 0.877045 0.877075 3.84219 0.877075 7.49988C0.877075 11.1575 3.84222 14.1227 7.49991 14.1227C11.1576 14.1227 14.1227 11.1575 14.1227 7.49988C14.1227 3.84219 11.1576 0.877045 7.49991 0.877045ZM1.82708 7.49988C1.82708 4.36686 4.36689 1.82704 7.49991 1.82704C10.6329 1.82704 13.1727 4.36686 13.1727 7.49988C13.1727 10.6329 10.6329 13.1727 7.49991 13.1727C4.36689 13.1727 1.82708 10.6329 1.82708 7.49988ZM10.1589 5.53774C10.3178 5.31191 10.2636 5.00001 10.0378 4.84109C9.81194 4.68217 9.50004 4.73642 9.34112 4.96225L6.51977 8.97154L5.35681 7.78706C5.16334 7.59002 4.84677 7.58711 4.64973 7.78058C4.45268 7.97404 4.44978 8.29061 4.64325 8.48765L6.22658 10.1003C6.33054 10.2062 6.47617 10.2604 6.62407 10.2483C6.77197 10.2363 6.90686 10.1591 6.99226 10.0377L10.1589 5.53774Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 12 |
+
"text": "Done"
|
| 13 |
+
},
|
| 14 |
+
"progress": {
|
| 15 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M5.49998 0.5C5.49998 0.223858 5.72383 0 5.99998 0H7.49998H8.99998C9.27612 0 9.49998 0.223858 9.49998 0.5C9.49998 0.776142 9.27612 1 8.99998 1H7.99998V2.11922C9.09832 2.20409 10.119 2.56622 10.992 3.13572C11.0116 3.10851 11.0336 3.08252 11.058 3.05806L11.858 2.25806C12.1021 2.01398 12.4978 2.01398 12.7419 2.25806C12.986 2.50214 12.986 2.89786 12.7419 3.14194L11.967 3.91682C13.1595 5.07925 13.9 6.70314 13.9 8.49998C13.9 12.0346 11.0346 14.9 7.49998 14.9C3.96535 14.9 1.09998 12.0346 1.09998 8.49998C1.09998 5.13362 3.69904 2.3743 6.99998 2.11922V1H5.99998C5.72383 1 5.49998 0.776142 5.49998 0.5ZM2.09998 8.49998C2.09998 5.51764 4.51764 3.09998 7.49998 3.09998C10.4823 3.09998 12.9 5.51764 12.9 8.49998C12.9 11.4823 10.4823 13.9 7.49998 13.9C4.51764 13.9 2.09998 11.4823 2.09998 8.49998ZM7.99998 4.5C7.99998 4.22386 7.77612 4 7.49998 4C7.22383 4 6.99998 4.22386 6.99998 4.5V9.5C6.99998 9.77614 7.22383 10 7.49998 10C7.77612 10 7.99998 9.77614 7.99998 9.5V4.5Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 16 |
+
"text": "In Progress"
|
| 17 |
+
},
|
| 18 |
+
"todo": {
|
| 19 |
+
"icon": "<svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"mr-2 h-4 w-4 text-muted-foreground\"><path d=\"M0.877075 7.49991C0.877075 3.84222 3.84222 0.877075 7.49991 0.877075C11.1576 0.877075 14.1227 3.84222 14.1227 7.49991C14.1227 11.1576 11.1576 14.1227 7.49991 14.1227C3.84222 14.1227 0.877075 11.1576 0.877075 7.49991ZM7.49991 1.82708C4.36689 1.82708 1.82708 4.36689 1.82708 7.49991C1.82708 10.6329 4.36689 13.1727 7.49991 13.1727C10.6329 13.1727 13.1727 10.6329 13.1727 7.49991C13.1727 4.36689 10.6329 1.82708 7.49991 1.82708Z\" fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"></path></svg>",
|
| 20 |
+
"text": "Todo"
|
| 21 |
+
}
|
| 22 |
+
}
|
MonsterUI/docs/examples/forms.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Forms Example built with MonsterUI (original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from fasthtml.common import *
|
| 5 |
+
from monsterui.all import *
|
| 6 |
+
from fasthtml.svg import *
|
| 7 |
+
|
| 8 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 9 |
+
|
| 10 |
+
def HelpText(c): return P(c,cls=TextPresets.muted_sm)
|
| 11 |
+
|
| 12 |
+
def heading():
|
| 13 |
+
return Div(cls="space-y-5")(
|
| 14 |
+
H2("Settings"),
|
| 15 |
+
Subtitle("Manage your account settings and set e-mail preferences."),
|
| 16 |
+
DividerSplit())
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
sidebar = NavContainer(
|
| 20 |
+
*map(lambda x: Li(A(x)), ("Profile", "Account", "Appearance", "Notifications", "Display")),
|
| 21 |
+
uk_switcher="connect: #component-nav; animation: uk-animation-fade",
|
| 22 |
+
cls=(NavT.secondary,"space-y-4 p-4 w-1/5"))
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def FormSectionDiv(*c, cls='space-y-2', **kwargs): return Div(*c, cls=cls, **kwargs)
|
| 26 |
+
|
| 27 |
+
def FormLayout(title, subtitle, *content, cls='space-y-3 mt-4'): return Container(Div(H3(title), Subtitle(subtitle), DividerLine(), Form(*content, cls=cls)))
|
| 28 |
+
|
| 29 |
+
def profile_form():
|
| 30 |
+
content = (FormSectionDiv(
|
| 31 |
+
LabelInput("Username", placeholder='sveltecult', id='username'),
|
| 32 |
+
HelpText("This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days.")),
|
| 33 |
+
FormSectionDiv(
|
| 34 |
+
LabelSelect(
|
| 35 |
+
Option("Select a verified email to display", value="", selected=True, disabled=True),
|
| 36 |
+
*[Option(o, value=o) for o in ('[email protected]', '[email protected]', '[email protected]')],
|
| 37 |
+
label="Email", id="email"),
|
| 38 |
+
HelpText("You can manage verified email addresses in your email settings.")),
|
| 39 |
+
FormSectionDiv(
|
| 40 |
+
LabelTextArea("Bio", id="bio", placeholder="Tell us a little bit about yourself"),
|
| 41 |
+
HelpText("You can @mention other users and organizations to link to them."),
|
| 42 |
+
P("String must contain at least 4 character(s)", cls="text-destructive")),
|
| 43 |
+
FormSectionDiv(
|
| 44 |
+
FormLabel("URLs"),
|
| 45 |
+
HelpText("Add links to your website, blog, or social media profiles."),
|
| 46 |
+
Input(value="https://www.franken-ui.dev"),
|
| 47 |
+
Input(value="https://github.com/sveltecult/franken-ui"),
|
| 48 |
+
Button("Add URL")),
|
| 49 |
+
Button('Update profile', cls=ButtonT.primary))
|
| 50 |
+
|
| 51 |
+
return FormLayout('Profile', 'This is how others will see you on the site.', *content)
|
| 52 |
+
|
| 53 |
+
def account_form():
|
| 54 |
+
content = (
|
| 55 |
+
FormSectionDiv(
|
| 56 |
+
LabelInput("Name", placeholder="Your name", id="name"),
|
| 57 |
+
HelpText("This is the name that will be displayed on your profile and in emails.")),
|
| 58 |
+
FormSectionDiv(
|
| 59 |
+
LabelInput("Date of Birth", type="date", placeholder="Pick a date", id="date_of_birth"),
|
| 60 |
+
HelpText("Your date of birth is used to calculate your age.")),
|
| 61 |
+
FormSectionDiv(
|
| 62 |
+
LabelSelect(*Options("Select a language", "English", "French", "German", "Spanish", "Portuguese", selected_idx=1, disabled_idxs={0}),
|
| 63 |
+
label='Language', id="language"),
|
| 64 |
+
HelpText("This is the language that will be used in the dashboard.")),
|
| 65 |
+
Button('Update profile', cls=ButtonT.primary))
|
| 66 |
+
|
| 67 |
+
return FormLayout('Account', 'Update your account settings. Set your preferred language and timezone.', *content)
|
| 68 |
+
|
| 69 |
+
def appearance_form():
|
| 70 |
+
def theme_item(bg_color, content_bg, text_bg):
|
| 71 |
+
common_content = f"space-y-2 rounded-md {content_bg} p-2 shadow-sm"
|
| 72 |
+
item_row = lambda: Div(cls=f"flex items-center space-x-2 {common_content}")(
|
| 73 |
+
Div(cls=f"h-4 w-4 rounded-full {text_bg}"),
|
| 74 |
+
Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}"))
|
| 75 |
+
|
| 76 |
+
return Div(cls=f"space-y-2 rounded-sm {bg_color} p-2")(
|
| 77 |
+
Div(cls=common_content)(
|
| 78 |
+
Div(cls=f"h-2 w-[80px] rounded-lg {text_bg}"),
|
| 79 |
+
Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}")),
|
| 80 |
+
item_row(),
|
| 81 |
+
item_row())
|
| 82 |
+
|
| 83 |
+
common_toggle_cls = "block cursor-pointer items-center rounded-md border-2 border-muted p-1 ring-ring"
|
| 84 |
+
|
| 85 |
+
content = (
|
| 86 |
+
FormSectionDiv(
|
| 87 |
+
LabelSelect(*Options('Select a font family', 'Inter', 'Geist', 'Open Sans', selected_idx=2, disabled_idxs={0}),
|
| 88 |
+
label='Font Family', id='font_family'),
|
| 89 |
+
HelpText("Set the font you want to use in the dashboard.")),
|
| 90 |
+
FormSectionDiv(
|
| 91 |
+
FormLabel("Theme"),
|
| 92 |
+
HelpText("Select the theme for the dashboard."),
|
| 93 |
+
Grid(
|
| 94 |
+
A(id="theme-toggle-light", cls=common_toggle_cls)(theme_item("bg-[#ecedef]", "bg-white", "bg-[#ecedef]")),
|
| 95 |
+
A(id="theme-toggle-dark", cls=f"{common_toggle_cls} bg-popover")(theme_item("bg-slate-950", "bg-slate-800", "bg-slate-400")),
|
| 96 |
+
cols_max=2,cls=('max-w-md','gap-8'))),
|
| 97 |
+
Button('Update preferences', cls=ButtonT.primary))
|
| 98 |
+
|
| 99 |
+
return FormLayout('Appearance', 'Customize the appearance of the app. Automatically switch between day and night themes.', *content)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
notification_items = [
|
| 103 |
+
{"title": "Communication emails", "description": "Receive emails about your account activity.", "checked": False, "disabled": False},
|
| 104 |
+
{"title": "Marketing emails", "description": "Receive emails about new products, features, and more.", "checked": False, "disabled": False},
|
| 105 |
+
{"title": "Social emails", "description": "Receive emails for friend requests, follows, and more.", "checked": True, "disabled": False},
|
| 106 |
+
{"title": "Security emails", "description": "Receive emails about your account activity and security.", "checked": True, "disabled": True}]
|
| 107 |
+
|
| 108 |
+
def notifications_form():
|
| 109 |
+
def RadioLabel(label): return DivLAligned(Radio(name="notification", checked=(label=="Nothing")), FormLabel(label))
|
| 110 |
+
|
| 111 |
+
def NotificationCard(item):
|
| 112 |
+
return Card(
|
| 113 |
+
Div(cls="space-y-0.5")(
|
| 114 |
+
FormLabel(Strong(item['title'], cls=TextT.sm),
|
| 115 |
+
HelpText(item['description']))))
|
| 116 |
+
content = Div(
|
| 117 |
+
FormSectionDiv(
|
| 118 |
+
FormLabel("Notify me about"),
|
| 119 |
+
*map(RadioLabel, ["All new messages", "Direct messages and mentions", "Nothing"])),
|
| 120 |
+
Div(
|
| 121 |
+
H4("Email Notifications", cls="mb-4"),
|
| 122 |
+
Grid(*map(NotificationCard, notification_items), cols=1)),
|
| 123 |
+
LabelCheckboxX("Use different settings for my mobile devices", id="notification_mobile"),
|
| 124 |
+
HelpText("You can manage your mobile notifications in the mobile settings page."),
|
| 125 |
+
Button('Update notifications', cls=ButtonT.primary))
|
| 126 |
+
|
| 127 |
+
return FormLayout('Notifications', 'Configure how you receive notifications.', *content)
|
| 128 |
+
|
| 129 |
+
def display_form():
|
| 130 |
+
content = (
|
| 131 |
+
Div(cls="space-y-2")(
|
| 132 |
+
Div(cls="mb-4")(
|
| 133 |
+
H5("Sidebar"),
|
| 134 |
+
Subtitle("Select the items you want to display in the sidebar.")),
|
| 135 |
+
*[Div(CheckboxX(id=f"display_{i}", checked=i in [0, 1, 2]),FormLabel(label))
|
| 136 |
+
for i, label in enumerate(["Recents", "Home", "Applications", "Desktop", "Downloads", "Documents"])]),
|
| 137 |
+
Button('Update display', cls=ButtonT.primary))
|
| 138 |
+
return FormLayout('Display', 'Turn items on or off to control what\'s displayed in the app.', *content)
|
| 139 |
+
|
| 140 |
+
@rt
|
| 141 |
+
def index():
|
| 142 |
+
return Title("Forms Example"),Container(
|
| 143 |
+
heading(),
|
| 144 |
+
Div(cls="flex gap-x-12")(
|
| 145 |
+
sidebar,
|
| 146 |
+
Ul(id="component-nav", cls="uk-switcher max-w-2xl")(
|
| 147 |
+
Li(cls="uk-active")(profile_form(),
|
| 148 |
+
*map(Li, [account_form(), appearance_form(), notifications_form(), display_form()])))))
|
| 149 |
+
|
| 150 |
+
serve()
|
MonsterUI/docs/examples/mail.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Mail Example built with MonsterUI (original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
from monsterui.all import *
|
| 5 |
+
from fasthtml.svg import *
|
| 6 |
+
import pathlib, json
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
|
| 9 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 10 |
+
|
| 11 |
+
sidebar_group1 = (('home', 'Inbox', '128'), ('file-text', 'Drafts', '9'), (' arrow-up-right', 'Sent', ''),
|
| 12 |
+
('ban', 'Junk', '23'), ('trash', 'Trash', ''), ('folder', 'Archive', ''))
|
| 13 |
+
|
| 14 |
+
sidebar_group2 = (('globe','Social','972'),('info','Updates','342'),('messages-square','Forums','128'),
|
| 15 |
+
('shopping-cart','Shopping','8'),('shopping-bag','Promotions','21'),)
|
| 16 |
+
|
| 17 |
+
def MailSbLi(icon, title, cnt):
|
| 18 |
+
return Li(A(DivLAligned(Span(UkIcon(icon)),Span(title),P(cnt, cls=TextPresets.muted_sm)),href='#', cls='hover:bg-secondary p-4'))
|
| 19 |
+
|
| 20 |
+
sidebar = NavContainer(
|
| 21 |
+
NavHeaderLi(H3("Email"), cls='p-3'),
|
| 22 |
+
Li(Select(map(Option, ('[email protected]','[email protected]', '[email protected]')))),
|
| 23 |
+
*[MailSbLi(i, t, c) for i, t, c in sidebar_group1],
|
| 24 |
+
Li(Hr()),
|
| 25 |
+
*[MailSbLi(i, t, c) for i, t, c in sidebar_group2],
|
| 26 |
+
cls='mt-3')
|
| 27 |
+
|
| 28 |
+
mail_data = json.load(open(pathlib.Path('data_/mail.json')))
|
| 29 |
+
|
| 30 |
+
def format_date(date_str):
|
| 31 |
+
date_obj = datetime.fromisoformat(date_str)
|
| 32 |
+
return date_obj.strftime("%Y-%m-%d %I:%M %p")
|
| 33 |
+
|
| 34 |
+
def MailItem(mail):
|
| 35 |
+
cls_base = 'relative rounded-lg border border-border p-3 text-sm hover:bg-secondary space-y-2'
|
| 36 |
+
cls = f"{cls_base} {'bg-muted' if mail == mail_data[0] else ''} {'tag-unread' if not mail['read'] else 'tag-mail'}"
|
| 37 |
+
|
| 38 |
+
return Li(
|
| 39 |
+
DivFullySpaced(
|
| 40 |
+
DivLAligned(
|
| 41 |
+
Strong(mail['name']),
|
| 42 |
+
Span(cls='flex h-2 w-2 rounded-full bg-blue-600') if not mail['read'] else ''),
|
| 43 |
+
Time(format_date(mail['date']), cls='text-xs')),
|
| 44 |
+
Small(mail['subject'], href=f"#mail-{mail['id']}"),
|
| 45 |
+
Div(mail['text'][:100] + '...', cls=TextPresets.muted_sm),
|
| 46 |
+
DivLAligned(
|
| 47 |
+
*[Label(A(label, href='#'), cls='uk-label-primary' if label == 'work' else '') for label in mail['labels']]),
|
| 48 |
+
cls=cls)
|
| 49 |
+
|
| 50 |
+
def MailList(mails): return Ul(cls='js-filter space-y-2 p-4 pt-0')(*[MailItem(mail) for mail in mails])
|
| 51 |
+
|
| 52 |
+
def MailContent():
|
| 53 |
+
return Div(cls='flex flex-col',uk_filter="target: .js-filter")(
|
| 54 |
+
Div(cls='flex px-4 py-2 ')(
|
| 55 |
+
H3('Inbox'),
|
| 56 |
+
TabContainer(Li(A("All Mail",href='#', role='button'),cls='uk-active', uk_filter_control="filter: .tag-mail"),
|
| 57 |
+
Li(A("Unread",href='#', role='button'), uk_filter_control="filter: .tag-unread"),
|
| 58 |
+
alt=True, cls='ml-auto max-w-40', )),
|
| 59 |
+
Div(cls='flex flex-1 flex-col')(
|
| 60 |
+
Div(cls='p-4')(
|
| 61 |
+
Div(cls='uk-inline w-full')(
|
| 62 |
+
Span(cls='uk-form-icon text-muted-foreground')(UkIcon('search')),
|
| 63 |
+
Input(placeholder='Search'))),
|
| 64 |
+
Div(cls='flex-1 overflow-y-auto max-h-[600px]')(MailList(mail_data))))
|
| 65 |
+
|
| 66 |
+
def IconNavItem(*d): return [Li(A(UkIcon(o[0],uk_tooltip=o[1]))) for o in d]
|
| 67 |
+
def IconNav(*c,cls=''): return Ul(cls=f'uk-iconnav {cls}')(*c)
|
| 68 |
+
|
| 69 |
+
def MailDetailView(mail):
|
| 70 |
+
top_icons = [('folder','Archive'), ('ban','Move to junk'), ('trash','Move to trash')]
|
| 71 |
+
reply_icons = [('reply','Reply'), ('reply','Reply all'), ('forward','Forward')]
|
| 72 |
+
dropdown_items = ['Mark as unread', 'Star read', 'Add Label', 'Mute Thread']
|
| 73 |
+
|
| 74 |
+
return Container(
|
| 75 |
+
DivFullySpaced(
|
| 76 |
+
DivLAligned(
|
| 77 |
+
DivLAligned(*[UkIcon(o[0],uk_tooltip=o[1]) for o in top_icons]),
|
| 78 |
+
Div(UkIcon('clock', uk_tooltip='Snooze'), cls='pl-2'),
|
| 79 |
+
cls='space-x-2 divide-x divide-border'),
|
| 80 |
+
DivLAligned(
|
| 81 |
+
*[UkIcon(o[0],uk_tooltip=o[1]) for o in reply_icons],
|
| 82 |
+
Div(UkIcon('ellipsis-vertical',button=True)),
|
| 83 |
+
DropDownNavContainer(*map(lambda x: Li(A(x)), dropdown_items)))),
|
| 84 |
+
DivLAligned(
|
| 85 |
+
Span(mail['name'][:2], cls='flex h-10 w-10 items-center justify-center rounded-full bg-muted'),
|
| 86 |
+
Div(Strong(mail['name']),
|
| 87 |
+
Div(mail['subject']),
|
| 88 |
+
DivLAligned(P('Reply-To:'), A(mail['email'], href=f"mailto:{mail['email']}"), cls='space-x-1'),
|
| 89 |
+
P(Time(format_date(mail['date']))),
|
| 90 |
+
cls='space-y-1'+TextT.sm),
|
| 91 |
+
cls='m-4 space-x-4'),
|
| 92 |
+
DividerLine(),
|
| 93 |
+
P(mail['text'], cls=TextT.sm +'p-4'),
|
| 94 |
+
DividerLine(),
|
| 95 |
+
Div(TextArea(id='message', placeholder=f"Reply {mail['name']}"),
|
| 96 |
+
DivFullySpaced(
|
| 97 |
+
LabelSwitch('Mute this thread',id='mute'),
|
| 98 |
+
Button('Send', cls=ButtonT.primary)),
|
| 99 |
+
cls='space-y-4'))
|
| 100 |
+
|
| 101 |
+
@rt
|
| 102 |
+
def index():
|
| 103 |
+
return Title("Mail Example"),Container(
|
| 104 |
+
Grid(Div(sidebar, cls='col-span-1'),
|
| 105 |
+
Div(MailContent(), cls='col-span-2'),
|
| 106 |
+
Div(MailDetailView(mail_data[0]), cls='col-span-2'),
|
| 107 |
+
cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5,
|
| 108 |
+
gap=0, cls='flex-1'),
|
| 109 |
+
cls=('flex', ContainerT.xl))
|
| 110 |
+
|
| 111 |
+
serve()
|
MonsterUI/docs/examples/music.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Music Example build with MonsterUI (Original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
|
| 5 |
+
from monsterui.all import *
|
| 6 |
+
|
| 7 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 8 |
+
|
| 9 |
+
def MusicLi(t,hk=''): return Li(A(DivFullySpaced(t,P(hk,cls=TextPresets.muted_sm))))
|
| 10 |
+
|
| 11 |
+
music_items = [("About Music", "" ),
|
| 12 |
+
("Preferences", "⌘" ),
|
| 13 |
+
("Hide Music" , "⌘H" ),
|
| 14 |
+
("Hide Others", "⇧⌘H"),
|
| 15 |
+
("Quit Music" , "⌘Q" )]
|
| 16 |
+
|
| 17 |
+
file_dd_items = [("New", ""),
|
| 18 |
+
("Open Stream URL", "⌘U"),
|
| 19 |
+
("Close Window", "⌘W"),
|
| 20 |
+
("Library", ""),
|
| 21 |
+
("Import", "⌘O"),
|
| 22 |
+
("Burn Playlist to Disc", ""),
|
| 23 |
+
("Show in Finder", "⇧⌘R"),
|
| 24 |
+
("Convert", ""),
|
| 25 |
+
("Page Setup", "Print")]
|
| 26 |
+
|
| 27 |
+
edit_actions = [("Undo", "⌘Z"),
|
| 28 |
+
("Redo", "⇧⌘Z"),
|
| 29 |
+
("Cut", "⌘X"),
|
| 30 |
+
("Copy", "⌘C"),
|
| 31 |
+
("Paste", "⌘V"),
|
| 32 |
+
("Select All", "⌘A"),
|
| 33 |
+
("Deselect All", "⇧⌘A")]
|
| 34 |
+
|
| 35 |
+
view_dd_data = ["Show Playing Next", "Show Lyrics", "Show Status Bar", "Hide Sidebar", "Enter Full Screen"]
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
music_headers = NavBar(
|
| 39 |
+
Button("Music", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("Music"),NavContainer(map(lambda x: MusicLi(*x), music_items)))),
|
| 40 |
+
Button("File", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("File"), NavContainer(map(lambda x: MusicLi(*x), file_dd_items)))),
|
| 41 |
+
Button("Edit", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("Edit")),NavContainer(
|
| 42 |
+
*map(lambda x: MusicLi(*x), edit_actions),
|
| 43 |
+
Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))),
|
| 44 |
+
Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe")))))),
|
| 45 |
+
Button("View", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("View"),NavContainer(map(lambda x: MusicLi(x), view_dd_data)))),
|
| 46 |
+
brand=DivLAligned(H2("Purrify"))
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
# music_headers = NavBarContainer(
|
| 53 |
+
# NavBarLSide(
|
| 54 |
+
# NavBarNav(
|
| 55 |
+
# Li(A("Music"),NavBarNavContainer(map(lambda x: MusicLi(*x), music_items))),
|
| 56 |
+
# Li(A("File"), NavBarNavContainer(map(lambda x: MusicLi(*x), file_dd_items))),
|
| 57 |
+
# Li(A("Edit")),
|
| 58 |
+
# NavBarNavContainer(
|
| 59 |
+
# *map(lambda x: MusicLi(*x), edit_actions),
|
| 60 |
+
# Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))),
|
| 61 |
+
# Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe"))))),
|
| 62 |
+
# Li(A("View"),
|
| 63 |
+
# NavBarNavContainer(map(lambda x: MusicLi(x), view_dd_data))),
|
| 64 |
+
# Li(A("Account"),
|
| 65 |
+
# NavBarNavContainer(
|
| 66 |
+
# NavHeaderLi("Switch Account"),
|
| 67 |
+
# *map(MusicLi, ("Andy", "Benoit", "Luis", "Manage Family", "Add Account")))))))
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def Album(title,artist):
|
| 71 |
+
img_url = 'https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp'
|
| 72 |
+
return Div(
|
| 73 |
+
Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=img_url)),
|
| 74 |
+
Div(cls='space-y-1')(Strong(title),P(artist,cls=TextT.muted)))
|
| 75 |
+
|
| 76 |
+
listen_now_albums = (("Roar", "Catty Perry"), ("Feline on a Prayer", "Cat Jovi"),("Fur Elise", "Ludwig van Beethovpurr"),("Purrple Rain", "Prince's Cat"))
|
| 77 |
+
|
| 78 |
+
made_for_you_albums = [("Like a Feline", "Catdonna"),
|
| 79 |
+
("Livin' La Vida Purrda", "Ricky Catin"),
|
| 80 |
+
("Meow Meow Rocket", "Elton Cat"),
|
| 81 |
+
("Rolling in the Purr", "Catdelle"),
|
| 82 |
+
("Purrs of Silence", "Cat Garfunkel"),
|
| 83 |
+
("Meow Me Maybe", "Carly Rae Purrsen"),]
|
| 84 |
+
|
| 85 |
+
music_content = (Div(H3("Listen Now"), cls="mt-6 space-y-1"),
|
| 86 |
+
Subtitle("Top picks for you. Updated daily."),
|
| 87 |
+
DividerLine(),
|
| 88 |
+
Grid(*[Album(t,a) for t,a in listen_now_albums], cls='gap-8'),
|
| 89 |
+
Div(H3("Made for You"), cls="mt-6 space-y-1"),
|
| 90 |
+
Subtitle("Your personal playlists. Updated daily."),
|
| 91 |
+
DividerLine(),
|
| 92 |
+
Grid(*[Album(t,a) for t,a in made_for_you_albums], cols_xl=6))
|
| 93 |
+
|
| 94 |
+
tabs = TabContainer(
|
| 95 |
+
Li(A('Music', href='#'), cls='uk-active'),
|
| 96 |
+
Li(A('Podcasts', href='#')),
|
| 97 |
+
Li(A('Live', cls='opacity-50'), cls='uk-disabled'),
|
| 98 |
+
uk_switcher='connect: #component-nav; animation: uk-animation-fade',
|
| 99 |
+
alt=True)
|
| 100 |
+
|
| 101 |
+
def podcast_tab():
|
| 102 |
+
return Div(
|
| 103 |
+
Div(cls='space-y-3 mt-6')(
|
| 104 |
+
H3("New Episodes"),
|
| 105 |
+
Subtitle("Your favorite podcasts. Updated daily.")),
|
| 106 |
+
Div(cls="uk-placeholder flex h-[450px] items-center justify-center rounded-md mt-4",uk_placeholder=True)(
|
| 107 |
+
DivVStacked(cls="space-y-6")(
|
| 108 |
+
UkIcon("microphone", 3),
|
| 109 |
+
H4("No episodes added"),
|
| 110 |
+
Subtitle("You have not added any podcasts. Add one below."),
|
| 111 |
+
Button("Add Podcast", cls=ButtonT.primary))))
|
| 112 |
+
|
| 113 |
+
discoved_data = [("play-circle","Listen Now"), ("binoculars", "Browse"), ("rss","Radio")]
|
| 114 |
+
library_data = [("play-circle", "Playlists"), ("music", "Songs"), ("user", "Made for You"), ("users", "Artists"), ("bookmark", "Albums")]
|
| 115 |
+
playlists_data = [("library","Recently Added"), ("library","Recently Played")]
|
| 116 |
+
|
| 117 |
+
def MusicSidebarLi(icon, text): return Li(A(DivLAligned(UkIcon(icon), P(text))))
|
| 118 |
+
sidebar = NavContainer(
|
| 119 |
+
NavHeaderLi(H3("Discover")), *[MusicSidebarLi(*o) for o in discoved_data],
|
| 120 |
+
NavHeaderLi(H3("Library")), *[MusicSidebarLi(*o) for o in library_data],
|
| 121 |
+
NavHeaderLi(H3("Playlists")),*[MusicSidebarLi(*o) for o in playlists_data],
|
| 122 |
+
cls=(NavT.primary,'space-y-3','pl-8'))
|
| 123 |
+
|
| 124 |
+
@rt
|
| 125 |
+
def index():
|
| 126 |
+
return Title("Music Example"),Container(music_headers, DividerSplit(),
|
| 127 |
+
Grid(sidebar,
|
| 128 |
+
Div(cls="col-span-4 border-l border-border")(
|
| 129 |
+
Div(cls="px-8 py-6")(
|
| 130 |
+
DivFullySpaced(
|
| 131 |
+
Div(cls="max-w-80")(tabs),
|
| 132 |
+
Button(cls=ButtonT.primary)(DivLAligned(UkIcon('circle-plus')),Div("Add music"))),
|
| 133 |
+
Ul(id="component-nav", cls="uk-switcher")(
|
| 134 |
+
Li(*music_content),
|
| 135 |
+
Li(podcast_tab())))),
|
| 136 |
+
cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5))
|
| 137 |
+
|
| 138 |
+
serve()
|
MonsterUI/docs/examples/playground.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Playground Example built with MonsterUI (original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
from monsterui.all import *
|
| 5 |
+
from fasthtml.svg import *
|
| 6 |
+
|
| 7 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 8 |
+
|
| 9 |
+
preset_options = ["Grammatical Standard English", "Summarize for a 2nd grader",
|
| 10 |
+
"Text to command","Q&A","English to other languages","Parse unstructured data",
|
| 11 |
+
"Classification","Natural language to Python","Explain code","Chat","More examples"]
|
| 12 |
+
|
| 13 |
+
def playground_navbar():
|
| 14 |
+
save_modal = Modal(
|
| 15 |
+
ModalTitle("Save preset"),
|
| 16 |
+
P("This will save the current playground state as a preset which you can access later or share with others.",cls=("mt-1.5", TextPresets.muted_sm)),
|
| 17 |
+
LabelInput("Name", id="name"),
|
| 18 |
+
LabelInput("Description", id="description"),
|
| 19 |
+
ModalCloseButton("Save", cls=ButtonT.primary),
|
| 20 |
+
id="save")
|
| 21 |
+
|
| 22 |
+
share_dd = Div(cls="space-y-6 p-4")(
|
| 23 |
+
H3("Share preset"),
|
| 24 |
+
P("Anyone who has this link and an OpenAI account will be able to view this.", cls=TextPresets.muted_sm),
|
| 25 |
+
Div(Input(value="https://platform.openai.com/playground/p/7bbKYQvsVkNmVb8NGcdUOLae?model=text-davinci-003", readonly=True),
|
| 26 |
+
Button(UkIcon('copy'), cls=(ButtonT.primary, "uk-drop-close",'mt-4'))))
|
| 27 |
+
|
| 28 |
+
rnav = (
|
| 29 |
+
Select(*Options(*preset_options), name='preset', optgroup_label="Examples",
|
| 30 |
+
placeholder='Load a preset', searchable=True, cls='h-9 w-[200px] lg:w-[300px]'),
|
| 31 |
+
Button("Save", cls=ButtonT.secondary, data_uk_toggle="#save"),save_modal,
|
| 32 |
+
Button("View Code", cls=ButtonT.secondary),
|
| 33 |
+
Button("Share", cls=ButtonT.secondary),DropDownNavContainer(share_dd),
|
| 34 |
+
Button(UkIcon(icon="ellipsis"), cls=ButtonT.secondary),
|
| 35 |
+
DropDownNavContainer(
|
| 36 |
+
Li(A("Content filter preferences")),
|
| 37 |
+
NavDividerLi(),
|
| 38 |
+
Li(A("Delete preset", cls="text-destructive")),
|
| 39 |
+
uk_dropdown="mode: click"))
|
| 40 |
+
|
| 41 |
+
return NavBar(*rnav, brand=H4('Playground'))
|
| 42 |
+
|
| 43 |
+
rsidebar = NavContainer(
|
| 44 |
+
Select(
|
| 45 |
+
Optgroup(map(Option,("text-davinci-003", "text-curie-001", "text-babbage-001", "text-ada-001")),label='GPT-3'),
|
| 46 |
+
Optgroup(map(Option,("code-davinci-002", "code-cushman-001")),label='Codex'),
|
| 47 |
+
label="Model",
|
| 48 |
+
searchable=True),
|
| 49 |
+
LabelRange(label='Temperature', value='12'),
|
| 50 |
+
LabelRange(label='Maximum Length', value='80'),
|
| 51 |
+
LabelRange(label='Top P', value='40'),
|
| 52 |
+
cls='space-y-6 mt-8')
|
| 53 |
+
|
| 54 |
+
@rt
|
| 55 |
+
def index():
|
| 56 |
+
navbar = playground_navbar()
|
| 57 |
+
main_content = Div(
|
| 58 |
+
Div(cls="flex-1")(
|
| 59 |
+
Textarea(cls="uk-textarea h-full p-4", placeholder="Write a tagline for an ice cream shop")),
|
| 60 |
+
cls="flex h-[700px] p-8 w-4/5")
|
| 61 |
+
|
| 62 |
+
bottom_buttons = Div(
|
| 63 |
+
Button("Submit", cls=ButtonT.primary),
|
| 64 |
+
Button(UkIcon(icon="history"), cls=ButtonT.secondary),
|
| 65 |
+
cls="flex gap-x-2")
|
| 66 |
+
|
| 67 |
+
return Title("Playground Example"),Div(navbar, Div(cls="flex w-full")(main_content, rsidebar), bottom_buttons)
|
| 68 |
+
|
| 69 |
+
serve()
|
MonsterUI/docs/examples/scrollspy.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"MonsterUI Scrollspy Example application"
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
from monsterui.all import *
|
| 5 |
+
import random
|
| 6 |
+
|
| 7 |
+
# Using the "slate" theme with Highlight.js enabled
|
| 8 |
+
hdrs = Theme.slate.headers(highlightjs=True)
|
| 9 |
+
app, rt = fast_app(hdrs=hdrs)
|
| 10 |
+
|
| 11 |
+
################################
|
| 12 |
+
### Example Data and Content ###
|
| 13 |
+
################################
|
| 14 |
+
products = [
|
| 15 |
+
{"name": "Laptop", "price": "$999"},
|
| 16 |
+
{"name": "Smartphone", "price": "$599"}
|
| 17 |
+
]
|
| 18 |
+
|
| 19 |
+
code_example = """
|
| 20 |
+
# Python Code Example
|
| 21 |
+
def greet(name):
|
| 22 |
+
return f"Hello, {name}!"
|
| 23 |
+
|
| 24 |
+
print(greet("World"))
|
| 25 |
+
"""
|
| 26 |
+
testimonials = [
|
| 27 |
+
{"name": "Alice", "feedback": "Great products and excellent customer service!"},
|
| 28 |
+
{"name": "Bob", "feedback": "Fast shipping and amazing quality!"},
|
| 29 |
+
{"name": "Charlie", "feedback": "Amazing experience! Will definitely buy again."},
|
| 30 |
+
{"name": "Diana", "feedback": "Affordable prices and great variety!"},
|
| 31 |
+
{"name": "Edward", "feedback": "Customer support was very helpful."},
|
| 32 |
+
{"name": "Fiona", "feedback": "Loved the design and quality!"}
|
| 33 |
+
]
|
| 34 |
+
|
| 35 |
+
# Team members
|
| 36 |
+
team = [
|
| 37 |
+
{"name": "Isaac Flath", "role": "CEO"},
|
| 38 |
+
{"name": "Benjamin Clavié", "role": "AI Researcher"},
|
| 39 |
+
{"name": "Alexis Gallagher", "role": "ML Engineer"},
|
| 40 |
+
{"name": "Hamel Husain", "role": "Data Scientist"},
|
| 41 |
+
{"name": "Austin Huang", "role": "Software Engineer"},
|
| 42 |
+
{"name": "Benjamin Warner", "role": "Product Manager"},
|
| 43 |
+
{"name": "Jonathan Whitaker", "role": "UX Designer"},
|
| 44 |
+
{"name": "Kerem Turgutlu", "role": "DevOps Engineer"},
|
| 45 |
+
{"name": "Curtis Allan", "role": "DevOps Engineer"},
|
| 46 |
+
{"name": "Audrey Roy Greenfeld", "role": "Security Analyst"},
|
| 47 |
+
{"name": "Nathan Cooper", "role": "Full Stack Developer"},
|
| 48 |
+
{"name": "Jeremy Howard", "role": "CTO"},
|
| 49 |
+
{"name": "Wayde Gilliam", "role": "Cloud Architect"},
|
| 50 |
+
{"name": "Daniel Roy Greenfeld", "role": "Blockchain Expert"},
|
| 51 |
+
{"name": "Tommy Collins", "role": "AI Ethics Researcher"}
|
| 52 |
+
]
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def ProductCard(p,img_id=1):
|
| 56 |
+
return Card(
|
| 57 |
+
PicSumImg(w=500, height=100, id=img_id),
|
| 58 |
+
DivFullySpaced(H4(p["name"]), P(Strong(p["price"], cls=TextT.sm))),
|
| 59 |
+
Button("Details", cls=(ButtonT.primary, "w-full")))
|
| 60 |
+
|
| 61 |
+
def TestimonialCard(t,img_id=1):
|
| 62 |
+
return Card(
|
| 63 |
+
DivLAligned(PicSumImg(w=50, h=50, cls='rounded-full', id=img_id), H4(t["name"])),
|
| 64 |
+
P(Q((t["feedback"]))))
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def TeamCard(m,img_id=1):
|
| 68 |
+
return Card(
|
| 69 |
+
DivLAligned(
|
| 70 |
+
PicSumImg(w=50, h=50, cls='rounded-full', id=img_id),
|
| 71 |
+
Div(H4(m["name"]), P(m["role"]))),
|
| 72 |
+
DivRAligned(
|
| 73 |
+
UkIcon('twitter', cls='w-5 h-5'),
|
| 74 |
+
UkIcon('linkedin', cls='w-5 h-5'),
|
| 75 |
+
UkIcon('github', cls='w-5 h-5'),
|
| 76 |
+
cls=TextT.gray+'space-x-2'
|
| 77 |
+
),
|
| 78 |
+
cls='p-3')
|
| 79 |
+
|
| 80 |
+
################################
|
| 81 |
+
### Navigation and Scrollspy ###
|
| 82 |
+
################################
|
| 83 |
+
|
| 84 |
+
scrollspy_links = (
|
| 85 |
+
A("Welcome", href="#welcome-section"),
|
| 86 |
+
A("Products", href="#products-section"),
|
| 87 |
+
A("Testimonials", href="#testimonials-section"),
|
| 88 |
+
A("Team", href="#team-section"),
|
| 89 |
+
A("Code Example", href="#code-section"))
|
| 90 |
+
@rt
|
| 91 |
+
def index():
|
| 92 |
+
def _Section(*c, **kwargs): return Section(*c, cls='space-y-3 my-48',**kwargs)
|
| 93 |
+
return Container(
|
| 94 |
+
NavBar(
|
| 95 |
+
*scrollspy_links,
|
| 96 |
+
brand=DivLAligned(H3("Scrollspy Demo!"),UkIcon('rocket',height=30,width=30)),
|
| 97 |
+
sticky=True, uk_scrollspy_nav=True,
|
| 98 |
+
scrollspy_cls=ScrollspyT.bold),
|
| 99 |
+
NavContainer(
|
| 100 |
+
*map(Li, scrollspy_links),
|
| 101 |
+
uk_scrollspy_nav=True,
|
| 102 |
+
sticky=True,
|
| 103 |
+
cls=(NavT.primary,'pt-20 px-5 pr-10')),
|
| 104 |
+
Container(
|
| 105 |
+
# Notice the ID of each section corresponds to the `scrollspy_links` dictionary
|
| 106 |
+
# So in scollspy `NavContainer` the `href` of each `Li` is the ID of the section
|
| 107 |
+
DivCentered(
|
| 108 |
+
H1("Welcome to the Store!"),
|
| 109 |
+
Subtitle("Explore our products and enjoy dynamic code examples."),
|
| 110 |
+
id="welcome-section"),
|
| 111 |
+
_Section(H2("Products"),
|
| 112 |
+
Grid(*[ProductCard(p,img_id=i) for i,p in enumerate(products)], cols_lg=2),
|
| 113 |
+
id="products-section"),
|
| 114 |
+
_Section(H2("Testimonials"),
|
| 115 |
+
Slider(*[TestimonialCard(t,img_id=i) for i,t in enumerate(testimonials)]),
|
| 116 |
+
id="testimonials-section"),
|
| 117 |
+
_Section(H2("Our Team"),
|
| 118 |
+
Grid(*[TeamCard(m,img_id=i) for i,m in enumerate(team)], cols_lg=2, cols_max=3),
|
| 119 |
+
id="team-section"),
|
| 120 |
+
_Section(H2("Code Example"),
|
| 121 |
+
CodeBlock(code_example, lang="python"),
|
| 122 |
+
id="code-section")),
|
| 123 |
+
cls=(ContainerT.xl,'uk-container-expand'))
|
| 124 |
+
|
| 125 |
+
serve()
|
MonsterUI/docs/examples/tasks.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""FrankenUI Tasks Example built with MonsterUI (original design by ShadCN)"""
|
| 2 |
+
|
| 3 |
+
from fasthtml.common import *
|
| 4 |
+
from monsterui.all import *
|
| 5 |
+
from fasthtml.svg import *
|
| 6 |
+
import json
|
| 7 |
+
|
| 8 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 9 |
+
|
| 10 |
+
def LAlignedCheckTxt(txt): return DivLAligned(UkIcon(icon='check'), P(txt, cls=TextPresets.muted_sm))
|
| 11 |
+
|
| 12 |
+
with open('data_/status_list.json', 'r') as f: data = json.load(f)
|
| 13 |
+
with open('data_/statuses.json', 'r') as f: statuses = json.load(f)
|
| 14 |
+
|
| 15 |
+
def _create_tbl_data(d):
|
| 16 |
+
return {'Done': d['selected'], 'Task': d['id'], 'Title': d['title'],
|
| 17 |
+
'Status' : d['status'], 'Priority': d['priority'] }
|
| 18 |
+
|
| 19 |
+
data = [_create_tbl_data(d) for d in data]
|
| 20 |
+
page_size = 15
|
| 21 |
+
current_page = 0
|
| 22 |
+
paginated_data = data[current_page*page_size:(current_page+1)*page_size]
|
| 23 |
+
|
| 24 |
+
priority_dd = [{'priority': "low", 'count': 36 }, {'priority': "medium", 'count': 33 }, {'priority': "high", 'count': 31 }]
|
| 25 |
+
|
| 26 |
+
status_dd = [{'status': "backlog", 'count': 21 },{'status': "todo", 'count': 21 },{'status': "progress", 'count': 20 },{'status': "done",'count': 19 },{'status': "cancelled", 'count': 19 }]
|
| 27 |
+
|
| 28 |
+
def create_hotkey_li(hotkey): return NavCloseLi(A(DivFullySpaced(hotkey[0], Span(hotkey[1], cls=TextPresets.muted_sm))))
|
| 29 |
+
|
| 30 |
+
hotkeys_a = (('Profile','⇧⌘P'),('Billing','⌘B'),('Settings','⌘S'),('New Team',''))
|
| 31 |
+
hotkeys_b = (('Logout',''), )
|
| 32 |
+
|
| 33 |
+
avatar_opts = DropDownNavContainer(
|
| 34 |
+
NavHeaderLi(P('sveltecult'),NavSubtitle('[email protected]')),
|
| 35 |
+
NavDividerLi(),
|
| 36 |
+
*map(create_hotkey_li, hotkeys_a),
|
| 37 |
+
NavDividerLi(),
|
| 38 |
+
*map(create_hotkey_li, hotkeys_b),)
|
| 39 |
+
|
| 40 |
+
def CreateTaskModal():
|
| 41 |
+
return Modal(
|
| 42 |
+
Div(cls='p-6')(
|
| 43 |
+
ModalTitle('Create Task'),
|
| 44 |
+
P('Fill out the information below to create a new task', cls=TextPresets.muted_sm),
|
| 45 |
+
Br(),
|
| 46 |
+
Form(cls='space-y-6')(
|
| 47 |
+
Grid(Div(Select(*map(Option,('Documentation', 'Bug', 'Feature')), label='Task Type', id='task_type')),
|
| 48 |
+
Div(Select(*map(Option,('In Progress', 'Backlog', 'Todo', 'Cancelled', 'Done')), label='Status', id='task_status')),
|
| 49 |
+
Div(Select(*map(Option, ('Low', 'Medium', 'High')), label='Priority', id='task_priority'))),
|
| 50 |
+
TextArea(label='Title', placeholder='Please describe the task that needs to be completed'),
|
| 51 |
+
DivRAligned(
|
| 52 |
+
ModalCloseButton('Cancel', cls=ButtonT.ghost),
|
| 53 |
+
ModalCloseButton('Submit', cls=ButtonT.primary),
|
| 54 |
+
cls='space-x-5'))),
|
| 55 |
+
id='TaskForm')
|
| 56 |
+
|
| 57 |
+
page_heading = DivFullySpaced(cls='space-y-2')(
|
| 58 |
+
Div(cls='space-y-2')(
|
| 59 |
+
H2('Welcome back!'),P("Here's a list of your tasks for this month!", cls=TextPresets.muted_sm)),
|
| 60 |
+
Div(DiceBearAvatar("sveltcult",8,8),avatar_opts))
|
| 61 |
+
|
| 62 |
+
table_controls =(Input(cls='w-[250px]',placeholder='Filter task'),
|
| 63 |
+
Button("Status"),
|
| 64 |
+
DropDownNavContainer(map(NavCloseLi,[A(DivFullySpaced(P(a['status']), P(a['count'])),cls='capitalize') for a in status_dd])),
|
| 65 |
+
Button("Priority"),
|
| 66 |
+
DropDownNavContainer(map(NavCloseLi,[A(DivFullySpaced(LAlignedCheckTxt(a['priority']), a['count']),cls='capitalize') for a in priority_dd])),
|
| 67 |
+
Button("View"),
|
| 68 |
+
DropDownNavContainer(map(NavCloseLi,[A(LAlignedCheckTxt(o)) for o in ['Title','Status','Priority']])),
|
| 69 |
+
Button('Create Task',cls=(ButtonT.primary, TextPresets.bold_sm), data_uk_toggle="target: #TaskForm"))
|
| 70 |
+
|
| 71 |
+
def task_dropdown():
|
| 72 |
+
return Div(Button(UkIcon('ellipsis')),
|
| 73 |
+
DropDownNavContainer(
|
| 74 |
+
map(NavCloseLi,[
|
| 75 |
+
*map(A,('Edit', 'Make a copy', 'Favorite')),
|
| 76 |
+
A(DivFullySpaced(*[P(o, cls=TextPresets.muted_sm) for o in ('Delete', '⌘⌫')]))])))
|
| 77 |
+
def header_render(col):
|
| 78 |
+
match col:
|
| 79 |
+
case "Done": return Th(CheckboxX(), shrink=True)
|
| 80 |
+
case 'Actions': return Th("", shrink=True)
|
| 81 |
+
case _: return Th(col, expand=True)
|
| 82 |
+
|
| 83 |
+
def cell_render(col, val):
|
| 84 |
+
def _Td(*args,cls='', **kwargs): return Td(*args, cls=f'p-2 {cls}',**kwargs)
|
| 85 |
+
match col:
|
| 86 |
+
case "Done": return _Td(shrink=True)(CheckboxX(selected=val))
|
| 87 |
+
case "Task": return _Td(val, cls='uk-visible@s') # Hide on small screens
|
| 88 |
+
case "Title": return _Td(val, cls='font-medium', expand=True)
|
| 89 |
+
case "Status" | "Priority": return _Td(cls='uk-visible@m uk-text-nowrap capitalize')(Span(val))
|
| 90 |
+
case "Actions": return _Td(task_dropdown(), shrink=True)
|
| 91 |
+
case _: raise ValueError(f"Unknown column: {col}")
|
| 92 |
+
|
| 93 |
+
task_columns = ["Done", 'Task', 'Title', 'Status', 'Priority', 'Actions']
|
| 94 |
+
|
| 95 |
+
tasks_table = Div(cls='mt-4')(
|
| 96 |
+
TableFromDicts(
|
| 97 |
+
header_data=task_columns,
|
| 98 |
+
body_data=paginated_data,
|
| 99 |
+
body_cell_render=cell_render,
|
| 100 |
+
header_cell_render=header_render,
|
| 101 |
+
sortable=True,
|
| 102 |
+
cls=(TableT.responsive, TableT.sm, TableT.divider)))
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def footer():
|
| 106 |
+
total_pages = (len(data) + page_size - 1) // page_size
|
| 107 |
+
return DivFullySpaced(
|
| 108 |
+
Div('1 of 100 row(s) selected.', cls=TextPresets.muted_sm),
|
| 109 |
+
DivLAligned(
|
| 110 |
+
DivCentered(f'Page {current_page + 1} of {total_pages}', cls=TextT.sm),
|
| 111 |
+
DivLAligned(*[UkIconLink(icon=i, button=True) for i in ('chevrons-left', 'chevron-left', 'chevron-right', 'chevrons-right')])))
|
| 112 |
+
|
| 113 |
+
tasks_ui = Div(DivFullySpaced(DivLAligned(table_controls), cls='mt-8'), tasks_table, footer())
|
| 114 |
+
|
| 115 |
+
@rt
|
| 116 |
+
def index(): return Container(page_heading, tasks_ui, CreateTaskModal())
|
| 117 |
+
|
| 118 |
+
serve()
|
MonsterUI/docs/examples/ticket.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components"""
|
| 2 |
+
from fasthtml.common import *
|
| 3 |
+
from monsterui.all import *
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
|
| 6 |
+
app, rt = fast_app(hdrs=Theme.blue.headers(daisy=True))
|
| 7 |
+
|
| 8 |
+
def TicketSteps(step):
|
| 9 |
+
return Steps(
|
| 10 |
+
LiStep("Submitted", data_content="📝",
|
| 11 |
+
cls=StepT.success if step > 0 else StepT.primary if step == 0 else StepT.neutral),
|
| 12 |
+
LiStep("In Review", data_content="🔎",
|
| 13 |
+
cls=StepT.success if step > 1 else StepT.primary if step == 1 else StepT.neutral),
|
| 14 |
+
LiStep("Processing", data_content="⚙️",
|
| 15 |
+
cls=StepT.success if step > 2 else StepT.primary if step == 2 else StepT.neutral),
|
| 16 |
+
LiStep("Resolved", data_content="✅",
|
| 17 |
+
cls=StepT.success if step > 3 else StepT.primary if step == 3 else StepT.neutral),
|
| 18 |
+
cls="w-full")
|
| 19 |
+
|
| 20 |
+
def StatusBadge(status):
|
| 21 |
+
styles = {'high': AlertT.error, 'medium': AlertT.warning,'low': AlertT.info}
|
| 22 |
+
alert_type = styles.get(status, AlertT.info)
|
| 23 |
+
return Alert(f"{status.title()} Priority", cls=(alert_type,"w-32 shadow-sm"))
|
| 24 |
+
|
| 25 |
+
def TicketCard(id, title, description, status, step, department):
|
| 26 |
+
return Card(
|
| 27 |
+
CardHeader(
|
| 28 |
+
DivFullySpaced(
|
| 29 |
+
Div(H3(f"#{id}", cls=TextT.muted),
|
| 30 |
+
H4(title),
|
| 31 |
+
cls='space-y-2'),
|
| 32 |
+
StatusBadge(status))),
|
| 33 |
+
CardBody(
|
| 34 |
+
P(description, cls=(TextT.muted, "mb-6")),
|
| 35 |
+
DividerSplit(cls="my-6"),
|
| 36 |
+
TicketSteps(step),
|
| 37 |
+
DividerSplit(cls="my-6"),
|
| 38 |
+
DivFullySpaced(
|
| 39 |
+
Div(Strong("Department"),
|
| 40 |
+
P(department),
|
| 41 |
+
cls=('space-y-3', TextPresets.muted_sm)),
|
| 42 |
+
Div(Strong("Last Updated"),
|
| 43 |
+
P(Time(datetime.now().strftime('%b %d, %H:%M'))),
|
| 44 |
+
cls=('space-y-3', TextPresets.muted_sm)),
|
| 45 |
+
Button("View Details", cls=ButtonT.primary),
|
| 46 |
+
cls='mt-6')),
|
| 47 |
+
cls=CardT.hover)
|
| 48 |
+
|
| 49 |
+
def NewTicketModal():
|
| 50 |
+
return Modal(
|
| 51 |
+
ModalHeader(H3("Create New Support Ticket")),
|
| 52 |
+
ModalBody(
|
| 53 |
+
Alert(
|
| 54 |
+
DivLAligned(UkIcon("info"), Span("Please provide as much detail as possible to help us assist you quickly.")),
|
| 55 |
+
cls=(AlertT.info,"mb-4")),
|
| 56 |
+
Form(
|
| 57 |
+
Grid(LabelInput("Title", id="title", placeholder="Brief description of your issue"),
|
| 58 |
+
LabelSelect(Options("IT Support", "HR", "Facilities", "Finance"), label="Department", id="department")),
|
| 59 |
+
LabelSelect(Options("Low", "Medium", "High"), label="Priority Level", id="priority"),
|
| 60 |
+
LabelTextArea("Description", id="description", placeholder="Please provide detailed information about your issue"),
|
| 61 |
+
DivRAligned(
|
| 62 |
+
Button("Cancel", cls=ButtonT.ghost, data_uk_toggle="target: #new-ticket"),
|
| 63 |
+
Button(Loading(cls=LoadingT.spinner), "Submit Ticket", cls=ButtonT.primary, data_uk_toggle="target: #success-toast; target: #new-ticket")),
|
| 64 |
+
cls='space-y-8')),
|
| 65 |
+
id="new-ticket")
|
| 66 |
+
|
| 67 |
+
@rt
|
| 68 |
+
def index():
|
| 69 |
+
tickets = [
|
| 70 |
+
{'id': "TK-1001", 'title': "Cloud Storage Access Error",
|
| 71 |
+
'description': "Unable to access cloud storage with persistent authorization errors. Multiple users affected across marketing department.",
|
| 72 |
+
'status': 'high', 'step': 2, 'department': 'IT Support'},
|
| 73 |
+
{'id': "TK-1002", 'title': "Email Integration Issue",
|
| 74 |
+
'description': "Exchange server not syncing with mobile devices. Affecting external client communications.",
|
| 75 |
+
'status': 'medium', 'step': 1, 'department': 'IT Support'},
|
| 76 |
+
{'id': "TK-1003", 'title': "Office Equipment Setup",
|
| 77 |
+
'description': "New department printer needs configuration and network integration. Required for upcoming client presentation.",
|
| 78 |
+
'status': 'low', 'step': 0, 'department': 'Facilities'}
|
| 79 |
+
]
|
| 80 |
+
|
| 81 |
+
return Title("Help Desk Dashboard"), Container(
|
| 82 |
+
Section(
|
| 83 |
+
DivFullySpaced(
|
| 84 |
+
H2("Active Tickets"),
|
| 85 |
+
Button(UkIcon("plus-circle", cls="mr-2"), "New Ticket", cls=ButtonT.primary, data_uk_toggle="target: #new-ticket"),
|
| 86 |
+
cls='mb-8'),
|
| 87 |
+
Grid(*[TicketCard(**ticket) for ticket in tickets], cols=1),
|
| 88 |
+
cls="my-6"),
|
| 89 |
+
NewTicketModal(),
|
| 90 |
+
Toast(DivLAligned(UkIcon('check-circle', cls='mr-2'), "Ticket submitted successfully! Our team will review it shortly."), id="success-toast", alert_cls=AlertT.success, cls=(ToastHT.end, ToastVT.bottom)),
|
| 91 |
+
Loading(htmx_indicator=True, type=LoadingT.dots, cls="fixed top-0 right-0 m-4"),
|
| 92 |
+
cls="mx-auto max-w-7xl"
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
serve()
|
MonsterUI/docs/favicon.ico
ADDED
|
|
Git LFS Details
|
MonsterUI/docs/getting_started/GettingStarted.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MonsterUI
|
| 2 |
+
|
| 3 |
+
MonsterUI is a UI framework for FastHTML for building beautiful web interfaces with minimal code. It combines the simplicity of Python with the power of Tailwind. Perfect for data scientists, ML engineers, and developers who want to quickly turn their Python code into polished web apps without the complexity of traditional UI frameworks. Follows semantic HTML patterns when possible.
|
| 4 |
+
|
| 5 |
+
MonsterUI adds the following Tailwind-based libraries [Franken UI](https://franken-ui.dev/) and [DaisyUI](https://daisyui.com/) to FastHTML, as well as Python's [Mistletoe](https://github.com/miyuchina/mistletoe) for Markdown, [HighlightJS](https://highlightjs.org/) for code highlighting, and [Katex](https://katex.org/) for latex support.
|
| 6 |
+
|
| 7 |
+
# Getting Started
|
| 8 |
+
|
| 9 |
+
## Installation
|
| 10 |
+
|
| 11 |
+
To install this library, uses
|
| 12 |
+
|
| 13 |
+
`pip install MonsterUI`
|
| 14 |
+
|
| 15 |
+
## Getting Started
|
| 16 |
+
|
| 17 |
+
### TLDR
|
| 18 |
+
|
| 19 |
+
Run `python file.py` on this to start:
|
| 20 |
+
|
| 21 |
+
``` python
|
| 22 |
+
from fasthtml.common import *
|
| 23 |
+
from monsterui.all import *
|
| 24 |
+
|
| 25 |
+
# Choose a theme color (blue, green, red, etc)
|
| 26 |
+
hdrs = Theme.blue.headers()
|
| 27 |
+
|
| 28 |
+
# Create your app with the theme
|
| 29 |
+
app, rt = fast_app(hdrs=hdrs)
|
| 30 |
+
|
| 31 |
+
@rt
|
| 32 |
+
def index():
|
| 33 |
+
socials = (('github','https://github.com/AnswerDotAI/MonsterUI'),
|
| 34 |
+
('twitter','https://twitter.com/isaac_flath/'),
|
| 35 |
+
('linkedin','https://www.linkedin.com/in/isaacflath/'))
|
| 36 |
+
return Titled("Your First App",
|
| 37 |
+
Card(
|
| 38 |
+
H1("Welcome!"),
|
| 39 |
+
P("Your first MonsterUI app", cls=TextPresets.muted_sm),
|
| 40 |
+
P("I'm excited to see what you build with MonsterUI!"),
|
| 41 |
+
footer=DivLAligned(*[UkIconLink(icon,href=url) for icon,url in socials])))
|
| 42 |
+
|
| 43 |
+
serve()
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
## LLM context files
|
| 47 |
+
|
| 48 |
+
Using LLMs for development is a best practice way to get started and
|
| 49 |
+
explore. While LLMs cannot code for you, they can be helpful assistants.
|
| 50 |
+
You must check, refactor, test, and vet any code any LLM generates for
|
| 51 |
+
you - but they are helpful productivity tools. Take a look inside the
|
| 52 |
+
`llms.txt` file to see links to particularly useful context files!
|
| 53 |
+
|
| 54 |
+
- [llms.txt](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/llms.txt): Links to what is included
|
| 55 |
+
- [llms-ctx.txt](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/llms-ctx.txt): MonsterUI Documentation Pages
|
| 56 |
+
- [API list](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/apilist.txt): API list for MonsterUI (included in llms-ctx.txt)
|
| 57 |
+
- [llms-ctx-full.txt](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/llms-ctx-full.txt): Full context that includes all api reference pages as markdown
|
| 58 |
+
|
| 59 |
+
In addition you can add `/md` (for markdown) to a url to get a markdown representation and `/rmd` for rendered markdown representation (nice for looking to see what would be put into context.
|
| 60 |
+
|
| 61 |
+
### Step by Step
|
| 62 |
+
|
| 63 |
+
To get started, check out:
|
| 64 |
+
|
| 65 |
+
1. Start by importing the modules as follows:
|
| 66 |
+
|
| 67 |
+
``` python
|
| 68 |
+
from fasthtml.common import *
|
| 69 |
+
from monsterui.all import *
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
2. Instantiate the app with the MonsterUI headers
|
| 73 |
+
|
| 74 |
+
``` python
|
| 75 |
+
app = FastHTML(hdrs=Theme.blue.headers())
|
| 76 |
+
|
| 77 |
+
# Alternatively, using the fast_app method
|
| 78 |
+
app, rt = fast_app(hdrs=Theme.slate.headers())
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
> *The color option can be any of the theme options available out of the
|
| 82 |
+
> box*
|
| 83 |
+
|
| 84 |
+
> `katex` and `highlightjs` are not included by default. To include them set `katex=True` or `highlightjs=True` when calling `.headers`. (i.e. `Theme.slate.headers(katex=True)`)*
|
| 85 |
+
|
| 86 |
+
From here, you can explore the API Reference & examples to see how to
|
| 87 |
+
implement the components. You can also check out these demo videos to as
|
| 88 |
+
a quick start guide:
|
| 89 |
+
|
| 90 |
+
- MonsterUI [documentation page and Tutorial
|
| 91 |
+
app](https://monsterui.answer.ai/tutorial_app)
|
| 92 |
+
- Isaac & Hamel : [Building his website’s team
|
| 93 |
+
page](https://youtu.be/22Jn46-mmM0)
|
| 94 |
+
- Isaac & Audrey : [Building a blog](https://youtu.be/gVWAsywxLXE)
|
| 95 |
+
- Isaac : [Building a blog](https://youtu.be/22NJgfAqgko)
|
| 96 |
+
|
| 97 |
+
More resources and improvements to the documentation will be added here
|
| 98 |
+
soon!
|
MonsterUI/docs/getting_started/StylingRulesOfThumb.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fasthtml.common import *
|
| 2 |
+
from monsterui.all import *
|
| 3 |
+
from utils import create_flippable_card, fn2code_string
|
| 4 |
+
|
| 5 |
+
def prerequisites():
|
| 6 |
+
return Section(
|
| 7 |
+
H2("Prerequisites"),
|
| 8 |
+
P("""The MonsterUI library automatically handles a lot of styling for you, but it assumes you structure your page with HTML.
|
| 9 |
+
If you aren't familiar with the basics of HTML, check out the """,
|
| 10 |
+
A("W3 Schools HTML references", href="https://www.w3schools.com/html", cls=AT.muted),
|
| 11 |
+
" to learn more about the basics of HTML."),
|
| 12 |
+
cls='my-4 py-4'
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
def next_steps():
|
| 16 |
+
return Section(
|
| 17 |
+
H2("Next Steps"),
|
| 18 |
+
P("""Once you have a good grasp of HTML, you can reference the following resources to continue to expand your capabilities. Instead of trying to learn all of these, focus on the ones that are relevant to something specific you want to do."""),
|
| 19 |
+
List(map(Li, [
|
| 20 |
+
A("Improving Aesthetics with Spacing", href="https://frankenui.fasthtml.io/docs/getting_started/SpacingTutorial", cls=AT.muted),
|
| 21 |
+
A("Manipulating Page and Element Layout", href="https://frankenui.fasthtml.io/docs/getting_started/LayoutTutorial", cls=AT.muted),
|
| 22 |
+
A("FlexBox Froggy", href="https://flexboxfroggy.com/", cls=AT.muted),
|
| 23 |
+
A("Accessibility", href="https://frankenui.fasthtml.io/docs/getting_started/Accessibility", cls=AT.muted),
|
| 24 |
+
]), cls=ListT.bullet),
|
| 25 |
+
cls='my-4 py-4'
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
def page():
|
| 29 |
+
return Article(
|
| 30 |
+
ArticleTitle("Styling Rules of Thumb"),
|
| 31 |
+
ArticleMeta("A guide to making a pretty good looking website in a hurry"),
|
| 32 |
+
prerequisites(),
|
| 33 |
+
next_steps(),
|
| 34 |
+
# button_section(),
|
| 35 |
+
# typography_section(),
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def accessibility_section():
|
| 40 |
+
# TODO: Alt tags
|
| 41 |
+
# TODO: Aria labels
|
| 42 |
+
# TODO: High contrast
|
| 43 |
+
# TODO: Prefers reduced motion
|
| 44 |
+
pass
|
| 45 |
+
|
| 46 |
+
def further_reading():
|
| 47 |
+
# TODO: Tailwind CSS
|
| 48 |
+
# TODO: FlexBox Froggy
|
| 49 |
+
# TODO: Accessibility
|
| 50 |
+
pass
|
| 51 |
+
|
| 52 |
+
def button_section():
|
| 53 |
+
def _ex_buttons():
|
| 54 |
+
return Form(
|
| 55 |
+
Grid(LabelInput("Email"),
|
| 56 |
+
LabelInput("Name"),
|
| 57 |
+
cols=2),
|
| 58 |
+
Grid(Button("Submit Information", cls=ButtonT.primary),
|
| 59 |
+
Button("Delete Information", cls=ButtonT.destructive),
|
| 60 |
+
Button("Cancel"),
|
| 61 |
+
cols=3)
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
return Section(
|
| 65 |
+
H2("Pick button styles based on desired behavior"),
|
| 66 |
+
Blockquote("The aethetic of a button should match the desired behavior",cls='uk-blockquote mb-8'),
|
| 67 |
+
Strong("What to do:"),
|
| 68 |
+
List(
|
| 69 |
+
Li("Use ButtonT.primary for the most important actions (ie add to card, checkout, etc.)"),
|
| 70 |
+
Li("Use ButtonT.secondary for actions that are important but not the primary action (ie save, etc.)"),
|
| 71 |
+
Li("Use ButtonT.destructive for destructive actions"),
|
| 72 |
+
Li("Use default styling for UX actions (ie go cancel, close etc.)"),
|
| 73 |
+
cls=ListT.bullet
|
| 74 |
+
),
|
| 75 |
+
create_flippable_card(*fn2code_string(_ex_buttons)),
|
| 76 |
+
cls='my-4 py-4'
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
def typography_section():
|
| 80 |
+
def _ex_typography():
|
| 81 |
+
return Div(cls='space-y-5')(
|
| 82 |
+
Div(cls='space-y-3')(
|
| 83 |
+
H1("The main heading"),
|
| 84 |
+
Blockquote("A section describing easy ways to make generally good looking text"),
|
| 85 |
+
),
|
| 86 |
+
Div(cls='space-y-3')(
|
| 87 |
+
H2("My First Section"),
|
| 88 |
+
P("A short description of what's in this section", cls=TextPresets.muted_sm),
|
| 89 |
+
P("""Now I can write the main content of the page with a normal P tag.
|
| 90 |
+
We can use this for longer text like paragraphs.
|
| 91 |
+
It's ideal because this text is highly readable.
|
| 92 |
+
I can write longer sentences and paragraphs without is being really hard to read with highly styled text.
|
| 93 |
+
Often if you aren't careful with styling you can make the text hard to read, especially if you aren't thinking about light vs dark backgrounds.
|
| 94 |
+
"""))
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
return Section(
|
| 99 |
+
H2("Use consistent typography"),
|
| 100 |
+
Blockquote("Consistency is key to a polished user experience",cls='uk-blockquote mb-8'),
|
| 101 |
+
Strong("What to do:"),
|
| 102 |
+
List(
|
| 103 |
+
Li("Use H1-4 for headings"),
|
| 104 |
+
Li("Use P with cls=TextPresets.muted_sm subheadings like "),
|
| 105 |
+
Li("Use P for most body text"),
|
| 106 |
+
cls=ListT.bullet),
|
| 107 |
+
create_flippable_card(*fn2code_string(_ex_typography)),
|
| 108 |
+
cls='my-4 py-4'
|
| 109 |
+
)
|
| 110 |
+
|
MonsterUI/docs/getting_started/app_product_gallery.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fasthtml.common import *
|
| 2 |
+
# MonsterUI shadows fasthtml components with the same name
|
| 3 |
+
from monsterui.all import *
|
| 4 |
+
# If you don't want shadowing behavior, you use import monsterui.core as ... style instead
|
| 5 |
+
|
| 6 |
+
# Get frankenui and tailwind headers via CDN using Theme.blue.headers()
|
| 7 |
+
hdrs = Theme.blue.headers()
|
| 8 |
+
|
| 9 |
+
# fast_app is shadowed by MonsterUI to make it default to no Pico, and add body classes
|
| 10 |
+
# needed for frankenui theme styling
|
| 11 |
+
app, rt = fast_app(hdrs=hdrs)
|
| 12 |
+
|
| 13 |
+
# Examples Product Data to render in the gallery and detail pages
|
| 14 |
+
products = [
|
| 15 |
+
{"name": "Laptop", "price": "$999", "img": "https://picsum.photos/400/100?random=1"},
|
| 16 |
+
{"name": "Smartphone", "price": "$599", "img": "https://picsum.photos/400/100?random=2"},
|
| 17 |
+
{"name": "Headphones", "price": "$199", "img": "https://picsum.photos/400/100?random=3"},
|
| 18 |
+
{"name": "Smartwatch", "price": "$299", "img": "https://picsum.photos/400/100?random=4"},
|
| 19 |
+
{"name": "Tablet", "price": "$449", "img": "https://picsum.photos/400/100?random=5"},
|
| 20 |
+
{"name": "Camera", "price": "$799", "img": "https://picsum.photos/400/100?random=6"},]
|
| 21 |
+
|
| 22 |
+
def ProductCard(p):
|
| 23 |
+
# Card does lots of boilerplate classes so you can just pass in the content
|
| 24 |
+
return Card(
|
| 25 |
+
# width:100% makes the image take the full width so we are guarenteed that we won't
|
| 26 |
+
# have the image cut off or not large enough. Because all our images are a consistent
|
| 27 |
+
# size we do not need to worry about stretching or skewing the image, this is ideal.
|
| 28 |
+
# If you have images of different sizes, you will need to use object-fit:cover and/or
|
| 29 |
+
# height to either strech, shrink, or crop the image. It is much better to adjust your
|
| 30 |
+
# images to be a consistent size upfront so you don't have to handle edge cases of
|
| 31 |
+
# different images skeweing/stretching differently.
|
| 32 |
+
Img(src=p["img"], alt=p["name"], style="width:100%"),
|
| 33 |
+
# All components can take a cls argument to add additional styling - `mt-2` adds margin
|
| 34 |
+
# to the top (see spacing tutorial for details on spacing).
|
| 35 |
+
#
|
| 36 |
+
# Often adding space makes a site look more put together - usually the 2 - 5 range is a
|
| 37 |
+
# good choice
|
| 38 |
+
H4(p["name"], cls="mt-2"),
|
| 39 |
+
# There are helpful Enums, such as TextPresetsT, ButtonT, ContainerT, etc that allow for easy
|
| 40 |
+
# discoverability of class options.
|
| 41 |
+
# bold_sm is helpful for things that you want to look like regular text, but stand out
|
| 42 |
+
# visually for emphasis.
|
| 43 |
+
P(p["price"], cls=TextPresets.bold_sm),
|
| 44 |
+
# ButtonT.primary is useful for actions you really want the user to take (like adding
|
| 45 |
+
# something to the card) - these stand out visually. For dangerous actions (like
|
| 46 |
+
# deleting something) you generally would want to use ButtonT.destructive. For UX actions
|
| 47 |
+
# that aren't a goal of the page (like cancelling something that hasn't been submitted)
|
| 48 |
+
# you generally want the default styling.
|
| 49 |
+
Button("Click me!", cls=(ButtonT.primary, "mt-2"),
|
| 50 |
+
# HTMX can be used as normal on any component
|
| 51 |
+
hx_get=product_detail.to(product_name=p['name']),
|
| 52 |
+
hx_push_url='true',
|
| 53 |
+
hx_target='body'))
|
| 54 |
+
|
| 55 |
+
@rt
|
| 56 |
+
def index():
|
| 57 |
+
# Titled using a H1 title, sets the page title, and wraps contents in Main(Container(...)) using
|
| 58 |
+
# frankenui styles. Generally you will want to use Titled for all of your pages
|
| 59 |
+
return Titled("Example Store Front!",
|
| 60 |
+
Grid(*[ProductCard(p) for p in products], cols_lg=3))
|
| 61 |
+
|
| 62 |
+
example_product_description = """\n
|
| 63 |
+
This is a sample detailed description of the {product_name}. You can see when clicking on the card
|
| 64 |
+
from the gallery you can:
|
| 65 |
+
|
| 66 |
+
+ Have a detailed description of the product on the page
|
| 67 |
+
+ Have an order form to fill out and submit
|
| 68 |
+
+ Anything else you want!
|
| 69 |
+
"""
|
| 70 |
+
|
| 71 |
+
@rt
|
| 72 |
+
def product_detail(product_name:str):
|
| 73 |
+
return Titled("Product Detail",
|
| 74 |
+
# Grid lays out its children in a responsive grid
|
| 75 |
+
Grid(
|
| 76 |
+
Div(
|
| 77 |
+
H1(product_name),
|
| 78 |
+
# render_md is a helper that renders markdown into HTML using frankenui styles.
|
| 79 |
+
render_md(example_product_description.format(product_name=product_name))),
|
| 80 |
+
Div(
|
| 81 |
+
H3("Order Form"),
|
| 82 |
+
# Form automatically has a class of 'space-y-3' for a margin between each child.
|
| 83 |
+
Form(
|
| 84 |
+
# LabelInput is a convience wrapper for a label and input that links them.
|
| 85 |
+
LabelInput("Name", id='name'),
|
| 86 |
+
LabelInput("Email", id='email'),
|
| 87 |
+
LabelInput("Quantity", id='quantity'),
|
| 88 |
+
# ButtonT.primary because this is the primary action of the page!
|
| 89 |
+
Button("Submit", cls=ButtonT.primary))
|
| 90 |
+
|
| 91 |
+
),
|
| 92 |
+
# Grid has defaults and args for cols at different breakpoints, but you can pass in
|
| 93 |
+
# your own to customize responsiveness.
|
| 94 |
+
cols_lg=2))
|
| 95 |
+
|
| 96 |
+
serve()
|
MonsterUI/docs/guides/Layout.ipynb
ADDED
|
@@ -0,0 +1,591 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"id": "09a2b6fa",
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"source": [
|
| 8 |
+
"# MonterUI Page Layout Guide \n",
|
| 9 |
+
"\n",
|
| 10 |
+
"This guide will discuss 3 tools for laying out your app pages, Grid, Flexbox, and Columns. This page will discuss the strengths and when to use each individually, and then a section for how to combine them for more complex layouts at the end.\n",
|
| 11 |
+
"\n",
|
| 12 |
+
"> Note: This guide is designed to get you started building layouts quickly, not to teach you all the details needed to build every possible custom layout with pixel-perfect control. To get more detailed and lower-level control, explore the tailwind docs.\n",
|
| 13 |
+
"\n",
|
| 14 |
+
"\n",
|
| 15 |
+
"This guide is for creating flexible layouts you envision, but does not discuss responsiveness to make different layouts that are both mobile and desktop friendly. Stay tunes for a responsiveness guide that will help with that!"
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"cell_type": "markdown",
|
| 20 |
+
"id": "3bdf365b",
|
| 21 |
+
"metadata": {},
|
| 22 |
+
"source": [
|
| 23 |
+
"# Grid\n",
|
| 24 |
+
"\n",
|
| 25 |
+
"Grids are best for regular predictable layouts with lots of the same shape of things that may need to change a lot for different screen sizes. I think the best way to see what it can do is to see a bunch of examples, so here they are!"
|
| 26 |
+
]
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"cell_type": "markdown",
|
| 30 |
+
"id": "7d1bc6e0",
|
| 31 |
+
"metadata": {},
|
| 32 |
+
"source": [
|
| 33 |
+
"## Minimal Image Cards\n",
|
| 34 |
+
"\n",
|
| 35 |
+
"This is a minimal example of a grid that just shows image and text. This is the foundation for many more complex layouts so make sure to understand what's going on here first before moving on!\n",
|
| 36 |
+
"\n",
|
| 37 |
+
"A grid lays things out in a...grid. As you can see, we have evenly sized cards by default."
|
| 38 |
+
]
|
| 39 |
+
},
|
| 40 |
+
{
|
| 41 |
+
"cell_type": "code",
|
| 42 |
+
"execution_count": 1,
|
| 43 |
+
"id": "8b6ef3ea",
|
| 44 |
+
"metadata": {
|
| 45 |
+
"scrolled": true
|
| 46 |
+
},
|
| 47 |
+
"outputs": [],
|
| 48 |
+
"source": [
|
| 49 |
+
"def picsum_img(seed): return Img(src=f'https://picsum.photos/300/200?random={seed}')\n",
|
| 50 |
+
" \n",
|
| 51 |
+
"Grid(*[Card(picsum_img(i),P(f\"Image {i}\")) for i in range(6)])"
|
| 52 |
+
]
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"cell_type": "markdown",
|
| 56 |
+
"id": "c35376e2",
|
| 57 |
+
"metadata": {},
|
| 58 |
+
"source": [
|
| 59 |
+
"## Dashboard Example"
|
| 60 |
+
]
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"cell_type": "markdown",
|
| 64 |
+
"id": "a7c9149c",
|
| 65 |
+
"metadata": {},
|
| 66 |
+
"source": [
|
| 67 |
+
"However, they don't have to be evenly sized! By providing `row-span-{int}` and `col-span-{int}` we can control how many rows or columns specific grid elements take up. By doing this, we can create a grid that has lots of different shapes and types of elements.\n",
|
| 68 |
+
"\n",
|
| 69 |
+
"Let's look at a dashboard layout at an examples of this."
|
| 70 |
+
]
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"cell_type": "code",
|
| 74 |
+
"execution_count": 54,
|
| 75 |
+
"id": "5abd8bd1",
|
| 76 |
+
"metadata": {
|
| 77 |
+
"scrolled": false
|
| 78 |
+
},
|
| 79 |
+
"outputs": [
|
| 80 |
+
{
|
| 81 |
+
"data": {
|
| 82 |
+
"text/html": [
|
| 83 |
+
"<iframe src=\"http://localhost:8000/_WvNL2TY3R0K8wGkaRFZ6GQ\" style=\"width: 100%; height: auto; border: none;\" onload=\"{\n",
|
| 84 |
+
" let frame = this;\n",
|
| 85 |
+
" window.addEventListener('message', function(e) {\n",
|
| 86 |
+
" if (e.source !== frame.contentWindow) return; // Only proceed if the message is from this iframe\n",
|
| 87 |
+
" if (e.data.height) frame.style.height = (e.data.height+1) + 'px';\n",
|
| 88 |
+
" }, false);\n",
|
| 89 |
+
" }\" allow=\"accelerometer; autoplay; camera; clipboard-read; clipboard-write; display-capture; encrypted-media; fullscreen; gamepad; geolocation; gyroscope; hid; identity-credentials-get; idle-detection; magnetometer; microphone; midi; payment; picture-in-picture; publickey-credentials-get; screen-wake-lock; serial; usb; web-share; xr-spatial-tracking\"></iframe> "
|
| 90 |
+
],
|
| 91 |
+
"text/plain": [
|
| 92 |
+
"<IPython.core.display.HTML object>"
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
"execution_count": 54,
|
| 96 |
+
"metadata": {},
|
| 97 |
+
"output_type": "execute_result"
|
| 98 |
+
}
|
| 99 |
+
],
|
| 100 |
+
"source": [
|
| 101 |
+
"def StatCard(title, value, color='primary'):\n",
|
| 102 |
+
" \"A card with a statistics. Since there is no row/col span class it will take up 1 slot\"\n",
|
| 103 |
+
" return Card(P(title, cls=TextPresets.muted_sm), H3(value, cls=f'text-{color}'),)\n",
|
| 104 |
+
"\n",
|
| 105 |
+
"stats = [StatCard(*data) for data in [\n",
|
| 106 |
+
" (\"Total Users\", \"1,234\", \"blue-600\"),\n",
|
| 107 |
+
" (\"Active Now\", \"342\", \"green-600\"),\n",
|
| 108 |
+
" (\"Revenue\", \"$45,678\", \"purple-600\"),\n",
|
| 109 |
+
" (\"Conversion\", \"2.4%\", \"amber-600\")]]\n",
|
| 110 |
+
"\n",
|
| 111 |
+
"def ChartCard(title): \n",
|
| 112 |
+
" \"A card for a chart. col-span-2 means it will take up 2 columns\"\n",
|
| 113 |
+
" return Div(cls=\"col-span-2\")( \n",
|
| 114 |
+
" Card(H3(title),Div(\"Chart Goes Here\", cls=\"h-64 uk-background-muted\")))\n",
|
| 115 |
+
"chart_cards = [ChartCard(title) for title in (\"Monthly Revenue\", \"User Growth\")]\n",
|
| 116 |
+
"\n",
|
| 117 |
+
"\n",
|
| 118 |
+
"sidebar = Form(\n",
|
| 119 |
+
" H3(\"SideBar\"),\n",
|
| 120 |
+
" LabelRange(\"Range For Filters\", min=0, max=100),\n",
|
| 121 |
+
" LabelInput(\"A search Bar\"),\n",
|
| 122 |
+
" LabelSelect(map(Option, [\"Product Line A\", \"Product Line B\", \"Product Line C\", \"Product Line D\"]),\n",
|
| 123 |
+
" label=\"Choose Product Line\"),\n",
|
| 124 |
+
" LabelCheckboxX(\"Include Inactive Users\"),\n",
|
| 125 |
+
" LabelCheckboxX(\"Include Users without order\"),\n",
|
| 126 |
+
" LabelCheckboxX(\"Include Users without email\"),\n",
|
| 127 |
+
" # This sidebar will take up 2 rows b/c of row-span-2\n",
|
| 128 |
+
" cls='row-span-2 space-y-5'\n",
|
| 129 |
+
")\n",
|
| 130 |
+
"\n",
|
| 131 |
+
"Container(Grid(sidebar, *stats, *chart_cards, cols=5))"
|
| 132 |
+
]
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"cell_type": "markdown",
|
| 136 |
+
"id": "cc9bb132",
|
| 137 |
+
"metadata": {},
|
| 138 |
+
"source": [
|
| 139 |
+
"# Flexbox"
|
| 140 |
+
]
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"cell_type": "markdown",
|
| 144 |
+
"id": "1dcb8075",
|
| 145 |
+
"metadata": {},
|
| 146 |
+
"source": [
|
| 147 |
+
"Using Grid for the overall layout, and flex for the individual elements is a powerful pattern. With `MonsterUI` you can do quite a bit without knowing anything about flexbox, which is what will be taught here. \n",
|
| 148 |
+
"\n",
|
| 149 |
+
"However, flexbox is well worth learning about it in more detail. You will run into situations where you need more flexbox knowledge than is covered here to build your vision. Thankfully you can get that knowledge by playing a [fantastic tutorial game called FlexBox Froggy](https://flexboxfroggy.com/)!"
|
| 150 |
+
]
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"cell_type": "markdown",
|
| 154 |
+
"id": "d10557e5",
|
| 155 |
+
"metadata": {},
|
| 156 |
+
"source": [
|
| 157 |
+
"## Forms\n",
|
| 158 |
+
"\n",
|
| 159 |
+
"Often you want to stack things horizontally. You can use the `DivHStacked` component to do this.\n",
|
| 160 |
+
"\n",
|
| 161 |
+
"`DivHStacked` is a helper function for flexbox and creates a div with these classes by default `cls=(FlexT.block, FlexT.row, FlexT.middle, 'space-x-4')`. "
|
| 162 |
+
]
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"cell_type": "code",
|
| 166 |
+
"execution_count": 71,
|
| 167 |
+
"id": "eaa39f3a",
|
| 168 |
+
"metadata": {},
|
| 169 |
+
"outputs": [
|
| 170 |
+
{
|
| 171 |
+
"data": {
|
| 172 |
+
"text/html": [
|
| 173 |
+
"<iframe src=\"http://localhost:8000/__LtKiA4BQ_mTEOe1cbj0tg\" style=\"width: 100%; height: auto; border: none;\" onload=\"{\n",
|
| 174 |
+
" let frame = this;\n",
|
| 175 |
+
" window.addEventListener('message', function(e) {\n",
|
| 176 |
+
" if (e.source !== frame.contentWindow) return; // Only proceed if the message is from this iframe\n",
|
| 177 |
+
" if (e.data.height) frame.style.height = (e.data.height+1) + 'px';\n",
|
| 178 |
+
" }, false);\n",
|
| 179 |
+
" }\" allow=\"accelerometer; autoplay; camera; clipboard-read; clipboard-write; display-capture; encrypted-media; fullscreen; gamepad; geolocation; gyroscope; hid; identity-credentials-get; idle-detection; magnetometer; microphone; midi; payment; picture-in-picture; publickey-credentials-get; screen-wake-lock; serial; usb; web-share; xr-spatial-tracking\"></iframe> "
|
| 180 |
+
],
|
| 181 |
+
"text/plain": [
|
| 182 |
+
"<IPython.core.display.HTML object>"
|
| 183 |
+
]
|
| 184 |
+
},
|
| 185 |
+
"execution_count": 71,
|
| 186 |
+
"metadata": {},
|
| 187 |
+
"output_type": "execute_result"
|
| 188 |
+
}
|
| 189 |
+
],
|
| 190 |
+
"source": [
|
| 191 |
+
"def InputGroup(label, placeholder='', button_text='Submit', cls=''):\n",
|
| 192 |
+
" # Div H Stacked makes the label and input show up on the same row instead of putting the input on a newline\n",
|
| 193 |
+
" return DivHStacked(\n",
|
| 194 |
+
" FormLabel(label, cls='whitespace-nowrap'),\n",
|
| 195 |
+
" Input(placeholder=placeholder))\n",
|
| 196 |
+
"\n",
|
| 197 |
+
"Container(\n",
|
| 198 |
+
" H3(\"Form with Input Groups\"),\n",
|
| 199 |
+
" Form(cls='space-y-4')(\n",
|
| 200 |
+
" InputGroup(\"Search Users\", \"Enter username...\"),\n",
|
| 201 |
+
" InputGroup(\"Filter Tags\", \"Add tags...\", \"Add\"),\n",
|
| 202 |
+
" InputGroup(\"Email List\", \"Enter email...\", \"Subscribe\"),\n",
|
| 203 |
+
" Div(*( Button(UkIcon(icon, cls='mr-2'), text) for icon, text in [(\"rocket\", \"Submit\"), (\"circle-x\", \"Cancel\")]), cls='space-x-4')))"
|
| 204 |
+
]
|
| 205 |
+
},
|
| 206 |
+
{
|
| 207 |
+
"cell_type": "markdown",
|
| 208 |
+
"id": "c4afd0d0",
|
| 209 |
+
"metadata": {},
|
| 210 |
+
"source": [
|
| 211 |
+
"## Avatar "
|
| 212 |
+
]
|
| 213 |
+
},
|
| 214 |
+
{
|
| 215 |
+
"cell_type": "markdown",
|
| 216 |
+
"id": "9154ae8f",
|
| 217 |
+
"metadata": {},
|
| 218 |
+
"source": [
|
| 219 |
+
"You can use this same `DivHStacked` to align things like text next to images. And you can use `DivVStacked` to stack things vertically to create design structures you like. `DivVStacked` works by using `cls=(FlexT.block,FlexT.column,FlexT.middle)`"
|
| 220 |
+
]
|
| 221 |
+
},
|
| 222 |
+
{
|
| 223 |
+
"cell_type": "code",
|
| 224 |
+
"execution_count": 170,
|
| 225 |
+
"id": "f782c6ca",
|
| 226 |
+
"metadata": {},
|
| 227 |
+
"outputs": [
|
| 228 |
+
{
|
| 229 |
+
"data": {
|
| 230 |
+
"text/markdown": [
|
| 231 |
+
"```html\n",
|
| 232 |
+
"<div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 233 |
+
"<span class=\"relative flex h-20 w-20 shrink-0 overflow-hidden rounded-full bg-accent\"><img alt=\"Avatar\" loading=\"lazy\" src=\"https://api.dicebear.com/8.x/lorelei/svg?seed=user\" class=\"aspect-square h-20 w-20\"></span> <div class=\"uk-flex uk-flex-column uk-flex-middle \">\n",
|
| 234 |
+
" <p class=\"uk-text-large \">John Doe</p>\n",
|
| 235 |
+
" <p class=\"uk-text-muted \">[email protected]</p>\n",
|
| 236 |
+
" <p class=\"uk-text-muted \">+1-123-456-7890</p>\n",
|
| 237 |
+
" </div>\n",
|
| 238 |
+
"</div>\n",
|
| 239 |
+
"\n",
|
| 240 |
+
"```"
|
| 241 |
+
],
|
| 242 |
+
"text/plain": [
|
| 243 |
+
"div((span((img((),{'alt': 'Avatar', 'loading': 'lazy', 'src': 'https://api.dicebear.com/8.x/lorelei/svg?seed=user', 'class': 'aspect-square h-20 w-20'}),),{'class': 'relative flex h-20 w-20 shrink-0 overflow-hidden rounded-full bg-accent'}), div((p(('John Doe',),{'class': 'uk-text-large '}), p(('[email protected]',),{'class': 'uk-text-muted '}), p(('+1-123-456-7890',),{'class': 'uk-text-muted '})),{'class': 'uk-flex uk-flex-column uk-flex-middle '})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'})"
|
| 244 |
+
]
|
| 245 |
+
},
|
| 246 |
+
"execution_count": 170,
|
| 247 |
+
"metadata": {},
|
| 248 |
+
"output_type": "execute_result"
|
| 249 |
+
}
|
| 250 |
+
],
|
| 251 |
+
"source": [
|
| 252 |
+
"# DivHStacked makes the a single row so text is to on same line as avatar\n",
|
| 253 |
+
"DivHStacked(\n",
|
| 254 |
+
" DiceBearAvatar(\"user\"), \n",
|
| 255 |
+
" # DivVStacked stacks things vertically together and centers it with flex\n",
|
| 256 |
+
" DivVStacked(\n",
|
| 257 |
+
" P(\"John Doe\", cls=TextT.lg),\n",
|
| 258 |
+
" P(\"[email protected]\", cls=TextT.muted), \n",
|
| 259 |
+
" P(\"+1-123-456-7890\"), cls=TextT.muted))"
|
| 260 |
+
]
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"cell_type": "markdown",
|
| 264 |
+
"id": "6234fdf8",
|
| 265 |
+
"metadata": {},
|
| 266 |
+
"source": [
|
| 267 |
+
"## Pricing Card\n",
|
| 268 |
+
"\n",
|
| 269 |
+
"These can be combined with icons and other styling to create larger components like a pricing card."
|
| 270 |
+
]
|
| 271 |
+
},
|
| 272 |
+
{
|
| 273 |
+
"cell_type": "code",
|
| 274 |
+
"execution_count": 165,
|
| 275 |
+
"id": "f1846dc1",
|
| 276 |
+
"metadata": {},
|
| 277 |
+
"outputs": [
|
| 278 |
+
{
|
| 279 |
+
"data": {
|
| 280 |
+
"text/markdown": [
|
| 281 |
+
"```html\n",
|
| 282 |
+
"<div class=\"uk-flex uk-flex-column uk-flex-middle space-y-4\">\n",
|
| 283 |
+
" <div class=\"uk-card \">\n",
|
| 284 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 285 |
+
" <div class=\"uk-flex uk-flex-column uk-flex-middle space-y-1\">\n",
|
| 286 |
+
" <h2 class=\"uk-h2 \">Pro Plan</h2>\n",
|
| 287 |
+
" <h3 class=\"uk-h3 text-primary\">$99</h3>\n",
|
| 288 |
+
" <p class=\"uk-text-muted \">per month</p>\n",
|
| 289 |
+
" </div>\n",
|
| 290 |
+
" <ul class=\"space-y-4\">\n",
|
| 291 |
+
" <div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 292 |
+
"<uk-icon icon=\"check\" class=\"text-green-500 mr-2\"></uk-icon> <li>Unlimited users</li>\n",
|
| 293 |
+
" </div>\n",
|
| 294 |
+
" <div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 295 |
+
"<uk-icon icon=\"check\" class=\"text-green-500 mr-2\"></uk-icon> <li>24/7 priority support</li>\n",
|
| 296 |
+
" </div>\n",
|
| 297 |
+
" <div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 298 |
+
"<uk-icon icon=\"check\" class=\"text-green-500 mr-2\"></uk-icon> <li>Custom branding options</li>\n",
|
| 299 |
+
" </div>\n",
|
| 300 |
+
" <div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 301 |
+
"<uk-icon icon=\"check\" class=\"text-green-500 mr-2\"></uk-icon> <li>Advanced analytics dashboard</li>\n",
|
| 302 |
+
" </div>\n",
|
| 303 |
+
" <div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 304 |
+
"<uk-icon icon=\"check\" class=\"text-green-500 mr-2\"></uk-icon> <li>Full API access</li>\n",
|
| 305 |
+
" </div>\n",
|
| 306 |
+
" <div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 307 |
+
"<uk-icon icon=\"check\" class=\"text-green-500 mr-2\"></uk-icon> <li>Priority request queue</li>\n",
|
| 308 |
+
" </div>\n",
|
| 309 |
+
" </ul>\n",
|
| 310 |
+
"<button type=\"submit\" class=\"uk-btn uk-btn-primary w-full\">Subscribe Now</button> </div>\n",
|
| 311 |
+
" </div>\n",
|
| 312 |
+
"</div>\n",
|
| 313 |
+
"\n",
|
| 314 |
+
"```"
|
| 315 |
+
],
|
| 316 |
+
"text/plain": [
|
| 317 |
+
"div((div((div((div((h2(('Pro Plan',),{'class': 'uk-h2 '}), h3(('$99',),{'class': 'uk-h3 text-primary'}), p(('per month',),{'class': 'uk-text-muted '})),{'class': 'uk-flex uk-flex-column uk-flex-middle space-y-1'}), ul((div((uk-icon((),{'icon': 'check', 'class': 'text-green-500 mr-2'}), li(('Unlimited users',),{})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'}), div((uk-icon((),{'icon': 'check', 'class': 'text-green-500 mr-2'}), li(('24/7 priority support',),{})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'}), div((uk-icon((),{'icon': 'check', 'class': 'text-green-500 mr-2'}), li(('Custom branding options',),{})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'}), div((uk-icon((),{'icon': 'check', 'class': 'text-green-500 mr-2'}), li(('Advanced analytics dashboard',),{})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'}), div((uk-icon((),{'icon': 'check', 'class': 'text-green-500 mr-2'}), li(('Full API access',),{})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'}), div((uk-icon((),{'icon': 'check', 'class': 'text-green-500 mr-2'}), li(('Priority request queue',),{})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'})),{'class': 'space-y-4'}), button(('Subscribe Now',),{'type': 'submit', 'class': 'uk-btn uk-btn-primary w-full'})),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}),),{'class': 'uk-flex uk-flex-column uk-flex-middle space-y-4'})"
|
| 318 |
+
]
|
| 319 |
+
},
|
| 320 |
+
"execution_count": 165,
|
| 321 |
+
"metadata": {},
|
| 322 |
+
"output_type": "execute_result"
|
| 323 |
+
}
|
| 324 |
+
],
|
| 325 |
+
"source": [
|
| 326 |
+
"features = [\n",
|
| 327 |
+
" \"Unlimited users\",\n",
|
| 328 |
+
" \"24/7 priority support\",\n",
|
| 329 |
+
" \"Custom branding options\", \n",
|
| 330 |
+
" \"Advanced analytics dashboard\",\n",
|
| 331 |
+
" \"Full API access\",\n",
|
| 332 |
+
" \"Priority request queue\"\n",
|
| 333 |
+
"]\n",
|
| 334 |
+
"\n",
|
| 335 |
+
"\n",
|
| 336 |
+
"def PricingCard(plan, price, features):\n",
|
| 337 |
+
" \"Create a polished pricing card with consistent styling\"\n",
|
| 338 |
+
" return Card(\n",
|
| 339 |
+
" DivVStacked( # Center and veritcally stack the plan name and price\n",
|
| 340 |
+
" H2(plan),\n",
|
| 341 |
+
" H3(price, cls='text-primary'),\n",
|
| 342 |
+
" P('per month',cls=TextT.muted),\n",
|
| 343 |
+
" cls='space-y-1'),\n",
|
| 344 |
+
" # DivHStacked makes green check and feature Li show up on same row instead of newline\n",
|
| 345 |
+
" Ul(*[DivHStacked(UkIcon('check', cls='text-green-500 mr-2'), Li(feature)) for feature in features], \n",
|
| 346 |
+
" cls='space-y-4'),\n",
|
| 347 |
+
" Button(\"Subscribe Now\", cls=(ButtonT.primary, 'w-full')))\n",
|
| 348 |
+
"\n",
|
| 349 |
+
"DivVStacked(PricingCard(\"Pro Plan\", \"$99\", features))"
|
| 350 |
+
]
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
"cell_type": "markdown",
|
| 354 |
+
"id": "ddf9cf78",
|
| 355 |
+
"metadata": {},
|
| 356 |
+
"source": [
|
| 357 |
+
"## Footer\n",
|
| 358 |
+
"\n",
|
| 359 |
+
"Or you can combine things to make advanced footers that have titles, organized links, and icons!\n",
|
| 360 |
+
"\n",
|
| 361 |
+
"In this example we add another flex helper function, `DivFullySpaced`. `DivFullySpaced` is a flex class that puts as much space between items as possible"
|
| 362 |
+
]
|
| 363 |
+
},
|
| 364 |
+
{
|
| 365 |
+
"cell_type": "code",
|
| 366 |
+
"execution_count": 176,
|
| 367 |
+
"id": "ee79e8cf",
|
| 368 |
+
"metadata": {
|
| 369 |
+
"scrolled": false
|
| 370 |
+
},
|
| 371 |
+
"outputs": [
|
| 372 |
+
{
|
| 373 |
+
"data": {
|
| 374 |
+
"text/markdown": [
|
| 375 |
+
"```html\n",
|
| 376 |
+
"<div class=\"uk-container bg-gray-50 py-12\">\n",
|
| 377 |
+
" <div class=\"space-y-8\">\n",
|
| 378 |
+
" <div class=\"uk-flex uk-flex-between uk-flex-middle w-full\">\n",
|
| 379 |
+
" <h3 class=\"uk-h3 \">Company Name</h3>\n",
|
| 380 |
+
" <div class=\"uk-flex uk-flex-row uk-flex-middle space-x-4\">\n",
|
| 381 |
+
"<uk-icon icon=\"twitter\" class=\"uk-text-lead\"></uk-icon><uk-icon icon=\"facebook\" class=\"uk-text-lead\"></uk-icon><uk-icon icon=\"github\" class=\"uk-text-lead\"></uk-icon><uk-icon icon=\"linkedin\" class=\"uk-text-lead\"></uk-icon> </div>\n",
|
| 382 |
+
" </div>\n",
|
| 383 |
+
"<hr class=\"my-4 h-[2px] w-full bg-secondary\"> <div class=\"uk-flex uk-flex-between uk-flex-middle w-full\">\n",
|
| 384 |
+
" <div class=\"uk-flex uk-flex-column uk-flex-middle space-y-4\">\n",
|
| 385 |
+
" <h4 class=\"uk-h4 \">Company</h4>\n",
|
| 386 |
+
"<a href=\"#about\" class=\"uk-text-muted\">About</a><a href=\"#blog\" class=\"uk-text-muted\">Blog</a><a href=\"#careers\" class=\"uk-text-muted\">Careers</a><a href=\"#press-kit\" class=\"uk-text-muted\">Press Kit</a> </div>\n",
|
| 387 |
+
" <div class=\"uk-flex uk-flex-column uk-flex-middle space-y-4\">\n",
|
| 388 |
+
" <h4 class=\"uk-h4 \">Resources</h4>\n",
|
| 389 |
+
"<a href=\"#documentation\" class=\"uk-text-muted\">Documentation</a><a href=\"#help-center\" class=\"uk-text-muted\">Help Center</a><a href=\"#status\" class=\"uk-text-muted\">Status</a><a href=\"#contact-sales\" class=\"uk-text-muted\">Contact Sales</a> </div>\n",
|
| 390 |
+
" <div class=\"uk-flex uk-flex-column uk-flex-middle space-y-4\">\n",
|
| 391 |
+
" <h4 class=\"uk-h4 \">Legal</h4>\n",
|
| 392 |
+
"<a href=\"#terms-of-service\" class=\"uk-text-muted\">Terms of Service</a><a href=\"#privacy-policy\" class=\"uk-text-muted\">Privacy Policy</a><a href=\"#cookie-settings\" class=\"uk-text-muted\">Cookie Settings</a><a href=\"#accessibility\" class=\"uk-text-muted\">Accessibility</a> </div>\n",
|
| 393 |
+
" </div>\n",
|
| 394 |
+
"<hr class=\"my-4 h-[2px] w-full bg-secondary\"> <p class=\"uk-text-small uk-text-lead\">© 2024 Company Name. All rights reserved.</p>\n",
|
| 395 |
+
" </div>\n",
|
| 396 |
+
"</div>\n",
|
| 397 |
+
"\n",
|
| 398 |
+
"```"
|
| 399 |
+
],
|
| 400 |
+
"text/plain": [
|
| 401 |
+
"div((div((div((h3(('Company Name',),{'class': 'uk-h3 '}), div((uk-icon((),{'icon': 'twitter', 'class': <TextT.lead: 'uk-text-lead'>}), uk-icon((),{'icon': 'facebook', 'class': <TextT.lead: 'uk-text-lead'>}), uk-icon((),{'icon': 'github', 'class': <TextT.lead: 'uk-text-lead'>}), uk-icon((),{'icon': 'linkedin', 'class': <TextT.lead: 'uk-text-lead'>})),{'class': 'uk-flex uk-flex-row uk-flex-middle space-x-4'})),{'class': 'uk-flex uk-flex-between uk-flex-middle w-full'}), hr((),{'class': 'my-4 h-[2px] w-full bg-secondary'}), div((div((h4(('Company',),{'class': 'uk-h4 '}), a(('About',),{'href': '#about', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Blog',),{'href': '#blog', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Careers',),{'href': '#careers', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Press Kit',),{'href': '#press-kit', 'class': <TextT.muted: 'uk-text-muted'>})),{'class': 'uk-flex uk-flex-column uk-flex-middle space-y-4'}), div((h4(('Resources',),{'class': 'uk-h4 '}), a(('Documentation',),{'href': '#documentation', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Help Center',),{'href': '#help-center', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Status',),{'href': '#status', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Contact Sales',),{'href': '#contact-sales', 'class': <TextT.muted: 'uk-text-muted'>})),{'class': 'uk-flex uk-flex-column uk-flex-middle space-y-4'}), div((h4(('Legal',),{'class': 'uk-h4 '}), a(('Terms of Service',),{'href': '#terms-of-service', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Privacy Policy',),{'href': '#privacy-policy', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Cookie Settings',),{'href': '#cookie-settings', 'class': <TextT.muted: 'uk-text-muted'>}), a(('Accessibility',),{'href': '#accessibility', 'class': <TextT.muted: 'uk-text-muted'>})),{'class': 'uk-flex uk-flex-column uk-flex-middle space-y-4'})),{'class': 'uk-flex uk-flex-between uk-flex-middle w-full'}), hr((),{'class': 'my-4 h-[2px] w-full bg-secondary'}), p(('© 2024 Company Name. All rights reserved.',),{'class': 'uk-text-small uk-text-lead'})),{'class': 'space-y-8'}),),{'class': 'uk-container bg-gray-50 py-12'})"
|
| 402 |
+
]
|
| 403 |
+
},
|
| 404 |
+
"execution_count": 176,
|
| 405 |
+
"metadata": {},
|
| 406 |
+
"output_type": "execute_result"
|
| 407 |
+
}
|
| 408 |
+
],
|
| 409 |
+
"source": [
|
| 410 |
+
"def FooterLinkGroup(title, links):\n",
|
| 411 |
+
" # DivVStacked centers and makes title and each link stack vertically\n",
|
| 412 |
+
" return DivVStacked(\n",
|
| 413 |
+
" H4(title),\n",
|
| 414 |
+
" *[A(text, href=f\"#{text.lower().replace(' ', '-')}\", cls=TextT.muted) for text in links])\n",
|
| 415 |
+
"\n",
|
| 416 |
+
"company = [\"About\", \"Blog\", \"Careers\", \"Press Kit\"]\n",
|
| 417 |
+
"resource = [\"Documentation\", \"Help Center\", \"Status\", \"Contact Sales\"]\n",
|
| 418 |
+
"legal = [\"Terms of Service\", \"Privacy Policy\", \"Cookie Settings\", \"Accessibility\"]\n",
|
| 419 |
+
"\n",
|
| 420 |
+
"Container(cls='uk-background-muted py-12')(Div(\n",
|
| 421 |
+
" # Company Name and social icons will be on the same row with as much sapce between as possible\n",
|
| 422 |
+
" DivFullySpaced( \n",
|
| 423 |
+
" H3(\"Company Name\"),\n",
|
| 424 |
+
" # DivHStacked makes the icons be on the same row in a group\n",
|
| 425 |
+
" DivHStacked(*[UkIcon(icon, cls=TextT.lead) for icon in \n",
|
| 426 |
+
" ['twitter', 'facebook', 'github', 'linkedin']])),\n",
|
| 427 |
+
" DividerLine(),\n",
|
| 428 |
+
" DivFullySpaced( # Each child will be spread out as much as possible based on number of children\n",
|
| 429 |
+
" FooterLinkGroup(\"Company\", company),\n",
|
| 430 |
+
" FooterLinkGroup(\"Resources\", resource),\n",
|
| 431 |
+
" FooterLinkGroup(\"Legal\", legal)), \n",
|
| 432 |
+
" DividerLine(),\n",
|
| 433 |
+
" P(\"© 2024 Company Name. All rights reserved.\", cls=TextT.lead+TextT.sm),\n",
|
| 434 |
+
" cls='space-y-8 p-8'))"
|
| 435 |
+
]
|
| 436 |
+
},
|
| 437 |
+
{
|
| 438 |
+
"cell_type": "markdown",
|
| 439 |
+
"id": "bb311899",
|
| 440 |
+
"metadata": {},
|
| 441 |
+
"source": [
|
| 442 |
+
"## Dashboard"
|
| 443 |
+
]
|
| 444 |
+
},
|
| 445 |
+
{
|
| 446 |
+
"cell_type": "code",
|
| 447 |
+
"execution_count": 5,
|
| 448 |
+
"id": "1e29f9de",
|
| 449 |
+
"metadata": {},
|
| 450 |
+
"outputs": [],
|
| 451 |
+
"source": [
|
| 452 |
+
"def StatsCard(label, value, change):\n",
|
| 453 |
+
" color = 'green' if change[0] == '+' else 'red'\n",
|
| 454 |
+
" return Card(DivVStacked( # Stacks vertically and centers all elements\n",
|
| 455 |
+
" P(label, cls=TextPresets.muted_sm),\n",
|
| 456 |
+
" H3(value),\n",
|
| 457 |
+
" P(f\"{change}% from last month\", cls=f\"text-{color}-600 text-sm\")))\n",
|
| 458 |
+
" \n",
|
| 459 |
+
"def RecentActivity(user, action, time):\n",
|
| 460 |
+
" return DivHStacked( # Makes Avatar and text be on same row\n",
|
| 461 |
+
" DiceBearAvatar(user, h=8, w=8),\n",
|
| 462 |
+
" P(f\"{user} {action}\", cls=\"flex-1\"),\n",
|
| 463 |
+
" P(time, cls=TextPresets.muted_sm))\n",
|
| 464 |
+
" \n",
|
| 465 |
+
"DivVStacked( # Centers the entire dashboard layout\n",
|
| 466 |
+
" # Page header\n",
|
| 467 |
+
" DivVStacked( # Stacks vertically and centers the title/subtitle\n",
|
| 468 |
+
" H2(\"Welcome back, Isaac!\"),\n",
|
| 469 |
+
" P(\"Here's what's happening with your projects today.\",cls=TextT.muted)),\n",
|
| 470 |
+
"\n",
|
| 471 |
+
" # DivHStacked puts all the stats cards on the same row\n",
|
| 472 |
+
" DivHStacked(*(StatsCard(label, value, change)\n",
|
| 473 |
+
" for label, value, change in [\n",
|
| 474 |
+
" (\"Total Projects\", \"12\", \"+2.5\"),\n",
|
| 475 |
+
" (\"Hours Logged\", \"164\", \"+12.3\"),\n",
|
| 476 |
+
" (\"Tasks Complete\", \"64%\", \"-4.1\"),\n",
|
| 477 |
+
" (\"Team Velocity\", \"23\", \"+8.4\")]\n",
|
| 478 |
+
" )),\n",
|
| 479 |
+
"\n",
|
| 480 |
+
" # Recent activity\n",
|
| 481 |
+
" Card(*(RecentActivity(user, action, time) \n",
|
| 482 |
+
" for user, action, time in [\n",
|
| 483 |
+
" (\"Sarah Chen\", \"completed Project Alpha deployment\", \"2h ago\"),\n",
|
| 484 |
+
" (\"James Wilson\", \"commented on Project Beta\", \"4h ago\"),\n",
|
| 485 |
+
" (\"Maria Garcia\", \"uploaded new design files\", \"6h ago\"),\n",
|
| 486 |
+
" (\"Alex Kumar\", \"started Sprint Planning\", \"8h ago\")]),\n",
|
| 487 |
+
" header=H3(\"Recent Activity\"),\n",
|
| 488 |
+
" ),\n",
|
| 489 |
+
" cls=\"space-y-6\"\n",
|
| 490 |
+
")"
|
| 491 |
+
]
|
| 492 |
+
},
|
| 493 |
+
{
|
| 494 |
+
"cell_type": "markdown",
|
| 495 |
+
"id": "edd8dc07",
|
| 496 |
+
"metadata": {},
|
| 497 |
+
"source": [
|
| 498 |
+
"## Columns\n",
|
| 499 |
+
"\n",
|
| 500 |
+
"Columns are a great for sections that have a lot of text."
|
| 501 |
+
]
|
| 502 |
+
},
|
| 503 |
+
{
|
| 504 |
+
"cell_type": "code",
|
| 505 |
+
"execution_count": 181,
|
| 506 |
+
"id": "6dfbd634",
|
| 507 |
+
"metadata": {},
|
| 508 |
+
"outputs": [
|
| 509 |
+
{
|
| 510 |
+
"data": {
|
| 511 |
+
"text/markdown": [
|
| 512 |
+
"```html\n",
|
| 513 |
+
"<div class=\"uk-container mt-5 uk-container-xlarge\">\n",
|
| 514 |
+
" <h1 class=\"uk-h1 text-center mb-8\">Lorem Ipsum</h1>\n",
|
| 515 |
+
" <div class=\"columns-2 gap-12\">\n",
|
| 516 |
+
" <p class=\"uk-paragraph \">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do \n",
|
| 517 |
+
" eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad \n",
|
| 518 |
+
" minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip \n",
|
| 519 |
+
" ex ea commodo consequat.</p>\n",
|
| 520 |
+
" <div class=\"uk-flex uk-flex-column uk-flex-middle uk-flex-center mt-8\">\n",
|
| 521 |
+
" <p class=\"uk-text-large uk-text-bold uk-text-center uk-text-italic text-primary\">Duis aute irure dolor in reprehenderit in voluptate velit esse \n",
|
| 522 |
+
" cillum dolore eu fugiat nulla pariatur.</p>\n",
|
| 523 |
+
" </div>\n",
|
| 524 |
+
" <p class=\"uk-paragraph \">Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \n",
|
| 525 |
+
" officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde \n",
|
| 526 |
+
" omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p>\n",
|
| 527 |
+
" <p class=\"uk-paragraph \">Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit \n",
|
| 528 |
+
" aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem \n",
|
| 529 |
+
" sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor.</p>\n",
|
| 530 |
+
" </div>\n",
|
| 531 |
+
"</div>\n",
|
| 532 |
+
"\n",
|
| 533 |
+
"```"
|
| 534 |
+
],
|
| 535 |
+
"text/plain": [
|
| 536 |
+
"div((h1(('Lorem Ipsum',),{'class': 'uk-h1 text-center mb-8'}), div((p(('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do \\n eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad \\n minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip \\n ex ea commodo consequat.',),{'class': 'uk-paragraph '}), div((p(('Duis aute irure dolor in reprehenderit in voluptate velit esse \\n cillum dolore eu fugiat nulla pariatur.',),{'class': 'uk-text-large uk-text-bold uk-text-center uk-text-italic text-primary'}),),{'class': 'uk-flex uk-flex-column uk-flex-middle uk-flex-center mt-8'}), p(('Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \\n officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde \\n omnis iste natus error sit voluptatem accusantium doloremque laudantium.',),{'class': 'uk-paragraph '}), p(('Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit \\n aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem \\n sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor.',),{'class': 'uk-paragraph '})),{'class': 'columns-2 gap-12'})),{'class': 'uk-container mt-5 uk-container-xlarge'})"
|
| 537 |
+
]
|
| 538 |
+
},
|
| 539 |
+
"execution_count": 181,
|
| 540 |
+
"metadata": {},
|
| 541 |
+
"output_type": "execute_result"
|
| 542 |
+
}
|
| 543 |
+
],
|
| 544 |
+
"source": [
|
| 545 |
+
"Container(\n",
|
| 546 |
+
" H1(\"Lorem Ipsum\", cls=\"text-center mb-8\"),\n",
|
| 547 |
+
"\n",
|
| 548 |
+
" # Use 2 columns for the main content\n",
|
| 549 |
+
" Div(cls=\"columns-2 gap-12\")(\n",
|
| 550 |
+
" P(\"\"\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do \n",
|
| 551 |
+
" eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad \n",
|
| 552 |
+
" minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip \n",
|
| 553 |
+
" ex ea commodo consequat.\"\"\"),\n",
|
| 554 |
+
"\n",
|
| 555 |
+
" DivCentered(cls='mt-8')(\n",
|
| 556 |
+
" P(\"\"\"Duis aute irure dolor in reprehenderit in voluptate velit esse \n",
|
| 557 |
+
" cillum dolore eu fugiat nulla pariatur.\"\"\", \n",
|
| 558 |
+
" cls=(TextT.lg, TextT.bold, TextT.center, TextT.italic, \"text-primary\"))),\n",
|
| 559 |
+
"\n",
|
| 560 |
+
" P(\"\"\"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \n",
|
| 561 |
+
" officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde \n",
|
| 562 |
+
" omnis iste natus error sit voluptatem accusantium doloremque laudantium.\"\"\"),\n",
|
| 563 |
+
"\n",
|
| 564 |
+
" P(\"\"\"Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit \n",
|
| 565 |
+
" aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem \n",
|
| 566 |
+
" sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor.\"\"\")))"
|
| 567 |
+
]
|
| 568 |
+
}
|
| 569 |
+
],
|
| 570 |
+
"metadata": {
|
| 571 |
+
"kernelspec": {
|
| 572 |
+
"display_name": "Python 3 (ipykernel)",
|
| 573 |
+
"language": "python",
|
| 574 |
+
"name": "python3"
|
| 575 |
+
},
|
| 576 |
+
"language_info": {
|
| 577 |
+
"codemirror_mode": {
|
| 578 |
+
"name": "ipython",
|
| 579 |
+
"version": 3
|
| 580 |
+
},
|
| 581 |
+
"file_extension": ".py",
|
| 582 |
+
"mimetype": "text/x-python",
|
| 583 |
+
"name": "python",
|
| 584 |
+
"nbconvert_exporter": "python",
|
| 585 |
+
"pygments_lexer": "ipython3",
|
| 586 |
+
"version": "3.12.8"
|
| 587 |
+
}
|
| 588 |
+
},
|
| 589 |
+
"nbformat": 4,
|
| 590 |
+
"nbformat_minor": 5
|
| 591 |
+
}
|
MonsterUI/docs/guides/Spacing.ipynb
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"id": "ce15239f",
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"source": [
|
| 8 |
+
"# Padding & Margin & Spacing, Oh my! (MonsterUI Spacing Guide)\n",
|
| 9 |
+
"\n",
|
| 10 |
+
"This guide will cover some essentials about how to properly space apps and what the differences are between:\n",
|
| 11 |
+
"\n",
|
| 12 |
+
"- Padding\n",
|
| 13 |
+
"- Margin\n",
|
| 14 |
+
"- Spacing\n",
|
| 15 |
+
"- Gap\n",
|
| 16 |
+
"\n",
|
| 17 |
+
"Manipulating the space between components can make a huge difference to the percieved quality of the page. Being able to tweak the spacing can have a big impact!\n",
|
| 18 |
+
"\n",
|
| 19 |
+
"> Tip: I find it works best to get everything on the page without adjusting spacing much, and adjusting spacing at the end. \n",
|
| 20 |
+
"\n",
|
| 21 |
+
"## Abreviations:\n",
|
| 22 |
+
"\n",
|
| 23 |
+
"First a few abbreviations that are helpful to know with tailwind (and a convention we follow in `MonsterUI`).\n",
|
| 24 |
+
"\n",
|
| 25 |
+
"- `t`, `b`, `l`, `r` = top, bottom, left, right\n",
|
| 26 |
+
"- `p`, `m` = padding, margin\n",
|
| 27 |
+
"- `x`, `y` = horizontal, vertical\n",
|
| 28 |
+
"\n",
|
| 29 |
+
"That means:\n",
|
| 30 |
+
"\n",
|
| 31 |
+
"- `mt` means margin on top of the element\n",
|
| 32 |
+
"- `px` means padding on the x axis (both left and right)\n",
|
| 33 |
+
"- `space-y` means apply spacing on the y axis (both top and bottom)\n",
|
| 34 |
+
"\n",
|
| 35 |
+
"## Padding vs Margin\n",
|
| 36 |
+
"\n",
|
| 37 |
+
"Margin applies space to the left of the component, and padding applies space on the left inside of the component. \n",
|
| 38 |
+
"\n",
|
| 39 |
+
"Please reference the example with cards below:\n",
|
| 40 |
+
"\n",
|
| 41 |
+
"- `ml-20` applies space to the left of the card (outside the card)\n",
|
| 42 |
+
"- `pl-20` applies space on the left inside of the card (inside the card)\n",
|
| 43 |
+
"\n",
|
| 44 |
+
"This means that if you want to move the whole thing but keep the actual container unchanged, use margin. If you want to change the container by adding space inside of it, use padding."
|
| 45 |
+
]
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"cell_type": "code",
|
| 49 |
+
"execution_count": 2,
|
| 50 |
+
"id": "aef6844a",
|
| 51 |
+
"metadata": {
|
| 52 |
+
"scrolled": true
|
| 53 |
+
},
|
| 54 |
+
"outputs": [
|
| 55 |
+
{
|
| 56 |
+
"data": {
|
| 57 |
+
"text/markdown": [
|
| 58 |
+
"```html\n",
|
| 59 |
+
"<div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4\">\n",
|
| 60 |
+
" <div class=\"uk-card ml-20\">\n",
|
| 61 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 62 |
+
" <h4 class=\"uk-h4 \" style=\"background-color: red\">A Simple Card with ml-20</h4>\n",
|
| 63 |
+
" </div>\n",
|
| 64 |
+
" </div>\n",
|
| 65 |
+
" <div class=\"uk-card pl-20\">\n",
|
| 66 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 67 |
+
" <h4 class=\"uk-h4 \" style=\"background-color: red\">A Simple Card with pl-20</h4>\n",
|
| 68 |
+
" </div>\n",
|
| 69 |
+
" </div>\n",
|
| 70 |
+
"</div>\n",
|
| 71 |
+
"\n",
|
| 72 |
+
"```"
|
| 73 |
+
],
|
| 74 |
+
"text/plain": [
|
| 75 |
+
"div((div((div((h4(('A Simple Card with ml-20',),{'class': 'uk-h4 ', 'style': 'background-color: red'}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card ml-20'}), div((div((h4(('A Simple Card with pl-20',),{'class': 'uk-h4 ', 'style': 'background-color: red'}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card pl-20'})),{'class': 'grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4'})"
|
| 76 |
+
]
|
| 77 |
+
},
|
| 78 |
+
"execution_count": 2,
|
| 79 |
+
"metadata": {},
|
| 80 |
+
"output_type": "execute_result"
|
| 81 |
+
}
|
| 82 |
+
],
|
| 83 |
+
"source": [
|
| 84 |
+
"Grid(\n",
|
| 85 |
+
" Card(H4(\"A Simple Card with ml-20\",style='background-color: red'), \n",
|
| 86 |
+
" cls='ml-20'),\n",
|
| 87 |
+
" Card(H4(\"A Simple Card with pl-20\", style='background-color: red'),\n",
|
| 88 |
+
" cls='pl-20'))"
|
| 89 |
+
]
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"cell_type": "markdown",
|
| 93 |
+
"id": "59ed6d0d",
|
| 94 |
+
"metadata": {},
|
| 95 |
+
"source": [
|
| 96 |
+
"## Space vs gap\n",
|
| 97 |
+
"\n",
|
| 98 |
+
"Spacing and gap are both about setting the space between components.\n",
|
| 99 |
+
"\n",
|
| 100 |
+
"+ Spacing applies margin to every element except for the first element in a group. \n",
|
| 101 |
+
"+ Gap creates a gap between every element in flexbox elements and grids.\n",
|
| 102 |
+
"\n",
|
| 103 |
+
">Rule of thumb: Use Gap when using grids.\n",
|
| 104 |
+
"\n",
|
| 105 |
+
"Let's take a look at some grid examples."
|
| 106 |
+
]
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"cell_type": "markdown",
|
| 110 |
+
"id": "22ad2226",
|
| 111 |
+
"metadata": {},
|
| 112 |
+
"source": [
|
| 113 |
+
"#### Grid"
|
| 114 |
+
]
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"cell_type": "code",
|
| 118 |
+
"execution_count": 8,
|
| 119 |
+
"id": "a296d044",
|
| 120 |
+
"metadata": {},
|
| 121 |
+
"outputs": [
|
| 122 |
+
{
|
| 123 |
+
"data": {
|
| 124 |
+
"text/markdown": [
|
| 125 |
+
"```html\n",
|
| 126 |
+
"<div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 \">\n",
|
| 127 |
+
" <div class=\"uk-card \">\n",
|
| 128 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 129 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 130 |
+
" </div>\n",
|
| 131 |
+
" </div>\n",
|
| 132 |
+
" <div class=\"uk-card \">\n",
|
| 133 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 134 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 135 |
+
" </div>\n",
|
| 136 |
+
" </div>\n",
|
| 137 |
+
" <div class=\"uk-card \">\n",
|
| 138 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 139 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 140 |
+
" </div>\n",
|
| 141 |
+
" </div>\n",
|
| 142 |
+
" <div class=\"uk-card \">\n",
|
| 143 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 144 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 145 |
+
" </div>\n",
|
| 146 |
+
" </div>\n",
|
| 147 |
+
" <div class=\"uk-card \">\n",
|
| 148 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 149 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 150 |
+
" </div>\n",
|
| 151 |
+
" </div>\n",
|
| 152 |
+
" <div class=\"uk-card \">\n",
|
| 153 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 154 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 155 |
+
" </div>\n",
|
| 156 |
+
" </div>\n",
|
| 157 |
+
"</div>\n",
|
| 158 |
+
"\n",
|
| 159 |
+
"```"
|
| 160 |
+
],
|
| 161 |
+
"text/plain": [
|
| 162 |
+
"div((div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '})),{'class': 'grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 '})"
|
| 163 |
+
]
|
| 164 |
+
},
|
| 165 |
+
"execution_count": 8,
|
| 166 |
+
"metadata": {},
|
| 167 |
+
"output_type": "execute_result"
|
| 168 |
+
}
|
| 169 |
+
],
|
| 170 |
+
"source": [
|
| 171 |
+
"Grid(\n",
|
| 172 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 173 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 174 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 175 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 176 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 177 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 178 |
+
" cls='')"
|
| 179 |
+
]
|
| 180 |
+
},
|
| 181 |
+
{
|
| 182 |
+
"cell_type": "markdown",
|
| 183 |
+
"id": "286c245f",
|
| 184 |
+
"metadata": {},
|
| 185 |
+
"source": [
|
| 186 |
+
"#### Grid with gap"
|
| 187 |
+
]
|
| 188 |
+
},
|
| 189 |
+
{
|
| 190 |
+
"cell_type": "code",
|
| 191 |
+
"execution_count": 7,
|
| 192 |
+
"id": "ed24f5d3",
|
| 193 |
+
"metadata": {},
|
| 194 |
+
"outputs": [
|
| 195 |
+
{
|
| 196 |
+
"data": {
|
| 197 |
+
"text/markdown": [
|
| 198 |
+
"```html\n",
|
| 199 |
+
"<div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4\">\n",
|
| 200 |
+
" <div class=\"uk-card \">\n",
|
| 201 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 202 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 203 |
+
" </div>\n",
|
| 204 |
+
" </div>\n",
|
| 205 |
+
" <div class=\"uk-card \">\n",
|
| 206 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 207 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 208 |
+
" </div>\n",
|
| 209 |
+
" </div>\n",
|
| 210 |
+
" <div class=\"uk-card \">\n",
|
| 211 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 212 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 213 |
+
" </div>\n",
|
| 214 |
+
" </div>\n",
|
| 215 |
+
" <div class=\"uk-card \">\n",
|
| 216 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 217 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 218 |
+
" </div>\n",
|
| 219 |
+
" </div>\n",
|
| 220 |
+
" <div class=\"uk-card \">\n",
|
| 221 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 222 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 223 |
+
" </div>\n",
|
| 224 |
+
" </div>\n",
|
| 225 |
+
" <div class=\"uk-card \">\n",
|
| 226 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 227 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 228 |
+
" </div>\n",
|
| 229 |
+
" </div>\n",
|
| 230 |
+
"</div>\n",
|
| 231 |
+
"\n",
|
| 232 |
+
"```"
|
| 233 |
+
],
|
| 234 |
+
"text/plain": [
|
| 235 |
+
"div((div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '})),{'class': 'grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4'})"
|
| 236 |
+
]
|
| 237 |
+
},
|
| 238 |
+
"execution_count": 7,
|
| 239 |
+
"metadata": {},
|
| 240 |
+
"output_type": "execute_result"
|
| 241 |
+
}
|
| 242 |
+
],
|
| 243 |
+
"source": [
|
| 244 |
+
"Grid(\n",
|
| 245 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 246 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 247 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 248 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 249 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 250 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 251 |
+
" cls='gap-4')"
|
| 252 |
+
]
|
| 253 |
+
},
|
| 254 |
+
{
|
| 255 |
+
"cell_type": "markdown",
|
| 256 |
+
"id": "7bb43e6d",
|
| 257 |
+
"metadata": {},
|
| 258 |
+
"source": [
|
| 259 |
+
"#### Grid with space"
|
| 260 |
+
]
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"cell_type": "code",
|
| 264 |
+
"execution_count": 5,
|
| 265 |
+
"id": "44130942",
|
| 266 |
+
"metadata": {},
|
| 267 |
+
"outputs": [
|
| 268 |
+
{
|
| 269 |
+
"data": {
|
| 270 |
+
"text/markdown": [
|
| 271 |
+
"```html\n",
|
| 272 |
+
"<div class=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 space-x-4 space-y-4\">\n",
|
| 273 |
+
" <div class=\"uk-card \">\n",
|
| 274 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 275 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 276 |
+
" </div>\n",
|
| 277 |
+
" </div>\n",
|
| 278 |
+
" <div class=\"uk-card \">\n",
|
| 279 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 280 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 281 |
+
" </div>\n",
|
| 282 |
+
" </div>\n",
|
| 283 |
+
" <div class=\"uk-card \">\n",
|
| 284 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 285 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 286 |
+
" </div>\n",
|
| 287 |
+
" </div>\n",
|
| 288 |
+
" <div class=\"uk-card \">\n",
|
| 289 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 290 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 291 |
+
" </div>\n",
|
| 292 |
+
" </div>\n",
|
| 293 |
+
" <div class=\"uk-card \">\n",
|
| 294 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 295 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 296 |
+
" </div>\n",
|
| 297 |
+
" </div>\n",
|
| 298 |
+
" <div class=\"uk-card \">\n",
|
| 299 |
+
" <div class=\"uk-card-body space-y-6\">\n",
|
| 300 |
+
" <h4 class=\"uk-h4 \">A Simple Card</h4>\n",
|
| 301 |
+
" </div>\n",
|
| 302 |
+
" </div>\n",
|
| 303 |
+
"</div>\n",
|
| 304 |
+
"\n",
|
| 305 |
+
"```"
|
| 306 |
+
],
|
| 307 |
+
"text/plain": [
|
| 308 |
+
"div((div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '}), div((div((h4(('A Simple Card',),{'class': 'uk-h4 '}),),{'class': 'uk-card-body space-y-6'}),),{'class': 'uk-card '})),{'class': 'grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 space-x-4 space-y-4'})"
|
| 309 |
+
]
|
| 310 |
+
},
|
| 311 |
+
"execution_count": 5,
|
| 312 |
+
"metadata": {},
|
| 313 |
+
"output_type": "execute_result"
|
| 314 |
+
}
|
| 315 |
+
],
|
| 316 |
+
"source": [
|
| 317 |
+
"Grid(\n",
|
| 318 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 319 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 320 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 321 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 322 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 323 |
+
" Card(H4(\"A Simple Card\")),\n",
|
| 324 |
+
" cls='space-x-4 space-y-4')"
|
| 325 |
+
]
|
| 326 |
+
},
|
| 327 |
+
{
|
| 328 |
+
"cell_type": "markdown",
|
| 329 |
+
"id": "50c48f82",
|
| 330 |
+
"metadata": {},
|
| 331 |
+
"source": [
|
| 332 |
+
"### Grid with no gap or space\n",
|
| 333 |
+
"\n",
|
| 334 |
+
"The first example has no gap or not space applied. As expected this means the cards are flush with each other. Often this is not what you want, because a little space between cards looks much nicer.\n",
|
| 335 |
+
"\n",
|
| 336 |
+
"### Grid with gap\n",
|
| 337 |
+
"\n",
|
| 338 |
+
"The second example has the same grid but with gap applied. As youc an see, this gives constent space between all elements of the grid looks great!\n",
|
| 339 |
+
"\n",
|
| 340 |
+
"### Grid with space\n",
|
| 341 |
+
"\n",
|
| 342 |
+
"The third example has the same grid but with space applied. As you can see, it's not really what we want. However it's a really good illustration of how space works so let's notice a few things about it:\n",
|
| 343 |
+
"\n",
|
| 344 |
+
"**X Axis**\n",
|
| 345 |
+
"\n",
|
| 346 |
+
"- The first card is flush with the left side of the page (no margin)\n",
|
| 347 |
+
"- The card below it isn't flush with the left side of the page (has margin)\n",
|
| 348 |
+
"\n",
|
| 349 |
+
"**Y Axis**\n",
|
| 350 |
+
"\n",
|
| 351 |
+
"- The first card is flush with the heading immediately above it (no margin)\n",
|
| 352 |
+
"- The card top it's right isn't flush with the heading above it (has margin)\n",
|
| 353 |
+
"\n",
|
| 354 |
+
"So `space` applies margin to every element except for the first element in a group!\n",
|
| 355 |
+
"\n",
|
| 356 |
+
"> Tip: Use your browser developer tools to inspect the examples\n",
|
| 357 |
+
"\n",
|
| 358 |
+
"Next, let's look at a form example where `gap` isn't a good choice but `space` works beautifully!"
|
| 359 |
+
]
|
| 360 |
+
},
|
| 361 |
+
{
|
| 362 |
+
"cell_type": "code",
|
| 363 |
+
"execution_count": null,
|
| 364 |
+
"id": "36d1949c",
|
| 365 |
+
"metadata": {},
|
| 366 |
+
"outputs": [],
|
| 367 |
+
"source": [
|
| 368 |
+
"Form(DivCentered(H3(\"My Form\")),\n",
|
| 369 |
+
" LabelInput(\"Name\"),\n",
|
| 370 |
+
" Grid(LabelInput(\"Phone\"), LabelInput(\"Email\"), cols=2),\n",
|
| 371 |
+
" cls='')"
|
| 372 |
+
]
|
| 373 |
+
},
|
| 374 |
+
{
|
| 375 |
+
"cell_type": "code",
|
| 376 |
+
"execution_count": null,
|
| 377 |
+
"id": "e8c5d280",
|
| 378 |
+
"metadata": {},
|
| 379 |
+
"outputs": [],
|
| 380 |
+
"source": [
|
| 381 |
+
"Form(DivCentered(H3(\"My Form with gap\")),\n",
|
| 382 |
+
" LabelInput(\"Name\"),\n",
|
| 383 |
+
" Grid(LabelInput(\"Phone\"), LabelInput(\"Email\"), cols=2),\n",
|
| 384 |
+
" cls='gap-y-5')"
|
| 385 |
+
]
|
| 386 |
+
},
|
| 387 |
+
{
|
| 388 |
+
"cell_type": "code",
|
| 389 |
+
"execution_count": 9,
|
| 390 |
+
"id": "b2566530",
|
| 391 |
+
"metadata": {},
|
| 392 |
+
"outputs": [
|
| 393 |
+
{
|
| 394 |
+
"data": {
|
| 395 |
+
"text/markdown": [
|
| 396 |
+
"```html\n",
|
| 397 |
+
"<form enctype=\"multipart/form-data\" class=\"space-y-5\"> <div class=\"uk-flex uk-flex-column uk-flex-middle uk-flex-center space-y-4\">\n",
|
| 398 |
+
" <h3 class=\"uk-h3 \">My Form with Spacing</h3>\n",
|
| 399 |
+
" </div>\n",
|
| 400 |
+
" <div class=\"space-y-2\">\n",
|
| 401 |
+
"<label class=\"uk-form-label \">Name</label> <input class=\"uk-input \">\n",
|
| 402 |
+
" </div>\n",
|
| 403 |
+
" <div class=\"grid grid-cols-2 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 gap-4\">\n",
|
| 404 |
+
" <div class=\"space-y-2\">\n",
|
| 405 |
+
"<label class=\"uk-form-label \">Phone</label> <input class=\"uk-input \">\n",
|
| 406 |
+
" </div>\n",
|
| 407 |
+
" <div class=\"space-y-2\">\n",
|
| 408 |
+
"<label class=\"uk-form-label \">Email</label> <input class=\"uk-input \">\n",
|
| 409 |
+
" </div>\n",
|
| 410 |
+
" </div>\n",
|
| 411 |
+
"</form>\n",
|
| 412 |
+
"```"
|
| 413 |
+
],
|
| 414 |
+
"text/plain": [
|
| 415 |
+
"form((div((h3(('My Form with Spacing',),{'class': 'uk-h3 '}),),{'class': 'uk-flex uk-flex-column uk-flex-middle uk-flex-center space-y-4'}), div((label(('Name',),{'for': '', 'class': 'uk-form-label '}), input((),{'id': '', 'class': 'uk-input '})),{'class': 'space-y-2'}), div((div((label(('Phone',),{'for': '', 'class': 'uk-form-label '}), input((),{'id': '', 'class': 'uk-input '})),{'class': 'space-y-2'}), div((label(('Email',),{'for': '', 'class': 'uk-form-label '}), input((),{'id': '', 'class': 'uk-input '})),{'class': 'space-y-2'})),{'class': 'grid grid-cols-2 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 gap-4'})),{'enctype': 'multipart/form-data', 'class': 'space-y-5'})"
|
| 416 |
+
]
|
| 417 |
+
},
|
| 418 |
+
"execution_count": 9,
|
| 419 |
+
"metadata": {},
|
| 420 |
+
"output_type": "execute_result"
|
| 421 |
+
}
|
| 422 |
+
],
|
| 423 |
+
"source": [
|
| 424 |
+
"Form(DivCentered(H3(\"My Form with Spacing\")),\n",
|
| 425 |
+
" LabelInput(\"Name\"),\n",
|
| 426 |
+
" Grid(LabelInput(\"Phone\"), LabelInput(\"Email\"), cols=2),\n",
|
| 427 |
+
" cls='space-y-5')"
|
| 428 |
+
]
|
| 429 |
+
},
|
| 430 |
+
{
|
| 431 |
+
"cell_type": "markdown",
|
| 432 |
+
"id": "85e821ad",
|
| 433 |
+
"metadata": {},
|
| 434 |
+
"source": [
|
| 435 |
+
"### Form with no gap or space\n",
|
| 436 |
+
"\n",
|
| 437 |
+
"The top form looks a bit scrunched with defaults, but it's certainly passable. There is a bit of a space between the label and it's associated input because of the defaults in MonsterUI.\n",
|
| 438 |
+
"\n",
|
| 439 |
+
"### Form with gap\n",
|
| 440 |
+
"\n",
|
| 441 |
+
"The second form with gap is identical to the first. Because we're not in a flex element or a grid, it doesn't do anything at all!\n",
|
| 442 |
+
"\n",
|
| 443 |
+
"### Form with space\n",
|
| 444 |
+
"\n",
|
| 445 |
+
"`Space-y-5` adds vertical space between each child which really spreads out the form for a nice aesthetic. If you recall from the grid example, it does not apply this margin to the first element - but in this situation (and many others) we do not want the spacing above the top element (heading) to be the same as the spacing between the form elements.\n",
|
| 446 |
+
"\n",
|
| 447 |
+
"> Tip: Use your browser developer tools to inspect the examples\n"
|
| 448 |
+
]
|
| 449 |
+
},
|
| 450 |
+
{
|
| 451 |
+
"cell_type": "markdown",
|
| 452 |
+
"id": "83d0f4ea",
|
| 453 |
+
"metadata": {},
|
| 454 |
+
"source": [
|
| 455 |
+
"# Further reading\n",
|
| 456 |
+
"\n",
|
| 457 |
+
"For further reading, check out the [Tailwind CSS guide](https://tailwindcss.com/docs/margin#adding-margin-to-a-single-side), which other users have found to be a useful as an additional guide."
|
| 458 |
+
]
|
| 459 |
+
}
|
| 460 |
+
],
|
| 461 |
+
"metadata": {
|
| 462 |
+
"kernelspec": {
|
| 463 |
+
"display_name": "Python 3 (ipykernel)",
|
| 464 |
+
"language": "python",
|
| 465 |
+
"name": "python3"
|
| 466 |
+
},
|
| 467 |
+
"language_info": {
|
| 468 |
+
"codemirror_mode": {
|
| 469 |
+
"name": "ipython",
|
| 470 |
+
"version": 3
|
| 471 |
+
},
|
| 472 |
+
"file_extension": ".py",
|
| 473 |
+
"mimetype": "text/x-python",
|
| 474 |
+
"name": "python",
|
| 475 |
+
"nbconvert_exporter": "python",
|
| 476 |
+
"pygments_lexer": "ipython3",
|
| 477 |
+
"version": "3.12.8"
|
| 478 |
+
}
|
| 479 |
+
},
|
| 480 |
+
"nbformat": 4,
|
| 481 |
+
"nbformat_minor": 5
|
| 482 |
+
}
|
MonsterUI/docs/htmxindicator.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fasthtml.common import *
|
| 2 |
+
from monsterui.all import *
|
| 3 |
+
import time
|
| 4 |
+
from fasthtml.components import Uk_theme_switcher
|
| 5 |
+
|
| 6 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 7 |
+
|
| 8 |
+
@rt
|
| 9 |
+
def index():
|
| 10 |
+
return Titled("Loading Demo",
|
| 11 |
+
Button("Load", id='load',
|
| 12 |
+
hx_get=load, hx_target='#content', hx_swap='beforeend',
|
| 13 |
+
hx_indicator='#loading'),
|
| 14 |
+
Div(id='content'),
|
| 15 |
+
Loading(id='loading', htmx_indicator=True))
|
| 16 |
+
|
| 17 |
+
@rt
|
| 18 |
+
def load():
|
| 19 |
+
time.sleep(1)
|
| 20 |
+
return P("Loading Demo")
|
| 21 |
+
|
| 22 |
+
@rt
|
| 23 |
+
def theme(): return Uk_theme_switcher()
|
| 24 |
+
|
| 25 |
+
serve()
|
MonsterUI/docs/llms-ctx-full.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
MonsterUI/docs/llms-ctx.txt
ADDED
|
@@ -0,0 +1,1603 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<project title="MonsterUI Documentation">> MonsterUI is a python library which brings styling to python for FastHTML apps.<api reference><doc title="API List" desc="Complete API Reference"># monsterui Module Documentation
|
| 2 |
+
|
| 3 |
+
## monsterui.core
|
| 4 |
+
|
| 5 |
+
- `class ThemeRadii(Enum)`
|
| 6 |
+
Members: none, sm, md, lg
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
- `class ThemeShadows`
|
| 10 |
+
|
| 11 |
+
- `class ThemeFont`
|
| 12 |
+
|
| 13 |
+
- `class Theme(Enum)`
|
| 14 |
+
Selector to choose theme and get all headers needed for app. Includes frankenui + tailwind + daisyui + highlight.js options
|
| 15 |
+
Members: slate, stone, gray, neutral, red, rose, orange, green, blue, yellow, violet, zinc
|
| 16 |
+
|
| 17 |
+
- `headers(self, mode, icons, daisy, highlightjs, katex, apex_charts, radii, shadows, font)`
|
| 18 |
+
Create frankenui and tailwind cdns
|
| 19 |
+
|
| 20 |
+
- `local_headers(self, mode, static_dir, icons, daisy, highlightjs, katex, apex_charts, radii, shadows, font)`
|
| 21 |
+
Create headers using local files downloaded from CDNs
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
## monsterui.daisy
|
| 25 |
+
|
| 26 |
+
- `class AlertT(Enum)`
|
| 27 |
+
Alert styles from DaisyUI
|
| 28 |
+
Members: info, success, warning, error
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
- `def Alert(*c, **kwargs)`
|
| 32 |
+
Alert informs users about important events.
|
| 33 |
+
|
| 34 |
+
- `class StepsT(Enum)`
|
| 35 |
+
Options for Steps
|
| 36 |
+
Members: vertical, horizonal
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
- `class StepT(Enum)`
|
| 40 |
+
Step styles for LiStep
|
| 41 |
+
Members: primary, secondary, accent, info, success, warning, error, neutral
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
- `def Steps(*li, **kwargs)`
|
| 45 |
+
Creates a steps container
|
| 46 |
+
|
| 47 |
+
- `def LiStep(*c, **kwargs)`
|
| 48 |
+
Creates a step list item
|
| 49 |
+
|
| 50 |
+
- `class LoadingT(Enum)`
|
| 51 |
+
Members: spinner, dots, ring, ball, bars, infinity, xs, sm, md, lg
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
- `def Loading(cls, htmx_indicator, **kwargs)`
|
| 55 |
+
Creates a loading animation component
|
| 56 |
+
|
| 57 |
+
- `class ToastHT(Enum)`
|
| 58 |
+
Horizontal position for Toast
|
| 59 |
+
Members: start, center, end
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
- `class ToastVT(Enum)`
|
| 63 |
+
Vertical position for Toast
|
| 64 |
+
Members: top, middle, bottom
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
## monsterui.foundations
|
| 68 |
+
|
| 69 |
+
> Data Structures and Utilties
|
| 70 |
+
|
| 71 |
+
- `def stringify(o)`
|
| 72 |
+
Converts input types into strings that can be passed to FT components
|
| 73 |
+
|
| 74 |
+
- `class VEnum(Enum)`
|
| 75 |
+
Members:
|
| 76 |
+
|
| 77 |
+
- `__str__(self)`
|
| 78 |
+
- `__add__(self, other)`
|
| 79 |
+
- `__radd__(self, other)`
|
| 80 |
+
|
| 81 |
+
## monsterui.franken
|
| 82 |
+
|
| 83 |
+
- `class TextT(Enum)`
|
| 84 |
+
Text Styles from https://franken-ui.dev/docs/text
|
| 85 |
+
Members: paragraph, lead, meta, gray, italic, xs, sm, lg, xl, light, normal, medium, bold, extrabold, muted, primary, secondary, success, warning, error, info, left, right, center, justify, start, end, top, middle, bottom, truncate, break_, nowrap, underline, highlight
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
- `class TextPresets(Enum)`
|
| 89 |
+
Common Typography Presets
|
| 90 |
+
Members: muted_sm, muted_lg, bold_sm, bold_lg, md_weight_sm, md_weight_muted
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
- `def CodeSpan(*c, **kwargs)`
|
| 94 |
+
A CodeSpan with Styling
|
| 95 |
+
|
| 96 |
+
- `def CodeBlock(*c, **kwargs)`
|
| 97 |
+
CodeBlock with Styling
|
| 98 |
+
|
| 99 |
+
- `def H1(*c, **kwargs)`
|
| 100 |
+
H1 with styling and appropriate size
|
| 101 |
+
|
| 102 |
+
- `def H2(*c, **kwargs)`
|
| 103 |
+
H2 with styling and appropriate size
|
| 104 |
+
|
| 105 |
+
- `def H3(*c, **kwargs)`
|
| 106 |
+
H3 with styling and appropriate size
|
| 107 |
+
|
| 108 |
+
- `def H4(*c, **kwargs)`
|
| 109 |
+
H4 with styling and appropriate size
|
| 110 |
+
|
| 111 |
+
- `def H5(*c, **kwargs)`
|
| 112 |
+
H5 with styling and appropriate size
|
| 113 |
+
|
| 114 |
+
- `def H6(*c, **kwargs)`
|
| 115 |
+
H6 with styling and appropriate size
|
| 116 |
+
|
| 117 |
+
- `def Subtitle(*c, **kwargs)`
|
| 118 |
+
Styled muted_sm text designed to go under Headings and Titles
|
| 119 |
+
|
| 120 |
+
- `def Q(*c, **kwargs)`
|
| 121 |
+
Styled quotation mark
|
| 122 |
+
|
| 123 |
+
- `def Em(*c, **kwargs)`
|
| 124 |
+
Styled emphasis text
|
| 125 |
+
|
| 126 |
+
- `def Strong(*c, **kwargs)`
|
| 127 |
+
Styled strong text
|
| 128 |
+
|
| 129 |
+
- `def I(*c, **kwargs)`
|
| 130 |
+
Styled italic text
|
| 131 |
+
|
| 132 |
+
- `def Small(*c, **kwargs)`
|
| 133 |
+
Styled small text
|
| 134 |
+
|
| 135 |
+
- `def Mark(*c, **kwargs)`
|
| 136 |
+
Styled highlighted text
|
| 137 |
+
|
| 138 |
+
- `def Del(*c, **kwargs)`
|
| 139 |
+
Styled deleted text
|
| 140 |
+
|
| 141 |
+
- `def Ins(*c, **kwargs)`
|
| 142 |
+
Styled inserted text
|
| 143 |
+
|
| 144 |
+
- `def Sub(*c, **kwargs)`
|
| 145 |
+
Styled subscript text
|
| 146 |
+
|
| 147 |
+
- `def Sup(*c, **kwargs)`
|
| 148 |
+
Styled superscript text
|
| 149 |
+
|
| 150 |
+
- `def Blockquote(*c, **kwargs)`
|
| 151 |
+
Blockquote with Styling
|
| 152 |
+
|
| 153 |
+
- `def Caption(*c, **kwargs)`
|
| 154 |
+
Styled caption text
|
| 155 |
+
|
| 156 |
+
- `def Cite(*c, **kwargs)`
|
| 157 |
+
Styled citation text
|
| 158 |
+
|
| 159 |
+
- `def Time(*c, **kwargs)`
|
| 160 |
+
Styled time element
|
| 161 |
+
|
| 162 |
+
- `def Address(*c, **kwargs)`
|
| 163 |
+
Styled address element
|
| 164 |
+
|
| 165 |
+
- `def Abbr(*c, **kwargs)`
|
| 166 |
+
Styled abbreviation with dotted underline
|
| 167 |
+
|
| 168 |
+
- `def Dfn(*c, **kwargs)`
|
| 169 |
+
Styled definition term with italic and medium weight
|
| 170 |
+
|
| 171 |
+
- `def Kbd(*c, **kwargs)`
|
| 172 |
+
Styled keyboard input with subtle background
|
| 173 |
+
|
| 174 |
+
- `def Samp(*c, **kwargs)`
|
| 175 |
+
Styled sample output with subtle background
|
| 176 |
+
|
| 177 |
+
- `def Var(*c, **kwargs)`
|
| 178 |
+
Styled variable with italic monospace
|
| 179 |
+
|
| 180 |
+
- `def Figure(*c, **kwargs)`
|
| 181 |
+
Styled figure container with card-like appearance
|
| 182 |
+
|
| 183 |
+
- `def Details(*c, **kwargs)`
|
| 184 |
+
Styled details element
|
| 185 |
+
|
| 186 |
+
- `def Summary(*c, **kwargs)`
|
| 187 |
+
Styled summary element
|
| 188 |
+
|
| 189 |
+
- `def Data(*c, **kwargs)`
|
| 190 |
+
Styled data element
|
| 191 |
+
|
| 192 |
+
- `def Meter(*c, **kwargs)`
|
| 193 |
+
Styled meter element
|
| 194 |
+
|
| 195 |
+
- `def S(*c, **kwargs)`
|
| 196 |
+
Styled strikethrough text (different semantic meaning from Del)
|
| 197 |
+
|
| 198 |
+
- `def U(*c, **kwargs)`
|
| 199 |
+
Styled underline (for proper names in Chinese, proper spelling etc)
|
| 200 |
+
|
| 201 |
+
- `def Output(*c, **kwargs)`
|
| 202 |
+
Styled output element for form results
|
| 203 |
+
|
| 204 |
+
- `def PicSumImg(h, w, id, grayscale, blur, **kwargs)`
|
| 205 |
+
Creates a placeholder image using https://picsum.photos/
|
| 206 |
+
|
| 207 |
+
- `def AccordionItem(title, *c)`
|
| 208 |
+
Creates a single item for use within an Accordion component, handling title, content, and open state.
|
| 209 |
+
|
| 210 |
+
- `def Accordion(*c, **kwargs)`
|
| 211 |
+
Creates a styled Accordion container using accordion component.
|
| 212 |
+
|
| 213 |
+
- `class ButtonT(Enum)`
|
| 214 |
+
Options for styling Buttons
|
| 215 |
+
Members: default, ghost, primary, secondary, destructive, text, link, xs, sm, lg, xl, icon
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
- `def Button(*c, **kwargs)`
|
| 219 |
+
Button with Styling (defaults to `submit` for form submission)
|
| 220 |
+
|
| 221 |
+
- `class ContainerT(Enum)`
|
| 222 |
+
Max width container sizes from https://franken-ui.dev/docs/container
|
| 223 |
+
Members: xs, sm, lg, xl, expand
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
- `class BackgroundT(Enum)`
|
| 227 |
+
Members: muted, primary, secondary, default
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
- `def Container(*c, **kwargs)`
|
| 231 |
+
Div to be used as a container that often wraps large sections or a page of content
|
| 232 |
+
|
| 233 |
+
- `def Titled(title, *c, **kwargs)`
|
| 234 |
+
Creates a standard page structure for titled page. Main(Container(title, content))
|
| 235 |
+
|
| 236 |
+
- `class DividerT(Enum)`
|
| 237 |
+
Divider Styles from https://franken-ui.dev/docs/divider
|
| 238 |
+
Members: icon, sm, vertical
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
- `def Divider(*c, **kwargs)`
|
| 242 |
+
Divider with default styling and margin
|
| 243 |
+
|
| 244 |
+
- `def DividerSplit(*c)`
|
| 245 |
+
Creates a simple horizontal line divider with configurable thickness and vertical spacing
|
| 246 |
+
|
| 247 |
+
- `def Article(*c, **kwargs)`
|
| 248 |
+
A styled article container for blog posts or similar content
|
| 249 |
+
|
| 250 |
+
- `def ArticleTitle(*c, **kwargs)`
|
| 251 |
+
A title component for use within an Article
|
| 252 |
+
|
| 253 |
+
- `def ArticleMeta(*c, **kwargs)`
|
| 254 |
+
A metadata component for use within an Article showing things like date, author etc
|
| 255 |
+
|
| 256 |
+
- `class SectionT(Enum)`
|
| 257 |
+
Section styles from https://franken-ui.dev/docs/section
|
| 258 |
+
Members: default, muted, primary, secondary, xs, sm, lg, xl, remove_vertical
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
- `def Section(*c, **kwargs)`
|
| 262 |
+
Section with styling and margins
|
| 263 |
+
|
| 264 |
+
- `def Form(*c, **kwargs)`
|
| 265 |
+
A Form with default spacing between form elements
|
| 266 |
+
|
| 267 |
+
- `def Fieldset(*c, **kwargs)`
|
| 268 |
+
A Fieldset with default styling
|
| 269 |
+
|
| 270 |
+
- `def Legend(*c, **kwargs)`
|
| 271 |
+
A Legend with default styling
|
| 272 |
+
|
| 273 |
+
- `def Input(*c, **kwargs)`
|
| 274 |
+
An Input with default styling
|
| 275 |
+
|
| 276 |
+
- `def Radio(*c, **kwargs)`
|
| 277 |
+
A Radio with default styling
|
| 278 |
+
|
| 279 |
+
- `def CheckboxX(*c, **kwargs)`
|
| 280 |
+
A Checkbox with default styling
|
| 281 |
+
|
| 282 |
+
- `def Range(*c, **kwargs)`
|
| 283 |
+
A Range with default styling
|
| 284 |
+
|
| 285 |
+
- `def TextArea(*c, **kwargs)`
|
| 286 |
+
A Textarea with default styling
|
| 287 |
+
|
| 288 |
+
- `def Switch(*c, **kwargs)`
|
| 289 |
+
A Switch with default styling
|
| 290 |
+
|
| 291 |
+
- `def Upload(*c, **kwargs)`
|
| 292 |
+
A file upload component with default styling
|
| 293 |
+
|
| 294 |
+
- `def UploadZone(*c, **kwargs)`
|
| 295 |
+
A file drop zone component with default styling
|
| 296 |
+
|
| 297 |
+
- `def FormLabel(*c, **kwargs)`
|
| 298 |
+
A Label with default styling
|
| 299 |
+
|
| 300 |
+
- `class LabelT(Enum)`
|
| 301 |
+
Members: primary, secondary, destructive
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
- `def Label(*c, **kwargs)`
|
| 305 |
+
FrankenUI labels, which look like pills
|
| 306 |
+
|
| 307 |
+
- `def UkFormSection(title, description, *c)`
|
| 308 |
+
A form section with a title, description and optional button
|
| 309 |
+
|
| 310 |
+
- `def GenericLabelInput(label, lbl_cls, input_cls, container, cls, id, input_fn, **kwargs)`
|
| 311 |
+
`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `LabelInput` which is created for you in this library
|
| 312 |
+
|
| 313 |
+
- `def LabelInput(label, lbl_cls, input_cls, cls, id, **kwargs)`
|
| 314 |
+
A `FormLabel` and `Input` pair that provides default spacing and links/names them based on id
|
| 315 |
+
|
| 316 |
+
- `def LabelRadio(label, lbl_cls, input_cls, container, cls, id, **kwargs)`
|
| 317 |
+
A FormLabel and Radio pair that provides default spacing and links/names them based on id
|
| 318 |
+
|
| 319 |
+
- `def LabelCheckboxX(label, lbl_cls, input_cls, container, cls, id, **kwargs)`
|
| 320 |
+
A FormLabel and CheckboxX pair that provides default spacing and links/names them based on id
|
| 321 |
+
|
| 322 |
+
- `def Options(*c)`
|
| 323 |
+
Helper function to wrap things into `Option`s for use in `Select`
|
| 324 |
+
|
| 325 |
+
- `def Select(*option, **kwargs)`
|
| 326 |
+
Creates a select dropdown with uk styling and option for adding a search box
|
| 327 |
+
|
| 328 |
+
- `def LabelSelect(*option, **kwargs)`
|
| 329 |
+
A FormLabel and Select pair that provides default spacing and links/names them based on id
|
| 330 |
+
|
| 331 |
+
- `@delegates(GenericLabelInput, but=['input_fn', 'cls']) def LabelRange(label, lbl_cls, input_cls, cls, id, value, min, max, step, label_range, **kwargs)`
|
| 332 |
+
A FormLabel and Range pair that provides default spacing and links/names them based on id
|
| 333 |
+
|
| 334 |
+
- `class AT(Enum)`
|
| 335 |
+
Link styles from https://franken-ui.dev/docs/link
|
| 336 |
+
Members: muted, text, reset, primary, classic
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
- `class ListT(Enum)`
|
| 340 |
+
List styles using Tailwind CSS
|
| 341 |
+
Members: disc, circle, square, decimal, hyphen, bullet, divider, striped
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
- `def ModalContainer(*c, **kwargs)`
|
| 345 |
+
Creates a modal container that components go in
|
| 346 |
+
|
| 347 |
+
- `def ModalDialog(*c, **kwargs)`
|
| 348 |
+
Creates a modal dialog
|
| 349 |
+
|
| 350 |
+
- `def ModalHeader(*c, **kwargs)`
|
| 351 |
+
Creates a modal header
|
| 352 |
+
|
| 353 |
+
- `def ModalBody(*c, **kwargs)`
|
| 354 |
+
Creates a modal body
|
| 355 |
+
|
| 356 |
+
- `def ModalFooter(*c, **kwargs)`
|
| 357 |
+
Creates a modal footer
|
| 358 |
+
|
| 359 |
+
- `def ModalTitle(*c, **kwargs)`
|
| 360 |
+
Creates a modal title
|
| 361 |
+
|
| 362 |
+
- `def ModalCloseButton(*c, **kwargs)`
|
| 363 |
+
Creates a button that closes a modal with js
|
| 364 |
+
|
| 365 |
+
- `def Modal(*c, **kwargs)`
|
| 366 |
+
Creates a modal with the appropriate classes to put the boilerplate in the appropriate places for you
|
| 367 |
+
|
| 368 |
+
- `def Placeholder(*c, **kwargs)`
|
| 369 |
+
Creates a placeholder
|
| 370 |
+
|
| 371 |
+
- `def Progress(*c, **kwargs)`
|
| 372 |
+
Creates a progress bar
|
| 373 |
+
|
| 374 |
+
- `def UkIcon(icon, height, width, stroke_width, cls, **kwargs)`
|
| 375 |
+
Creates an icon using lucide icons
|
| 376 |
+
|
| 377 |
+
- `def UkIconLink(icon, height, width, stroke_width, cls, button, **kwargs)`
|
| 378 |
+
Creates an icon link using lucide icons
|
| 379 |
+
|
| 380 |
+
- `def DiceBearAvatar(seed_name, h, w)`
|
| 381 |
+
Creates an Avatar using https://dicebear.com/
|
| 382 |
+
|
| 383 |
+
- `def Center(*c, **kwargs)`
|
| 384 |
+
Centers contents both vertically and horizontally by default
|
| 385 |
+
|
| 386 |
+
- `class FlexT(Enum)`
|
| 387 |
+
Flexbox modifiers using Tailwind CSS
|
| 388 |
+
Members: block, inline, left, center, right, between, around, stretch, top, middle, bottom, row, row_reverse, column, column_reverse, nowrap, wrap, wrap_reverse
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
- `def Grid(*div, **kwargs)`
|
| 392 |
+
Creates a responsive grid layout with smart defaults based on content
|
| 393 |
+
|
| 394 |
+
- `def DivFullySpaced(*c, **kwargs)`
|
| 395 |
+
Creates a flex div with it's components having as much space between them as possible
|
| 396 |
+
|
| 397 |
+
- `def DivCentered(*c, **kwargs)`
|
| 398 |
+
Creates a flex div with it's components centered in it
|
| 399 |
+
|
| 400 |
+
- `def DivLAligned(*c, **kwargs)`
|
| 401 |
+
Creates a flex div with it's components aligned to the left
|
| 402 |
+
|
| 403 |
+
- `def DivRAligned(*c, **kwargs)`
|
| 404 |
+
Creates a flex div with it's components aligned to the right
|
| 405 |
+
|
| 406 |
+
- `def DivVStacked(*c, **kwargs)`
|
| 407 |
+
Creates a flex div with it's components stacked vertically
|
| 408 |
+
|
| 409 |
+
- `def DivHStacked(*c, **kwargs)`
|
| 410 |
+
Creates a flex div with it's components stacked horizontally
|
| 411 |
+
|
| 412 |
+
- `class NavT(Enum)`
|
| 413 |
+
Members: default, primary, secondary
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
- `def NavContainer(*li, **kwargs)`
|
| 417 |
+
Creates a navigation container (useful for creating a sidebar navigation). A Nav is a list (NavBar is something different)
|
| 418 |
+
|
| 419 |
+
- `def NavParentLi(*nav_container, **kwargs)`
|
| 420 |
+
Creates a navigation list item with a parent nav for nesting
|
| 421 |
+
|
| 422 |
+
- `def NavDividerLi(*c, **kwargs)`
|
| 423 |
+
Creates a navigation list item with a divider
|
| 424 |
+
|
| 425 |
+
- `def NavHeaderLi(*c, **kwargs)`
|
| 426 |
+
Creates a navigation list item with a header
|
| 427 |
+
|
| 428 |
+
- `def NavSubtitle(*c, **kwargs)`
|
| 429 |
+
Creates a navigation subtitle
|
| 430 |
+
|
| 431 |
+
- `def NavCloseLi(*c, **kwargs)`
|
| 432 |
+
Creates a navigation list item with a close button
|
| 433 |
+
|
| 434 |
+
- `class ScrollspyT(Enum)`
|
| 435 |
+
Members: underline, bold
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
- `def NavBar(*c)`
|
| 439 |
+
Creates a responsive navigation bar with mobile menu support
|
| 440 |
+
|
| 441 |
+
- `def SliderContainer(*c, **kwargs)`
|
| 442 |
+
Creates a slider container
|
| 443 |
+
|
| 444 |
+
- `def SliderItems(*c, **kwargs)`
|
| 445 |
+
Creates a slider items container
|
| 446 |
+
|
| 447 |
+
- `def SliderNav(cls, prev_cls, next_cls, **kwargs)`
|
| 448 |
+
Navigation arrows for Slider component
|
| 449 |
+
|
| 450 |
+
- `def Slider(*c, **kwargs)`
|
| 451 |
+
Creates a slider with optional navigation arrows
|
| 452 |
+
|
| 453 |
+
- `def DropDownNavContainer(*li, **kwargs)`
|
| 454 |
+
A Nav that is part of a DropDown
|
| 455 |
+
|
| 456 |
+
- `def TabContainer(*li, **kwargs)`
|
| 457 |
+
A TabContainer where children will be different tabs
|
| 458 |
+
|
| 459 |
+
- `class CardT(Enum)`
|
| 460 |
+
Card styles from UIkit
|
| 461 |
+
Members: default, primary, secondary, destructive, hover
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
- `def CardTitle(*c, **kwargs)`
|
| 465 |
+
Creates a card title
|
| 466 |
+
|
| 467 |
+
- `def CardHeader(*c, **kwargs)`
|
| 468 |
+
Creates a card header
|
| 469 |
+
|
| 470 |
+
- `def CardBody(*c, **kwargs)`
|
| 471 |
+
Creates a card body
|
| 472 |
+
|
| 473 |
+
- `def CardFooter(*c, **kwargs)`
|
| 474 |
+
Creates a card footer
|
| 475 |
+
|
| 476 |
+
- `def CardContainer(*c, **kwargs)`
|
| 477 |
+
Creates a card container
|
| 478 |
+
|
| 479 |
+
- `def Card(*c, **kwargs)`
|
| 480 |
+
Creates a Card with a header, body, and footer
|
| 481 |
+
|
| 482 |
+
- `class TableT(Enum)`
|
| 483 |
+
Members: divider, striped, hover, sm, lg, justify, middle, responsive
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
- `def Table(*c, **kwargs)`
|
| 487 |
+
Creates a table
|
| 488 |
+
|
| 489 |
+
- `def TableFromLists(header_data, body_data, footer_data, header_cell_render, body_cell_render, footer_cell_render, cls, sortable, **kwargs)`
|
| 490 |
+
Creates a Table from a list of header data and a list of lists of body data
|
| 491 |
+
|
| 492 |
+
- `def TableFromDicts(header_data, body_data, footer_data, header_cell_render, body_cell_render, footer_cell_render, cls, sortable, **kwargs)`
|
| 493 |
+
Creates a Table from a list of header data and a list of dicts of body data
|
| 494 |
+
|
| 495 |
+
- `def apply_classes(html_str, class_map, class_map_mods)`
|
| 496 |
+
Apply classes to html string
|
| 497 |
+
|
| 498 |
+
- `class FrankenRenderer`
|
| 499 |
+
Custom renderer for Franken UI that handles image paths
|
| 500 |
+
|
| 501 |
+
- `def __init__(self, *args, **kwargs)`
|
| 502 |
+
- `def render_image(self, token)`
|
| 503 |
+
Modify image paths if they're relative and self.img_dir is specified
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
- `def render_md(md_content, class_map, class_map_mods, img_dir, renderer)`
|
| 507 |
+
Renders markdown using mistletoe and lxml with custom image handling
|
| 508 |
+
|
| 509 |
+
- `def ThemePicker(color, radii, shadows, font, mode, cls, custom_themes)`
|
| 510 |
+
Theme picker component with configurable sections
|
| 511 |
+
|
| 512 |
+
- `def LightboxContainer(*lightboxitem, **kwargs)`
|
| 513 |
+
Lightbox container that will hold `LightboxItems`
|
| 514 |
+
|
| 515 |
+
- `def LightboxItem(*c, **kwargs)`
|
| 516 |
+
Anchor tag with appropriate structure to go inside a `LightBoxContainer`
|
| 517 |
+
|
| 518 |
+
- `def ApexChart(**kws)`
|
| 519 |
+
Apex chart component
|
| 520 |
+
</doc></api reference><examples><doc title="Cards" desc="FrankenUI Cards Example built with MonsterUI (original design by ShadCN)">"""FrankenUI Cards Example built with MonsterUI (original design by ShadCN)"""
|
| 521 |
+
|
| 522 |
+
from fasthtml.common import *
|
| 523 |
+
from fasthtml.components import Uk_input_tag
|
| 524 |
+
from fasthtml.svg import *
|
| 525 |
+
from monsterui.all import *
|
| 526 |
+
import calendar
|
| 527 |
+
from datetime import datetime
|
| 528 |
+
|
| 529 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 530 |
+
|
| 531 |
+
CreateAccount = Card(
|
| 532 |
+
Grid(Button(DivLAligned(UkIcon('github'),Div('Github'))),Button('Google')),
|
| 533 |
+
DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm),
|
| 534 |
+
LabelInput('Email', id='email', placeholder='[email protected]'),
|
| 535 |
+
LabelInput('Password', id='password',placeholder='Password', type='Password'),
|
| 536 |
+
header=(H3('Create an Account'),Subtitle('Enter your email below to create your account')),
|
| 537 |
+
footer=Button('Create Account',cls=(ButtonT.primary,'w-full')))
|
| 538 |
+
|
| 539 |
+
PaypalSVG_data = "M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z"
|
| 540 |
+
AppleSVG_data = "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"
|
| 541 |
+
Card1Svg = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(Rect(width="20", height="14", x="2", y="5", rx="2"),Path(d="M2 10h20"))
|
| 542 |
+
PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor")),
|
| 543 |
+
AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor"))
|
| 544 |
+
|
| 545 |
+
PaymentMethod = Card(
|
| 546 |
+
Grid(Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary'),
|
| 547 |
+
Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20'),
|
| 548 |
+
Button(DivCentered(AppleSvg, "Apple"), cls='h-20')),
|
| 549 |
+
Form(LabelInput('Name', id='name', placeholder='John Doe'),
|
| 550 |
+
LabelInput('Card Number', id='card_number', placeholder='[email protected]'),
|
| 551 |
+
Grid(LabelSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'),
|
| 552 |
+
LabelSelect(*Options(*range(2024,2030),selected_idx=0), label='Year', id='expire_year'),
|
| 553 |
+
LabelInput('CVV', id='cvv',placeholder='CVV', cls='mt-0'))),
|
| 554 |
+
header=(H3('Payment Method'),Subtitle('Add a new payment method to your account.')))
|
| 555 |
+
|
| 556 |
+
area_opts = ('Team','Billing','Account','Deployment','Support')
|
| 557 |
+
severity_opts = ('Severity 1 (Highest)', 'Severity 2', 'Severity 3', 'Severity 4 (Lowest)')
|
| 558 |
+
ReportIssue = Card(
|
| 559 |
+
Grid(Div(LabelSelect(*Options(*area_opts), label='Area', id='area')),
|
| 560 |
+
Div(LabelSelect(*Options(*severity_opts),label='Severity',id='area'))),
|
| 561 |
+
LabelInput( label='Subject', id='subject', placeholder='I need help with'),
|
| 562 |
+
LabelTextArea( label='Description', id='description',placeholder='Please include all information relevant to your issue'),
|
| 563 |
+
Div(FormLabel('Tags', fr='#tags'),
|
| 564 |
+
Uk_input_tag(name="Tags",state="danger", value="Spam,Invalid", uk_cloak=True, id='tags')),
|
| 565 |
+
header=(H3('Report Issue'),Subtitle('What area are you having problems with?')),
|
| 566 |
+
footer = DivFullySpaced(Button('Cancel'), Button(cls=ButtonT.primary)('Submit')))
|
| 567 |
+
|
| 568 |
+
monster_desc ="Python-first beautifully designed components because you deserve to focus on features that matter and your app deserves to be beautiful from day one."
|
| 569 |
+
MonsterUI = Card(H4("Monster UI"),
|
| 570 |
+
Subtitle(monster_desc),
|
| 571 |
+
DivLAligned(
|
| 572 |
+
Div("Python"),
|
| 573 |
+
DivLAligned(UkIcon('star'),Div("20k"), cls='space-x-1'),
|
| 574 |
+
Div(datetime.now().strftime("%B %d, %Y")),
|
| 575 |
+
cls=('space-x-4',TextPresets.muted_sm)))
|
| 576 |
+
|
| 577 |
+
def CookieTableRow(heading, description, active=False):
|
| 578 |
+
return Tr(Td(H5(heading)),
|
| 579 |
+
Td(P(description, cls=TextPresets.muted_sm)),
|
| 580 |
+
Td(Switch(checked=active)))
|
| 581 |
+
|
| 582 |
+
CookieSettings = Card(
|
| 583 |
+
Table(Tbody(
|
| 584 |
+
CookieTableRow('Strictly Necessary', 'These cookies are essential in order to use the website and use its features.', True),
|
| 585 |
+
CookieTableRow('Functional Cookies', 'These cookies allow the website to provide personalized functionality.'),
|
| 586 |
+
CookieTableRow('Performance Cookies', 'These cookies help to improve the performance of the website.'))),
|
| 587 |
+
header=(H4('Cookie Settings'),Subtitle('Manage your cookie settings here.')),
|
| 588 |
+
footer=Button('Save Preferences', cls=(ButtonT.primary, 'w-full')))
|
| 589 |
+
|
| 590 |
+
team_members = [("Sofia Davis", "[email protected]", "Owner"),("Jackson Lee", "[email protected]", "Member"),]
|
| 591 |
+
def TeamMemberRow(name, email, role):
|
| 592 |
+
return DivFullySpaced(
|
| 593 |
+
DivLAligned(
|
| 594 |
+
DiceBearAvatar(name, 10,10),
|
| 595 |
+
Div(P(name, cls=(TextT.sm, TextT.medium)),
|
| 596 |
+
P(email, cls=TextPresets.muted_sm))),
|
| 597 |
+
Button(role, UkIcon('chevron-down', cls='ml-4')),
|
| 598 |
+
DropDownNavContainer(map(NavCloseLi, [
|
| 599 |
+
A(Div('Viewer', NavSubtitle('Can view and comment.'))),
|
| 600 |
+
A(Div('Developer', NavSubtitle('Can view, comment and edit.'))),
|
| 601 |
+
A(Div('Billing', NavSubtitle('Can view, comment and manage billing.'))),
|
| 602 |
+
A(Div('Owner', NavSubtitle('Admin-level access to all resources.')))])))
|
| 603 |
+
|
| 604 |
+
TeamMembers = Card(*[TeamMemberRow(*member) for member in team_members],
|
| 605 |
+
header = (H4('Team Members'),Subtitle('Invite your team members to collaborate.')))
|
| 606 |
+
|
| 607 |
+
access_roles = ("Read and write access", "Read-only access")
|
| 608 |
+
team_members = [("Olivia Martin", "[email protected]", "Read and write access"),
|
| 609 |
+
("Isabella Nguyen", "[email protected]", "Read-only access"),
|
| 610 |
+
("Sofia Davis", "[email protected]", "Read-only access")]
|
| 611 |
+
|
| 612 |
+
def TeamMemberRow(name, email, role):
|
| 613 |
+
return DivFullySpaced(
|
| 614 |
+
DivLAligned(DiceBearAvatar(name, 10,10),
|
| 615 |
+
Div(P(name, cls=(TextT.sm, TextT.medium)),
|
| 616 |
+
P(email, cls=TextPresets.muted_sm))),
|
| 617 |
+
Select(*Options(*access_roles, selected_idx=access_roles.index(role))))
|
| 618 |
+
|
| 619 |
+
ShareDocument = Card(
|
| 620 |
+
DivLAligned(Input(value='http://example.com/link/to/document'),Button('Copy link', cls='whitespace-nowrap')),
|
| 621 |
+
Divider(),
|
| 622 |
+
H4('People with access', cls=TextPresets.bold_sm),
|
| 623 |
+
*[TeamMemberRow(*member) for member in team_members],
|
| 624 |
+
header = (H4('Share this document'),Subtitle('Anyone with the link can view this document.')))
|
| 625 |
+
|
| 626 |
+
DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024'))
|
| 627 |
+
|
| 628 |
+
section_content =(('bell','Everything',"Email digest, mentions & all activity."),
|
| 629 |
+
('user',"Available","Only mentions and comments"),
|
| 630 |
+
('ban', "Ignoring","Turn of all notifications"))
|
| 631 |
+
|
| 632 |
+
def NotificationRow(icon, name, desc):
|
| 633 |
+
return Li(cls='-mx-1')(A(DivLAligned(UkIcon(icon),Div(P(name),P(desc, cls=TextPresets.muted_sm)))))
|
| 634 |
+
|
| 635 |
+
Notifications = Card(
|
| 636 |
+
NavContainer(
|
| 637 |
+
*[NotificationRow(*row) for row in section_content],
|
| 638 |
+
cls=NavT.secondary),
|
| 639 |
+
header = (H4('Notification'),Subtitle('Choose what you want to be notified about.')),
|
| 640 |
+
body_cls='pt-0')
|
| 641 |
+
|
| 642 |
+
TeamCard = Card(
|
| 643 |
+
DivLAligned(
|
| 644 |
+
DiceBearAvatar("Isaac Flath", h=24, w=24),
|
| 645 |
+
Div(H3("Isaac Flath"), P("Library Creator"))),
|
| 646 |
+
footer=DivFullySpaced(
|
| 647 |
+
DivHStacked(UkIcon("map-pin", height=16), P("Alexandria, VA")),
|
| 648 |
+
DivHStacked(*(UkIconLink(icon, height=16) for icon in ("mail", "linkedin", "github")))),
|
| 649 |
+
cls=CardT.hover)
|
| 650 |
+
|
| 651 |
+
@rt
|
| 652 |
+
def index():
|
| 653 |
+
return Title("Cards Example"),Container(Grid(
|
| 654 |
+
*map(Div,(
|
| 655 |
+
Div(PaymentMethod,CreateAccount, TeamCard, cls='space-y-4'),
|
| 656 |
+
Div(TeamMembers, ShareDocument,DateCard,Notifications, cls='space-y-4'),
|
| 657 |
+
Div(ReportIssue,MonsterUI,CookieSettings, cls='space-y-4'))),
|
| 658 |
+
cols_md=1, cols_lg=2, cols_xl=3))
|
| 659 |
+
|
| 660 |
+
serve()</doc><doc title="Dashboard" desc="FrankenUI Dashboard Example built with MonsterUI (original design by ShadCN)">"""FrankenUI Dashboard Example built with MonsterUI (original design by ShadCN)"""
|
| 661 |
+
|
| 662 |
+
from fasthtml.common import * # Bring in all of fasthtml
|
| 663 |
+
import fasthtml.common as fh # Used to get unstyled components
|
| 664 |
+
from monsterui.all import * # Bring in all of monsterui, including shadowing fasthtml components with styled components
|
| 665 |
+
from fasthtml.svg import *
|
| 666 |
+
import numpy as np
|
| 667 |
+
import plotly.express as px
|
| 668 |
+
import pandas as pd
|
| 669 |
+
import numpy as np
|
| 670 |
+
|
| 671 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 672 |
+
|
| 673 |
+
def generate_chart(num_points=30):
|
| 674 |
+
df = pd.DataFrame({
|
| 675 |
+
'Date': pd.date_range('2024-01-01', periods=num_points),
|
| 676 |
+
'Revenue': np.random.normal(100, 10, num_points).cumsum(),
|
| 677 |
+
'Users': np.random.normal(80, 8, num_points).cumsum(),
|
| 678 |
+
'Growth': np.random.normal(60, 6, num_points).cumsum()})
|
| 679 |
+
|
| 680 |
+
fig = px.line(df, x='Date', y=['Revenue', 'Users', 'Growth'], template='plotly_white', line_shape='spline')
|
| 681 |
+
|
| 682 |
+
fig.update_traces(mode='lines+markers')
|
| 683 |
+
fig.update_layout(
|
| 684 |
+
margin=dict(l=20, r=20, t=20, b=20), hovermode='x unified',
|
| 685 |
+
showlegend=True, legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1),
|
| 686 |
+
plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)',
|
| 687 |
+
xaxis=dict(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,0,0.1)'),
|
| 688 |
+
yaxis=dict(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,0,0.1)'))
|
| 689 |
+
|
| 690 |
+
return fig.to_html(include_plotlyjs=True, full_html=False, config={'displayModeBar': False})
|
| 691 |
+
|
| 692 |
+
def InfoCard(title, value, change): return Card(H3(value),P(change, cls=TextPresets.muted_sm), header = H4(title))
|
| 693 |
+
|
| 694 |
+
rev = InfoCard("Total Revenue", "$45,231.89", "+20.1% from last month")
|
| 695 |
+
sub = InfoCard("Subscriptions", "+2350", "+180.1% from last month")
|
| 696 |
+
sal = InfoCard("Sales", "+12,234", "+19% from last month")
|
| 697 |
+
act = InfoCard("Active Now", "+573", "+201 since last hour")
|
| 698 |
+
|
| 699 |
+
info_card_data = [("Total Revenue", "$45,231.89", "+20.1% from last month"),
|
| 700 |
+
("Subscriptions", "+2350", "+180.1% from last month"),
|
| 701 |
+
("Sales", "+12,234", "+19% from last month"),
|
| 702 |
+
("Active Now", "+573", "+201 since last hour")]
|
| 703 |
+
|
| 704 |
+
top_info_row = Grid(*[InfoCard(*row) for row in info_card_data])
|
| 705 |
+
|
| 706 |
+
def AvatarItem(name, email, amount):
|
| 707 |
+
return DivFullySpaced(
|
| 708 |
+
DivLAligned(
|
| 709 |
+
DiceBearAvatar(name, 9,9),
|
| 710 |
+
Div(Strong(name, cls=TextT.sm),
|
| 711 |
+
Address(A(email,href=f'mailto:{email}')))),
|
| 712 |
+
fh.Data(amount, cls="ml-auto font-medium", value=amount[2:]))
|
| 713 |
+
|
| 714 |
+
recent_sales = Card(
|
| 715 |
+
Div(cls="space-y-8")(
|
| 716 |
+
*[AvatarItem(n,e,d) for (n,e,d) in (
|
| 717 |
+
("Olivia Martin", "[email protected]", "+$1,999.00"),
|
| 718 |
+
("Jackson Lee", "[email protected]", "+$39.00"),
|
| 719 |
+
("Isabella Nguyen", "[email protected]", "+$299.00"),
|
| 720 |
+
("William Kim", "[email protected]", "+$99.00"),
|
| 721 |
+
("Sofia Davis", "[email protected]", "+$39.00"))]),
|
| 722 |
+
header=Div(H3("Recent Sales"),Subtitle("You made 265 sales this month.")),
|
| 723 |
+
cls='col-span-3')
|
| 724 |
+
|
| 725 |
+
teams = [["Alicia Koch"],['Acme Inc', 'Monster Inc.'],['Create a Team']]
|
| 726 |
+
|
| 727 |
+
opt_hdrs = ["Personal", "Team", ""]
|
| 728 |
+
|
| 729 |
+
team_dropdown = Select(
|
| 730 |
+
Optgroup(Option(A("Alicia Koch")), label="Personal Account"),
|
| 731 |
+
Optgroup(Option(A("Acme Inc")), Option(A("Monster Inc.")), label="Teams"),
|
| 732 |
+
Option(A("Create a Team")),
|
| 733 |
+
cls='flex items-center')
|
| 734 |
+
|
| 735 |
+
hotkeys = [('Profile','⇧⌘P'),('Billing','⌘B'),('Settings','⌘S'),('New Team', ''), ('Logout', '')]
|
| 736 |
+
|
| 737 |
+
def NavSpacedLi(t,s): return NavCloseLi(A(DivFullySpaced(P(t),P(s,cls=TextPresets.muted_sm))))
|
| 738 |
+
|
| 739 |
+
avatar_dropdown = Div(
|
| 740 |
+
DiceBearAvatar('Alicia Koch',8,8),
|
| 741 |
+
DropDownNavContainer(
|
| 742 |
+
NavHeaderLi('sveltecult',NavSubtitle("[email protected]")),
|
| 743 |
+
*[NavSpacedLi(*hk) for hk in hotkeys],))
|
| 744 |
+
|
| 745 |
+
top_nav = NavBar(
|
| 746 |
+
team_dropdown, *map(A, ["Overview", "Customers", "Products", "Settings"]),
|
| 747 |
+
brand=DivLAligned(avatar_dropdown, Input(placeholder='Search')))
|
| 748 |
+
|
| 749 |
+
@rt
|
| 750 |
+
def index():
|
| 751 |
+
return Title("Dashboard Example"), Container(
|
| 752 |
+
top_nav,
|
| 753 |
+
H2('Dashboard'),
|
| 754 |
+
TabContainer(
|
| 755 |
+
Li(A("Overview"),cls='uk-active'),
|
| 756 |
+
*map(lambda x: Li(A(x)), ["Analytics", "Reports", "Notifications"]),
|
| 757 |
+
alt=True),
|
| 758 |
+
top_info_row,
|
| 759 |
+
Grid(
|
| 760 |
+
Card(Safe(generate_chart(100)), cls='col-span-4'),
|
| 761 |
+
recent_sales,
|
| 762 |
+
gap=4,cols_xl=7,cols_lg=7,cols_md=1,cols_sm=1,cols_xs=1),
|
| 763 |
+
cls=('space-y-4', ContainerT.xl))
|
| 764 |
+
|
| 765 |
+
serve()</doc><doc title="Forms" desc="FrankenUI Forms Example built with MonsterUI (original design by ShadCN)">"""FrankenUI Forms Example built with MonsterUI (original design by ShadCN)"""
|
| 766 |
+
|
| 767 |
+
|
| 768 |
+
from fasthtml.common import *
|
| 769 |
+
from monsterui.all import *
|
| 770 |
+
from fasthtml.svg import *
|
| 771 |
+
|
| 772 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 773 |
+
|
| 774 |
+
def HelpText(c): return P(c,cls=TextPresets.muted_sm)
|
| 775 |
+
|
| 776 |
+
def heading():
|
| 777 |
+
return Div(cls="space-y-5")(
|
| 778 |
+
H2("Settings"),
|
| 779 |
+
Subtitle("Manage your account settings and set e-mail preferences."),
|
| 780 |
+
DividerSplit())
|
| 781 |
+
|
| 782 |
+
|
| 783 |
+
sidebar = NavContainer(
|
| 784 |
+
*map(lambda x: Li(A(x)), ("Profile", "Account", "Appearance", "Notifications", "Display")),
|
| 785 |
+
uk_switcher="connect: #component-nav; animation: uk-animation-fade",
|
| 786 |
+
cls=(NavT.secondary,"space-y-4 p-4 w-1/5"))
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
def FormSectionDiv(*c, cls='space-y-2', **kwargs): return Div(*c, cls=cls, **kwargs)
|
| 790 |
+
|
| 791 |
+
def FormLayout(title, subtitle, *content, cls='space-y-3 mt-4'): return Container(Div(H3(title), Subtitle(subtitle), DividerLine(), Form(*content, cls=cls)))
|
| 792 |
+
|
| 793 |
+
def profile_form():
|
| 794 |
+
content = (FormSectionDiv(
|
| 795 |
+
LabelInput("Username", placeholder='sveltecult', id='username'),
|
| 796 |
+
HelpText("This is your public display name. It can be your real name or a pseudonym. You can only change this once every 30 days.")),
|
| 797 |
+
FormSectionDiv(
|
| 798 |
+
LabelSelect(
|
| 799 |
+
Option("Select a verified email to display", value="", selected=True, disabled=True),
|
| 800 |
+
*[Option(o, value=o) for o in ('[email protected]', '[email protected]', '[email protected]')],
|
| 801 |
+
label="Email", id="email"),
|
| 802 |
+
HelpText("You can manage verified email addresses in your email settings.")),
|
| 803 |
+
FormSectionDiv(
|
| 804 |
+
LabelTextArea("Bio", id="bio", placeholder="Tell us a little bit about yourself"),
|
| 805 |
+
HelpText("You can @mention other users and organizations to link to them."),
|
| 806 |
+
P("String must contain at least 4 character(s)", cls="text-destructive")),
|
| 807 |
+
FormSectionDiv(
|
| 808 |
+
FormLabel("URLs"),
|
| 809 |
+
HelpText("Add links to your website, blog, or social media profiles."),
|
| 810 |
+
Input(value="https://www.franken-ui.dev"),
|
| 811 |
+
Input(value="https://github.com/sveltecult/franken-ui"),
|
| 812 |
+
Button("Add URL")),
|
| 813 |
+
Button('Update profile', cls=ButtonT.primary))
|
| 814 |
+
|
| 815 |
+
return FormLayout('Profile', 'This is how others will see you on the site.', *content)
|
| 816 |
+
|
| 817 |
+
def account_form():
|
| 818 |
+
content = (
|
| 819 |
+
FormSectionDiv(
|
| 820 |
+
LabelInput("Name", placeholder="Your name", id="name"),
|
| 821 |
+
HelpText("This is the name that will be displayed on your profile and in emails.")),
|
| 822 |
+
FormSectionDiv(
|
| 823 |
+
LabelInput("Date of Birth", type="date", placeholder="Pick a date", id="date_of_birth"),
|
| 824 |
+
HelpText("Your date of birth is used to calculate your age.")),
|
| 825 |
+
FormSectionDiv(
|
| 826 |
+
LabelSelect(*Options("Select a language", "English", "French", "German", "Spanish", "Portuguese", selected_idx=1, disabled_idxs={0}),
|
| 827 |
+
label='Language', id="language"),
|
| 828 |
+
HelpText("This is the language that will be used in the dashboard.")),
|
| 829 |
+
Button('Update profile', cls=ButtonT.primary))
|
| 830 |
+
|
| 831 |
+
return FormLayout('Account', 'Update your account settings. Set your preferred language and timezone.', *content)
|
| 832 |
+
|
| 833 |
+
def appearance_form():
|
| 834 |
+
def theme_item(bg_color, content_bg, text_bg):
|
| 835 |
+
common_content = f"space-y-2 rounded-md {content_bg} p-2 shadow-sm"
|
| 836 |
+
item_row = lambda: Div(cls=f"flex items-center space-x-2 {common_content}")(
|
| 837 |
+
Div(cls=f"h-4 w-4 rounded-full {text_bg}"),
|
| 838 |
+
Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}"))
|
| 839 |
+
|
| 840 |
+
return Div(cls=f"space-y-2 rounded-sm {bg_color} p-2")(
|
| 841 |
+
Div(cls=common_content)(
|
| 842 |
+
Div(cls=f"h-2 w-[80px] rounded-lg {text_bg}"),
|
| 843 |
+
Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}")),
|
| 844 |
+
item_row(),
|
| 845 |
+
item_row())
|
| 846 |
+
|
| 847 |
+
common_toggle_cls = "block cursor-pointer items-center rounded-md border-2 border-muted p-1 ring-ring"
|
| 848 |
+
|
| 849 |
+
content = (
|
| 850 |
+
FormSectionDiv(
|
| 851 |
+
LabelSelect(*Options('Select a font family', 'Inter', 'Geist', 'Open Sans', selected_idx=2, disabled_idxs={0}),
|
| 852 |
+
label='Font Family', id='font_family'),
|
| 853 |
+
HelpText("Set the font you want to use in the dashboard.")),
|
| 854 |
+
FormSectionDiv(
|
| 855 |
+
FormLabel("Theme"),
|
| 856 |
+
HelpText("Select the theme for the dashboard."),
|
| 857 |
+
Grid(
|
| 858 |
+
A(id="theme-toggle-light", cls=common_toggle_cls)(theme_item("bg-[#ecedef]", "bg-white", "bg-[#ecedef]")),
|
| 859 |
+
A(id="theme-toggle-dark", cls=f"{common_toggle_cls} bg-popover")(theme_item("bg-slate-950", "bg-slate-800", "bg-slate-400")),
|
| 860 |
+
cols_max=2,cls=('max-w-md','gap-8'))),
|
| 861 |
+
Button('Update preferences', cls=ButtonT.primary))
|
| 862 |
+
|
| 863 |
+
return FormLayout('Appearance', 'Customize the appearance of the app. Automatically switch between day and night themes.', *content)
|
| 864 |
+
|
| 865 |
+
|
| 866 |
+
notification_items = [
|
| 867 |
+
{"title": "Communication emails", "description": "Receive emails about your account activity.", "checked": False, "disabled": False},
|
| 868 |
+
{"title": "Marketing emails", "description": "Receive emails about new products, features, and more.", "checked": False, "disabled": False},
|
| 869 |
+
{"title": "Social emails", "description": "Receive emails for friend requests, follows, and more.", "checked": True, "disabled": False},
|
| 870 |
+
{"title": "Security emails", "description": "Receive emails about your account activity and security.", "checked": True, "disabled": True}]
|
| 871 |
+
|
| 872 |
+
def notifications_form():
|
| 873 |
+
def RadioLabel(label): return DivLAligned(Radio(name="notification", checked=(label=="Nothing")), FormLabel(label))
|
| 874 |
+
|
| 875 |
+
def NotificationCard(item):
|
| 876 |
+
return Card(
|
| 877 |
+
Div(cls="space-y-0.5")(
|
| 878 |
+
FormLabel(Strong(item['title'], cls=TextT.sm),
|
| 879 |
+
HelpText(item['description']))))
|
| 880 |
+
content = Div(
|
| 881 |
+
FormSectionDiv(
|
| 882 |
+
FormLabel("Notify me about"),
|
| 883 |
+
*map(RadioLabel, ["All new messages", "Direct messages and mentions", "Nothing"])),
|
| 884 |
+
Div(
|
| 885 |
+
H4("Email Notifications", cls="mb-4"),
|
| 886 |
+
Grid(*map(NotificationCard, notification_items), cols=1)),
|
| 887 |
+
LabelCheckboxX("Use different settings for my mobile devices", id="notification_mobile"),
|
| 888 |
+
HelpText("You can manage your mobile notifications in the mobile settings page."),
|
| 889 |
+
Button('Update notifications', cls=ButtonT.primary))
|
| 890 |
+
|
| 891 |
+
return FormLayout('Notifications', 'Configure how you receive notifications.', *content)
|
| 892 |
+
|
| 893 |
+
def display_form():
|
| 894 |
+
content = (
|
| 895 |
+
Div(cls="space-y-2")(
|
| 896 |
+
Div(cls="mb-4")(
|
| 897 |
+
H5("Sidebar"),
|
| 898 |
+
Subtitle("Select the items you want to display in the sidebar.")),
|
| 899 |
+
*[Div(CheckboxX(id=f"display_{i}", checked=i in [0, 1, 2]),FormLabel(label))
|
| 900 |
+
for i, label in enumerate(["Recents", "Home", "Applications", "Desktop", "Downloads", "Documents"])]),
|
| 901 |
+
Button('Update display', cls=ButtonT.primary))
|
| 902 |
+
return FormLayout('Display', 'Turn items on or off to control what\'s displayed in the app.', *content)
|
| 903 |
+
|
| 904 |
+
@rt
|
| 905 |
+
def index():
|
| 906 |
+
return Title("Forms Example"),Container(
|
| 907 |
+
heading(),
|
| 908 |
+
Div(cls="flex gap-x-12")(
|
| 909 |
+
sidebar,
|
| 910 |
+
Ul(id="component-nav", cls="uk-switcher max-w-2xl")(
|
| 911 |
+
Li(cls="uk-active")(profile_form(),
|
| 912 |
+
*map(Li, [account_form(), appearance_form(), notifications_form(), display_form()])))))
|
| 913 |
+
|
| 914 |
+
serve()</doc><doc title="Scrollspy" desc="MonsterUI Scrollspy Example application">"MonsterUI Scrollspy Example application"
|
| 915 |
+
|
| 916 |
+
from fasthtml.common import *
|
| 917 |
+
from monsterui.all import *
|
| 918 |
+
import random
|
| 919 |
+
|
| 920 |
+
# Using the "slate" theme with Highlight.js enabled
|
| 921 |
+
hdrs = Theme.slate.headers(highlightjs=True)
|
| 922 |
+
app, rt = fast_app(hdrs=hdrs)
|
| 923 |
+
|
| 924 |
+
################################
|
| 925 |
+
### Example Data and Content ###
|
| 926 |
+
################################
|
| 927 |
+
products = [
|
| 928 |
+
{"name": "Laptop", "price": "$999"},
|
| 929 |
+
{"name": "Smartphone", "price": "$599"}
|
| 930 |
+
]
|
| 931 |
+
|
| 932 |
+
code_example = """
|
| 933 |
+
# Python Code Example
|
| 934 |
+
def greet(name):
|
| 935 |
+
return f"Hello, {name}!"
|
| 936 |
+
|
| 937 |
+
print(greet("World"))
|
| 938 |
+
"""
|
| 939 |
+
testimonials = [
|
| 940 |
+
{"name": "Alice", "feedback": "Great products and excellent customer service!"},
|
| 941 |
+
{"name": "Bob", "feedback": "Fast shipping and amazing quality!"},
|
| 942 |
+
{"name": "Charlie", "feedback": "Amazing experience! Will definitely buy again."},
|
| 943 |
+
{"name": "Diana", "feedback": "Affordable prices and great variety!"},
|
| 944 |
+
{"name": "Edward", "feedback": "Customer support was very helpful."},
|
| 945 |
+
{"name": "Fiona", "feedback": "Loved the design and quality!"}
|
| 946 |
+
]
|
| 947 |
+
|
| 948 |
+
# Team members
|
| 949 |
+
team = [
|
| 950 |
+
{"name": "Isaac Flath", "role": "CEO"},
|
| 951 |
+
{"name": "Benjamin Clavié", "role": "AI Researcher"},
|
| 952 |
+
{"name": "Alexis Gallagher", "role": "ML Engineer"},
|
| 953 |
+
{"name": "Hamel Husain", "role": "Data Scientist"},
|
| 954 |
+
{"name": "Austin Huang", "role": "Software Engineer"},
|
| 955 |
+
{"name": "Benjamin Warner", "role": "Product Manager"},
|
| 956 |
+
{"name": "Jonathan Whitaker", "role": "UX Designer"},
|
| 957 |
+
{"name": "Kerem Turgutlu", "role": "DevOps Engineer"},
|
| 958 |
+
{"name": "Curtis Allan", "role": "DevOps Engineer"},
|
| 959 |
+
{"name": "Audrey Roy Greenfeld", "role": "Security Analyst"},
|
| 960 |
+
{"name": "Nathan Cooper", "role": "Full Stack Developer"},
|
| 961 |
+
{"name": "Jeremy Howard", "role": "CTO"},
|
| 962 |
+
{"name": "Wayde Gilliam", "role": "Cloud Architect"},
|
| 963 |
+
{"name": "Daniel Roy Greenfeld", "role": "Blockchain Expert"},
|
| 964 |
+
{"name": "Tommy Collins", "role": "AI Ethics Researcher"}
|
| 965 |
+
]
|
| 966 |
+
|
| 967 |
+
|
| 968 |
+
def ProductCard(p,img_id=1):
|
| 969 |
+
return Card(
|
| 970 |
+
PicSumImg(w=500, height=100, id=img_id),
|
| 971 |
+
DivFullySpaced(H4(p["name"]), P(Strong(p["price"], cls=TextT.sm))),
|
| 972 |
+
Button("Details", cls=(ButtonT.primary, "w-full")))
|
| 973 |
+
|
| 974 |
+
def TestimonialCard(t,img_id=1):
|
| 975 |
+
return Card(
|
| 976 |
+
DivLAligned(PicSumImg(w=50, h=50, cls='rounded-full', id=img_id), H4(t["name"])),
|
| 977 |
+
P(Q((t["feedback"]))))
|
| 978 |
+
|
| 979 |
+
|
| 980 |
+
def TeamCard(m,img_id=1):
|
| 981 |
+
return Card(
|
| 982 |
+
DivLAligned(
|
| 983 |
+
PicSumImg(w=50, h=50, cls='rounded-full', id=img_id),
|
| 984 |
+
Div(H4(m["name"]), P(m["role"]))),
|
| 985 |
+
DivRAligned(
|
| 986 |
+
UkIcon('twitter', cls='w-5 h-5'),
|
| 987 |
+
UkIcon('linkedin', cls='w-5 h-5'),
|
| 988 |
+
UkIcon('github', cls='w-5 h-5'),
|
| 989 |
+
cls=TextT.gray+'space-x-2'
|
| 990 |
+
),
|
| 991 |
+
cls='p-3')
|
| 992 |
+
|
| 993 |
+
################################
|
| 994 |
+
### Navigation and Scrollspy ###
|
| 995 |
+
################################
|
| 996 |
+
|
| 997 |
+
scrollspy_links = (
|
| 998 |
+
A("Welcome", href="#welcome-section"),
|
| 999 |
+
A("Products", href="#products-section"),
|
| 1000 |
+
A("Testimonials", href="#testimonials-section"),
|
| 1001 |
+
A("Team", href="#team-section"),
|
| 1002 |
+
A("Code Example", href="#code-section"))
|
| 1003 |
+
@rt
|
| 1004 |
+
def index():
|
| 1005 |
+
def _Section(*c, **kwargs): return Section(*c, cls='space-y-3 my-48',**kwargs)
|
| 1006 |
+
return Container(
|
| 1007 |
+
NavBar(
|
| 1008 |
+
*scrollspy_links,
|
| 1009 |
+
brand=DivLAligned(H3("Scrollspy Demo!"),UkIcon('rocket',height=30,width=30)),
|
| 1010 |
+
sticky=True, uk_scrollspy_nav=True,
|
| 1011 |
+
scrollspy_cls=ScrollspyT.bold),
|
| 1012 |
+
NavContainer(
|
| 1013 |
+
*map(Li, scrollspy_links),
|
| 1014 |
+
uk_scrollspy_nav=True,
|
| 1015 |
+
sticky=True,
|
| 1016 |
+
cls=(NavT.primary,'pt-20 px-5 pr-10')),
|
| 1017 |
+
Container(
|
| 1018 |
+
# Notice the ID of each section corresponds to the `scrollspy_links` dictionary
|
| 1019 |
+
# So in scollspy `NavContainer` the `href` of each `Li` is the ID of the section
|
| 1020 |
+
DivCentered(
|
| 1021 |
+
H1("Welcome to the Store!"),
|
| 1022 |
+
Subtitle("Explore our products and enjoy dynamic code examples."),
|
| 1023 |
+
id="welcome-section"),
|
| 1024 |
+
_Section(H2("Products"),
|
| 1025 |
+
Grid(*[ProductCard(p,img_id=i) for i,p in enumerate(products)], cols_lg=2),
|
| 1026 |
+
id="products-section"),
|
| 1027 |
+
_Section(H2("Testimonials"),
|
| 1028 |
+
Slider(*[TestimonialCard(t,img_id=i) for i,t in enumerate(testimonials)]),
|
| 1029 |
+
id="testimonials-section"),
|
| 1030 |
+
_Section(H2("Our Team"),
|
| 1031 |
+
Grid(*[TeamCard(m,img_id=i) for i,m in enumerate(team)], cols_lg=2, cols_max=3),
|
| 1032 |
+
id="team-section"),
|
| 1033 |
+
_Section(H2("Code Example"),
|
| 1034 |
+
CodeBlock(code_example, lang="python"),
|
| 1035 |
+
id="code-section")),
|
| 1036 |
+
cls=(ContainerT.xl,'uk-container-expand'))
|
| 1037 |
+
|
| 1038 |
+
serve()</doc><doc title="Ticket" desc="MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components">"""MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components"""
|
| 1039 |
+
from fasthtml.common import *
|
| 1040 |
+
from monsterui.all import *
|
| 1041 |
+
from datetime import datetime
|
| 1042 |
+
|
| 1043 |
+
app, rt = fast_app(hdrs=Theme.blue.headers(daisy=True))
|
| 1044 |
+
|
| 1045 |
+
def TicketSteps(step):
|
| 1046 |
+
return Steps(
|
| 1047 |
+
LiStep("Submitted", data_content="📝",
|
| 1048 |
+
cls=StepT.success if step > 0 else StepT.primary if step == 0 else StepT.neutral),
|
| 1049 |
+
LiStep("In Review", data_content="🔎",
|
| 1050 |
+
cls=StepT.success if step > 1 else StepT.primary if step == 1 else StepT.neutral),
|
| 1051 |
+
LiStep("Processing", data_content="⚙️",
|
| 1052 |
+
cls=StepT.success if step > 2 else StepT.primary if step == 2 else StepT.neutral),
|
| 1053 |
+
LiStep("Resolved", data_content="✅",
|
| 1054 |
+
cls=StepT.success if step > 3 else StepT.primary if step == 3 else StepT.neutral),
|
| 1055 |
+
cls="w-full")
|
| 1056 |
+
|
| 1057 |
+
def StatusBadge(status):
|
| 1058 |
+
styles = {'high': AlertT.error, 'medium': AlertT.warning,'low': AlertT.info}
|
| 1059 |
+
alert_type = styles.get(status, AlertT.info)
|
| 1060 |
+
return Alert(f"{status.title()} Priority", cls=(alert_type,"w-32 shadow-sm"))
|
| 1061 |
+
|
| 1062 |
+
def TicketCard(id, title, description, status, step, department):
|
| 1063 |
+
return Card(
|
| 1064 |
+
CardHeader(
|
| 1065 |
+
DivFullySpaced(
|
| 1066 |
+
Div(H3(f"#{id}", cls=TextT.muted),
|
| 1067 |
+
H4(title),
|
| 1068 |
+
cls='space-y-2'),
|
| 1069 |
+
StatusBadge(status))),
|
| 1070 |
+
CardBody(
|
| 1071 |
+
P(description, cls=(TextT.muted, "mb-6")),
|
| 1072 |
+
DividerSplit(cls="my-6"),
|
| 1073 |
+
TicketSteps(step),
|
| 1074 |
+
DividerSplit(cls="my-6"),
|
| 1075 |
+
DivFullySpaced(
|
| 1076 |
+
Div(Strong("Department"),
|
| 1077 |
+
P(department),
|
| 1078 |
+
cls=('space-y-3', TextPresets.muted_sm)),
|
| 1079 |
+
Div(Strong("Last Updated"),
|
| 1080 |
+
P(Time(datetime.now().strftime('%b %d, %H:%M'))),
|
| 1081 |
+
cls=('space-y-3', TextPresets.muted_sm)),
|
| 1082 |
+
Button("View Details", cls=ButtonT.primary),
|
| 1083 |
+
cls='mt-6')),
|
| 1084 |
+
cls=CardT.hover)
|
| 1085 |
+
|
| 1086 |
+
def NewTicketModal():
|
| 1087 |
+
return Modal(
|
| 1088 |
+
ModalHeader(H3("Create New Support Ticket")),
|
| 1089 |
+
ModalBody(
|
| 1090 |
+
Alert(
|
| 1091 |
+
DivLAligned(UkIcon("info"), Span("Please provide as much detail as possible to help us assist you quickly.")),
|
| 1092 |
+
cls=(AlertT.info,"mb-4")),
|
| 1093 |
+
Form(
|
| 1094 |
+
Grid(LabelInput("Title", id="title", placeholder="Brief description of your issue"),
|
| 1095 |
+
LabelSelect(Options("IT Support", "HR", "Facilities", "Finance"), label="Department", id="department")),
|
| 1096 |
+
LabelSelect(Options("Low", "Medium", "High"), label="Priority Level", id="priority"),
|
| 1097 |
+
LabelTextArea("Description", id="description", placeholder="Please provide detailed information about your issue"),
|
| 1098 |
+
DivRAligned(
|
| 1099 |
+
Button("Cancel", cls=ButtonT.ghost, data_uk_toggle="target: #new-ticket"),
|
| 1100 |
+
Button(Loading(cls=LoadingT.spinner), "Submit Ticket", cls=ButtonT.primary, data_uk_toggle="target: #success-toast; target: #new-ticket")),
|
| 1101 |
+
cls='space-y-8')),
|
| 1102 |
+
id="new-ticket")
|
| 1103 |
+
|
| 1104 |
+
@rt
|
| 1105 |
+
def index():
|
| 1106 |
+
tickets = [
|
| 1107 |
+
{'id': "TK-1001", 'title': "Cloud Storage Access Error",
|
| 1108 |
+
'description': "Unable to access cloud storage with persistent authorization errors. Multiple users affected across marketing department.",
|
| 1109 |
+
'status': 'high', 'step': 2, 'department': 'IT Support'},
|
| 1110 |
+
{'id': "TK-1002", 'title': "Email Integration Issue",
|
| 1111 |
+
'description': "Exchange server not syncing with mobile devices. Affecting external client communications.",
|
| 1112 |
+
'status': 'medium', 'step': 1, 'department': 'IT Support'},
|
| 1113 |
+
{'id': "TK-1003", 'title': "Office Equipment Setup",
|
| 1114 |
+
'description': "New department printer needs configuration and network integration. Required for upcoming client presentation.",
|
| 1115 |
+
'status': 'low', 'step': 0, 'department': 'Facilities'}
|
| 1116 |
+
]
|
| 1117 |
+
|
| 1118 |
+
return Title("Help Desk Dashboard"), Container(
|
| 1119 |
+
Section(
|
| 1120 |
+
DivFullySpaced(
|
| 1121 |
+
H2("Active Tickets"),
|
| 1122 |
+
Button(UkIcon("plus-circle", cls="mr-2"), "New Ticket", cls=ButtonT.primary, data_uk_toggle="target: #new-ticket"),
|
| 1123 |
+
cls='mb-8'),
|
| 1124 |
+
Grid(*[TicketCard(**ticket) for ticket in tickets], cols=1),
|
| 1125 |
+
cls="my-6"),
|
| 1126 |
+
NewTicketModal(),
|
| 1127 |
+
Toast(DivLAligned(UkIcon('check-circle', cls='mr-2'), "Ticket submitted successfully! Our team will review it shortly."), id="success-toast", alert_cls=AlertT.success, cls=(ToastHT.end, ToastVT.bottom)),
|
| 1128 |
+
Loading(htmx_indicator=True, type=LoadingT.dots, cls="fixed top-0 right-0 m-4"),
|
| 1129 |
+
cls="mx-auto max-w-7xl"
|
| 1130 |
+
)
|
| 1131 |
+
|
| 1132 |
+
serve()</doc><doc title="Playground" desc="FrankenUI Playground Example built with MonsterUI (original design by ShadCN)">"""FrankenUI Playground Example built with MonsterUI (original design by ShadCN)"""
|
| 1133 |
+
|
| 1134 |
+
from fasthtml.common import *
|
| 1135 |
+
from monsterui.all import *
|
| 1136 |
+
from fasthtml.svg import *
|
| 1137 |
+
|
| 1138 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 1139 |
+
|
| 1140 |
+
preset_options = ["Grammatical Standard English", "Summarize for a 2nd grader",
|
| 1141 |
+
"Text to command","Q&A","English to other languages","Parse unstructured data",
|
| 1142 |
+
"Classification","Natural language to Python","Explain code","Chat","More examples"]
|
| 1143 |
+
|
| 1144 |
+
def playground_navbar():
|
| 1145 |
+
save_modal = Modal(
|
| 1146 |
+
ModalTitle("Save preset"),
|
| 1147 |
+
P("This will save the current playground state as a preset which you can access later or share with others.",cls=("mt-1.5", TextPresets.muted_sm)),
|
| 1148 |
+
LabelInput("Name", id="name"),
|
| 1149 |
+
LabelInput("Description", id="description"),
|
| 1150 |
+
ModalCloseButton("Save", cls=ButtonT.primary),
|
| 1151 |
+
id="save")
|
| 1152 |
+
|
| 1153 |
+
share_dd = Div(cls="space-y-6 p-4")(
|
| 1154 |
+
H3("Share preset"),
|
| 1155 |
+
P("Anyone who has this link and an OpenAI account will be able to view this.", cls=TextPresets.muted_sm),
|
| 1156 |
+
Div(Input(value="https://platform.openai.com/playground/p/7bbKYQvsVkNmVb8NGcdUOLae?model=text-davinci-003", readonly=True),
|
| 1157 |
+
Button(UkIcon('copy'), cls=(ButtonT.primary, "uk-drop-close",'mt-4'))))
|
| 1158 |
+
|
| 1159 |
+
rnav = (
|
| 1160 |
+
Select(*Options(*preset_options), name='preset', optgroup_label="Examples",
|
| 1161 |
+
placeholder='Load a preset', searchable=True, cls='h-9 w-[200px] lg:w-[300px]'),
|
| 1162 |
+
Button("Save", cls=ButtonT.secondary, data_uk_toggle="#save"),save_modal,
|
| 1163 |
+
Button("View Code", cls=ButtonT.secondary),
|
| 1164 |
+
Button("Share", cls=ButtonT.secondary),DropDownNavContainer(share_dd),
|
| 1165 |
+
Button(UkIcon(icon="ellipsis"), cls=ButtonT.secondary),
|
| 1166 |
+
DropDownNavContainer(
|
| 1167 |
+
Li(A("Content filter preferences")),
|
| 1168 |
+
NavDividerLi(),
|
| 1169 |
+
Li(A("Delete preset", cls="text-destructive")),
|
| 1170 |
+
uk_dropdown="mode: click"))
|
| 1171 |
+
|
| 1172 |
+
return NavBar(*rnav, brand=H4('Playground'))
|
| 1173 |
+
|
| 1174 |
+
rsidebar = NavContainer(
|
| 1175 |
+
Select(
|
| 1176 |
+
Optgroup(map(Option,("text-davinci-003", "text-curie-001", "text-babbage-001", "text-ada-001")),label='GPT-3'),
|
| 1177 |
+
Optgroup(map(Option,("code-davinci-002", "code-cushman-001")),label='Codex'),
|
| 1178 |
+
label="Model",
|
| 1179 |
+
searchable=True),
|
| 1180 |
+
LabelRange(label='Temperature', value='12'),
|
| 1181 |
+
LabelRange(label='Maximum Length', value='80'),
|
| 1182 |
+
LabelRange(label='Top P', value='40'),
|
| 1183 |
+
cls='space-y-6 mt-8')
|
| 1184 |
+
|
| 1185 |
+
@rt
|
| 1186 |
+
def index():
|
| 1187 |
+
navbar = playground_navbar()
|
| 1188 |
+
main_content = Div(
|
| 1189 |
+
Div(cls="flex-1")(
|
| 1190 |
+
Textarea(cls="uk-textarea h-full p-4", placeholder="Write a tagline for an ice cream shop")),
|
| 1191 |
+
cls="flex h-[700px] p-8 w-4/5")
|
| 1192 |
+
|
| 1193 |
+
bottom_buttons = Div(
|
| 1194 |
+
Button("Submit", cls=ButtonT.primary),
|
| 1195 |
+
Button(UkIcon(icon="history"), cls=ButtonT.secondary),
|
| 1196 |
+
cls="flex gap-x-2")
|
| 1197 |
+
|
| 1198 |
+
return Title("Playground Example"),Div(navbar, Div(cls="flex w-full")(main_content, rsidebar), bottom_buttons)
|
| 1199 |
+
|
| 1200 |
+
serve()</doc><doc title="Mail" desc="FrankenUI Mail Example built with MonsterUI (original design by ShadCN)">"""FrankenUI Mail Example built with MonsterUI (original design by ShadCN)"""
|
| 1201 |
+
|
| 1202 |
+
from fasthtml.common import *
|
| 1203 |
+
from monsterui.all import *
|
| 1204 |
+
from fasthtml.svg import *
|
| 1205 |
+
import pathlib, json
|
| 1206 |
+
from datetime import datetime
|
| 1207 |
+
|
| 1208 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 1209 |
+
|
| 1210 |
+
sidebar_group1 = (('home', 'Inbox', '128'), ('file-text', 'Drafts', '9'), (' arrow-up-right', 'Sent', ''),
|
| 1211 |
+
('ban', 'Junk', '23'), ('trash', 'Trash', ''), ('folder', 'Archive', ''))
|
| 1212 |
+
|
| 1213 |
+
sidebar_group2 = (('globe','Social','972'),('info','Updates','342'),('messages-square','Forums','128'),
|
| 1214 |
+
('shopping-cart','Shopping','8'),('shopping-bag','Promotions','21'),)
|
| 1215 |
+
|
| 1216 |
+
def MailSbLi(icon, title, cnt):
|
| 1217 |
+
return Li(A(DivLAligned(Span(UkIcon(icon)),Span(title),P(cnt, cls=TextPresets.muted_sm)),href='#', cls='hover:bg-secondary p-4'))
|
| 1218 |
+
|
| 1219 |
+
sidebar = NavContainer(
|
| 1220 |
+
NavHeaderLi(H3("Email"), cls='p-3'),
|
| 1221 |
+
Li(Select(map(Option, ('[email protected]','[email protected]', '[email protected]')))),
|
| 1222 |
+
*[MailSbLi(i, t, c) for i, t, c in sidebar_group1],
|
| 1223 |
+
Li(Hr()),
|
| 1224 |
+
*[MailSbLi(i, t, c) for i, t, c in sidebar_group2],
|
| 1225 |
+
cls='mt-3')
|
| 1226 |
+
|
| 1227 |
+
mail_data = json.load(open(pathlib.Path('data_/mail.json')))
|
| 1228 |
+
|
| 1229 |
+
def format_date(date_str):
|
| 1230 |
+
date_obj = datetime.fromisoformat(date_str)
|
| 1231 |
+
return date_obj.strftime("%Y-%m-%d %I:%M %p")
|
| 1232 |
+
|
| 1233 |
+
def MailItem(mail):
|
| 1234 |
+
cls_base = 'relative rounded-lg border border-border p-3 text-sm hover:bg-secondary space-y-2'
|
| 1235 |
+
cls = f"{cls_base} {'bg-muted' if mail == mail_data[0] else ''} {'tag-unread' if not mail['read'] else 'tag-mail'}"
|
| 1236 |
+
|
| 1237 |
+
return Li(
|
| 1238 |
+
DivFullySpaced(
|
| 1239 |
+
DivLAligned(
|
| 1240 |
+
Strong(mail['name']),
|
| 1241 |
+
Span(cls='flex h-2 w-2 rounded-full bg-blue-600') if not mail['read'] else ''),
|
| 1242 |
+
Time(format_date(mail['date']), cls='text-xs')),
|
| 1243 |
+
Small(mail['subject'], href=f"#mail-{mail['id']}"),
|
| 1244 |
+
Div(mail['text'][:100] + '...', cls=TextPresets.muted_sm),
|
| 1245 |
+
DivLAligned(
|
| 1246 |
+
*[Label(A(label, href='#'), cls='uk-label-primary' if label == 'work' else '') for label in mail['labels']]),
|
| 1247 |
+
cls=cls)
|
| 1248 |
+
|
| 1249 |
+
def MailList(mails): return Ul(cls='js-filter space-y-2 p-4 pt-0')(*[MailItem(mail) for mail in mails])
|
| 1250 |
+
|
| 1251 |
+
def MailContent():
|
| 1252 |
+
return Div(cls='flex flex-col',uk_filter="target: .js-filter")(
|
| 1253 |
+
Div(cls='flex px-4 py-2 ')(
|
| 1254 |
+
H3('Inbox'),
|
| 1255 |
+
TabContainer(Li(A("All Mail",href='#', role='button'),cls='uk-active', uk_filter_control="filter: .tag-mail"),
|
| 1256 |
+
Li(A("Unread",href='#', role='button'), uk_filter_control="filter: .tag-unread"),
|
| 1257 |
+
alt=True, cls='ml-auto max-w-40', )),
|
| 1258 |
+
Div(cls='flex flex-1 flex-col')(
|
| 1259 |
+
Div(cls='p-4')(
|
| 1260 |
+
Div(cls='uk-inline w-full')(
|
| 1261 |
+
Span(cls='uk-form-icon text-muted-foreground')(UkIcon('search')),
|
| 1262 |
+
Input(placeholder='Search'))),
|
| 1263 |
+
Div(cls='flex-1 overflow-y-auto max-h-[600px]')(MailList(mail_data))))
|
| 1264 |
+
|
| 1265 |
+
def IconNavItem(*d): return [Li(A(UkIcon(o[0],uk_tooltip=o[1]))) for o in d]
|
| 1266 |
+
def IconNav(*c,cls=''): return Ul(cls=f'uk-iconnav {cls}')(*c)
|
| 1267 |
+
|
| 1268 |
+
def MailDetailView(mail):
|
| 1269 |
+
top_icons = [('folder','Archive'), ('ban','Move to junk'), ('trash','Move to trash')]
|
| 1270 |
+
reply_icons = [('reply','Reply'), ('reply','Reply all'), ('forward','Forward')]
|
| 1271 |
+
dropdown_items = ['Mark as unread', 'Star read', 'Add Label', 'Mute Thread']
|
| 1272 |
+
|
| 1273 |
+
return Container(
|
| 1274 |
+
DivFullySpaced(
|
| 1275 |
+
DivLAligned(
|
| 1276 |
+
DivLAligned(*[UkIcon(o[0],uk_tooltip=o[1]) for o in top_icons]),
|
| 1277 |
+
Div(UkIcon('clock', uk_tooltip='Snooze'), cls='pl-2'),
|
| 1278 |
+
cls='space-x-2 divide-x divide-border'),
|
| 1279 |
+
DivLAligned(
|
| 1280 |
+
*[UkIcon(o[0],uk_tooltip=o[1]) for o in reply_icons],
|
| 1281 |
+
Div(UkIcon('ellipsis-vertical',button=True)),
|
| 1282 |
+
DropDownNavContainer(*map(lambda x: Li(A(x)), dropdown_items)))),
|
| 1283 |
+
DivLAligned(
|
| 1284 |
+
Span(mail['name'][:2], cls='flex h-10 w-10 items-center justify-center rounded-full bg-muted'),
|
| 1285 |
+
Div(Strong(mail['name']),
|
| 1286 |
+
Div(mail['subject']),
|
| 1287 |
+
DivLAligned(P('Reply-To:'), A(mail['email'], href=f"mailto:{mail['email']}"), cls='space-x-1'),
|
| 1288 |
+
P(Time(format_date(mail['date']))),
|
| 1289 |
+
cls='space-y-1'+TextT.sm),
|
| 1290 |
+
cls='m-4 space-x-4'),
|
| 1291 |
+
DividerLine(),
|
| 1292 |
+
P(mail['text'], cls=TextT.sm +'p-4'),
|
| 1293 |
+
DividerLine(),
|
| 1294 |
+
Div(TextArea(id='message', placeholder=f"Reply {mail['name']}"),
|
| 1295 |
+
DivFullySpaced(
|
| 1296 |
+
LabelSwitch('Mute this thread',id='mute'),
|
| 1297 |
+
Button('Send', cls=ButtonT.primary)),
|
| 1298 |
+
cls='space-y-4'))
|
| 1299 |
+
|
| 1300 |
+
@rt
|
| 1301 |
+
def index():
|
| 1302 |
+
return Title("Mail Example"),Container(
|
| 1303 |
+
Grid(Div(sidebar, cls='col-span-1'),
|
| 1304 |
+
Div(MailContent(), cls='col-span-2'),
|
| 1305 |
+
Div(MailDetailView(mail_data[0]), cls='col-span-2'),
|
| 1306 |
+
cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5,
|
| 1307 |
+
gap=0, cls='flex-1'),
|
| 1308 |
+
cls=('flex', ContainerT.xl))
|
| 1309 |
+
|
| 1310 |
+
serve()</doc><doc title="Tasks" desc="FrankenUI Tasks Example built with MonsterUI (original design by ShadCN)">"""FrankenUI Tasks Example built with MonsterUI (original design by ShadCN)"""
|
| 1311 |
+
|
| 1312 |
+
from fasthtml.common import *
|
| 1313 |
+
from monsterui.all import *
|
| 1314 |
+
from fasthtml.svg import *
|
| 1315 |
+
import json
|
| 1316 |
+
|
| 1317 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 1318 |
+
|
| 1319 |
+
def LAlignedCheckTxt(txt): return DivLAligned(UkIcon(icon='check'), P(txt, cls=TextPresets.muted_sm))
|
| 1320 |
+
|
| 1321 |
+
with open('data_/status_list.json', 'r') as f: data = json.load(f)
|
| 1322 |
+
with open('data_/statuses.json', 'r') as f: statuses = json.load(f)
|
| 1323 |
+
|
| 1324 |
+
def _create_tbl_data(d):
|
| 1325 |
+
return {'Done': d['selected'], 'Task': d['id'], 'Title': d['title'],
|
| 1326 |
+
'Status' : d['status'], 'Priority': d['priority'] }
|
| 1327 |
+
|
| 1328 |
+
data = [_create_tbl_data(d) for d in data]
|
| 1329 |
+
page_size = 15
|
| 1330 |
+
current_page = 0
|
| 1331 |
+
paginated_data = data[current_page*page_size:(current_page+1)*page_size]
|
| 1332 |
+
|
| 1333 |
+
priority_dd = [{'priority': "low", 'count': 36 }, {'priority': "medium", 'count': 33 }, {'priority': "high", 'count': 31 }]
|
| 1334 |
+
|
| 1335 |
+
status_dd = [{'status': "backlog", 'count': 21 },{'status': "todo", 'count': 21 },{'status': "progress", 'count': 20 },{'status': "done",'count': 19 },{'status': "cancelled", 'count': 19 }]
|
| 1336 |
+
|
| 1337 |
+
def create_hotkey_li(hotkey): return NavCloseLi(A(DivFullySpaced(hotkey[0], Span(hotkey[1], cls=TextPresets.muted_sm))))
|
| 1338 |
+
|
| 1339 |
+
hotkeys_a = (('Profile','⇧⌘P'),('Billing','⌘B'),('Settings','⌘S'),('New Team',''))
|
| 1340 |
+
hotkeys_b = (('Logout',''), )
|
| 1341 |
+
|
| 1342 |
+
avatar_opts = DropDownNavContainer(
|
| 1343 |
+
NavHeaderLi(P('sveltecult'),NavSubtitle('[email protected]')),
|
| 1344 |
+
NavDividerLi(),
|
| 1345 |
+
*map(create_hotkey_li, hotkeys_a),
|
| 1346 |
+
NavDividerLi(),
|
| 1347 |
+
*map(create_hotkey_li, hotkeys_b),)
|
| 1348 |
+
|
| 1349 |
+
def CreateTaskModal():
|
| 1350 |
+
return Modal(
|
| 1351 |
+
Div(cls='p-6')(
|
| 1352 |
+
ModalTitle('Create Task'),
|
| 1353 |
+
P('Fill out the information below to create a new task', cls=TextPresets.muted_sm),
|
| 1354 |
+
Br(),
|
| 1355 |
+
Form(cls='space-y-6')(
|
| 1356 |
+
Grid(Div(Select(*map(Option,('Documentation', 'Bug', 'Feature')), label='Task Type', id='task_type')),
|
| 1357 |
+
Div(Select(*map(Option,('In Progress', 'Backlog', 'Todo', 'Cancelled', 'Done')), label='Status', id='task_status')),
|
| 1358 |
+
Div(Select(*map(Option, ('Low', 'Medium', 'High')), label='Priority', id='task_priority'))),
|
| 1359 |
+
TextArea(label='Title', placeholder='Please describe the task that needs to be completed'),
|
| 1360 |
+
DivRAligned(
|
| 1361 |
+
ModalCloseButton('Cancel', cls=ButtonT.ghost),
|
| 1362 |
+
ModalCloseButton('Submit', cls=ButtonT.primary),
|
| 1363 |
+
cls='space-x-5'))),
|
| 1364 |
+
id='TaskForm')
|
| 1365 |
+
|
| 1366 |
+
page_heading = DivFullySpaced(cls='space-y-2')(
|
| 1367 |
+
Div(cls='space-y-2')(
|
| 1368 |
+
H2('Welcome back!'),P("Here's a list of your tasks for this month!", cls=TextPresets.muted_sm)),
|
| 1369 |
+
Div(DiceBearAvatar("sveltcult",8,8),avatar_opts))
|
| 1370 |
+
|
| 1371 |
+
table_controls =(Input(cls='w-[250px]',placeholder='Filter task'),
|
| 1372 |
+
Button("Status"),
|
| 1373 |
+
DropDownNavContainer(map(NavCloseLi,[A(DivFullySpaced(P(a['status']), P(a['count'])),cls='capitalize') for a in status_dd])),
|
| 1374 |
+
Button("Priority"),
|
| 1375 |
+
DropDownNavContainer(map(NavCloseLi,[A(DivFullySpaced(LAlignedCheckTxt(a['priority']), a['count']),cls='capitalize') for a in priority_dd])),
|
| 1376 |
+
Button("View"),
|
| 1377 |
+
DropDownNavContainer(map(NavCloseLi,[A(LAlignedCheckTxt(o)) for o in ['Title','Status','Priority']])),
|
| 1378 |
+
Button('Create Task',cls=(ButtonT.primary, TextPresets.bold_sm), data_uk_toggle="target: #TaskForm"))
|
| 1379 |
+
|
| 1380 |
+
def task_dropdown():
|
| 1381 |
+
return Div(Button(UkIcon('ellipsis')),
|
| 1382 |
+
DropDownNavContainer(
|
| 1383 |
+
map(NavCloseLi,[
|
| 1384 |
+
*map(A,('Edit', 'Make a copy', 'Favorite')),
|
| 1385 |
+
A(DivFullySpaced(*[P(o, cls=TextPresets.muted_sm) for o in ('Delete', '⌘⌫')]))])))
|
| 1386 |
+
def header_render(col):
|
| 1387 |
+
match col:
|
| 1388 |
+
case "Done": return Th(CheckboxX(), shrink=True)
|
| 1389 |
+
case 'Actions': return Th("", shrink=True)
|
| 1390 |
+
case _: return Th(col, expand=True)
|
| 1391 |
+
|
| 1392 |
+
def cell_render(col, val):
|
| 1393 |
+
def _Td(*args,cls='', **kwargs): return Td(*args, cls=f'p-2 {cls}',**kwargs)
|
| 1394 |
+
match col:
|
| 1395 |
+
case "Done": return _Td(shrink=True)(CheckboxX(selected=val))
|
| 1396 |
+
case "Task": return _Td(val, cls='uk-visible@s') # Hide on small screens
|
| 1397 |
+
case "Title": return _Td(val, cls='font-medium', expand=True)
|
| 1398 |
+
case "Status" | "Priority": return _Td(cls='uk-visible@m uk-text-nowrap capitalize')(Span(val))
|
| 1399 |
+
case "Actions": return _Td(task_dropdown(), shrink=True)
|
| 1400 |
+
case _: raise ValueError(f"Unknown column: {col}")
|
| 1401 |
+
|
| 1402 |
+
task_columns = ["Done", 'Task', 'Title', 'Status', 'Priority', 'Actions']
|
| 1403 |
+
|
| 1404 |
+
tasks_table = Div(cls='mt-4')(
|
| 1405 |
+
TableFromDicts(
|
| 1406 |
+
header_data=task_columns,
|
| 1407 |
+
body_data=paginated_data,
|
| 1408 |
+
body_cell_render=cell_render,
|
| 1409 |
+
header_cell_render=header_render,
|
| 1410 |
+
sortable=True,
|
| 1411 |
+
cls=(TableT.responsive, TableT.sm, TableT.divider)))
|
| 1412 |
+
|
| 1413 |
+
|
| 1414 |
+
def footer():
|
| 1415 |
+
total_pages = (len(data) + page_size - 1) // page_size
|
| 1416 |
+
return DivFullySpaced(
|
| 1417 |
+
Div('1 of 100 row(s) selected.', cls=TextPresets.muted_sm),
|
| 1418 |
+
DivLAligned(
|
| 1419 |
+
DivCentered(f'Page {current_page + 1} of {total_pages}', cls=TextT.sm),
|
| 1420 |
+
DivLAligned(*[UkIconLink(icon=i, button=True) for i in ('chevrons-left', 'chevron-left', 'chevron-right', 'chevrons-right')])))
|
| 1421 |
+
|
| 1422 |
+
tasks_ui = Div(DivFullySpaced(DivLAligned(table_controls), cls='mt-8'), tasks_table, footer())
|
| 1423 |
+
|
| 1424 |
+
@rt
|
| 1425 |
+
def index(): return Container(page_heading, tasks_ui, CreateTaskModal())
|
| 1426 |
+
|
| 1427 |
+
serve()</doc><doc title="Music" desc="FrankenUI Music Example build with MonsterUI (Original design by ShadCN)">"""FrankenUI Music Example build with MonsterUI (Original design by ShadCN)"""
|
| 1428 |
+
|
| 1429 |
+
from fasthtml.common import *
|
| 1430 |
+
|
| 1431 |
+
from monsterui.all import *
|
| 1432 |
+
|
| 1433 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 1434 |
+
|
| 1435 |
+
def MusicLi(t,hk=''): return Li(A(DivFullySpaced(t,P(hk,cls=TextPresets.muted_sm))))
|
| 1436 |
+
|
| 1437 |
+
music_items = [("About Music", "" ),
|
| 1438 |
+
("Preferences", "⌘" ),
|
| 1439 |
+
("Hide Music" , "⌘H" ),
|
| 1440 |
+
("Hide Others", "⇧⌘H"),
|
| 1441 |
+
("Quit Music" , "⌘Q" )]
|
| 1442 |
+
|
| 1443 |
+
file_dd_items = [("New", ""),
|
| 1444 |
+
("Open Stream URL", "⌘U"),
|
| 1445 |
+
("Close Window", "⌘W"),
|
| 1446 |
+
("Library", ""),
|
| 1447 |
+
("Import", "⌘O"),
|
| 1448 |
+
("Burn Playlist to Disc", ""),
|
| 1449 |
+
("Show in Finder", "⇧⌘R"),
|
| 1450 |
+
("Convert", ""),
|
| 1451 |
+
("Page Setup", "Print")]
|
| 1452 |
+
|
| 1453 |
+
edit_actions = [("Undo", "⌘Z"),
|
| 1454 |
+
("Redo", "⇧⌘Z"),
|
| 1455 |
+
("Cut", "⌘X"),
|
| 1456 |
+
("Copy", "⌘C"),
|
| 1457 |
+
("Paste", "⌘V"),
|
| 1458 |
+
("Select All", "⌘A"),
|
| 1459 |
+
("Deselect All", "⇧⌘A")]
|
| 1460 |
+
|
| 1461 |
+
view_dd_data = ["Show Playing Next", "Show Lyrics", "Show Status Bar", "Hide Sidebar", "Enter Full Screen"]
|
| 1462 |
+
|
| 1463 |
+
|
| 1464 |
+
music_headers = NavBar(
|
| 1465 |
+
Button("Music", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("Music"),NavContainer(map(lambda x: MusicLi(*x), music_items)))),
|
| 1466 |
+
Button("File", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("File"), NavContainer(map(lambda x: MusicLi(*x), file_dd_items)))),
|
| 1467 |
+
Button("Edit", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("Edit")),NavContainer(
|
| 1468 |
+
*map(lambda x: MusicLi(*x), edit_actions),
|
| 1469 |
+
Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))),
|
| 1470 |
+
Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe")))))),
|
| 1471 |
+
Button("View", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("View"),NavContainer(map(lambda x: MusicLi(x), view_dd_data)))),
|
| 1472 |
+
brand=DivLAligned(H2("Purrify"))
|
| 1473 |
+
)
|
| 1474 |
+
|
| 1475 |
+
|
| 1476 |
+
|
| 1477 |
+
|
| 1478 |
+
# music_headers = NavBarContainer(
|
| 1479 |
+
# NavBarLSide(
|
| 1480 |
+
# NavBarNav(
|
| 1481 |
+
# Li(A("Music"),NavBarNavContainer(map(lambda x: MusicLi(*x), music_items))),
|
| 1482 |
+
# Li(A("File"), NavBarNavContainer(map(lambda x: MusicLi(*x), file_dd_items))),
|
| 1483 |
+
# Li(A("Edit")),
|
| 1484 |
+
# NavBarNavContainer(
|
| 1485 |
+
# *map(lambda x: MusicLi(*x), edit_actions),
|
| 1486 |
+
# Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))),
|
| 1487 |
+
# Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe"))))),
|
| 1488 |
+
# Li(A("View"),
|
| 1489 |
+
# NavBarNavContainer(map(lambda x: MusicLi(x), view_dd_data))),
|
| 1490 |
+
# Li(A("Account"),
|
| 1491 |
+
# NavBarNavContainer(
|
| 1492 |
+
# NavHeaderLi("Switch Account"),
|
| 1493 |
+
# *map(MusicLi, ("Andy", "Benoit", "Luis", "Manage Family", "Add Account")))))))
|
| 1494 |
+
|
| 1495 |
+
|
| 1496 |
+
def Album(title,artist):
|
| 1497 |
+
img_url = 'https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp'
|
| 1498 |
+
return Div(
|
| 1499 |
+
Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=img_url)),
|
| 1500 |
+
Div(cls='space-y-1')(Strong(title),P(artist,cls=TextT.muted)))
|
| 1501 |
+
|
| 1502 |
+
listen_now_albums = (("Roar", "Catty Perry"), ("Feline on a Prayer", "Cat Jovi"),("Fur Elise", "Ludwig van Beethovpurr"),("Purrple Rain", "Prince's Cat"))
|
| 1503 |
+
|
| 1504 |
+
made_for_you_albums = [("Like a Feline", "Catdonna"),
|
| 1505 |
+
("Livin' La Vida Purrda", "Ricky Catin"),
|
| 1506 |
+
("Meow Meow Rocket", "Elton Cat"),
|
| 1507 |
+
("Rolling in the Purr", "Catdelle"),
|
| 1508 |
+
("Purrs of Silence", "Cat Garfunkel"),
|
| 1509 |
+
("Meow Me Maybe", "Carly Rae Purrsen"),]
|
| 1510 |
+
|
| 1511 |
+
music_content = (Div(H3("Listen Now"), cls="mt-6 space-y-1"),
|
| 1512 |
+
Subtitle("Top picks for you. Updated daily."),
|
| 1513 |
+
DividerLine(),
|
| 1514 |
+
Grid(*[Album(t,a) for t,a in listen_now_albums], cls='gap-8'),
|
| 1515 |
+
Div(H3("Made for You"), cls="mt-6 space-y-1"),
|
| 1516 |
+
Subtitle("Your personal playlists. Updated daily."),
|
| 1517 |
+
DividerLine(),
|
| 1518 |
+
Grid(*[Album(t,a) for t,a in made_for_you_albums], cols_xl=6))
|
| 1519 |
+
|
| 1520 |
+
tabs = TabContainer(
|
| 1521 |
+
Li(A('Music', href='#'), cls='uk-active'),
|
| 1522 |
+
Li(A('Podcasts', href='#')),
|
| 1523 |
+
Li(A('Live', cls='opacity-50'), cls='uk-disabled'),
|
| 1524 |
+
uk_switcher='connect: #component-nav; animation: uk-animation-fade',
|
| 1525 |
+
alt=True)
|
| 1526 |
+
|
| 1527 |
+
def podcast_tab():
|
| 1528 |
+
return Div(
|
| 1529 |
+
Div(cls='space-y-3 mt-6')(
|
| 1530 |
+
H3("New Episodes"),
|
| 1531 |
+
Subtitle("Your favorite podcasts. Updated daily.")),
|
| 1532 |
+
Div(cls="uk-placeholder flex h-[450px] items-center justify-center rounded-md mt-4",uk_placeholder=True)(
|
| 1533 |
+
DivVStacked(cls="space-y-6")(
|
| 1534 |
+
UkIcon("microphone", 3),
|
| 1535 |
+
H4("No episodes added"),
|
| 1536 |
+
Subtitle("You have not added any podcasts. Add one below."),
|
| 1537 |
+
Button("Add Podcast", cls=ButtonT.primary))))
|
| 1538 |
+
|
| 1539 |
+
discoved_data = [("play-circle","Listen Now"), ("binoculars", "Browse"), ("rss","Radio")]
|
| 1540 |
+
library_data = [("play-circle", "Playlists"), ("music", "Songs"), ("user", "Made for You"), ("users", "Artists"), ("bookmark", "Albums")]
|
| 1541 |
+
playlists_data = [("library","Recently Added"), ("library","Recently Played")]
|
| 1542 |
+
|
| 1543 |
+
def MusicSidebarLi(icon, text): return Li(A(DivLAligned(UkIcon(icon), P(text))))
|
| 1544 |
+
sidebar = NavContainer(
|
| 1545 |
+
NavHeaderLi(H3("Discover")), *[MusicSidebarLi(*o) for o in discoved_data],
|
| 1546 |
+
NavHeaderLi(H3("Library")), *[MusicSidebarLi(*o) for o in library_data],
|
| 1547 |
+
NavHeaderLi(H3("Playlists")),*[MusicSidebarLi(*o) for o in playlists_data],
|
| 1548 |
+
cls=(NavT.primary,'space-y-3','pl-8'))
|
| 1549 |
+
|
| 1550 |
+
@rt
|
| 1551 |
+
def index():
|
| 1552 |
+
return Title("Music Example"),Container(music_headers, DividerSplit(),
|
| 1553 |
+
Grid(sidebar,
|
| 1554 |
+
Div(cls="col-span-4 border-l border-border")(
|
| 1555 |
+
Div(cls="px-8 py-6")(
|
| 1556 |
+
DivFullySpaced(
|
| 1557 |
+
Div(cls="max-w-80")(tabs),
|
| 1558 |
+
Button(cls=ButtonT.primary)(DivLAligned(UkIcon('circle-plus')),Div("Add music"))),
|
| 1559 |
+
Ul(id="component-nav", cls="uk-switcher")(
|
| 1560 |
+
Li(*music_content),
|
| 1561 |
+
Li(podcast_tab())))),
|
| 1562 |
+
cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5))
|
| 1563 |
+
|
| 1564 |
+
serve()</doc><doc title="Auth" desc="FrankenUI Auth Example built with MonsterUI (original design by ShadCN)">"""FrankenUI Auth Example built with MonsterUI (original design by ShadCN)"""
|
| 1565 |
+
|
| 1566 |
+
from fasthtml.common import *
|
| 1567 |
+
from monsterui.all import *
|
| 1568 |
+
from fasthtml.svg import *
|
| 1569 |
+
|
| 1570 |
+
app, rt = fast_app(hdrs=Theme.blue.headers())
|
| 1571 |
+
|
| 1572 |
+
@rt
|
| 1573 |
+
def index():
|
| 1574 |
+
left = Div(cls="col-span-1 hidden flex-col justify-between bg-zinc-900 p-8 text-white lg:flex")(
|
| 1575 |
+
Div(cls=(TextT.bold))("Acme Inc"),
|
| 1576 |
+
Blockquote(cls="space-y-2")(
|
| 1577 |
+
P(cls=TextT.lg)('"This library has saved me countless hours of work and helped me deliver stunning designs to my clients faster than ever before."'),
|
| 1578 |
+
Footer(cls=TextT.sm)("Sofia Davis")))
|
| 1579 |
+
|
| 1580 |
+
right = Div(cls="col-span-2 flex flex-col p-8 lg:col-span-1")(
|
| 1581 |
+
DivRAligned(Button("Login", cls=ButtonT.ghost)),
|
| 1582 |
+
DivCentered(cls='flex-1')(
|
| 1583 |
+
Container(
|
| 1584 |
+
DivVStacked(
|
| 1585 |
+
H3("Create an account"),
|
| 1586 |
+
Small("Enter your email below to create your account", cls=TextT.muted)),
|
| 1587 |
+
Form(
|
| 1588 |
+
Input(placeholder="[email protected]"),
|
| 1589 |
+
Button(Span(cls="mr-2", uk_spinner="ratio: 0.54"), "Sign in with Email", cls=(ButtonT.primary, "w-full"), disabled=True),
|
| 1590 |
+
DividerSplit(Small("Or continue with"),cls=TextT.muted),
|
| 1591 |
+
Button(UkIcon('github',cls='mr-2'), "Github", cls=(ButtonT.default, "w-full")),
|
| 1592 |
+
cls='space-y-6'),
|
| 1593 |
+
DivVStacked(Small(
|
| 1594 |
+
"By clicking continue, you agree to our ",
|
| 1595 |
+
A(cls=AT.muted, href="#demo")("Terms of Service")," and ",
|
| 1596 |
+
A(cls=AT.muted, href="#demo")("Privacy Policy"),".",
|
| 1597 |
+
cls=(TextT.muted,"text-center"))),
|
| 1598 |
+
cls="space-y-6")))
|
| 1599 |
+
|
| 1600 |
+
return Title("Auth Example"),Grid(left,right,cols=2, gap=0,cls='h-screen')
|
| 1601 |
+
|
| 1602 |
+
|
| 1603 |
+
serve()</doc></examples></project>
|
MonsterUI/docs/llms.txt
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MonsterUI Documentation
|
| 2 |
+
|
| 3 |
+
> MonsterUI is a python library which brings styling to python for FastHTML apps.
|
| 4 |
+
|
| 5 |
+
## API Reference
|
| 6 |
+
- [API List](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/apilist.txt): Complete API Reference
|
| 7 |
+
|
| 8 |
+
## Examples
|
| 9 |
+
- [Cards](https://monsterui.answer.ai/cards/md): FrankenUI Cards Example built with MonsterUI (original design by ShadCN)
|
| 10 |
+
- [Dashboard](https://monsterui.answer.ai/dashboard/md): FrankenUI Dashboard Example built with MonsterUI (original design by ShadCN)
|
| 11 |
+
- [Forms](https://monsterui.answer.ai/forms/md): FrankenUI Forms Example built with MonsterUI (original design by ShadCN)
|
| 12 |
+
- [Scrollspy](https://monsterui.answer.ai/scrollspy/md): MonsterUI Scrollspy Example application
|
| 13 |
+
- [Ticket](https://monsterui.answer.ai/ticket/md): MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components
|
| 14 |
+
- [Playground](https://monsterui.answer.ai/playground/md): FrankenUI Playground Example built with MonsterUI (original design by ShadCN)
|
| 15 |
+
- [Mail](https://monsterui.answer.ai/mail/md): FrankenUI Mail Example built with MonsterUI (original design by ShadCN)
|
| 16 |
+
- [Tasks](https://monsterui.answer.ai/tasks/md): FrankenUI Tasks Example built with MonsterUI (original design by ShadCN)
|
| 17 |
+
- [Music](https://monsterui.answer.ai/music/md): FrankenUI Music Example build with MonsterUI (Original design by ShadCN)
|
| 18 |
+
- [Auth](https://monsterui.answer.ai/auth/md): FrankenUI Auth Example built with MonsterUI (original design by ShadCN)
|
| 19 |
+
|
| 20 |
+
## Optional
|
| 21 |
+
- [Accordion | Link](https://monsterui.answer.ai/api_ref/docs_accordion_link/md): Accordion API Reference
|
| 22 |
+
- [Button | Link](https://monsterui.answer.ai/api_ref/docs_button_link/md): Buttons & Links API Reference
|
| 23 |
+
- [Cards](https://monsterui.answer.ai/api_ref/docs_cards/md): Cards API Reference
|
| 24 |
+
- [Charts](https://monsterui.answer.ai/api_ref/docs_charts/md): Charts API Reference
|
| 25 |
+
- [Containers](https://monsterui.answer.ai/api_ref/docs_containers/md): Articles, Containers & Sections API Reference
|
| 26 |
+
- [Dividers](https://monsterui.answer.ai/api_ref/docs_dividers/md): Dividers API Reference
|
| 27 |
+
- [Forms](https://monsterui.answer.ai/api_ref/docs_forms/md): Forms and User Inputs API Reference
|
| 28 |
+
- [Html](https://monsterui.answer.ai/api_ref/docs_html/md): HTML Styling API Reference
|
| 29 |
+
- [Icons | Images](https://monsterui.answer.ai/api_ref/docs_icons_images/md): Icons & Images API Reference
|
| 30 |
+
- [Layout](https://monsterui.answer.ai/api_ref/docs_layout/md): Layout (Flex and Grid) API Reference
|
| 31 |
+
- [Lightbox](https://monsterui.answer.ai/api_ref/docs_lightbox/md): Lightbox API Reference
|
| 32 |
+
- [Lists](https://monsterui.answer.ai/api_ref/docs_lists/md): Lists API Reference
|
| 33 |
+
- [Loading](https://monsterui.answer.ai/api_ref/docs_loading/md): Loading Indicators API Reference
|
| 34 |
+
- [Markdown](https://monsterui.answer.ai/api_ref/docs_markdown/md): Markdown + automated HTML styling API Reference
|
| 35 |
+
- [Modals](https://monsterui.answer.ai/api_ref/docs_modals/md): Modals API Reference
|
| 36 |
+
- [Navigation](https://monsterui.answer.ai/api_ref/docs_navigation/md): Navigation (Nav, NavBar, Tabs, etc.) API Reference
|
| 37 |
+
- [Notifications](https://monsterui.answer.ai/api_ref/docs_notifications/md): Alerts & Toasts API Reference
|
| 38 |
+
- [Sliders](https://monsterui.answer.ai/api_ref/docs_sliders/md): Carousel Sliders API Reference
|
| 39 |
+
- [Steps](https://monsterui.answer.ai/api_ref/docs_steps/md): Steps API Reference
|
| 40 |
+
- [Tables](https://monsterui.answer.ai/api_ref/docs_tables/md): Tables API Reference
|
| 41 |
+
- [Theme | Headers](https://monsterui.answer.ai/api_ref/docs_theme_headers/md): Theme and Headers API Reference
|
| 42 |
+
- [Typography](https://monsterui.answer.ai/api_ref/docs_typography/md): Typography API Reference
|
| 43 |
+
- [Layout](https://monsterui.answer.ai/tutorial_layout/md): MonsterUI Page Layout Guide
|
| 44 |
+
- [Spacing](https://monsterui.answer.ai/tutorial_spacing/md): Padding & Margin & Spacing, Oh my! (MonsterUI Spacing Guide)
|
MonsterUI/docs/main.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fasthtml.common import *
|
| 2 |
+
from functools import partial
|
| 3 |
+
from monsterui.all import *
|
| 4 |
+
from fasthtml.components import Uk_theme_switcher
|
| 5 |
+
from utils import render_nb
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from toolslm.download import read_html,html2md
|
| 8 |
+
from starlette.responses import PlainTextResponse
|
| 9 |
+
import httpx
|
| 10 |
+
|
| 11 |
+
def _not_found(req, exc):
|
| 12 |
+
_path = req.url.path.rstrip('/')
|
| 13 |
+
if _path.endswith('.md') or _path.endswith('.rmd'):
|
| 14 |
+
url = f'https://monsterui.answer.ai{_path[:-3].rstrip("/").rstrip(".")}'.rstrip("/").rstrip(".")
|
| 15 |
+
try:
|
| 16 |
+
r = httpx.head(url, follow_redirects=True, timeout=1.0)
|
| 17 |
+
if r.status_code < 400: # Accept 2xx and 3xx status codes
|
| 18 |
+
if _path.endswith('.rmd') or _path.endswith('/rmd'): return Container(render_md(read_html(url, sel='#content')))
|
| 19 |
+
elif _path.endswith('.md') or _path.endswith('/md'): return PlainTextResponse(read_html(url, sel='#content'))
|
| 20 |
+
except (httpx.TimeoutException, httpx.NetworkError): pass
|
| 21 |
+
return _create_page(
|
| 22 |
+
Container(Card(CardBody(H1("404 - Page Not Found"), P("The page you're looking for doesn't exist.")))),
|
| 23 |
+
req,
|
| 24 |
+
None)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
app,rt = fast_app(exception_handlers={404:_not_found}, pico=False,
|
| 28 |
+
hdrs=(*Theme.blue.headers(highlightjs=True,apex_charts=True), Link(rel="icon", type="image/x-icon", href="/favicon.ico"),
|
| 29 |
+
Link(rel="stylesheet", href="/custom_theme.css", type="text/css")),
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
def is_htmx(request=None):
|
| 33 |
+
"Check if the request is an HTMX request"
|
| 34 |
+
return request and 'hx-request' in request.headers
|
| 35 |
+
|
| 36 |
+
def _create_page(content, # The content to display (without the layout/sidebar)
|
| 37 |
+
request, # Request object to determine if HTMX request
|
| 38 |
+
sidebar_section, # The open section on the sidebar
|
| 39 |
+
):
|
| 40 |
+
"Makes page load sidebar if direct request, otherwise loads content only via HTMX"
|
| 41 |
+
if is_htmx(request): return content
|
| 42 |
+
else: return with_layout(sidebar_section, content)
|
| 43 |
+
|
| 44 |
+
def with_layout(sidebar_section, content):
|
| 45 |
+
"Puts the sidebar and content into a layout"
|
| 46 |
+
return Title(f"MonsterUI"), Div(cls="flex flex-col md:flex-row w-full")(
|
| 47 |
+
Button(UkIcon("menu",50,50,cls='mt-4'), cls="md:hidden mb-4", data_uk_toggle="target: #mobile-sidebar"),
|
| 48 |
+
Div(sidebar(sidebar_section), id='mobile-sidebar', hidden=True),
|
| 49 |
+
Div(cls="md:flex w-full")(
|
| 50 |
+
Div(sidebar(sidebar_section), cls="hidden md:block w-1/5 max-w-52"),
|
| 51 |
+
Div(content, cls='md:w-4/5 w-full mr-5', id="content", )))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
###
|
| 55 |
+
# Build the Example Pages
|
| 56 |
+
###
|
| 57 |
+
|
| 58 |
+
from examples.tasks import index as tasks_homepage
|
| 59 |
+
from examples.cards import index as cards_homepage
|
| 60 |
+
from examples.dashboard import index as dashboard_homepage
|
| 61 |
+
from examples.forms import index as forms_homepage
|
| 62 |
+
from examples.music import index as music_homepage
|
| 63 |
+
from examples.auth import index as auth_homepage
|
| 64 |
+
from examples.playground import index as playground_homepage
|
| 65 |
+
from examples.mail import index as mail_homepage
|
| 66 |
+
from examples.scrollspy import index as scrollspy_homepage
|
| 67 |
+
from examples.ticket import index as ticket_homepage
|
| 68 |
+
def _example_route(name, homepage, o:str, request=None):
|
| 69 |
+
match o:
|
| 70 |
+
case 'code' | 'rmd': return Div(render_md(f'''```python\n\n{open(f'examples/{name}.py').read()}\n\n```'''))
|
| 71 |
+
case 'md': return PlainTextResponse(open(f'examples/{name}.py').read())
|
| 72 |
+
case _: return _create_example_page(homepage, request)
|
| 73 |
+
|
| 74 |
+
_create_example_page = partial(_create_page, sidebar_section='Examples')
|
| 75 |
+
|
| 76 |
+
@rt('/scrollspy')
|
| 77 |
+
@rt('/scrollspy/{o}')
|
| 78 |
+
def scrollspy(o:str='', request=None): return _example_route('scrollspy', Div(DivRAligned(A("See Code",href='/scrollspy/code',cls='m-4 uk-btn'+ButtonT.default)),scrollspy_homepage()), o, request)
|
| 79 |
+
|
| 80 |
+
@rt('/ticket')
|
| 81 |
+
@rt('/ticket/{o}')
|
| 82 |
+
def ticket(o:str='', request=None): return _example_route('ticket', Div(DivRAligned(A("See Code",href='/ticket/code',cls='m-4 uk-btn'+ButtonT.default)),ticket_homepage()), o, request)
|
| 83 |
+
|
| 84 |
+
@rt('/tasks')
|
| 85 |
+
@rt('/tasks/{o}')
|
| 86 |
+
def tasks(o:str='', request=None): return _example_route('tasks', Div(DivRAligned(A("See Code",href='/tasks/code',cls='m-4 uk-btn'+ButtonT.default)),tasks_homepage()), o, request)
|
| 87 |
+
|
| 88 |
+
@rt('/cards')
|
| 89 |
+
@rt('/cards/{o}')
|
| 90 |
+
def cards(o:str, request=None): return _example_route('cards', Div(DivRAligned(A("See Code",href='/cards/code',cls='m-4 uk-btn'+ButtonT.default)),cards_homepage()), o, request)
|
| 91 |
+
|
| 92 |
+
@rt('/dashboard')
|
| 93 |
+
@rt('/dashboard/{o}')
|
| 94 |
+
def dashboard(o:str, request=None): return _example_route('dashboard', Div(DivRAligned(A("See Code",href='/dashboard/code',cls='m-4 uk-btn'+ButtonT.default)),dashboard_homepage()), o, request)
|
| 95 |
+
|
| 96 |
+
@rt('/forms')
|
| 97 |
+
@rt('/forms/{o}')
|
| 98 |
+
def forms(o:str, request=None): return _example_route('forms', Div(DivRAligned(A("See Code",href='/forms/code',cls='m-4 uk-btn'+ButtonT.default)),forms_homepage()), o, request)
|
| 99 |
+
|
| 100 |
+
@rt('/music')
|
| 101 |
+
@rt('/music/{o}')
|
| 102 |
+
def music(o:str, request=None): return _example_route('music', Div(DivRAligned(A("See Code",href='/music/code',cls='m-4 uk-btn'+ButtonT.default)),music_homepage()), o, request)
|
| 103 |
+
|
| 104 |
+
@rt('/auth')
|
| 105 |
+
@rt('/auth/{o}')
|
| 106 |
+
def auth(o:str, request=None): return _example_route('auth', Div(DivRAligned(A("See Code",href='/auth/code',cls='m-4 uk-btn'+ButtonT.default)),auth_homepage()), o, request)
|
| 107 |
+
|
| 108 |
+
@rt('/playground')
|
| 109 |
+
@rt('/playground/{o}')
|
| 110 |
+
def playground(o:str, request=None): return _example_route('playground', Div(DivRAligned(A("See Code",href='/playground/code',cls='m-4 uk-btn'+ButtonT.default)),playground_homepage()), o, request)
|
| 111 |
+
|
| 112 |
+
@rt('/mail')
|
| 113 |
+
@rt('/mail/{o}')
|
| 114 |
+
def mail(o:str, request=None): return _example_route('mail', Div(DivRAligned(A("See Code",href='/mail/code',cls='m-4 uk-btn'+ButtonT.default)),mail_homepage()), o, request)
|
| 115 |
+
|
| 116 |
+
###
|
| 117 |
+
# Build the API Reference Pages
|
| 118 |
+
###
|
| 119 |
+
|
| 120 |
+
import api_reference.api_reference as api_reference
|
| 121 |
+
def fname2title(ref_fn_name): return ref_fn_name[5:].replace('_',' | ').title()
|
| 122 |
+
|
| 123 |
+
reference_fns = L([o for o in dir(api_reference) if o.startswith('docs_')])
|
| 124 |
+
@rt('/api_ref/{o}')
|
| 125 |
+
def api_route(request, o:str):
|
| 126 |
+
if o not in reference_fns: raise HTTPException(404)
|
| 127 |
+
content = getattr(api_reference, o)()
|
| 128 |
+
return _create_page(Container(content), request=request, sidebar_section='API Reference')
|
| 129 |
+
|
| 130 |
+
@rt('/api_ref/{o}/md')
|
| 131 |
+
def api_route_md(request, o:str):
|
| 132 |
+
if o not in reference_fns: raise HTTPException(404)
|
| 133 |
+
content = getattr(api_reference, o)()
|
| 134 |
+
return PlainTextResponse(html2md(to_xml(content)))
|
| 135 |
+
|
| 136 |
+
@rt('/api_ref/{o}rmd')
|
| 137 |
+
def api_route_md(request, o:str):
|
| 138 |
+
if o not in reference_fns: raise HTTPException(404)
|
| 139 |
+
content = getattr(api_reference, o)()
|
| 140 |
+
return Div(render_md(html2md(to_xml(content))))
|
| 141 |
+
|
| 142 |
+
###
|
| 143 |
+
# Build the Guides Pages
|
| 144 |
+
###
|
| 145 |
+
@rt('/tutorial_spacing')
|
| 146 |
+
@rt('/tutorial_spacing/{o}')
|
| 147 |
+
def tutorial_spacing(o:str='', request=None):
|
| 148 |
+
if o=='md': return PlainTextResponse(read_html(f'https://monsterui.answer.ai/tutorial_spacing', sel='#content'))
|
| 149 |
+
if o=='rmd': return Div(render_md(read_html(f'https://monsterui.answer.ai/tutorial_spacing', sel='#content')))
|
| 150 |
+
return _create_page(render_nb('guides/Spacing.ipynb'), request, 'Guides')
|
| 151 |
+
@rt('/tutorial_layout')
|
| 152 |
+
@rt('/tutorial_layout/{o}')
|
| 153 |
+
def tutorial_layout(o:str='', request=None):
|
| 154 |
+
if o=='md': return PlainTextResponse(read_html(f'https://monsterui.answer.ai/tutorial_layout', sel='#content'))
|
| 155 |
+
if o=='rmd': return Div(render_md(read_html(f'https://monsterui.answer.ai/tutorial_layout', sel='#content')))
|
| 156 |
+
return _create_page(render_nb('guides/Layout.ipynb'), request, 'Guides',)
|
| 157 |
+
|
| 158 |
+
###
|
| 159 |
+
# Build the Theme Switcher Page
|
| 160 |
+
###
|
| 161 |
+
|
| 162 |
+
@rt
|
| 163 |
+
def theme_switcher(request):
|
| 164 |
+
return _create_page(Div(ThemePicker(custom_themes=[("Grass", "#10b981")]),cls="p-12"), request, None)
|
| 165 |
+
|
| 166 |
+
###
|
| 167 |
+
# Build the Getting Started Pages
|
| 168 |
+
###
|
| 169 |
+
|
| 170 |
+
gs_path = Path('getting_started')
|
| 171 |
+
|
| 172 |
+
@rt('/tutorial_app')
|
| 173 |
+
@rt('/tutorial_app/{o}')
|
| 174 |
+
def tutorial_app(o:str='', request=None):
|
| 175 |
+
pass
|
| 176 |
+
if o=='md': return PlainTextResponse(read_html(f'https://monsterui.answer.ai/tutorial_app', sel='#content'))
|
| 177 |
+
if o=='rmd': return Div(render_md(read_html(f'https://monsterui.answer.ai/tutorial_app', sel='#content')))
|
| 178 |
+
app_code = open(gs_path/'app_product_gallery.py').read()
|
| 179 |
+
app_rendered = Div(Pre(Code(app_code)))
|
| 180 |
+
content = Container(cls='space-y-4')(
|
| 181 |
+
H1("Tutorial App"),
|
| 182 |
+
render_md("""This is a heavily commented example of a product gallery app built with MonsterUI for those that like to learn by example. \
|
| 183 |
+
This tutorial app assumes you have some familiarity with fasthtml apps already, so focuses on what MonsterUI adds on top of fasthtml.
|
| 184 |
+
To make the most out of this tutorial, you should follow these steps:"""),
|
| 185 |
+
Ol(
|
| 186 |
+
Li("Briefly read through this to get an overview of what is happening, without focusing on any details"),
|
| 187 |
+
Li("Install fasthtml and MonsterUI"),
|
| 188 |
+
Li("Copy the code into your own project locally and run it using `python app.py`"),
|
| 189 |
+
Li("Go through the code in detail to understand how it works by experimenting with changing things"),
|
| 190 |
+
cls=ListT.decimal+TextT.lg
|
| 191 |
+
),
|
| 192 |
+
render_md("""> Tip: Try adding `import fasthtml.common as fh`, so you can replace things with the base fasthtml components to see what happens!
|
| 193 |
+
For example, try replacing `H4` with `fh.H4` or `Button` with `fh.Button`."""),
|
| 194 |
+
Divider(),
|
| 195 |
+
app_rendered)
|
| 196 |
+
return _create_page(content, request, 'Getting Started')
|
| 197 |
+
|
| 198 |
+
@rt('/')
|
| 199 |
+
@rt('/{o}')
|
| 200 |
+
def index(o:str='', request=None):
|
| 201 |
+
if o=='md': return PlainTextResponse(read_html(f'https://monsterui.answer.ai/getting_started', sel='#content'))
|
| 202 |
+
if o=='rmd': return Div(render_md(read_html(f'https://monsterui.answer.ai/getting_started', sel='#content')))
|
| 203 |
+
content = Container(render_md(open(gs_path/'GettingStarted.md').read()))
|
| 204 |
+
return _create_page(content, request, 'Getting Started')
|
| 205 |
+
|
| 206 |
+
###
|
| 207 |
+
# Build the Sidebar
|
| 208 |
+
###
|
| 209 |
+
|
| 210 |
+
def sidebar(open_section):
|
| 211 |
+
def create_li(title, href):
|
| 212 |
+
return Li(A(title,hx_target="#content", hx_get=href, hx_push_url='true'))
|
| 213 |
+
|
| 214 |
+
return NavContainer(
|
| 215 |
+
NavParentLi(
|
| 216 |
+
A(DivFullySpaced("Getting Started", )),
|
| 217 |
+
NavContainer(create_li("Getting Started", index),
|
| 218 |
+
create_li("Tutorial App", tutorial_app),
|
| 219 |
+
parent=False),
|
| 220 |
+
cls='uk-open' if open_section=='Getting Started' else ''
|
| 221 |
+
),
|
| 222 |
+
NavParentLi(
|
| 223 |
+
A(DivFullySpaced("API Reference", )),
|
| 224 |
+
NavContainer(
|
| 225 |
+
*[create_li(fname2title(o), f"/api_ref/{o}") for o in reference_fns],
|
| 226 |
+
parent=False,
|
| 227 |
+
),
|
| 228 |
+
cls='uk-open' if open_section=='API Reference' else ''
|
| 229 |
+
),
|
| 230 |
+
NavParentLi(
|
| 231 |
+
A(DivFullySpaced('Guides', )),
|
| 232 |
+
NavContainer(
|
| 233 |
+
*[create_li(title, href) for title, href in [
|
| 234 |
+
('Spacing', tutorial_spacing),
|
| 235 |
+
('Layout', tutorial_layout),
|
| 236 |
+
]],
|
| 237 |
+
parent=False
|
| 238 |
+
),
|
| 239 |
+
cls='uk-open' if open_section=='Guides' else ''
|
| 240 |
+
),
|
| 241 |
+
|
| 242 |
+
NavParentLi(
|
| 243 |
+
A(DivFullySpaced('Examples', )),
|
| 244 |
+
NavContainer(
|
| 245 |
+
*[create_li(title, href) for title, href in [
|
| 246 |
+
('Task', '/tasks/'),
|
| 247 |
+
('Card', '/cards/'),
|
| 248 |
+
('Dashboard', '/dashboard/'),
|
| 249 |
+
('Form', '/forms/'),
|
| 250 |
+
('Music', '/music/'),
|
| 251 |
+
('Auth', '/auth/'),
|
| 252 |
+
('Playground', '/playground/'),
|
| 253 |
+
('Mail', '/mail/'),
|
| 254 |
+
('Ticket', '/ticket/'),
|
| 255 |
+
('Scrollspy', '/scrollspy/'),
|
| 256 |
+
]],
|
| 257 |
+
parent=False
|
| 258 |
+
),
|
| 259 |
+
cls='uk-open' if open_section=='Examples' else ''
|
| 260 |
+
),
|
| 261 |
+
create_li("Theme", theme_switcher),
|
| 262 |
+
uk_nav=True,
|
| 263 |
+
cls=(NavT.primary, "space-y-4 p-4 w-full md:w-full")
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
serve()
|
MonsterUI/docs/requirements.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
toolslm
|
| 2 |
+
python-fasthtml
|
| 3 |
+
pandas
|
| 4 |
+
plotly
|
| 5 |
+
monsterui @ git+https://github.com/AnswerDotAI/monsterui
|
| 6 |
+
fastcore
|
| 7 |
+
fh_matplotlib
|
| 8 |
+
numpy
|
| 9 |
+
matplotlib
|
| 10 |
+
pysymbol_llm @ git+https://github.com/AnswerDotAI/pysymbol-llm.git
|
| 11 |
+
mistletoe
|
| 12 |
+
lxml
|
| 13 |
+
html2text
|
MonsterUI/docs/updatellms.sh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
./createllms.sh
|
| 3 |
+
pysym2md --output_file apilist.txt monsterui
|
| 4 |
+
llms_txt2ctx llms.txt >llms-ctx.txt
|
| 5 |
+
llms_txt2ctx llms.txt --optional True > llms-ctx-full.txt
|
MonsterUI/docs/utils.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Utilities for building the docs page that don't belong anywhere else"""
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
__all__ = ['hjs', 'HShow', 'create_server']
|
| 5 |
+
|
| 6 |
+
from fasthtml.common import *
|
| 7 |
+
from monsterui.all import *
|
| 8 |
+
from fasthtml.jupyter import *
|
| 9 |
+
from collections.abc import Callable
|
| 10 |
+
import inspect
|
| 11 |
+
import ast
|
| 12 |
+
def get_last_statement(code): return ast.unparse(ast.parse(code).body[-1])
|
| 13 |
+
import json
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def create_flippable_card(content, source_code, extra_cls=None):
|
| 18 |
+
"Creates a card that flips between content and source code"
|
| 19 |
+
_id = 'f'+str(unqid())
|
| 20 |
+
_card = Card(
|
| 21 |
+
Button(
|
| 22 |
+
DivFullySpaced(UkIcon('corner-down-right', 20, 20, 3),"See Source"),
|
| 23 |
+
data_uk_toggle=f"target: #{_id}", id=_id, cls=ButtonT.primary),
|
| 24 |
+
Button(
|
| 25 |
+
DivFullySpaced(UkIcon('corner-down-right', 20, 20, 3),"See Output"),
|
| 26 |
+
data_uk_toggle=f"target: #{_id}", id=_id, cls=ButtonT.primary, hidden=True),
|
| 27 |
+
Div(content, id=_id),
|
| 28 |
+
Div(Pre(Code(source_code, cls="hljs language-python")), id=_id, hidden=True),
|
| 29 |
+
cls='my-8')
|
| 30 |
+
return Div(_card, cls=extra_cls) if extra_cls else _card
|
| 31 |
+
|
| 32 |
+
def fn2code_string(fn: Callable) -> tuple: return fn(), inspect.getsource(fn)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def render_nb(path):
|
| 36 |
+
"Renders a Jupyter notebook with markdown cells and flippable code cards"
|
| 37 |
+
namespace = globals().copy()
|
| 38 |
+
# Read and parse the notebook
|
| 39 |
+
nb_content = json.loads(Path(path).read_text())
|
| 40 |
+
cells = nb_content['cells']
|
| 41 |
+
|
| 42 |
+
# Convert cells to appropriate HTML elements
|
| 43 |
+
rendered_cells = []
|
| 44 |
+
for cell in cells:
|
| 45 |
+
if cell['cell_type'] == 'markdown':
|
| 46 |
+
# Combine all markdown lines and render
|
| 47 |
+
md_content = ''.join(cell['source'])
|
| 48 |
+
rendered_cells.append(render_md(md_content))
|
| 49 |
+
elif cell['cell_type'] == 'code':
|
| 50 |
+
# Skip empty code cells
|
| 51 |
+
if not ''.join(cell['source']).strip(): continue
|
| 52 |
+
# Create flippable card for code
|
| 53 |
+
code_content = ''.join(cell['source'])
|
| 54 |
+
exec(code_content, namespace)
|
| 55 |
+
result = eval(get_last_statement(code_content), namespace)
|
| 56 |
+
|
| 57 |
+
rendered_cells.append(create_flippable_card(result, code_content))
|
| 58 |
+
|
| 59 |
+
# Return all cells wrapped in a container with vertical spacing
|
| 60 |
+
return Container(cls='space-y-4')(*rendered_cells)
|