Skip to content

Commit 5e9ab55

Browse files
committed
prototype that also works with CSS
1 parent ef242db commit 5e9ab55

File tree

4 files changed

+56
-28
lines changed

4 files changed

+56
-28
lines changed

src/js/src/components.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -63,31 +63,47 @@ export function DjangoForm({
6363
return null;
6464
}
6565

66-
export function OnlyOnceJS({ jsPath, autoRemove }: OnlyOnceProps): null {
66+
export function LoadOnlyOnce({
67+
path,
68+
nodeName,
69+
autoRemove,
70+
}: OnlyOnceProps): null {
6771
React.useEffect(() => {
68-
// Check if the script element already exists
69-
let el = document.head.querySelector(
70-
"script.reactpy-staticfile[src='" + jsPath + "']",
71-
);
72+
// Check if the element already exists
73+
let el: null | HTMLElement = null;
74+
if (nodeName === "script") {
75+
el = document.head.querySelector(
76+
"script.reactpy-staticfile[src='" + path + "']",
77+
);
78+
} else if (nodeName === "link") {
79+
el = document.head.querySelector(
80+
"link.reactpy-staticfile[href='" + path + "']",
81+
);
82+
} else {
83+
throw new Error("Invalid nodeName provided to LoadOnlyOnce");
84+
}
7285

73-
// Create a new script element, if needed
86+
// Create a new element, if needed
7487
if (el === null) {
75-
el = document.createElement("script");
88+
el = document.createElement(nodeName);
7689
el.className = "reactpy-staticfile";
77-
if (jsPath) {
78-
el.setAttribute("src", jsPath);
90+
if (nodeName === "script") {
91+
el.setAttribute("src", path);
92+
} else if (nodeName === "link") {
93+
el.setAttribute("href", path);
94+
el.setAttribute("rel", "stylesheet");
7995
}
8096
document.head.appendChild(el);
8197
}
8298

83-
// If requested, auto remove the script when it is no longer needed
99+
// If requested, auto remove the element when it is no longer needed
84100
if (autoRemove) {
85101
// Keep track of the number of ReactPy components that are dependent on this script
86102
let count = Number(el.getAttribute("data-count"));
87103
count += 1;
88104
el.setAttribute("data-count", count.toString());
89105

90-
// Remove the script element when the last dependent component is unmounted
106+
// Remove the element when the last dependent component is unmounted
91107
return () => {
92108
count -= 1;
93109
if (count === 0) {

src/js/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { OnlyOnceJS, DjangoForm, bind } from "./components";
1+
export { LoadOnlyOnce, DjangoForm, bind } from "./components";
22
export { mountComponent } from "./mount";

src/js/src/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface DjangoFormProps {
2525
}
2626

2727
export interface OnlyOnceProps {
28-
jsPath: string;
28+
path: string;
29+
nodeName: "script" | "link";
2930
autoRemove: boolean;
3031
}

src/reactpy_django/components.py

+26-15
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@
2727
from reactpy_django.types import AsyncFormEvent, SyncFormEvent, ViewToComponentConstructor, ViewToIframeConstructor
2828

2929

30-
DjangoJS = web.export(
31-
web.module_from_file("reactpy-django", file=Path(__file__).parent / "static" / "reactpy_django" / "client.js"),
32-
("OnlyOnceJS"),
33-
)
34-
35-
3630
def view_to_component(
3731
view: Callable | View | str,
3832
transforms: Sequence[Callable[[VdomDict], Any]] = (),
@@ -92,7 +86,9 @@ def constructor(
9286
return constructor
9387

9488

95-
def django_css(static_path: str, key: Key | None = None) -> ComponentType:
89+
def django_css(
90+
static_path: str, only_once: bool = False, auto_remove: bool = False, key: Key | None = None
91+
) -> ComponentType:
9692
"""Fetches a CSS static file for use within ReactPy. This allows for deferred CSS loading.
9793
9894
Args:
@@ -102,11 +98,11 @@ def django_css(static_path: str, key: Key | None = None) -> ComponentType:
10298
immediate siblings
10399
"""
104100

105-
return _django_css(static_path=static_path, key=key)
101+
return _django_css(static_path=static_path, only_once=only_once, auto_remove=auto_remove, key=key)
106102

107103

108104
def django_js(
109-
static_path: str, only_once: bool = False, only_once_auto_remove: bool = False, key: Key | None = None
105+
static_path: str, only_once: bool = False, auto_remove: bool = False, key: Key | None = None
110106
) -> ComponentType:
111107
"""Fetches a JS static file for use within ReactPy. This allows for deferred JS loading.
112108
@@ -117,9 +113,7 @@ def django_js(
117113
immediate siblings
118114
"""
119115

120-
return _django_js(
121-
static_path=static_path, only_once=only_once, only_once_auto_remove=only_once_auto_remove, key=key
122-
)
116+
return _django_js(static_path=static_path, only_once=only_once, auto_remove=auto_remove, key=key)
123117

124118

125119
def django_form(
@@ -285,13 +279,30 @@ def _view_to_iframe(
285279

286280

287281
@component
288-
def _django_css(static_path: str):
282+
def _django_css(static_path: str, only_once: bool, auto_remove: bool):
283+
if only_once:
284+
return LoadOnlyOnce({
285+
"path": static(static_path),
286+
"nodeName": "link",
287+
"autoRemove": auto_remove,
288+
})
289+
289290
return html.style(cached_static_file(static_path))
290291

291292

292293
@component
293-
def _django_js(static_path: str, only_once: bool, only_once_auto_remove: bool):
294+
def _django_js(static_path: str, only_once: bool, auto_remove: bool):
294295
if only_once:
295-
return DjangoJS({"jsPath": static(static_path), "autoRemove": only_once_auto_remove})
296+
return LoadOnlyOnce({
297+
"path": static(static_path),
298+
"nodeName": "script",
299+
"autoRemove": auto_remove,
300+
})
296301

297302
return html.script(cached_static_file(static_path))
303+
304+
305+
LoadOnlyOnce = web.export(
306+
web.module_from_file("reactpy-django", file=Path(__file__).parent / "static" / "reactpy_django" / "client.js"),
307+
("LoadOnlyOnce"),
308+
)

0 commit comments

Comments
 (0)