feat: initial project setup with implementation plan
- Add PLAN.md with 40-step checklist across 10 phases - Add CLAUDE.md with project-specific instructions - Set up nix flake with FastHTML/MonsterUI dependencies - Create Python package skeleton (src/animaltrack) - Vendor FastHTML and MonsterUI documentation - Add Docker build configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
150
docs/vendor/monsterui/examples/forms.md
vendored
Normal file
150
docs/vendor/monsterui/examples/forms.md
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
"""FrankenUI Forms Example built with MonsterUI (original design by ShadCN)"""
|
||||
|
||||
|
||||
from fasthtml.common import *
|
||||
from monsterui.all import *
|
||||
from fasthtml.svg import *
|
||||
|
||||
app, rt = fast_app(hdrs=Theme.blue.headers())
|
||||
|
||||
def HelpText(c): return P(c,cls=TextPresets.muted_sm)
|
||||
|
||||
def heading():
|
||||
return Div(cls="space-y-5")(
|
||||
H2("Settings"),
|
||||
Subtitle("Manage your account settings and set e-mail preferences."),
|
||||
DividerSplit())
|
||||
|
||||
|
||||
sidebar = NavContainer(
|
||||
*map(lambda x: Li(A(x)), ("Profile", "Account", "Appearance", "Notifications", "Display")),
|
||||
uk_switcher="connect: #component-nav; animation: uk-animation-fade",
|
||||
cls=(NavT.secondary,"space-y-4 p-4 w-1/5"))
|
||||
|
||||
|
||||
def FormSectionDiv(*c, cls='space-y-2', **kwargs): return Div(*c, cls=cls, **kwargs)
|
||||
|
||||
def FormLayout(title, subtitle, *content, cls='space-y-3 mt-4'): return Container(Div(H3(title), Subtitle(subtitle), DividerLine(), Form(*content, cls=cls)))
|
||||
|
||||
def profile_form():
|
||||
content = (FormSectionDiv(
|
||||
LabelInput("Username", placeholder='sveltecult', id='username'),
|
||||
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.")),
|
||||
FormSectionDiv(
|
||||
LabelSelect(
|
||||
Option("Select a verified email to display", value="", selected=True, disabled=True),
|
||||
*[Option(o, value=o) for o in ('m@example.com', 'm@yahoo.com', 'm@cloud.com')],
|
||||
label="Email", id="email"),
|
||||
HelpText("You can manage verified email addresses in your email settings.")),
|
||||
FormSectionDiv(
|
||||
LabelTextArea("Bio", id="bio", placeholder="Tell us a little bit about yourself"),
|
||||
HelpText("You can @mention other users and organizations to link to them."),
|
||||
P("String must contain at least 4 character(s)", cls="text-destructive")),
|
||||
FormSectionDiv(
|
||||
FormLabel("URLs"),
|
||||
HelpText("Add links to your website, blog, or social media profiles."),
|
||||
Input(value="https://www.franken-ui.dev"),
|
||||
Input(value="https://github.com/sveltecult/franken-ui"),
|
||||
Button("Add URL")),
|
||||
Button('Update profile', cls=ButtonT.primary))
|
||||
|
||||
return FormLayout('Profile', 'This is how others will see you on the site.', *content)
|
||||
|
||||
def account_form():
|
||||
content = (
|
||||
FormSectionDiv(
|
||||
LabelInput("Name", placeholder="Your name", id="name"),
|
||||
HelpText("This is the name that will be displayed on your profile and in emails.")),
|
||||
FormSectionDiv(
|
||||
LabelInput("Date of Birth", type="date", placeholder="Pick a date", id="date_of_birth"),
|
||||
HelpText("Your date of birth is used to calculate your age.")),
|
||||
FormSectionDiv(
|
||||
LabelSelect(*Options("Select a language", "English", "French", "German", "Spanish", "Portuguese", selected_idx=1, disabled_idxs={0}),
|
||||
label='Language', id="language"),
|
||||
HelpText("This is the language that will be used in the dashboard.")),
|
||||
Button('Update profile', cls=ButtonT.primary))
|
||||
|
||||
return FormLayout('Account', 'Update your account settings. Set your preferred language and timezone.', *content)
|
||||
|
||||
def appearance_form():
|
||||
def theme_item(bg_color, content_bg, text_bg):
|
||||
common_content = f"space-y-2 rounded-md {content_bg} p-2 shadow-sm"
|
||||
item_row = lambda: Div(cls=f"flex items-center space-x-2 {common_content}")(
|
||||
Div(cls=f"h-4 w-4 rounded-full {text_bg}"),
|
||||
Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}"))
|
||||
|
||||
return Div(cls=f"space-y-2 rounded-sm {bg_color} p-2")(
|
||||
Div(cls=common_content)(
|
||||
Div(cls=f"h-2 w-[80px] rounded-lg {text_bg}"),
|
||||
Div(cls=f"h-2 w-[100px] rounded-lg {text_bg}")),
|
||||
item_row(),
|
||||
item_row())
|
||||
|
||||
common_toggle_cls = "block cursor-pointer items-center rounded-md border-2 border-muted p-1 ring-ring"
|
||||
|
||||
content = (
|
||||
FormSectionDiv(
|
||||
LabelSelect(*Options('Select a font family', 'Inter', 'Geist', 'Open Sans', selected_idx=2, disabled_idxs={0}),
|
||||
label='Font Family', id='font_family'),
|
||||
HelpText("Set the font you want to use in the dashboard.")),
|
||||
FormSectionDiv(
|
||||
FormLabel("Theme"),
|
||||
HelpText("Select the theme for the dashboard."),
|
||||
Grid(
|
||||
A(id="theme-toggle-light", cls=common_toggle_cls)(theme_item("bg-[#ecedef]", "bg-white", "bg-[#ecedef]")),
|
||||
A(id="theme-toggle-dark", cls=f"{common_toggle_cls} bg-popover")(theme_item("bg-slate-950", "bg-slate-800", "bg-slate-400")),
|
||||
cols_max=2,cls=('max-w-md','gap-8'))),
|
||||
Button('Update preferences', cls=ButtonT.primary))
|
||||
|
||||
return FormLayout('Appearance', 'Customize the appearance of the app. Automatically switch between day and night themes.', *content)
|
||||
|
||||
|
||||
notification_items = [
|
||||
{"title": "Communication emails", "description": "Receive emails about your account activity.", "checked": False, "disabled": False},
|
||||
{"title": "Marketing emails", "description": "Receive emails about new products, features, and more.", "checked": False, "disabled": False},
|
||||
{"title": "Social emails", "description": "Receive emails for friend requests, follows, and more.", "checked": True, "disabled": False},
|
||||
{"title": "Security emails", "description": "Receive emails about your account activity and security.", "checked": True, "disabled": True}]
|
||||
|
||||
def notifications_form():
|
||||
def RadioLabel(label): return DivLAligned(Radio(name="notification", checked=(label=="Nothing")), FormLabel(label))
|
||||
|
||||
def NotificationCard(item):
|
||||
return Card(
|
||||
Div(cls="space-y-0.5")(
|
||||
FormLabel(Strong(item['title'], cls=TextT.sm),
|
||||
HelpText(item['description']))))
|
||||
content = Div(
|
||||
FormSectionDiv(
|
||||
FormLabel("Notify me about"),
|
||||
*map(RadioLabel, ["All new messages", "Direct messages and mentions", "Nothing"])),
|
||||
Div(
|
||||
H4("Email Notifications", cls="mb-4"),
|
||||
Grid(*map(NotificationCard, notification_items), cols=1)),
|
||||
LabelCheckboxX("Use different settings for my mobile devices", id="notification_mobile"),
|
||||
HelpText("You can manage your mobile notifications in the mobile settings page."),
|
||||
Button('Update notifications', cls=ButtonT.primary))
|
||||
|
||||
return FormLayout('Notifications', 'Configure how you receive notifications.', *content)
|
||||
|
||||
def display_form():
|
||||
content = (
|
||||
Div(cls="space-y-2")(
|
||||
Div(cls="mb-4")(
|
||||
H5("Sidebar"),
|
||||
Subtitle("Select the items you want to display in the sidebar.")),
|
||||
*[Div(CheckboxX(id=f"display_{i}", checked=i in [0, 1, 2]),FormLabel(label))
|
||||
for i, label in enumerate(["Recents", "Home", "Applications", "Desktop", "Downloads", "Documents"])]),
|
||||
Button('Update display', cls=ButtonT.primary))
|
||||
return FormLayout('Display', 'Turn items on or off to control what\'s displayed in the app.', *content)
|
||||
|
||||
@rt
|
||||
def index():
|
||||
return Title("Forms Example"),Container(
|
||||
heading(),
|
||||
Div(cls="flex gap-x-12")(
|
||||
sidebar,
|
||||
Ul(id="component-nav", cls="uk-switcher max-w-2xl")(
|
||||
Li(cls="uk-active")(profile_form(),
|
||||
*map(Li, [account_form(), appearance_form(), notifications_form(), display_form()])))))
|
||||
|
||||
serve()
|
||||
Reference in New Issue
Block a user