Spaces:
Sleeping
Sleeping
File size: 8,246 Bytes
88bf7e3 8402a8c 88bf7e3 8402a8c addbb97 8402a8c cbd8a2b 8402a8c 88bf7e3 8402a8c 8abff3a 51814f6 8402a8c addbb97 e84b596 addbb97 e84b596 addbb97 e84b596 73ab857 e84b596 addbb97 8402a8c e84b596 4b507bf e84b596 8402a8c cbd8a2b 4664117 cbd8a2b 4664117 cbd8a2b 88bf7e3 8402a8c 8d65d3c e84b596 8402a8c 4b507bf d8a4ec1 8402a8c e84b596 aab4a1f d8a4ec1 8402a8c 5b946bc d8a4ec1 5b946bc 4b507bf 5b946bc 805d1f4 4366ec6 b070982 e84b596 88bf7e3 e063967 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
from fasthtml.common import *
# MonsterUI shadows fasthtml components with the same name
from monsterui.all import *
# If you don't want shadowing behavior, you use import monsterui.core as ... style instead
from fasthtml.components import Uk_input_tag
from fasthtml.svg import *
import calendar
from datetime import datetime
# Get frankenui and tailwind headers via CDN using Theme.blue.headers()
hdrs = Theme.blue.headers()
# fast_app is shadowed by MonsterUI to make it default to no Pico, and add body classes
# needed for frankenui theme styling
app, rt = fast_app(hdrs=hdrs, live=True)
products = [
{"name": "Kalpasi (Black Stone Flower)", "price": "$509.50", "img": "https://picsum.photos/400/100?random=1"},
{"name": "Radhuni (Wild Celery Seeds)", "price": "$505.25", "img": "https://picsum.photos/400/100?random=2"},
{"name": "Kokum", "price": "$503.00", "img": "https://picsum.photos/400/100?random=3"},
{"name": "Mace (Javitri)", "price": "$504.50", "img": "https://picsum.photos/400/100?random=4"},
{"name": "Anardana (Dried Pomegranate)", "price": "$506.25", "img": "https://picsum.photos/400/100?random=5"},
{"name": "Ajmod (Indian Lovage)", "price": "$507.00", "img": "https://picsum.photos/400/100?random=6"},
{"name": "Marathi Moggu", "price": "$508.50", "img": "https://picsum.photos/400/100?random=7"},
{"name": "Kalonji (Nigella Seeds)", "price": "$504.25", "img": "https://picsum.photos/400/100?random=8"}
]
CreateAccount = Card(
Grid(
Button(DivLAligned(UkIcon('github'), Div('Github')), cls="w-full"),
Button('Google', cls="w-full")
),
DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm),
LabelInput('Email', id='email', placeholder='m@example.com'),
LabelInput('Password', id='password',placeholder='Password', type='Password'),
header=(H3('Create an Account'), Subtitle('Enter your email below to create your account')),
footer=Button('Create Account', cls=(ButtonT.primary, 'w-full'))
)
PaypalSVG_data = "..." # unchanged
AppleSVG_data = "..." # unchanged
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")
)
PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor"))
AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor"))
PaymentMethod = Card(
Grid(
Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary rounded-lg', type="button"),
Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20 border rounded-lg', type="button"),
Button(DivCentered(AppleSvg, "Apple"), cls='h-20 border rounded-lg', type="button")
),
Form(
LabelInput('Name', id='name', placeholder='John Doe'),
LabelInput('Card Number', id='card_number', placeholder='1234 5678 9012 3456'),
Grid(
LabelSelect(*Options(*calendar.month_name[1:], selected_idx=0), label='Expires', id='expire_month'),
LabelSelect(*Options(*range(2024,2030), selected_idx=0), label='Year', id='expire_year'),
LabelInput('CVV', id='cvv', placeholder='CVV', cls='mt-0')
)
),
header=(H3('Payment Method'), Subtitle('Add a new payment method to your account.'))
)
def ProductCard(p):
# Card does lots of boilerplate classes so you can just pass in the content
return Card(
# width:100% makes the image take the full width so we are guarenteed that we won't
# have the image cut off or not large enough. Because all our images are a consistent
# size we do not need to worry about stretching or skewing the image, this is ideal.
# If you have images of different sizes, you will need to use object-fit:cover and/or
# height to either strech, shrink, or crop the image. It is much better to adjust your
# images to be a consistent size upfront so you don't have to handle edge cases of
# different images skeweing/stretching differently.
Img(src=p["img"], alt=p["name"], cls="w-full h-40 object-cover rounded-t-lg"),
# All components can take a cls argument to add additional styling - `mt-2` adds margin
# to the top (see spacing tutorial for details on spacing).
#
# Often adding space makes a site look more put together - usually the 2 - 5 range is a
# good choice
H4(p["name"], cls="mt-3 text-lg font-semibold text-gray-900"),
# There are helpful Enums, such as TextPresetsT, ButtonT, ContainerT, etc that allow for easy
# discoverability of class options.
# bold_sm is helpful for things that you want to look like regular text, but stand out
# visually for emphasis.
P(p["price"], cls=(TextPresets.bold_sm, "text-gray-700 mt-1")),
# ButtonT.primary is useful for actions you really want the user to take (like adding
# something to the cart) - these stand out visually. For dangerous actions (like
# deleting something) you generally would want to use ButtonT.destructive. For UX actions
# that aren't a goal of the page (like cancelling something that hasn't been submitted)
# you generally want the default styling.
Button("Add to cart", cls=(ButtonT.primary, "mt-3 w-full"),
hx_get=product_detail.to(product_name=p['name'], product_price=p['price']),
hx_push_url='true',
hx_target='body'),
cls="shadow-lg rounded-lg overflow-hidden bg-white hover:shadow-xl transition-shadow"
)
# Define this once, at module level (top of file is best)
example_product_description = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Praesent euismod, sapien nec facilisis tincidunt, nunc
nibh posuere justo, vitae luctus neque magna vel nulla.
Curabitur at felis ac nulla fermentum tincidunt.
Integer non risus nec nulla cursus porttitor.
Suspendisse potenti. Donec vel sapien nec erat
malesuada viverra sed a lorem.
- Proin facilisis ligula sed sapien tincidunt, at
fermentum magna volutpat.
- Curabitur vitae lectus nec justo cursus
sollicitudin non sed est.
- Sed ut perspiciatis unde omnis iste natus error
sit voluptatem accusantium doloremque laudantium.
"""
@rt
def index():
# Titled using a H1 title, sets the page title, and wraps contents in Main(Container(...)) using
# frankenui styles. Generally you will want to use Titled for all of your pages
return Titled("Store",
Grid(*[ProductCard(p) for p in products], cols_lg=3, gap=6, cls="p-6")
)
@rt
def product_detail(product_name: str, product_price: str):
return (
Title("Product Detail"),
Grid(
Div(
H3(product_name, cls="text-xl font-semibold mb-4"),
render_md(example_product_description.format(product_name=product_name))
),
Div(
H3("Order", cls="text-lg font-semibold mb-3"),
Form(
LabelInput("Product", id="name", value=product_name, readonly=True,
cls="bg-gray-100 cursor-not-allowed"),
LabelInput("Price", id="price", value=product_price, readonly=True,
cls="bg-gray-100 cursor-not-allowed"),
LabelInput("Quantity", id="quantity", placeholder="1"),
LabelInput("Email", id="email", placeholder="accountemail@example.com"),
Div(PaymentMethod, cls="space-y-4 mt-4"),
Button("Add to order", cls=(ButtonT.primary, "w-full mt-4")),
Button("Continue shopping",
cls=(ButtonT.secondary, "w-full mt-2"),
hx_get="/",
hx_push_url="true",
hx_target="body")
)
),
cols_lg=2,
cls="gap-8 p-6"
)
)
serve() |