pyjs-examples 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyjs_examples/__init__.py +1 -0
- pyjs_examples/calculator/README.md +55 -0
- pyjs_examples/calculator/__init__.py +1 -0
- pyjs_examples/calculator/__main__.py +9 -0
- pyjs_examples/calculator/calculator.py +325 -0
- pyjs_examples/calculator/tests/test_browser_calculator.py +67 -0
- pyjs_examples/calculator/tests/test_calculator.py +72 -0
- pyjs_examples/catalog.py +1433 -0
- pyjs_examples/chatroom/README.md +50 -0
- pyjs_examples/chatroom/__init__.py +1 -0
- pyjs_examples/chatroom/__main__.py +12 -0
- pyjs_examples/chatroom/chat_server.py +156 -0
- pyjs_examples/chatroom/chatroom.py +704 -0
- pyjs_examples/chatroom/tests/test_browser_chatroom.py +59 -0
- pyjs_examples/chatroom/tests/test_chat_server.py +53 -0
- pyjs_examples/chatroom/visual_test.py +156 -0
- pyjs_examples/counter.py +73 -0
- pyjs_examples/kanban/README.md +31 -0
- pyjs_examples/kanban/__init__.py +1 -0
- pyjs_examples/kanban/__main__.py +8 -0
- pyjs_examples/kanban/kanban.py +581 -0
- pyjs_examples/kanban/tests/renders/kanban-board.png +0 -0
- pyjs_examples/kanban/tests/test_browser_kanban.py +150 -0
- pyjs_examples/kanban/tests/test_kanban.py +38 -0
- pyjs_examples/kanban/visual_test.py +108 -0
- pyjs_examples/nativescript_counter/README.md +31 -0
- pyjs_examples/nativescript_counter/__init__.py +5 -0
- pyjs_examples/nativescript_counter/__main__.py +17 -0
- pyjs_examples/nativescript_counter/counter.py +54 -0
- pyjs_examples/nativescript_counter/tests/test_android_counter.py +31 -0
- pyjs_examples/nativescript_counter/tests/test_counter.py +11 -0
- pyjs_examples/samples/__init__.py +1 -0
- pyjs_examples/samples/state_counter/README.md +19 -0
- pyjs_examples/samples/state_counter/__init__.py +1 -0
- pyjs_examples/samples/state_counter/__main__.py +3 -0
- pyjs_examples/samples/state_counter/state_counter.py +60 -0
- pyjs_examples/samples/state_counter/tests/test_browser_state_counter.py +18 -0
- pyjs_examples/samples/state_counter/tests/test_state_counter.py +17 -0
- pyjs_examples/samples/ui_dialog/README.md +19 -0
- pyjs_examples/samples/ui_dialog/__init__.py +1 -0
- pyjs_examples/samples/ui_dialog/__main__.py +3 -0
- pyjs_examples/samples/ui_dialog/tests/test_browser_ui_dialog.py +21 -0
- pyjs_examples/samples/ui_dialog/tests/test_ui_dialog.py +10 -0
- pyjs_examples/samples/ui_dialog/ui_dialog.py +65 -0
- pyjs_examples/space_invaders/README.md +45 -0
- pyjs_examples/space_invaders/__init__.py +1 -0
- pyjs_examples/space_invaders/__main__.py +3 -0
- pyjs_examples/space_invaders/space_invaders.py +1025 -0
- pyjs_examples/space_invaders/tests/renders/action_scene.png +0 -0
- pyjs_examples/space_invaders/tests/renders/initial_wave.png +0 -0
- pyjs_examples/space_invaders/tests/test_browser_game.py +86 -0
- pyjs_examples/space_invaders/tests/test_game.py +83 -0
- pyjs_examples/todomvc/README.md +50 -0
- pyjs_examples/todomvc/__init__.py +1 -0
- pyjs_examples/todomvc/__main__.py +12 -0
- pyjs_examples/todomvc/base.css +141 -0
- pyjs_examples/todomvc/index.css +393 -0
- pyjs_examples/todomvc/tests/test_browser_todomvc.py +145 -0
- pyjs_examples/todomvc/tests/test_todomvc.py +84 -0
- pyjs_examples/todomvc/todomvc.py +426 -0
- pyjs_examples/workspace/README.md +25 -0
- pyjs_examples/workspace/__init__.py +1 -0
- pyjs_examples/workspace/__main__.py +3 -0
- pyjs_examples/workspace/tests/test_browser_workspace.py +109 -0
- pyjs_examples/workspace/tests/test_workspace.py +27 -0
- pyjs_examples/workspace/workspace.py +749 -0
- pyjs_examples-0.0.1.dist-info/METADATA +71 -0
- pyjs_examples-0.0.1.dist-info/RECORD +71 -0
- pyjs_examples-0.0.1.dist-info/WHEEL +5 -0
- pyjs_examples-0.0.1.dist-info/entry_points.txt +11 -0
- pyjs_examples-0.0.1.dist-info/top_level.txt +1 -0
pyjs_examples/catalog.py
ADDED
|
@@ -0,0 +1,1433 @@
|
|
|
1
|
+
from pyjs import js
|
|
2
|
+
from pyjs.web.domx import a, button, div, h1, h2, h3, input, p, ref, tag, tw
|
|
3
|
+
from pyjs.web.events import Event
|
|
4
|
+
from pyjs.web.html import HTMLInputElement
|
|
5
|
+
from pyjs.web.ui import Widget
|
|
6
|
+
from pyjs.web.ui.accordion import AccordionGroup, AccordionItem
|
|
7
|
+
from pyjs.web.ui.alert import Alert, AlertAction, AlertDescription, AlertTitle
|
|
8
|
+
from pyjs.web.ui.alert_dialog import (
|
|
9
|
+
AlertDialog,
|
|
10
|
+
AlertDialogAction,
|
|
11
|
+
AlertDialogCancel,
|
|
12
|
+
AlertDialogContent,
|
|
13
|
+
AlertDialogDescription,
|
|
14
|
+
AlertDialogFooter,
|
|
15
|
+
AlertDialogHeader,
|
|
16
|
+
AlertDialogTitle,
|
|
17
|
+
AlertDialogTrigger,
|
|
18
|
+
)
|
|
19
|
+
from pyjs.web.ui.aspect_ratio import AspectRatio
|
|
20
|
+
from pyjs.web.ui.attachment import (
|
|
21
|
+
Attachment,
|
|
22
|
+
AttachmentContent,
|
|
23
|
+
AttachmentDescription,
|
|
24
|
+
AttachmentGroup,
|
|
25
|
+
AttachmentMedia,
|
|
26
|
+
AttachmentTitle,
|
|
27
|
+
)
|
|
28
|
+
from pyjs.web.ui.avatar import Avatar, AvatarBadge, AvatarGroup, AvatarGroupCount
|
|
29
|
+
from pyjs.web.ui.badge import Badge
|
|
30
|
+
from pyjs.web.ui.breadcrumb import (
|
|
31
|
+
Breadcrumb,
|
|
32
|
+
BreadcrumbItem,
|
|
33
|
+
BreadcrumbLink,
|
|
34
|
+
BreadcrumbList,
|
|
35
|
+
BreadcrumbPage,
|
|
36
|
+
BreadcrumbSeparator,
|
|
37
|
+
)
|
|
38
|
+
from pyjs.web.ui.bubble import Bubble, BubbleContent, BubbleGroup
|
|
39
|
+
from pyjs.web.ui.button import Button, ButtonGroup, ButtonGroupText
|
|
40
|
+
from pyjs.web.ui.calendar import Calendar
|
|
41
|
+
from pyjs.web.ui.card import (
|
|
42
|
+
Card,
|
|
43
|
+
CardAction,
|
|
44
|
+
CardContent,
|
|
45
|
+
CardDescription,
|
|
46
|
+
CardFooter,
|
|
47
|
+
CardHeader,
|
|
48
|
+
CardTitle,
|
|
49
|
+
)
|
|
50
|
+
from pyjs.web.ui.carousel import Carousel, CarouselItem
|
|
51
|
+
from pyjs.web.ui.chart import BarChart
|
|
52
|
+
from pyjs.web.ui.checkbox import Checkbox
|
|
53
|
+
from pyjs.web.ui.collapsible import Collapsible, CollapsibleContent, CollapsibleTrigger
|
|
54
|
+
from pyjs.web.ui.combobox import Combobox
|
|
55
|
+
from pyjs.web.ui.command import Command, CommandItem
|
|
56
|
+
from pyjs.web.ui.context_menu import (
|
|
57
|
+
ContextMenu,
|
|
58
|
+
ContextMenuContent,
|
|
59
|
+
ContextMenuItem,
|
|
60
|
+
ContextMenuTrigger,
|
|
61
|
+
)
|
|
62
|
+
from pyjs.web.ui.date_picker import DatePicker
|
|
63
|
+
from pyjs.web.ui.dialog import (
|
|
64
|
+
Dialog,
|
|
65
|
+
DialogClose,
|
|
66
|
+
DialogContent,
|
|
67
|
+
DialogDescription,
|
|
68
|
+
DialogFooter,
|
|
69
|
+
DialogHeader,
|
|
70
|
+
DialogTitle,
|
|
71
|
+
DialogTrigger,
|
|
72
|
+
)
|
|
73
|
+
from pyjs.web.ui.direction import Direction
|
|
74
|
+
from pyjs.web.ui.drawer import Drawer, DrawerContent, DrawerTitle, DrawerTrigger
|
|
75
|
+
from pyjs.web.ui.empty import (
|
|
76
|
+
Empty,
|
|
77
|
+
EmptyContent,
|
|
78
|
+
EmptyDescription,
|
|
79
|
+
EmptyHeader,
|
|
80
|
+
EmptyMedia,
|
|
81
|
+
EmptyTitle,
|
|
82
|
+
)
|
|
83
|
+
from pyjs.web.ui.field import (
|
|
84
|
+
Field,
|
|
85
|
+
FieldContent,
|
|
86
|
+
FieldDescription,
|
|
87
|
+
FieldError,
|
|
88
|
+
FieldGroup,
|
|
89
|
+
FieldLabel,
|
|
90
|
+
FieldSet,
|
|
91
|
+
FieldTitle,
|
|
92
|
+
)
|
|
93
|
+
from pyjs.web.ui.hover_card import HoverCard, HoverCardContent, HoverCardTrigger
|
|
94
|
+
from pyjs.web.ui.input import Input
|
|
95
|
+
from pyjs.web.ui.input_group import InputGroup, InputGroupAddon, InputGroupInput
|
|
96
|
+
from pyjs.web.ui.input_otp import InputOTP
|
|
97
|
+
from pyjs.web.ui.item import Item, ItemContent, ItemDescription, ItemMedia, ItemTitle
|
|
98
|
+
from pyjs.web.ui.kbd import Kbd, KbdGroup
|
|
99
|
+
from pyjs.web.ui.label import Label
|
|
100
|
+
from pyjs.web.ui.marker import Marker, MarkerContent, MarkerIcon
|
|
101
|
+
from pyjs.web.ui.menubar import Menubar, MenubarItem, MenubarMenu
|
|
102
|
+
from pyjs.web.ui.message import Message, MessageContent, MessageGroup, MessageHeader
|
|
103
|
+
from pyjs.web.ui.message_scroller import MessageScroller
|
|
104
|
+
from pyjs.web.ui.native_select import NativeSelect, NativeSelectOption
|
|
105
|
+
from pyjs.web.ui.navigation_menu import (
|
|
106
|
+
NavigationMenu,
|
|
107
|
+
NavigationMenuLink,
|
|
108
|
+
NavigationMenuList,
|
|
109
|
+
)
|
|
110
|
+
from pyjs.web.ui.pagination import (
|
|
111
|
+
Pagination,
|
|
112
|
+
PaginationContent,
|
|
113
|
+
PaginationEllipsis,
|
|
114
|
+
PaginationItem,
|
|
115
|
+
PaginationLink,
|
|
116
|
+
PaginationNext,
|
|
117
|
+
PaginationPrevious,
|
|
118
|
+
)
|
|
119
|
+
from pyjs.web.ui.popover import (
|
|
120
|
+
Popover,
|
|
121
|
+
PopoverClose,
|
|
122
|
+
PopoverContent,
|
|
123
|
+
PopoverDescription,
|
|
124
|
+
PopoverHeader,
|
|
125
|
+
PopoverTitle,
|
|
126
|
+
PopoverTrigger,
|
|
127
|
+
)
|
|
128
|
+
from pyjs.web.ui.progress import ProgressBar
|
|
129
|
+
from pyjs.web.ui.radio_group import RadioGroup, RadioGroupItem
|
|
130
|
+
from pyjs.web.ui.resizable import ResizableHandle, ResizablePanel, ResizablePanelGroup
|
|
131
|
+
from pyjs.web.ui.scroll_area import ScrollArea
|
|
132
|
+
from pyjs.web.ui.select import Select, SelectContent, SelectItem, SelectTrigger
|
|
133
|
+
from pyjs.web.ui.separator import Separator
|
|
134
|
+
from pyjs.web.ui.sheet import (
|
|
135
|
+
Sheet,
|
|
136
|
+
SheetClose,
|
|
137
|
+
SheetContent,
|
|
138
|
+
SheetDescription,
|
|
139
|
+
SheetFooter,
|
|
140
|
+
SheetHeader,
|
|
141
|
+
SheetTitle,
|
|
142
|
+
SheetTrigger,
|
|
143
|
+
)
|
|
144
|
+
from pyjs.web.ui.sidebar import Sidebar
|
|
145
|
+
from pyjs.web.ui.skeleton import Skeleton
|
|
146
|
+
from pyjs.web.ui.slider import Slider
|
|
147
|
+
from pyjs.web.ui.sonner import Toaster
|
|
148
|
+
from pyjs.web.ui.spinner import Spinner
|
|
149
|
+
from pyjs.web.ui.switch import Switch
|
|
150
|
+
from pyjs.web.ui.table import (
|
|
151
|
+
Table,
|
|
152
|
+
TableBody,
|
|
153
|
+
TableCaption,
|
|
154
|
+
TableCell,
|
|
155
|
+
TableFooter,
|
|
156
|
+
TableHead,
|
|
157
|
+
TableHeader,
|
|
158
|
+
TableRow,
|
|
159
|
+
)
|
|
160
|
+
from pyjs.web.ui.tabs import Tab, TabGroup
|
|
161
|
+
from pyjs.web.ui.textarea import Textarea
|
|
162
|
+
from pyjs.web.ui.toggle import Toggle, ToggleGroup
|
|
163
|
+
from pyjs.web.ui.tooltip import Tooltip, TooltipContent, TooltipTrigger
|
|
164
|
+
from pyjs.web.ui.typography import TypographyH3, TypographyInlineCode, TypographyLead, TypographyP
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@js
|
|
168
|
+
def field(label: str, input_element: HTMLInputElement):
|
|
169
|
+
return tag(
|
|
170
|
+
"label",
|
|
171
|
+
tw("grid gap-2 text-left text-sm font-medium text-slate-900"),
|
|
172
|
+
label,
|
|
173
|
+
input_element,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@js
|
|
178
|
+
def example_card(title: str, description: str, command: str):
|
|
179
|
+
return div(
|
|
180
|
+
tw("rounded-lg border border-slate-200 bg-white p-5 shadow-sm"),
|
|
181
|
+
h3(tw("font-semibold text-slate-950"), title),
|
|
182
|
+
p(tw("mt-2 text-sm leading-6 text-slate-600"), description),
|
|
183
|
+
tag(
|
|
184
|
+
"code",
|
|
185
|
+
tw("mt-4 block overflow-x-auto rounded-md bg-slate-950 p-3 text-xs text-slate-50"),
|
|
186
|
+
command,
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@js
|
|
192
|
+
def component_showcase(component_id: str, title: str, description: str, *children):
|
|
193
|
+
return tag(
|
|
194
|
+
"section",
|
|
195
|
+
tw("mt-16 scroll-mt-24 border-t border-slate-200 pt-12"),
|
|
196
|
+
{"data-catalog-component": component_id, "id": component_id},
|
|
197
|
+
h2(tw("text-2xl font-bold tracking-tight text-slate-950"), title),
|
|
198
|
+
p(tw("mt-3 max-w-2xl leading-7 text-slate-600"), description),
|
|
199
|
+
div(
|
|
200
|
+
tw("mt-7 rounded-lg border border-slate-200 bg-white p-8"),
|
|
201
|
+
*children,
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@js
|
|
207
|
+
def catalog_sidebar_link(component_id: str, title: str):
|
|
208
|
+
return a(
|
|
209
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
210
|
+
{"href": "#" + component_id},
|
|
211
|
+
title,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@js
|
|
216
|
+
def remaining_sidebar_links():
|
|
217
|
+
return div(
|
|
218
|
+
tw("contents"),
|
|
219
|
+
catalog_sidebar_link("attachment", "Attachment"),
|
|
220
|
+
catalog_sidebar_link("bubble", "Bubble"),
|
|
221
|
+
catalog_sidebar_link("calendar", "Calendar"),
|
|
222
|
+
catalog_sidebar_link("carousel", "Carousel"),
|
|
223
|
+
catalog_sidebar_link("chart", "Chart"),
|
|
224
|
+
catalog_sidebar_link("combobox", "Combobox"),
|
|
225
|
+
catalog_sidebar_link("command", "Command"),
|
|
226
|
+
catalog_sidebar_link("context-menu", "Context Menu"),
|
|
227
|
+
catalog_sidebar_link("date-picker", "Date Picker"),
|
|
228
|
+
catalog_sidebar_link("direction", "Direction"),
|
|
229
|
+
catalog_sidebar_link("drawer", "Drawer"),
|
|
230
|
+
catalog_sidebar_link("input-group", "Input Group"),
|
|
231
|
+
catalog_sidebar_link("input-otp", "Input OTP"),
|
|
232
|
+
catalog_sidebar_link("item", "Item"),
|
|
233
|
+
catalog_sidebar_link("marker", "Marker"),
|
|
234
|
+
catalog_sidebar_link("menubar", "Menubar"),
|
|
235
|
+
catalog_sidebar_link("message", "Message"),
|
|
236
|
+
catalog_sidebar_link("message-scroller", "Message Scroller"),
|
|
237
|
+
catalog_sidebar_link("native-select", "Native Select"),
|
|
238
|
+
catalog_sidebar_link("navigation-menu", "Navigation Menu"),
|
|
239
|
+
catalog_sidebar_link("pagination", "Pagination"),
|
|
240
|
+
catalog_sidebar_link("resizable", "Resizable"),
|
|
241
|
+
catalog_sidebar_link("scroll-area", "Scroll Area"),
|
|
242
|
+
catalog_sidebar_link("select", "Select"),
|
|
243
|
+
catalog_sidebar_link("sidebar", "Sidebar"),
|
|
244
|
+
catalog_sidebar_link("slider", "Slider"),
|
|
245
|
+
catalog_sidebar_link("sonner", "Sonner"),
|
|
246
|
+
catalog_sidebar_link("typography", "Typography"),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@js
|
|
251
|
+
def remaining_component_showcases():
|
|
252
|
+
toaster = Toaster()
|
|
253
|
+
toaster.notify("Project saved", "The latest changes are now live.")
|
|
254
|
+
return div(
|
|
255
|
+
tw("contents"),
|
|
256
|
+
component_showcase(
|
|
257
|
+
"attachment",
|
|
258
|
+
"Attachment",
|
|
259
|
+
"Represents a file with metadata and related actions.",
|
|
260
|
+
AttachmentGroup(
|
|
261
|
+
Attachment(
|
|
262
|
+
AttachmentMedia("PDF"),
|
|
263
|
+
AttachmentContent(
|
|
264
|
+
AttachmentTitle("design-notes.pdf"),
|
|
265
|
+
AttachmentDescription("240 KB · PDF"),
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
),
|
|
269
|
+
),
|
|
270
|
+
component_showcase(
|
|
271
|
+
"bubble",
|
|
272
|
+
"Bubble",
|
|
273
|
+
"Displays compact conversational content and sent-state styling.",
|
|
274
|
+
BubbleGroup(
|
|
275
|
+
Bubble(BubbleContent("Can we ship this today?")),
|
|
276
|
+
Bubble(BubbleContent("Yes—the browser tests are green."), variant="sent"),
|
|
277
|
+
),
|
|
278
|
+
),
|
|
279
|
+
component_showcase(
|
|
280
|
+
"calendar",
|
|
281
|
+
"Calendar",
|
|
282
|
+
"Selects a day from a semantic monthly grid.",
|
|
283
|
+
Calendar("July", 2026, starts_on=3, selected_day=21),
|
|
284
|
+
),
|
|
285
|
+
component_showcase(
|
|
286
|
+
"carousel",
|
|
287
|
+
"Carousel",
|
|
288
|
+
"Moves through a finite collection of slides with native buttons.",
|
|
289
|
+
Carousel(
|
|
290
|
+
CarouselItem(div(tw("rounded-md bg-slate-100 p-12 text-center"), "First slide")),
|
|
291
|
+
CarouselItem(div(tw("rounded-md bg-slate-200 p-12 text-center"), "Second slide")),
|
|
292
|
+
),
|
|
293
|
+
),
|
|
294
|
+
component_showcase(
|
|
295
|
+
"chart",
|
|
296
|
+
"Chart",
|
|
297
|
+
"Renders accessible SVG data graphics without a JavaScript chart dependency.",
|
|
298
|
+
BarChart([18, 32, 24, 41], ["Q1", "Q2", "Q3", "Q4"], label="Quarterly usage"),
|
|
299
|
+
),
|
|
300
|
+
component_showcase(
|
|
301
|
+
"combobox",
|
|
302
|
+
"Combobox",
|
|
303
|
+
"Filters and selects from a searchable collection.",
|
|
304
|
+
Combobox(
|
|
305
|
+
CommandItem("Python", "Python"),
|
|
306
|
+
CommandItem("JavaScript", "JavaScript"),
|
|
307
|
+
CommandItem("Rust", "Rust"),
|
|
308
|
+
placeholder="Choose a language...",
|
|
309
|
+
),
|
|
310
|
+
),
|
|
311
|
+
component_showcase(
|
|
312
|
+
"command",
|
|
313
|
+
"Command",
|
|
314
|
+
"Filters a keyboard-friendly command collection.",
|
|
315
|
+
Command(
|
|
316
|
+
CommandItem("Open file", "Open file"),
|
|
317
|
+
CommandItem("Save all", "Save all"),
|
|
318
|
+
CommandItem("Close window", "Close window"),
|
|
319
|
+
),
|
|
320
|
+
),
|
|
321
|
+
component_showcase(
|
|
322
|
+
"context-menu",
|
|
323
|
+
"Context Menu",
|
|
324
|
+
"Opens an action menu from the native contextmenu event.",
|
|
325
|
+
ContextMenu(
|
|
326
|
+
ContextMenuTrigger(
|
|
327
|
+
div(tw("rounded-md border border-dashed p-12 text-center"), "Right click here")
|
|
328
|
+
),
|
|
329
|
+
ContextMenuContent(
|
|
330
|
+
ContextMenuItem("copy", "Copy"),
|
|
331
|
+
ContextMenuItem("rename", "Rename"),
|
|
332
|
+
),
|
|
333
|
+
),
|
|
334
|
+
),
|
|
335
|
+
component_showcase(
|
|
336
|
+
"date-picker",
|
|
337
|
+
"Date Picker",
|
|
338
|
+
"Composes Calendar and Popover into a compact date control.",
|
|
339
|
+
DatePicker(Calendar("July", 2026, starts_on=3)),
|
|
340
|
+
),
|
|
341
|
+
component_showcase(
|
|
342
|
+
"direction",
|
|
343
|
+
"Direction",
|
|
344
|
+
"Applies left-to-right or right-to-left direction to a subtree.",
|
|
345
|
+
Direction(
|
|
346
|
+
div(tw("rounded-md border p-4"), "واجهة من اليمين إلى اليسار"), direction="rtl"
|
|
347
|
+
),
|
|
348
|
+
),
|
|
349
|
+
component_showcase(
|
|
350
|
+
"drawer",
|
|
351
|
+
"Drawer",
|
|
352
|
+
"Presents a modal task surface from the bottom edge.",
|
|
353
|
+
Drawer(DrawerTrigger("Open drawer"), DrawerContent("Account", DrawerTitle("Account"))),
|
|
354
|
+
),
|
|
355
|
+
component_showcase(
|
|
356
|
+
"input-group",
|
|
357
|
+
"Input Group",
|
|
358
|
+
"Combines an input with contextual prefixes and controls.",
|
|
359
|
+
InputGroup(InputGroupAddon("https://"), InputGroupInput(placeholder="example.com")),
|
|
360
|
+
),
|
|
361
|
+
component_showcase(
|
|
362
|
+
"input-otp",
|
|
363
|
+
"Input OTP",
|
|
364
|
+
"Captures a one-time code with a single accessible input and visual slots.",
|
|
365
|
+
InputOTP(6),
|
|
366
|
+
),
|
|
367
|
+
component_showcase(
|
|
368
|
+
"item",
|
|
369
|
+
"Item",
|
|
370
|
+
"Groups media, text, and actions into a reusable list row.",
|
|
371
|
+
Item(
|
|
372
|
+
ItemMedia("AL"),
|
|
373
|
+
ItemContent(ItemTitle("Ada Lovelace"), ItemDescription("Administrator")),
|
|
374
|
+
variant="outline",
|
|
375
|
+
),
|
|
376
|
+
),
|
|
377
|
+
component_showcase(
|
|
378
|
+
"marker",
|
|
379
|
+
"Marker",
|
|
380
|
+
"Annotates compact status or metadata.",
|
|
381
|
+
Marker(MarkerIcon("●"), MarkerContent("Live")),
|
|
382
|
+
),
|
|
383
|
+
component_showcase(
|
|
384
|
+
"menubar",
|
|
385
|
+
"Menubar",
|
|
386
|
+
"Groups application menus into a familiar horizontal control.",
|
|
387
|
+
Menubar(
|
|
388
|
+
MenubarMenu("File", MenubarItem("new", "New"), MenubarItem("open", "Open")),
|
|
389
|
+
MenubarMenu("Edit", MenubarItem("undo", "Undo"), MenubarItem("redo", "Redo")),
|
|
390
|
+
),
|
|
391
|
+
),
|
|
392
|
+
component_showcase(
|
|
393
|
+
"message",
|
|
394
|
+
"Message",
|
|
395
|
+
"Composes sender metadata and conversational content.",
|
|
396
|
+
MessageGroup(
|
|
397
|
+
Message(MessageContent(MessageHeader("Ada · 2:14 PM"), Bubble("Ready to review.")))
|
|
398
|
+
),
|
|
399
|
+
),
|
|
400
|
+
component_showcase(
|
|
401
|
+
"message-scroller",
|
|
402
|
+
"Message Scroller",
|
|
403
|
+
"Keeps a message log scrollable and offers a jump-to-latest action.",
|
|
404
|
+
MessageScroller(Bubble("Earlier update"), Bubble("Latest update", variant="sent")),
|
|
405
|
+
),
|
|
406
|
+
component_showcase(
|
|
407
|
+
"native-select",
|
|
408
|
+
"Native Select",
|
|
409
|
+
"Styles the platform select when native behavior is the best fit.",
|
|
410
|
+
NativeSelect(
|
|
411
|
+
NativeSelectOption("comfortable", "Comfortable", selected=True),
|
|
412
|
+
NativeSelectOption("compact", "Compact"),
|
|
413
|
+
label="Density",
|
|
414
|
+
),
|
|
415
|
+
),
|
|
416
|
+
component_showcase(
|
|
417
|
+
"navigation-menu",
|
|
418
|
+
"Navigation Menu",
|
|
419
|
+
"Provides semantic site navigation with current-page state.",
|
|
420
|
+
NavigationMenu(
|
|
421
|
+
NavigationMenuList(
|
|
422
|
+
NavigationMenuLink("#navigation-menu", "Overview", current=True),
|
|
423
|
+
NavigationMenuLink("#examples", "Examples"),
|
|
424
|
+
)
|
|
425
|
+
),
|
|
426
|
+
),
|
|
427
|
+
component_showcase(
|
|
428
|
+
"pagination",
|
|
429
|
+
"Pagination",
|
|
430
|
+
"Navigates a long collection through addressable pages.",
|
|
431
|
+
Pagination(
|
|
432
|
+
PaginationContent(
|
|
433
|
+
PaginationItem(PaginationPrevious("?page=1")),
|
|
434
|
+
PaginationItem(PaginationLink("?page=1", "1", current=True)),
|
|
435
|
+
PaginationItem(PaginationLink("?page=2", "2")),
|
|
436
|
+
PaginationItem(PaginationEllipsis()),
|
|
437
|
+
PaginationItem(PaginationNext("?page=2")),
|
|
438
|
+
)
|
|
439
|
+
),
|
|
440
|
+
),
|
|
441
|
+
component_showcase(
|
|
442
|
+
"resizable",
|
|
443
|
+
"Resizable",
|
|
444
|
+
"Defines accessible panel and separator structure.",
|
|
445
|
+
ResizablePanelGroup(
|
|
446
|
+
ResizablePanel(div(tw("p-6"), "Navigation"), initial_size=35),
|
|
447
|
+
ResizableHandle(with_handle=True),
|
|
448
|
+
ResizablePanel(div(tw("p-6"), "Content"), initial_size=65),
|
|
449
|
+
),
|
|
450
|
+
),
|
|
451
|
+
component_showcase(
|
|
452
|
+
"scroll-area",
|
|
453
|
+
"Scroll Area",
|
|
454
|
+
"Constrains overflowing content while preserving native scrolling.",
|
|
455
|
+
ScrollArea(
|
|
456
|
+
div(
|
|
457
|
+
tw("grid gap-3 p-3"),
|
|
458
|
+
div("Release 1"),
|
|
459
|
+
div("Release 2"),
|
|
460
|
+
div("Release 3"),
|
|
461
|
+
div("Release 4"),
|
|
462
|
+
div("Release 5"),
|
|
463
|
+
div("Release 6"),
|
|
464
|
+
div("Release 7"),
|
|
465
|
+
div("Release 8"),
|
|
466
|
+
),
|
|
467
|
+
height="8rem",
|
|
468
|
+
),
|
|
469
|
+
),
|
|
470
|
+
component_showcase(
|
|
471
|
+
"select",
|
|
472
|
+
"Select",
|
|
473
|
+
"Chooses one value from a styled floating listbox.",
|
|
474
|
+
Select(
|
|
475
|
+
SelectTrigger("Choose a fruit"),
|
|
476
|
+
SelectContent(SelectItem("apple", "Apple"), SelectItem("pear", "Pear")),
|
|
477
|
+
),
|
|
478
|
+
),
|
|
479
|
+
component_showcase(
|
|
480
|
+
"sidebar",
|
|
481
|
+
"Sidebar",
|
|
482
|
+
"Provides the structural surface used by application navigation.",
|
|
483
|
+
Sidebar(div(tw("p-5 font-medium"), "Workspace navigation")),
|
|
484
|
+
),
|
|
485
|
+
component_showcase(
|
|
486
|
+
"slider",
|
|
487
|
+
"Slider",
|
|
488
|
+
"Selects a numeric value using the native range control.",
|
|
489
|
+
Slider(45, label="Volume"),
|
|
490
|
+
),
|
|
491
|
+
component_showcase(
|
|
492
|
+
"sonner",
|
|
493
|
+
"Sonner",
|
|
494
|
+
"Announces and displays dismissible notifications.",
|
|
495
|
+
toaster,
|
|
496
|
+
),
|
|
497
|
+
component_showcase(
|
|
498
|
+
"typography",
|
|
499
|
+
"Typography",
|
|
500
|
+
"Applies a consistent prose hierarchy to semantic elements.",
|
|
501
|
+
div(
|
|
502
|
+
tw("grid gap-3"),
|
|
503
|
+
TypographyH3("The Joke Tax Chronicles"),
|
|
504
|
+
TypographyLead("A king once taxed every joke told in the kingdom."),
|
|
505
|
+
TypographyP(
|
|
506
|
+
"The people called this levy the ", TypographyInlineCode("joke tax"), "."
|
|
507
|
+
),
|
|
508
|
+
),
|
|
509
|
+
),
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
@js
|
|
514
|
+
class CatalogForm(Widget):
|
|
515
|
+
def __init__(self):
|
|
516
|
+
super().__init__()
|
|
517
|
+
self.email = ref(Input(input_type="email", placeholder="you@example.com", name="email"))
|
|
518
|
+
self.bio = ref(Textarea(placeholder="Tell us a little about yourself", name="bio"))
|
|
519
|
+
self.terms = ref(Checkbox(name="terms"))
|
|
520
|
+
self.updates = ref(Switch(name="updates", checked=True))
|
|
521
|
+
self.error = ref(FieldError("Enter an email address and accept the terms."))
|
|
522
|
+
self.error.setAttribute("hidden", "")
|
|
523
|
+
self.status = ref(
|
|
524
|
+
p(
|
|
525
|
+
tw("text-sm text-slate-500"),
|
|
526
|
+
{"aria-live": "polite", "data-testid": "form-status"},
|
|
527
|
+
"Your settings have not been submitted.",
|
|
528
|
+
)
|
|
529
|
+
)
|
|
530
|
+
self.form = ref(
|
|
531
|
+
tag(
|
|
532
|
+
"form",
|
|
533
|
+
tw("grid w-full max-w-lg gap-6"),
|
|
534
|
+
{"data-testid": "catalog-form", "novalidate": ""},
|
|
535
|
+
FieldSet(
|
|
536
|
+
FieldGroup(
|
|
537
|
+
Field(
|
|
538
|
+
FieldLabel(
|
|
539
|
+
FieldTitle("Email"),
|
|
540
|
+
self.email,
|
|
541
|
+
FieldDescription("We will only use this for account updates."),
|
|
542
|
+
),
|
|
543
|
+
),
|
|
544
|
+
Field(
|
|
545
|
+
FieldLabel(
|
|
546
|
+
FieldTitle("Bio"),
|
|
547
|
+
self.bio,
|
|
548
|
+
FieldDescription("A short introduction shown on your profile."),
|
|
549
|
+
),
|
|
550
|
+
),
|
|
551
|
+
Field(
|
|
552
|
+
Label(
|
|
553
|
+
self.terms,
|
|
554
|
+
FieldContent(
|
|
555
|
+
FieldTitle("Accept the terms"),
|
|
556
|
+
FieldDescription(
|
|
557
|
+
"I agree to the terms of service and privacy policy."
|
|
558
|
+
),
|
|
559
|
+
),
|
|
560
|
+
),
|
|
561
|
+
orientation="horizontal",
|
|
562
|
+
),
|
|
563
|
+
Field(
|
|
564
|
+
Label(
|
|
565
|
+
self.updates,
|
|
566
|
+
FieldContent(
|
|
567
|
+
FieldTitle("Product updates"),
|
|
568
|
+
FieldDescription(
|
|
569
|
+
"Send occasional news about new PyJS components."
|
|
570
|
+
),
|
|
571
|
+
),
|
|
572
|
+
),
|
|
573
|
+
orientation="horizontal",
|
|
574
|
+
),
|
|
575
|
+
self.error,
|
|
576
|
+
)
|
|
577
|
+
),
|
|
578
|
+
button(
|
|
579
|
+
tw(
|
|
580
|
+
"inline-flex h-9 w-fit items-center justify-center rounded-md "
|
|
581
|
+
"bg-slate-950 px-4 text-sm font-medium text-white shadow-sm "
|
|
582
|
+
"hover:bg-slate-800 focus-visible:outline-none focus-visible:ring-2 "
|
|
583
|
+
"focus-visible:ring-slate-400"
|
|
584
|
+
),
|
|
585
|
+
{"type": "submit"},
|
|
586
|
+
"Save preferences",
|
|
587
|
+
),
|
|
588
|
+
self.status,
|
|
589
|
+
)
|
|
590
|
+
)
|
|
591
|
+
self.form.addEventListener("submit", self.submit)
|
|
592
|
+
self.add(self.form)
|
|
593
|
+
|
|
594
|
+
def submit(self, event: Event):
|
|
595
|
+
event.preventDefault()
|
|
596
|
+
email_missing = self.email.get_value() == ""
|
|
597
|
+
terms_missing = not self.terms.is_checked()
|
|
598
|
+
self.email.set_invalid(email_missing)
|
|
599
|
+
self.terms.set_invalid(terms_missing)
|
|
600
|
+
|
|
601
|
+
if email_missing or terms_missing:
|
|
602
|
+
self.error.removeAttribute("hidden")
|
|
603
|
+
self.status.textContent = "Please correct the highlighted fields."
|
|
604
|
+
return
|
|
605
|
+
|
|
606
|
+
self.error.setAttribute("hidden", "")
|
|
607
|
+
self.status.textContent = "Preferences saved for " + self.email.get_value() + "."
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
@js
|
|
611
|
+
class CatalogApp(Widget):
|
|
612
|
+
def __init__(self):
|
|
613
|
+
super().__init__()
|
|
614
|
+
self.name_input = ref(
|
|
615
|
+
input(
|
|
616
|
+
tw(
|
|
617
|
+
"h-9 rounded-md border border-slate-300 bg-white px-3 text-sm shadow-sm "
|
|
618
|
+
"outline-none focus:border-slate-500 focus:ring-2 focus:ring-slate-200"
|
|
619
|
+
),
|
|
620
|
+
{"autofocus": "", "value": "Ada Lovelace"},
|
|
621
|
+
)
|
|
622
|
+
)
|
|
623
|
+
self.username_input = ref(
|
|
624
|
+
input(
|
|
625
|
+
tw(
|
|
626
|
+
"h-9 rounded-md border border-slate-300 bg-white px-3 text-sm shadow-sm "
|
|
627
|
+
"outline-none focus:border-slate-500 focus:ring-2 focus:ring-slate-200"
|
|
628
|
+
),
|
|
629
|
+
{"value": "@ada"},
|
|
630
|
+
)
|
|
631
|
+
)
|
|
632
|
+
self.save_button = ref(
|
|
633
|
+
button(
|
|
634
|
+
tw(
|
|
635
|
+
"inline-flex h-9 items-center justify-center rounded-md bg-slate-950 px-4 "
|
|
636
|
+
"text-sm font-medium text-white shadow-sm hover:bg-slate-800 "
|
|
637
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400"
|
|
638
|
+
),
|
|
639
|
+
{"type": "button"},
|
|
640
|
+
"Save changes",
|
|
641
|
+
)
|
|
642
|
+
)
|
|
643
|
+
self.status = ref(
|
|
644
|
+
p(
|
|
645
|
+
tw("mt-4 text-sm text-slate-500"),
|
|
646
|
+
{"aria-live": "polite", "data-testid": "profile-status"},
|
|
647
|
+
"Open the dialog to edit the profile.",
|
|
648
|
+
)
|
|
649
|
+
)
|
|
650
|
+
self.dialog = ref(
|
|
651
|
+
Dialog(
|
|
652
|
+
DialogTrigger("Edit profile"),
|
|
653
|
+
DialogContent(
|
|
654
|
+
"Edit profile",
|
|
655
|
+
DialogHeader(
|
|
656
|
+
DialogTitle("Edit profile"),
|
|
657
|
+
DialogDescription(
|
|
658
|
+
"Make changes to your profile here. Click save when you are done."
|
|
659
|
+
),
|
|
660
|
+
),
|
|
661
|
+
div(
|
|
662
|
+
tw("grid gap-4 py-2"),
|
|
663
|
+
field("Name", self.name_input),
|
|
664
|
+
field("Username", self.username_input),
|
|
665
|
+
),
|
|
666
|
+
DialogFooter(DialogClose("Cancel"), self.save_button),
|
|
667
|
+
),
|
|
668
|
+
)
|
|
669
|
+
)
|
|
670
|
+
self.save_button.addEventListener("click", self.save_profile)
|
|
671
|
+
|
|
672
|
+
sidebar = tag(
|
|
673
|
+
"aside",
|
|
674
|
+
tw("hidden w-64 shrink-0 border-r border-slate-200 bg-white px-6 py-8 lg:block"),
|
|
675
|
+
tag(
|
|
676
|
+
"nav",
|
|
677
|
+
tw("sticky top-24 grid max-h-[calc(100vh-7rem)] gap-8 overflow-y-auto text-sm"),
|
|
678
|
+
div(
|
|
679
|
+
p(tw("mb-3 font-semibold text-slate-950"), "Components"),
|
|
680
|
+
div(
|
|
681
|
+
tw("grid gap-1"),
|
|
682
|
+
a(
|
|
683
|
+
tw(
|
|
684
|
+
"block rounded-md bg-slate-100 px-3 py-2 font-medium text-slate-950"
|
|
685
|
+
),
|
|
686
|
+
{"href": "#dialog"},
|
|
687
|
+
"Dialog",
|
|
688
|
+
),
|
|
689
|
+
a(
|
|
690
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
691
|
+
{"href": "#accordion"},
|
|
692
|
+
"Accordion",
|
|
693
|
+
),
|
|
694
|
+
a(
|
|
695
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
696
|
+
{"href": "#alert"},
|
|
697
|
+
"Alert",
|
|
698
|
+
),
|
|
699
|
+
a(
|
|
700
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
701
|
+
{"href": "#alert-dialog"},
|
|
702
|
+
"Alert Dialog",
|
|
703
|
+
),
|
|
704
|
+
a(
|
|
705
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
706
|
+
{"href": "#aspect-ratio"},
|
|
707
|
+
"Aspect Ratio",
|
|
708
|
+
),
|
|
709
|
+
a(
|
|
710
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
711
|
+
{"href": "#avatar"},
|
|
712
|
+
"Avatar",
|
|
713
|
+
),
|
|
714
|
+
a(
|
|
715
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
716
|
+
{"href": "#badge"},
|
|
717
|
+
"Badge",
|
|
718
|
+
),
|
|
719
|
+
a(
|
|
720
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
721
|
+
{"href": "#breadcrumb"},
|
|
722
|
+
"Breadcrumb",
|
|
723
|
+
),
|
|
724
|
+
a(
|
|
725
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
726
|
+
{"href": "#button"},
|
|
727
|
+
"Button",
|
|
728
|
+
),
|
|
729
|
+
a(
|
|
730
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
731
|
+
{"href": "#button-group"},
|
|
732
|
+
"Button Group",
|
|
733
|
+
),
|
|
734
|
+
a(
|
|
735
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
736
|
+
{"href": "#card"},
|
|
737
|
+
"Card",
|
|
738
|
+
),
|
|
739
|
+
a(
|
|
740
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
741
|
+
{"href": "#collapsible"},
|
|
742
|
+
"Collapsible",
|
|
743
|
+
),
|
|
744
|
+
a(
|
|
745
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
746
|
+
{"href": "#empty"},
|
|
747
|
+
"Empty",
|
|
748
|
+
),
|
|
749
|
+
a(
|
|
750
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
751
|
+
{"href": "#forms"},
|
|
752
|
+
"Forms",
|
|
753
|
+
),
|
|
754
|
+
a(
|
|
755
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
756
|
+
{"href": "#hover-card"},
|
|
757
|
+
"Hover Card",
|
|
758
|
+
),
|
|
759
|
+
a(
|
|
760
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
761
|
+
{"href": "#kbd"},
|
|
762
|
+
"Kbd",
|
|
763
|
+
),
|
|
764
|
+
a(
|
|
765
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
766
|
+
{"href": "#popover"},
|
|
767
|
+
"Popover",
|
|
768
|
+
),
|
|
769
|
+
a(
|
|
770
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
771
|
+
{"href": "#progress"},
|
|
772
|
+
"Progress",
|
|
773
|
+
),
|
|
774
|
+
a(
|
|
775
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
776
|
+
{"href": "#radio-group"},
|
|
777
|
+
"Radio Group",
|
|
778
|
+
),
|
|
779
|
+
a(
|
|
780
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
781
|
+
{"href": "#separator"},
|
|
782
|
+
"Separator",
|
|
783
|
+
),
|
|
784
|
+
a(
|
|
785
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
786
|
+
{"href": "#sheet"},
|
|
787
|
+
"Sheet",
|
|
788
|
+
),
|
|
789
|
+
a(
|
|
790
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
791
|
+
{"href": "#skeleton"},
|
|
792
|
+
"Skeleton",
|
|
793
|
+
),
|
|
794
|
+
a(
|
|
795
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
796
|
+
{"href": "#spinner"},
|
|
797
|
+
"Spinner",
|
|
798
|
+
),
|
|
799
|
+
a(
|
|
800
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
801
|
+
{"href": "#table"},
|
|
802
|
+
"Table",
|
|
803
|
+
),
|
|
804
|
+
a(
|
|
805
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
806
|
+
{"href": "#tabs"},
|
|
807
|
+
"Tabs",
|
|
808
|
+
),
|
|
809
|
+
a(
|
|
810
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
811
|
+
{"href": "#toggle"},
|
|
812
|
+
"Toggle",
|
|
813
|
+
),
|
|
814
|
+
a(
|
|
815
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
816
|
+
{"href": "#toggle-group"},
|
|
817
|
+
"Toggle Group",
|
|
818
|
+
),
|
|
819
|
+
a(
|
|
820
|
+
tw("block rounded-md px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
821
|
+
{"href": "#tooltip"},
|
|
822
|
+
"Tooltip",
|
|
823
|
+
),
|
|
824
|
+
remaining_sidebar_links(),
|
|
825
|
+
),
|
|
826
|
+
),
|
|
827
|
+
div(
|
|
828
|
+
p(tw("mb-3 font-semibold text-slate-950"), "Catalog"),
|
|
829
|
+
a(
|
|
830
|
+
tw("block px-3 py-2 text-slate-600 hover:text-slate-950"),
|
|
831
|
+
{"href": "#examples"},
|
|
832
|
+
"Example applications",
|
|
833
|
+
),
|
|
834
|
+
),
|
|
835
|
+
),
|
|
836
|
+
)
|
|
837
|
+
dialog_preview = div(
|
|
838
|
+
tw(
|
|
839
|
+
"flex min-h-72 items-center justify-center rounded-lg border border-slate-200 "
|
|
840
|
+
"bg-white p-8"
|
|
841
|
+
),
|
|
842
|
+
div(tw("text-center"), self.dialog, self.status),
|
|
843
|
+
)
|
|
844
|
+
source = (
|
|
845
|
+
"Dialog(\n"
|
|
846
|
+
' DialogTrigger("Edit profile"),\n'
|
|
847
|
+
" DialogContent(\n"
|
|
848
|
+
' "Edit profile",\n'
|
|
849
|
+
" DialogHeader(\n"
|
|
850
|
+
' DialogTitle("Edit profile"),\n'
|
|
851
|
+
' DialogDescription("Make changes to your profile."),\n'
|
|
852
|
+
" ),\n"
|
|
853
|
+
" profile_fields,\n"
|
|
854
|
+
' DialogFooter(DialogClose("Cancel"), save_button),\n'
|
|
855
|
+
" ),\n"
|
|
856
|
+
")"
|
|
857
|
+
)
|
|
858
|
+
content = tag(
|
|
859
|
+
"main",
|
|
860
|
+
tw("min-w-0 flex-1 px-6 py-12 sm:px-10 lg:px-14"),
|
|
861
|
+
tag(
|
|
862
|
+
"section",
|
|
863
|
+
tw("mx-auto max-w-5xl scroll-mt-24"),
|
|
864
|
+
{"data-catalog-component": "dialog", "id": "dialog"},
|
|
865
|
+
p(
|
|
866
|
+
tw("text-sm font-medium text-slate-500"),
|
|
867
|
+
"Components / Dialog",
|
|
868
|
+
),
|
|
869
|
+
h1(
|
|
870
|
+
tw("mt-3 text-4xl font-bold tracking-tight text-slate-950"),
|
|
871
|
+
"Dialog",
|
|
872
|
+
),
|
|
873
|
+
p(
|
|
874
|
+
tw("mt-4 max-w-2xl text-lg leading-8 text-slate-600"),
|
|
875
|
+
"A modal window layered over the page. It traps focus, supports Escape and "
|
|
876
|
+
"backdrop dismissal, and restores focus to its trigger.",
|
|
877
|
+
),
|
|
878
|
+
div(tw("mt-10"), dialog_preview),
|
|
879
|
+
div(
|
|
880
|
+
tw("mt-8 overflow-hidden rounded-lg border border-slate-800 bg-slate-950"),
|
|
881
|
+
div(
|
|
882
|
+
tw("border-b border-slate-800 px-4 py-3 text-xs text-slate-400"),
|
|
883
|
+
"Python",
|
|
884
|
+
),
|
|
885
|
+
tag(
|
|
886
|
+
"pre",
|
|
887
|
+
tw("overflow-x-auto p-5 text-sm leading-6 text-slate-100"),
|
|
888
|
+
tag("code", source),
|
|
889
|
+
),
|
|
890
|
+
),
|
|
891
|
+
component_showcase(
|
|
892
|
+
"accordion",
|
|
893
|
+
"Accordion",
|
|
894
|
+
"Expands and collapses vertically stacked sections of content.",
|
|
895
|
+
AccordionGroup(
|
|
896
|
+
AccordionItem(
|
|
897
|
+
"product",
|
|
898
|
+
"Is it accessible?",
|
|
899
|
+
"Yes. It uses semantic buttons, expanded state, and keyboard focus.",
|
|
900
|
+
open=True,
|
|
901
|
+
),
|
|
902
|
+
AccordionItem(
|
|
903
|
+
"styling",
|
|
904
|
+
"Can it be styled?",
|
|
905
|
+
"Yes. The component exposes stable data-slot and data-state attributes.",
|
|
906
|
+
),
|
|
907
|
+
AccordionItem(
|
|
908
|
+
"animation",
|
|
909
|
+
"Can it be animated?",
|
|
910
|
+
"Yes. The state attributes provide hooks for CSS transitions.",
|
|
911
|
+
),
|
|
912
|
+
),
|
|
913
|
+
),
|
|
914
|
+
component_showcase(
|
|
915
|
+
"alert",
|
|
916
|
+
"Alert",
|
|
917
|
+
"Displays an important message without interrupting the current task.",
|
|
918
|
+
div(
|
|
919
|
+
tw("grid gap-4"),
|
|
920
|
+
Alert(
|
|
921
|
+
AlertTitle("Heads up!"),
|
|
922
|
+
AlertDescription("The catalog now includes foundation components."),
|
|
923
|
+
AlertAction(
|
|
924
|
+
button(
|
|
925
|
+
tw("text-xs font-medium underline underline-offset-4"),
|
|
926
|
+
{"type": "button"},
|
|
927
|
+
"Details",
|
|
928
|
+
)
|
|
929
|
+
),
|
|
930
|
+
),
|
|
931
|
+
Alert(
|
|
932
|
+
AlertTitle("Unable to save"),
|
|
933
|
+
AlertDescription("Check the form values and try again."),
|
|
934
|
+
variant="destructive",
|
|
935
|
+
),
|
|
936
|
+
),
|
|
937
|
+
),
|
|
938
|
+
component_showcase(
|
|
939
|
+
"alert-dialog",
|
|
940
|
+
"Alert Dialog",
|
|
941
|
+
"Interrupts a destructive action until the user explicitly confirms or cancels.",
|
|
942
|
+
AlertDialog(
|
|
943
|
+
AlertDialogTrigger("Delete project"),
|
|
944
|
+
AlertDialogContent(
|
|
945
|
+
"Delete project",
|
|
946
|
+
AlertDialogHeader(
|
|
947
|
+
AlertDialogTitle("Are you absolutely sure?"),
|
|
948
|
+
AlertDialogDescription(
|
|
949
|
+
"This permanently deletes the project and its deployment history."
|
|
950
|
+
),
|
|
951
|
+
),
|
|
952
|
+
AlertDialogFooter(
|
|
953
|
+
AlertDialogCancel("Cancel"),
|
|
954
|
+
AlertDialogAction("Continue"),
|
|
955
|
+
),
|
|
956
|
+
),
|
|
957
|
+
),
|
|
958
|
+
),
|
|
959
|
+
component_showcase(
|
|
960
|
+
"aspect-ratio",
|
|
961
|
+
"Aspect Ratio",
|
|
962
|
+
"Keeps media and previews at a consistent proportional size.",
|
|
963
|
+
div(
|
|
964
|
+
tw("max-w-xl"),
|
|
965
|
+
AspectRatio(
|
|
966
|
+
div(
|
|
967
|
+
tw(
|
|
968
|
+
"absolute inset-0 flex items-end rounded-lg bg-gradient-to-br "
|
|
969
|
+
"from-slate-950 via-slate-700 to-slate-400 p-6 text-white"
|
|
970
|
+
),
|
|
971
|
+
p(tw("text-lg font-medium"), "16:9 preview"),
|
|
972
|
+
),
|
|
973
|
+
ratio="16 / 9",
|
|
974
|
+
),
|
|
975
|
+
),
|
|
976
|
+
),
|
|
977
|
+
component_showcase(
|
|
978
|
+
"avatar",
|
|
979
|
+
"Avatar",
|
|
980
|
+
"Represents a person with an image, initials fallback, size, and status.",
|
|
981
|
+
AvatarGroup(
|
|
982
|
+
Avatar(AvatarBadge(), fallback="AL", alt="Ada Lovelace"),
|
|
983
|
+
Avatar(fallback="GH", alt="Grace Hopper"),
|
|
984
|
+
Avatar(fallback="KT", alt="Katherine Johnson"),
|
|
985
|
+
AvatarGroupCount("+3"),
|
|
986
|
+
),
|
|
987
|
+
),
|
|
988
|
+
component_showcase(
|
|
989
|
+
"badge",
|
|
990
|
+
"Badge",
|
|
991
|
+
"A compact label for status, categories, and short metadata.",
|
|
992
|
+
div(
|
|
993
|
+
tw("flex flex-wrap items-center gap-3"),
|
|
994
|
+
Badge("Default"),
|
|
995
|
+
Badge("Secondary", variant="secondary"),
|
|
996
|
+
Badge("Destructive", variant="destructive"),
|
|
997
|
+
Badge("Outline", variant="outline"),
|
|
998
|
+
Badge("Ghost", variant="ghost"),
|
|
999
|
+
Badge("Link", variant="link"),
|
|
1000
|
+
),
|
|
1001
|
+
),
|
|
1002
|
+
component_showcase(
|
|
1003
|
+
"breadcrumb",
|
|
1004
|
+
"Breadcrumb",
|
|
1005
|
+
"Shows the current page's location within a navigational hierarchy.",
|
|
1006
|
+
Breadcrumb(
|
|
1007
|
+
BreadcrumbList(
|
|
1008
|
+
BreadcrumbItem(BreadcrumbLink("#", "Catalog")),
|
|
1009
|
+
BreadcrumbSeparator(),
|
|
1010
|
+
BreadcrumbItem(BreadcrumbLink("#dialog", "Components")),
|
|
1011
|
+
BreadcrumbSeparator(),
|
|
1012
|
+
BreadcrumbItem(BreadcrumbPage("Breadcrumb")),
|
|
1013
|
+
)
|
|
1014
|
+
),
|
|
1015
|
+
),
|
|
1016
|
+
component_showcase(
|
|
1017
|
+
"button",
|
|
1018
|
+
"Button",
|
|
1019
|
+
"Triggers an action using native button behavior and familiar visual variants.",
|
|
1020
|
+
div(
|
|
1021
|
+
tw("flex flex-wrap items-center gap-3"),
|
|
1022
|
+
Button("Default"),
|
|
1023
|
+
Button("Outline", variant="outline"),
|
|
1024
|
+
Button("Secondary", variant="secondary"),
|
|
1025
|
+
Button("Ghost", variant="ghost"),
|
|
1026
|
+
Button("Delete", variant="destructive"),
|
|
1027
|
+
Button("Learn more", variant="link"),
|
|
1028
|
+
),
|
|
1029
|
+
),
|
|
1030
|
+
component_showcase(
|
|
1031
|
+
"button-group",
|
|
1032
|
+
"Button Group",
|
|
1033
|
+
"Combines related controls into a visually connected group.",
|
|
1034
|
+
ButtonGroup(
|
|
1035
|
+
Button("Back", variant="outline"),
|
|
1036
|
+
ButtonGroupText("Page 2 of 8"),
|
|
1037
|
+
Button("Next", variant="outline"),
|
|
1038
|
+
),
|
|
1039
|
+
),
|
|
1040
|
+
component_showcase(
|
|
1041
|
+
"card",
|
|
1042
|
+
"Card",
|
|
1043
|
+
"Groups related content and actions inside a bordered surface.",
|
|
1044
|
+
div(
|
|
1045
|
+
tw("max-w-md"),
|
|
1046
|
+
Card(
|
|
1047
|
+
CardHeader(
|
|
1048
|
+
CardTitle("Team plan"),
|
|
1049
|
+
CardDescription("For growing product teams."),
|
|
1050
|
+
CardAction(Badge("Popular", variant="secondary")),
|
|
1051
|
+
),
|
|
1052
|
+
CardContent(
|
|
1053
|
+
"Unlimited projects, shared components, and priority support."
|
|
1054
|
+
),
|
|
1055
|
+
CardFooter(
|
|
1056
|
+
Button("Choose plan"),
|
|
1057
|
+
Button("Details", variant="outline"),
|
|
1058
|
+
),
|
|
1059
|
+
),
|
|
1060
|
+
),
|
|
1061
|
+
),
|
|
1062
|
+
component_showcase(
|
|
1063
|
+
"collapsible",
|
|
1064
|
+
"Collapsible",
|
|
1065
|
+
"Reveals additional content without leaving the current context.",
|
|
1066
|
+
Collapsible(
|
|
1067
|
+
CollapsibleTrigger(
|
|
1068
|
+
tag("span", "Three deployment files"),
|
|
1069
|
+
tag("span", {"aria-hidden": "true"}, "+"),
|
|
1070
|
+
),
|
|
1071
|
+
CollapsibleContent(
|
|
1072
|
+
tag("code", tw("block rounded bg-slate-100 px-2 py-1"), "app.py"),
|
|
1073
|
+
tag(
|
|
1074
|
+
"code",
|
|
1075
|
+
tw("mt-1 block rounded bg-slate-100 px-2 py-1"),
|
|
1076
|
+
"pyproject.toml",
|
|
1077
|
+
),
|
|
1078
|
+
tag(
|
|
1079
|
+
"code", tw("mt-1 block rounded bg-slate-100 px-2 py-1"), "README.md"
|
|
1080
|
+
),
|
|
1081
|
+
),
|
|
1082
|
+
),
|
|
1083
|
+
),
|
|
1084
|
+
component_showcase(
|
|
1085
|
+
"empty",
|
|
1086
|
+
"Empty",
|
|
1087
|
+
"Explains an empty state and offers a clear next action.",
|
|
1088
|
+
Empty(
|
|
1089
|
+
EmptyHeader(
|
|
1090
|
+
EmptyMedia("+", variant="icon"),
|
|
1091
|
+
EmptyTitle("No projects yet"),
|
|
1092
|
+
EmptyDescription(
|
|
1093
|
+
"Create your first project to start building with PyJS UI."
|
|
1094
|
+
),
|
|
1095
|
+
),
|
|
1096
|
+
EmptyContent(Button("Create project")),
|
|
1097
|
+
),
|
|
1098
|
+
),
|
|
1099
|
+
component_showcase(
|
|
1100
|
+
"forms",
|
|
1101
|
+
"Forms",
|
|
1102
|
+
"Collects and validates user input using native, accessible form controls.",
|
|
1103
|
+
CatalogForm(),
|
|
1104
|
+
),
|
|
1105
|
+
component_showcase(
|
|
1106
|
+
"hover-card",
|
|
1107
|
+
"Hover Card",
|
|
1108
|
+
"Previews useful context when a link is hovered or receives keyboard focus.",
|
|
1109
|
+
HoverCard(
|
|
1110
|
+
HoverCardTrigger("@ada", href="#hover-card"),
|
|
1111
|
+
HoverCardContent(
|
|
1112
|
+
div(
|
|
1113
|
+
tw("flex gap-4"),
|
|
1114
|
+
Avatar(fallback="AL", alt="Ada Lovelace"),
|
|
1115
|
+
div(
|
|
1116
|
+
h3(tw("font-semibold text-slate-950"), "Ada Lovelace"),
|
|
1117
|
+
p(
|
|
1118
|
+
tw("mt-1 text-sm leading-5 text-slate-600"),
|
|
1119
|
+
"Mathematician and author of the first published algorithm.",
|
|
1120
|
+
),
|
|
1121
|
+
),
|
|
1122
|
+
)
|
|
1123
|
+
),
|
|
1124
|
+
align="start",
|
|
1125
|
+
),
|
|
1126
|
+
),
|
|
1127
|
+
component_showcase(
|
|
1128
|
+
"kbd",
|
|
1129
|
+
"Kbd",
|
|
1130
|
+
"Displays keyboard input and shortcut combinations.",
|
|
1131
|
+
div(
|
|
1132
|
+
tw("flex items-center gap-3 text-sm text-slate-600"),
|
|
1133
|
+
"Open command menu",
|
|
1134
|
+
KbdGroup(Kbd("Ctrl"), "+", Kbd("K")),
|
|
1135
|
+
),
|
|
1136
|
+
),
|
|
1137
|
+
component_showcase(
|
|
1138
|
+
"popover",
|
|
1139
|
+
"Popover",
|
|
1140
|
+
"Displays interactive content in a floating panel anchored to its trigger.",
|
|
1141
|
+
Popover(
|
|
1142
|
+
PopoverTrigger("Open settings"),
|
|
1143
|
+
PopoverContent(
|
|
1144
|
+
PopoverHeader(
|
|
1145
|
+
PopoverTitle("Dimensions"),
|
|
1146
|
+
PopoverDescription("Set the size for the workspace."),
|
|
1147
|
+
),
|
|
1148
|
+
div(
|
|
1149
|
+
tw("grid gap-2 text-sm"),
|
|
1150
|
+
Label("Width", Input(placeholder="100%", name="width")),
|
|
1151
|
+
Label("Height", Input(placeholder="320px", name="height")),
|
|
1152
|
+
),
|
|
1153
|
+
PopoverClose("Done"),
|
|
1154
|
+
label="Dimensions",
|
|
1155
|
+
),
|
|
1156
|
+
),
|
|
1157
|
+
),
|
|
1158
|
+
component_showcase(
|
|
1159
|
+
"progress",
|
|
1160
|
+
"Progress",
|
|
1161
|
+
"Displays completion for a task whose current value is known.",
|
|
1162
|
+
div(
|
|
1163
|
+
tw("grid max-w-lg gap-3"),
|
|
1164
|
+
div(
|
|
1165
|
+
tw("flex items-center justify-between text-sm"),
|
|
1166
|
+
tag("span", tw("font-medium text-slate-950"), "Uploading files"),
|
|
1167
|
+
tag("span", tw("text-slate-500"), "66%"),
|
|
1168
|
+
),
|
|
1169
|
+
ProgressBar(66, "File upload progress"),
|
|
1170
|
+
),
|
|
1171
|
+
),
|
|
1172
|
+
component_showcase(
|
|
1173
|
+
"radio-group",
|
|
1174
|
+
"Radio Group",
|
|
1175
|
+
"Chooses one value from a visible set of mutually exclusive options.",
|
|
1176
|
+
RadioGroup(
|
|
1177
|
+
RadioGroupItem("default", "Default"),
|
|
1178
|
+
RadioGroupItem("comfortable", "Comfortable", checked=True),
|
|
1179
|
+
RadioGroupItem("compact", "Compact"),
|
|
1180
|
+
name="catalog-density",
|
|
1181
|
+
),
|
|
1182
|
+
),
|
|
1183
|
+
component_showcase(
|
|
1184
|
+
"separator",
|
|
1185
|
+
"Separator",
|
|
1186
|
+
"Visually or semantically separates groups of related content.",
|
|
1187
|
+
div(
|
|
1188
|
+
tw("flex h-6 items-center gap-4 text-sm text-slate-700"),
|
|
1189
|
+
"Overview",
|
|
1190
|
+
Separator("vertical"),
|
|
1191
|
+
"Components",
|
|
1192
|
+
Separator("vertical", decorative=False),
|
|
1193
|
+
"Examples",
|
|
1194
|
+
),
|
|
1195
|
+
),
|
|
1196
|
+
component_showcase(
|
|
1197
|
+
"sheet",
|
|
1198
|
+
"Sheet",
|
|
1199
|
+
"Extends a modal panel from an edge of the viewport for supporting tasks.",
|
|
1200
|
+
Sheet(
|
|
1201
|
+
SheetTrigger("Edit profile"),
|
|
1202
|
+
SheetContent(
|
|
1203
|
+
"Edit profile",
|
|
1204
|
+
SheetHeader(
|
|
1205
|
+
SheetTitle("Edit profile"),
|
|
1206
|
+
SheetDescription(
|
|
1207
|
+
"Make changes here, then close the sheet when finished."
|
|
1208
|
+
),
|
|
1209
|
+
),
|
|
1210
|
+
div(
|
|
1211
|
+
tw("grid gap-4"),
|
|
1212
|
+
Label(
|
|
1213
|
+
"Name",
|
|
1214
|
+
Input(placeholder="Ada Lovelace", name="sheet-name"),
|
|
1215
|
+
),
|
|
1216
|
+
Label(
|
|
1217
|
+
"Username",
|
|
1218
|
+
Input(placeholder="@ada", name="sheet-username"),
|
|
1219
|
+
),
|
|
1220
|
+
),
|
|
1221
|
+
SheetFooter(SheetClose("Save changes")),
|
|
1222
|
+
side="right",
|
|
1223
|
+
),
|
|
1224
|
+
),
|
|
1225
|
+
),
|
|
1226
|
+
component_showcase(
|
|
1227
|
+
"skeleton",
|
|
1228
|
+
"Skeleton",
|
|
1229
|
+
"A placeholder that preserves layout while content is loading.",
|
|
1230
|
+
div(
|
|
1231
|
+
tw("flex max-w-md items-center gap-4"),
|
|
1232
|
+
Skeleton("3rem", "3rem"),
|
|
1233
|
+
div(
|
|
1234
|
+
tw("grid flex-1 gap-2"),
|
|
1235
|
+
Skeleton("75%", "0.875rem"),
|
|
1236
|
+
Skeleton("100%", "0.875rem"),
|
|
1237
|
+
),
|
|
1238
|
+
),
|
|
1239
|
+
),
|
|
1240
|
+
component_showcase(
|
|
1241
|
+
"spinner",
|
|
1242
|
+
"Spinner",
|
|
1243
|
+
"Communicates that an indeterminate operation is in progress.",
|
|
1244
|
+
div(
|
|
1245
|
+
tw("flex items-center gap-4"),
|
|
1246
|
+
Spinner(),
|
|
1247
|
+
Button("Saving", variant="secondary"),
|
|
1248
|
+
),
|
|
1249
|
+
),
|
|
1250
|
+
component_showcase(
|
|
1251
|
+
"table",
|
|
1252
|
+
"Table",
|
|
1253
|
+
"A responsive collection of semantic elements for tabular data.",
|
|
1254
|
+
Table(
|
|
1255
|
+
TableCaption("A list of recent invoices."),
|
|
1256
|
+
TableHeader(
|
|
1257
|
+
TableRow(
|
|
1258
|
+
TableHead("Invoice"),
|
|
1259
|
+
TableHead("Status"),
|
|
1260
|
+
TableHead("Amount"),
|
|
1261
|
+
)
|
|
1262
|
+
),
|
|
1263
|
+
TableBody(
|
|
1264
|
+
TableRow(
|
|
1265
|
+
TableCell("INV001"),
|
|
1266
|
+
TableCell(Badge("Paid", variant="secondary")),
|
|
1267
|
+
TableCell("$250.00"),
|
|
1268
|
+
),
|
|
1269
|
+
TableRow(
|
|
1270
|
+
TableCell("INV002"),
|
|
1271
|
+
TableCell(Badge("Pending", variant="outline")),
|
|
1272
|
+
TableCell("$150.00"),
|
|
1273
|
+
),
|
|
1274
|
+
TableRow(
|
|
1275
|
+
TableCell("INV003"),
|
|
1276
|
+
TableCell(Badge("Paid", variant="secondary")),
|
|
1277
|
+
TableCell("$350.00"),
|
|
1278
|
+
),
|
|
1279
|
+
),
|
|
1280
|
+
TableFooter(
|
|
1281
|
+
TableRow(
|
|
1282
|
+
TableCell("Total"),
|
|
1283
|
+
TableCell("3 invoices"),
|
|
1284
|
+
TableCell("$750.00"),
|
|
1285
|
+
)
|
|
1286
|
+
),
|
|
1287
|
+
),
|
|
1288
|
+
),
|
|
1289
|
+
component_showcase(
|
|
1290
|
+
"tabs",
|
|
1291
|
+
"Tabs",
|
|
1292
|
+
"Switches between related sections while showing one panel at a time.",
|
|
1293
|
+
TabGroup(
|
|
1294
|
+
Tab(
|
|
1295
|
+
"account",
|
|
1296
|
+
"Account",
|
|
1297
|
+
h3(tw("font-medium text-slate-950"), "Account"),
|
|
1298
|
+
p(
|
|
1299
|
+
tw("mt-1 text-slate-600"),
|
|
1300
|
+
"Update your name, email address, and profile details.",
|
|
1301
|
+
),
|
|
1302
|
+
),
|
|
1303
|
+
Tab(
|
|
1304
|
+
"password",
|
|
1305
|
+
"Password",
|
|
1306
|
+
h3(tw("font-medium text-slate-950"), "Password"),
|
|
1307
|
+
p(
|
|
1308
|
+
tw("mt-1 text-slate-600"),
|
|
1309
|
+
"Choose a strong password you do not use elsewhere.",
|
|
1310
|
+
),
|
|
1311
|
+
),
|
|
1312
|
+
Tab(
|
|
1313
|
+
"notifications",
|
|
1314
|
+
"Notifications",
|
|
1315
|
+
h3(tw("font-medium text-slate-950"), "Notifications"),
|
|
1316
|
+
p(
|
|
1317
|
+
tw("mt-1 text-slate-600"),
|
|
1318
|
+
"Decide which product updates should reach your inbox.",
|
|
1319
|
+
),
|
|
1320
|
+
),
|
|
1321
|
+
default_value="account",
|
|
1322
|
+
),
|
|
1323
|
+
),
|
|
1324
|
+
component_showcase(
|
|
1325
|
+
"toggle",
|
|
1326
|
+
"Toggle",
|
|
1327
|
+
"A two-state button that keeps its pressed state on the DOM.",
|
|
1328
|
+
div(
|
|
1329
|
+
tw("flex items-center gap-2"),
|
|
1330
|
+
Toggle("Bold", pressed=True),
|
|
1331
|
+
Toggle("Italic", variant="outline"),
|
|
1332
|
+
),
|
|
1333
|
+
),
|
|
1334
|
+
component_showcase(
|
|
1335
|
+
"toggle-group",
|
|
1336
|
+
"Toggle Group",
|
|
1337
|
+
"Groups related toggles while optionally enforcing a single selection.",
|
|
1338
|
+
ToggleGroup(
|
|
1339
|
+
Toggle("Left", value="left"),
|
|
1340
|
+
Toggle("Center", value="center", pressed=True),
|
|
1341
|
+
Toggle("Right", value="right"),
|
|
1342
|
+
),
|
|
1343
|
+
),
|
|
1344
|
+
component_showcase(
|
|
1345
|
+
"tooltip",
|
|
1346
|
+
"Tooltip",
|
|
1347
|
+
"Names or describes a control when it is hovered or receives keyboard focus.",
|
|
1348
|
+
Tooltip(
|
|
1349
|
+
TooltipTrigger("?"),
|
|
1350
|
+
TooltipContent("Add this page to your library"),
|
|
1351
|
+
),
|
|
1352
|
+
),
|
|
1353
|
+
remaining_component_showcases(),
|
|
1354
|
+
div(
|
|
1355
|
+
tw("mt-16 scroll-mt-24 border-t border-slate-200 pt-12"),
|
|
1356
|
+
{"id": "examples"},
|
|
1357
|
+
h2(tw("text-2xl font-bold tracking-tight text-slate-950"), "Examples"),
|
|
1358
|
+
p(
|
|
1359
|
+
tw("mt-3 max-w-2xl text-slate-600"),
|
|
1360
|
+
"Complete applications that exercise PyJS components and browser APIs.",
|
|
1361
|
+
),
|
|
1362
|
+
div(
|
|
1363
|
+
tw("mt-7 grid gap-5 sm:grid-cols-2"),
|
|
1364
|
+
example_card(
|
|
1365
|
+
"Calculator",
|
|
1366
|
+
"A server-rendered calculator that remembers its latest result.",
|
|
1367
|
+
".venv/bin/python -m pyjs_examples.calculator",
|
|
1368
|
+
),
|
|
1369
|
+
example_card(
|
|
1370
|
+
"Project workspace",
|
|
1371
|
+
"A launch dashboard combining navigation, tasks, forms, and overlays.",
|
|
1372
|
+
".venv/bin/python -m pyjs_examples.workspace",
|
|
1373
|
+
),
|
|
1374
|
+
example_card(
|
|
1375
|
+
"Chatroom",
|
|
1376
|
+
"A Slack-inspired, multi-user WebSocket application.",
|
|
1377
|
+
".venv/bin/python -m pyjs_examples.chatroom",
|
|
1378
|
+
),
|
|
1379
|
+
example_card(
|
|
1380
|
+
"Space Invaders",
|
|
1381
|
+
"A tested 3D arcade game rendered with WebGPU.",
|
|
1382
|
+
".venv/bin/python -m pyjs_examples.space_invaders",
|
|
1383
|
+
),
|
|
1384
|
+
example_card(
|
|
1385
|
+
"TodoMVC",
|
|
1386
|
+
"The official TodoMVC design implemented with custom elements.",
|
|
1387
|
+
".venv/bin/python -m pyjs_examples.todomvc",
|
|
1388
|
+
),
|
|
1389
|
+
example_card(
|
|
1390
|
+
"Counter",
|
|
1391
|
+
"The smallest reactive custom-element example.",
|
|
1392
|
+
".venv/bin/python -m pyjs_examples.counter",
|
|
1393
|
+
),
|
|
1394
|
+
),
|
|
1395
|
+
),
|
|
1396
|
+
),
|
|
1397
|
+
)
|
|
1398
|
+
header = tag(
|
|
1399
|
+
"header",
|
|
1400
|
+
tw(
|
|
1401
|
+
"sticky top-0 z-40 flex h-16 items-center border-b border-slate-200 "
|
|
1402
|
+
"bg-white/95 px-6 backdrop-blur"
|
|
1403
|
+
),
|
|
1404
|
+
div(
|
|
1405
|
+
tw("mx-auto flex w-full max-w-7xl items-center justify-between"),
|
|
1406
|
+
a(
|
|
1407
|
+
tw("text-lg font-bold tracking-tight text-slate-950"),
|
|
1408
|
+
{"href": "#"},
|
|
1409
|
+
"PyJS UI",
|
|
1410
|
+
),
|
|
1411
|
+
p(tw("text-sm text-slate-500"), "Components and examples"),
|
|
1412
|
+
),
|
|
1413
|
+
)
|
|
1414
|
+
self.add(
|
|
1415
|
+
tw("block min-h-screen bg-slate-50 text-slate-950"),
|
|
1416
|
+
header,
|
|
1417
|
+
div(tw("mx-auto flex max-w-7xl"), sidebar, content),
|
|
1418
|
+
)
|
|
1419
|
+
|
|
1420
|
+
def save_profile(self, event: Event):
|
|
1421
|
+
self.status.textContent = "Saved profile for " + self.name_input.value + "."
|
|
1422
|
+
self.dialog.close()
|
|
1423
|
+
|
|
1424
|
+
|
|
1425
|
+
def main():
|
|
1426
|
+
return CatalogApp()
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
if __name__ == "__main__":
|
|
1430
|
+
from pyjs.server import serve
|
|
1431
|
+
|
|
1432
|
+
module = "pyjs_examples.catalog" if __package__ else "catalog"
|
|
1433
|
+
serve(module)
|