Skip to content

Commit 452a406

Browse files
authored
show loading r logo until template iframe is loaded (#1573)
1 parent 0eb96bb commit 452a406

File tree

2 files changed

+166
-16
lines changed

2 files changed

+166
-16
lines changed

pcweb/components/r_svg_loader.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import reflex as rx
2+
3+
loading_style = {
4+
"rect": {
5+
"opacity": "0",
6+
"animation": "fadeInStayOut 6s linear infinite",
7+
},
8+
"@keyframes fadeInStayOut": {
9+
"0%": {"opacity": "0"},
10+
"5%": {"opacity": "1"},
11+
"50%": {"opacity": "1"},
12+
"55%": {"opacity": "0"},
13+
"100%": {"opacity": "0"},
14+
},
15+
}
16+
17+
for i in range(1, 14):
18+
loading_style[f"rect:nth-child({i})"] = {"animation_delay": f"{(i - 1) * 0.2}s"}
19+
20+
21+
def svg_loading():
22+
return rx.box(
23+
rx.html(
24+
"""<svg width="88" height="88" viewBox="0 0 88 88" fill="none" xmlns="http://www.w3.org/2000/svg">
25+
<rect x="32" y="41" width="6" height="6" fill="#6E56CF"/>
26+
<rect x="38" y="29" width="6" height="6" fill="#6E56CF"/>
27+
<rect x="50" y="47" width="6" height="6" fill="#6E56CF"/>
28+
<rect x="32" y="53" width="6" height="6" fill="#6E56CF"/>
29+
<rect x="44" y="41" width="6" height="6" fill="#6E56CF"/>
30+
<rect x="50" y="35" width="6" height="6" fill="#6E56CF"/>
31+
<rect x="50" y="53" width="6" height="6" fill="#6E56CF"/>
32+
<rect x="44" y="29" width="6" height="6" fill="#6E56CF"/>
33+
<rect x="32" y="29" width="6" height="6" fill="#6E56CF"/>
34+
<rect x="32" y="47" width="6" height="6" fill="#6E56CF"/>
35+
<rect x="50" y="29" width="6" height="6" fill="#6E56CF"/>
36+
<rect x="38" y="41" width="6" height="6" fill="#6E56CF"/>
37+
<rect x="32" y="35" width="6" height="6" fill="#6E56CF"/>
38+
</svg>"""
39+
),
40+
style=loading_style,
41+
position="absolute",
42+
)
43+
44+
45+
spinner_style = {
46+
"g rect": {
47+
"transform-origin": "0 0",
48+
},
49+
"g rect:nth-of-type(1)": {
50+
"animation": "growShrinkWidth 3s linear infinite",
51+
"width": "0",
52+
},
53+
"g rect:nth-of-type(2)": {
54+
"animation": "growShrinkHeight 3s linear infinite 0.35s",
55+
"height": "0",
56+
},
57+
"g rect:nth-of-type(3)": {
58+
"animation": "growShrinkWidthReverse 3s linear infinite 0.7s",
59+
"width": "0",
60+
},
61+
"g rect:nth-of-type(4)": {
62+
"animation": "growShrinkHeightReverse 3s linear infinite 1.05s",
63+
"height": "0",
64+
},
65+
"@keyframes growShrinkWidth": {
66+
"0%": {"width": "0", "transform": "translateX(0)"},
67+
"12.5%": {"width": "40px", "transform": "translateX(0)"},
68+
"37.5%": {"width": "40px", "transform": "translateX(0)"},
69+
"50%": {"width": "40px", "transform": "translateX(0)"},
70+
"62.5%": {"width": "0", "transform": "translateX(40px)"},
71+
"100%": {"width": "0", "transform": "translateX(40px)"},
72+
},
73+
"@keyframes growShrinkHeight": {
74+
"0%": {"height": "0", "transform": "translateY(0)"},
75+
"12.5%": {"height": "40px", "transform": "translateY(0)"},
76+
"37.5%": {"height": "40px", "transform": "translateY(0)"},
77+
"50%": {"height": "40px", "transform": "translateY(0)"},
78+
"62.5%": {"height": "0", "transform": "translateY(40px)"},
79+
"100%": {"height": "0", "transform": "translateY(40px)"},
80+
},
81+
"@keyframes growShrinkWidthReverse": {
82+
"0%": {"width": "0", "transform": "translateX(41px)"},
83+
"12.5%": {"width": "41px", "transform": "translateX(0)"},
84+
"37.5%": {"width": "41px", "transform": "translateX(0)"},
85+
"50%": {"width": "41px", "transform": "translateX(0)"},
86+
"62.5%": {"width": "0", "transform": "translateX(0)"},
87+
"100%": {"width": "0", "transform": "translateX(0)"},
88+
},
89+
"@keyframes growShrinkHeightReverse": {
90+
"0%": {"height": "0", "transform": "translateY(40px)"},
91+
"12.5%": {"height": "40px", "transform": "translateY(0)"},
92+
"37.5%": {"height": "40px", "transform": "translateY(0)"},
93+
"50%": {"height": "40px", "transform": "translateY(0)"},
94+
"62.5%": {"height": "0", "transform": "translateY(0)"},
95+
"100%": {"height": "0", "transform": "translateY(0)"},
96+
},
97+
}
98+
99+
100+
def spinner_svg(mask_name: str):
101+
return rx.box(
102+
rx.html(
103+
f"""<svg width="88" height="88" viewBox="0 0 88 88" fill="none" xmlns="http://www.w3.org/2000/svg">
104+
<mask id="{mask_name}" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="20" y="20" width="48" height="48">
105+
<rect x="21" y="21" width="46" height="46" rx="7" stroke="black" stroke-width="2"/>
106+
</mask>
107+
<g mask="url(#{mask_name})">
108+
<rect x="20" y="20" width="0" height="8" fill="#6E56CF"/>
109+
<rect x="60" y="20" width="8" height="0" fill="#6E56CF"/>
110+
<rect x="27" y="60" width="0" height="8" fill="#6E56CF"/>
111+
<rect x="20" y="28" width="8" height="0" fill="#6E56CF"/>
112+
</g>
113+
</svg>"""
114+
),
115+
style=spinner_style,
116+
)
117+
118+
119+
def r_svg_loader():
120+
return rx.fragment(
121+
spinner_svg(mask_name="mask"),
122+
svg_loading(),
123+
)

pcweb/pages/gallery/gallery.py

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from pcweb.templates.webpage import webpage
55
from pcweb.components.button import button
66
from pcweb.components.icons import get_icon
7+
from pcweb.components.r_svg_loader import r_svg_loader
78

89
REFLEX_BUILD_TEMPLATES_PATH = "reflex_build_templates/"
910
REFLEX_BUILD_TEMPLATES_IMAGES = "reflex_build_template_images/"
1011

12+
1113
def get_templatey_apps(paths):
1214
"""Method to parse each markdown file and return the data from the file"""
1315
gallery_apps = {}
@@ -17,6 +19,7 @@ def get_templatey_apps(paths):
1719
gallery_apps[key] = document
1820
return gallery_apps
1921

22+
2023
paths = flexdown.utils.get_flexdown_files(REFLEX_BUILD_TEMPLATES_PATH)
2124
template_apps_data = get_templatey_apps(paths)
2225

@@ -33,6 +36,10 @@ def app_dialog_with_trigger(
3336
rx.dialog.trigger(trigger_content, class_name="w-full h-full"),
3437
rx.dialog.content(
3538
rx.el.div(
39+
rx.el.div(
40+
r_svg_loader(),
41+
class_name="absolute inset-0 flex items-center justify-center",
42+
),
3643
rx.el.div(
3744
rx.el.div(
3845
rx.el.p(
@@ -54,16 +61,24 @@ def app_dialog_with_trigger(
5461
),
5562
rx.el.iframe(
5663
src=app_url,
57-
class_name="w-full h-full xl:rounded-md shadow-small",
64+
class_name="w-full h-full xl:rounded-md shadow-small z-10",
5865
id="iFrame",
5966
),
60-
class_name="flex flex-col w-full h-full gap-y-3",
67+
class_name="flex flex-col w-full h-full gap-y-3 relative",
6168
),
6269
class_name="w-full !max-w-[90em] xl:max-w-[110em] 2xl:max-w-[120em] h-[80vh]",
6370
),
6471
)
6572

66-
def extended_gallery_grid_item(app_url: str, app_name: str, app_author: str, app_thread: str, app_image: str, app_inner_page: str):
73+
74+
def extended_gallery_grid_item(
75+
app_url: str,
76+
app_name: str,
77+
app_author: str,
78+
app_thread: str,
79+
app_image: str,
80+
app_inner_page: str,
81+
):
6782
return app_dialog_with_trigger(
6883
app_url=app_url,
6984
app_author=app_author,
@@ -90,7 +105,9 @@ def extended_gallery_grid_item(app_url: str, app_name: str, app_author: str, app
90105
class_name="no-underline flex-1",
91106
on_click=rx.stop_propagation,
92107
),
93-
button("Preview", variant="primary", size="md", class_name="flex-1"),
108+
button(
109+
"Preview", variant="primary", size="md", class_name="flex-1"
110+
),
94111
class_name="flex flex-row gap-x-2 w-full items-stretch px-4 pb-4",
95112
),
96113
class_name="absolute inset-0 flex items-end justify-center z-20 opacity-0 translate-y-2 group-hover:opacity-100 group-hover:translate-y-0 transition-all duration-300 ease-out pointer-events-none group-hover:pointer-events-auto z-[99]",
@@ -99,10 +116,16 @@ def extended_gallery_grid_item(app_url: str, app_name: str, app_author: str, app
99116
),
100117
rx.el.div(
101118
rx.el.div(
102-
get_icon(icon="badge_logo", class_name="size-5 flex-shrink-0",),
119+
get_icon(
120+
icon="badge_logo",
121+
class_name="size-5 flex-shrink-0",
122+
),
103123
rx.el.div(
104124
rx.el.p(app_name, class_name="text-sm font-medium truncate"),
105-
rx.el.p(app_author, class_name="text-sm font-regular text-slate-9 truncate"),
125+
rx.el.p(
126+
app_author,
127+
class_name="text-sm font-regular text-slate-9 truncate",
128+
),
106129
class_name="flex flex-col gap-y-0 min-w-0 w-full",
107130
),
108131
class_name="flex flex-row gap-x-2 items-start min-w-0 w-full",
@@ -122,24 +145,27 @@ def create_grid_with_items():
122145
app_name = meta.get("title", "Untitled").replace("_", " ").title()
123146
app_author = meta.get("author", "Team Reflex")
124147
app_thread = f"/gen/{app_name.lower().replace(' ', '-')}/"
125-
app_image = meta.get('image', "")
148+
app_image = meta.get("image", "")
126149
slug = re.sub(r"[\s_]+", "-", meta.get("title", "")).lower()
127150
app_inner_page = f"/templates/{slug}"
128151

129-
items.append(extended_gallery_grid_item(
130-
app_url=app_url,
131-
app_name=app_name,
132-
app_author=app_author,
133-
app_thread=app_thread,
134-
app_image=f"/{REFLEX_BUILD_TEMPLATES_IMAGES}{app_image}",
135-
app_inner_page=app_inner_page
136-
))
152+
items.append(
153+
extended_gallery_grid_item(
154+
app_url=app_url,
155+
app_name=app_name,
156+
app_author=app_author,
157+
app_thread=app_thread,
158+
app_image=f"/{REFLEX_BUILD_TEMPLATES_IMAGES}{app_image}",
159+
app_inner_page=app_inner_page,
160+
)
161+
)
137162

138163
return rx.el.div(
139164
*items,
140-
class_name="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 md:px-8 lg:px-8"
165+
class_name="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 md:px-8 lg:px-8",
141166
)
142167

168+
143169
def create_header():
144170
return rx.box(
145171
rx.box(
@@ -156,6 +182,7 @@ def create_header():
156182
class_name="flex flex-col justify-center items-center gap-6 w-full text-center",
157183
)
158184

185+
159186
@webpage(path="/templates", title="Templates · Reflex")
160187
def gallery() -> rx.Component:
161188
return rx.el.section(

0 commit comments

Comments
 (0)