from starhtml import *
from starui import *
selected_color = Signal("selected_color", "titanium")
favorited = Signal("favorited", False)
just_added = Signal("just_added", False)
titanium_qty = Signal("titanium_qty", 0)
blue_qty = Signal("blue_qty", 0)
white_qty = Signal("white_qty", 0)
favorited2 = Signal("favorited2", False)
airpods_qty = Signal("airpods_qty", 0)
just_added2 = Signal("just_added2", False)
cart_count = Signal("cart_count", titanium_qty + blue_qty + white_qty + airpods_qty)
def product_card(emoji, title, description, price, reviews, special_badge, features, signals, has_color_picker=False, color_quantities=None):
if has_color_picker and color_quantities:
fav_signal, animation_signal = signals
titanium_sig, blue_sig, white_sig = color_quantities['titanium'], color_quantities['blue'], color_quantities['white']
else:
fav_signal, cart_signal, animation_signal = signals
card_content = [
Div(emoji, cls="text-6xl flex items-center justify-center h-32 bg-gray-50 rounded-lg mb-4"),
Div(
CardTitle(title, cls="text-lg mb-1"),
CardDescription(description),
Div(
Span("★★★★★", cls="text-yellow-400 text-sm"),
Span(reviews, cls="text-sm text-muted-foreground ml-2"),
cls="flex items-center mt-2 mb-3"
),
Div(
Span(price, cls="text-2xl font-bold text-primary" + (" mr-2" if special_badge else "")),
special_badge if special_badge else None,
cls="flex items-center mb-4"
)
)
]
if has_color_picker:
colors = [("titanium", "bg-gray-800"), ("blue", "bg-blue-500"), ("white", "bg-white")]
card_content[1] = Div(
*card_content[1].children[:4],
Div(
P("Color:", cls="text-sm font-medium mb-2"),
Div(
*[Button(
"",
variant="ghost",
size="sm",
cls=f"w-8 h-8 rounded-full {color_cls} border-2 p-0 {'ml-2' if i > 0 else ''} hover:scale-110 transition-transform",
data_attr_cls=selected_color.eq(color_name).if_("border-primary ring-2 ring-primary ring-offset-2", "border-gray-300"),
data_on_click=selected_color.set(color_name)
) for i, (color_name, color_cls) in enumerate(colors)],
cls="flex items-center"
),
cls="mb-4"
)
)
elif features:
card_content[1] = Div(
*card_content[1].children,
Div(*[P(feat, cls="text-sm text-muted-foreground mb-1") for feat in features], cls="mb-4")
)
return Card(
CardContent(Div(*card_content, cls="p-6")),
CardFooter(
Button(
Icon("lucide:heart", cls="h-4 w-4 mr-2",
data_attr_cls=fav_signal.if_("fill-current text-red-500", "")),
data_text=fav_signal.if_("Favorited", "Add to Favorites"),
variant="outline",
size="sm",
data_on_click=fav_signal.toggle(),
cls="min-w-[140px] select-none"
),
Button(
Icon("lucide:shopping-cart", cls="h-4 w-4 mr-2", data_show=~animation_signal),
Icon("lucide:check", cls="h-4 w-4 mr-2", data_show=animation_signal),
data_text=animation_signal.if_("Added!", "Add to Cart"),
data_on_click=(
[cart_signal.add(1), animation_signal.set(True), set_timeout([animation_signal.set(False)], 1000)]
if not has_color_picker else
[
titanium_sig.set(titanium_sig + selected_color.eq("titanium")),
blue_sig.set(blue_sig + selected_color.eq("blue")),
white_sig.set(white_sig + selected_color.eq("white")),
animation_signal.set(True),
set_timeout([animation_signal.set(False)], 1000)
]
),
data_attr_disabled=animation_signal,
variant="outline",
size="sm",
cls="flex-1 min-w-[120px] select-none",
data_attr_cls=animation_signal.if_("bg-green-50 text-green-700 border-green-300 hover:bg-green-100")
),
cls="flex gap-2"
),
cls="w-full max-w-xs hover:shadow-lg transition-shadow duration-200"
)
Div(
selected_color, favorited, just_added, titanium_qty, blue_qty, white_qty, favorited2, airpods_qty, just_added2, cart_count,
Div(
Div(
Div(
Icon("lucide:shopping-cart", cls="h-5 w-5 mr-2"),
Span("Shopping Cart", cls="font-semibold text-lg"),
cls="flex items-center mb-3"
),
Div(
*[Div(
Span("• ", cls="mr-2"),
Span(f"iPhone 15 Pro - {color} ", cls="font-medium"),
Span(data_text="(x" + qty + ")", cls="text-muted-foreground"),
data_show=qty > 0,
cls="text-sm mb-1"
) for color, qty in [("Titanium", titanium_qty), ("Blue", blue_qty), ("White", white_qty)]],
Div(
Span("• ", cls="mr-2"),
Span("AirPods Pro (2nd gen) ", cls="font-medium"),
Span(data_text="(x" + airpods_qty + ")", cls="text-muted-foreground"),
data_show=airpods_qty > 0,
cls="text-sm mb-1"
),
P(
"No items in cart",
data_show=cart_count == 0,
cls="text-sm text-muted-foreground italic"
),
cls="space-y-1"
),
Div(
Span("Total items: ", cls="text-sm text-muted-foreground"),
Span(data_text=cart_count, cls="font-bold text-primary"),
data_show=cart_count > 0,
cls="mt-3 pt-3 border-t"
)
),
cls="mb-8 p-4 bg-muted/50 rounded-lg border"
),
Div(
product_card(
"📱",
"iPhone 15 Pro",
"Natural Titanium, 128GB",
"$999",
"4.9 (1,234 reviews)",
Div(Span("$1,099", cls="text-sm line-through text-muted-foreground"), Badge("9% OFF", variant="destructive", cls="ml-2")),
None,
(favorited, just_added),
has_color_picker=True,
color_quantities={
'titanium': titanium_qty,
'blue': blue_qty,
'white': white_qty
}
),
product_card(
"🎧",
"AirPods Pro",
"2nd Generation with MagSafe",
"$249",
"4.8 (892 reviews)",
Badge("Best Seller", variant="secondary", cls="ml-2"),
["✓ Active Noise Cancellation", "✓ Spatial Audio", "✓ 6hr battery + 24hr case"],
(favorited2, airpods_qty, just_added2)
),
cls="grid grid-cols-1 md:grid-cols-2 gap-6"
),
cls="w-full max-w-2xl"
)