Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2d79831

Browse filesBrowse files
authored
Refactoring related to ReactPy v1.1.0 (#50)
1 parent 44f76d2 commit 2d79831
Copy full SHA for 2d79831

File tree

13 files changed

+169
-245
lines changed
Filter options

13 files changed

+169
-245
lines changed

‎.github/workflows/test-python.yml

Copy file name to clipboardExpand all lines: .github/workflows/test-python.yml
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,18 @@ jobs:
8080
run: pip install --upgrade pip hatch uv
8181
- name: Check Python formatting
8282
run: hatch fmt src tests --check
83+
84+
python-types:
85+
runs-on: ubuntu-latest
86+
steps:
87+
- uses: actions/checkout@v4
88+
- uses: oven-sh/setup-bun@v2
89+
with:
90+
bun-version: latest
91+
- uses: actions/setup-python@v5
92+
with:
93+
python-version: 3.x
94+
- name: Install Python Dependencies
95+
run: pip install --upgrade pip hatch uv
96+
- name: Run Python type checker
97+
run: hatch run python:type_check

‎CHANGELOG.md

Copy file name to clipboardExpand all lines: CHANGELOG.md
+7-19Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1010

1111
<!--
1212
Using the following categories, list your changes in this order:
13+
[Added, Changed, Deprecated, Removed, Fixed, Security]
1314
14-
### Added
15-
- for new features.
16-
17-
### Changed
18-
- for changes in existing functionality.
19-
20-
### Deprecated
21-
- for soon-to-be removed features.
22-
23-
### Removed
24-
- for removed features.
25-
26-
### Fixed
27-
- for bug fixes.
28-
29-
### Security
30-
- for vulnerability fixes.
31-
-->
15+
Don't forget to remove deprecated code on each major release!
16+
-->
3217

3318
<!--changelog-start-->
3419

3520
## [Unreleased]
3621

3722
### Changed
3823

39-
- Set upper limit on ReactPy version to `<2.0.0`.
24+
- Set maximum ReactPy version to `<2.0.0`.
25+
- Set minimum ReactPy version to `1.1.0`.
26+
- `link` element now calculates URL changes using the client.
27+
- Refactoring related to `reactpy>=1.1.0` changes.
4028

4129
### Fixed
4230

‎README.md

Copy file name to clipboardExpand all lines: README.md
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# <img src="https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg" align="left" height="45"/> ReactPy Router
22

33
<p>
4-
<a href="https://github.com/reactive-python/reactpy-router/actions/workflows/test-src.yml">
5-
<img src="https://github.com/reactive-python/reactpy-router/actions/workflows/test-src.yml/badge.svg">
4+
<a href="https://github.com/reactive-python/reactpy-router/actions/workflows/test-python.yml">
5+
<img src="https://github.com/reactive-python/reactpy-router/actions/workflows/test-python.yml/badge.svg">
66
</a>
77
<a href="https://pypi.python.org/pypi/reactpy-router">
88
<img src="https://img.shields.io/pypi/v/reactpy-router.svg?label=PyPI">

‎docs/src/about/contributing.md

Copy file name to clipboardExpand all lines: docs/src/about/contributing.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ By utilizing `hatch`, the following commands are available to manage the develop
4343
| `hatch fmt --formatter` | Run only formatters |
4444
| `hatch run javascript:check` | Run the JavaScript linter/formatter |
4545
| `hatch run javascript:fix` | Run the JavaScript linter/formatter and write fixes to disk |
46+
| `hatch run python:type_check` | Run the Python type checker |
4647

4748
??? tip "Configure your IDE for linting"
4849

‎pyproject.toml

Copy file name to clipboardExpand all lines: pyproject.toml
+12-2Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ classifiers = [
2828
"Environment :: Web Environment",
2929
"Typing :: Typed",
3030
]
31-
dependencies = ["reactpy>=1.0.0, <2.0.0", "typing_extensions"]
31+
dependencies = ["reactpy>=1.1.0, <2.0.0", "typing_extensions"]
3232
dynamic = ["version"]
3333
urls.Changelog = "https://reactive-python.github.io/reactpy-router/latest/about/changelog/"
3434
urls.Documentation = "https://reactive-python.github.io/reactpy-router/latest/"
@@ -53,7 +53,7 @@ installer = "uv"
5353
[[tool.hatch.build.hooks.build-scripts.scripts]]
5454
commands = [
5555
"bun install --cwd src/js",
56-
"bun build src/js/src/index.js --outfile src/reactpy_router/static/bundle.js --minify",
56+
"bun build src/js/src/index.ts --outfile src/reactpy_router/static/bundle.js --minify",
5757
]
5858
artifacts = []
5959

@@ -106,6 +106,16 @@ linkcheck = [
106106
deploy_latest = ["cd docs && mike deploy --push --update-aliases {args} latest"]
107107
deploy_develop = ["cd docs && mike deploy --push develop"]
108108

109+
################################
110+
# >>> Hatch Python Scripts <<< #
111+
################################
112+
113+
[tool.hatch.envs.python]
114+
extra-dependencies = ["pyright"]
115+
116+
[tool.hatch.envs.python.scripts]
117+
type_check = ["pyright src"]
118+
109119
############################
110120
# >>> Hatch JS Scripts <<< #
111121
############################

‎src/js/src/components.ts

Copy file name to clipboard
+101Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import React from "preact/compat";
2+
import ReactDOM from "preact/compat";
3+
import { createLocationObject, pushState, replaceState } from "./utils";
4+
import { HistoryProps, LinkProps, NavigateProps } from "./types";
5+
6+
/**
7+
* Interface used to bind a ReactPy node to React.
8+
*/
9+
export function bind(node) {
10+
return {
11+
create: (type, props, children) =>
12+
React.createElement(type, props, ...children),
13+
render: (element) => {
14+
ReactDOM.render(element, node);
15+
},
16+
unmount: () => ReactDOM.unmountComponentAtNode(node),
17+
};
18+
}
19+
20+
/**
21+
* History component that captures browser "history go back" actions and notifies the server.
22+
*/
23+
export function History({ onHistoryChangeCallback }: HistoryProps): null {
24+
// Tell the server about history "popstate" events
25+
React.useEffect(() => {
26+
const listener = () => {
27+
onHistoryChangeCallback(createLocationObject());
28+
};
29+
30+
// Register the event listener
31+
window.addEventListener("popstate", listener);
32+
33+
// Delete the event listener when the component is unmounted
34+
return () => window.removeEventListener("popstate", listener);
35+
});
36+
37+
// Tell the server about the URL during the initial page load
38+
React.useEffect(() => {
39+
onHistoryChangeCallback(createLocationObject());
40+
return () => {};
41+
}, []);
42+
return null;
43+
}
44+
45+
/**
46+
* Link component that captures clicks on anchor links and notifies the server.
47+
*
48+
* This component is not the actual `<a>` link element. It is just an event
49+
* listener for ReactPy-Router's server-side link component.
50+
*/
51+
export function Link({ onClickCallback, linkClass }: LinkProps): null {
52+
React.useEffect(() => {
53+
// Event function that will tell the server about clicks
54+
const handleClick = (event: Event) => {
55+
let click_event = event as MouseEvent;
56+
if (!click_event.ctrlKey) {
57+
event.preventDefault();
58+
let to = (event.currentTarget as HTMLElement).getAttribute("href");
59+
pushState(to);
60+
onClickCallback(createLocationObject());
61+
}
62+
};
63+
64+
// Register the event listener
65+
let link = document.querySelector(`.${linkClass}`);
66+
if (link) {
67+
link.addEventListener("click", handleClick);
68+
} else {
69+
console.warn(`Link component with class name ${linkClass} not found.`);
70+
}
71+
72+
// Delete the event listener when the component is unmounted
73+
return () => {
74+
if (link) {
75+
link.removeEventListener("click", handleClick);
76+
}
77+
};
78+
});
79+
return null;
80+
}
81+
82+
/**
83+
* Client-side portion of the navigate component, that allows the server to command the client to change URLs.
84+
*/
85+
export function Navigate({
86+
onNavigateCallback,
87+
to,
88+
replace = false,
89+
}: NavigateProps): null {
90+
React.useEffect(() => {
91+
if (replace) {
92+
replaceState(to);
93+
} else {
94+
pushState(to);
95+
}
96+
onNavigateCallback(createLocationObject());
97+
return () => {};
98+
}, []);
99+
100+
return null;
101+
}

‎src/js/src/index.ts

Copy file name to clipboard
+1-129Lines changed: 1 addition & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1 @@
1-
import React from "preact/compat";
2-
import ReactDOM from "preact/compat";
3-
import { createLocationObject, pushState, replaceState } from "./utils";
4-
import {
5-
HistoryProps,
6-
LinkProps,
7-
NavigateProps,
8-
FirstLoadProps,
9-
} from "./types";
10-
11-
/**
12-
* Interface used to bind a ReactPy node to React.
13-
*/
14-
export function bind(node) {
15-
return {
16-
create: (type, props, children) =>
17-
React.createElement(type, props, ...children),
18-
render: (element) => {
19-
ReactDOM.render(element, node);
20-
},
21-
unmount: () => ReactDOM.unmountComponentAtNode(node),
22-
};
23-
}
24-
25-
/**
26-
* History component that captures browser "history go back" actions and notifies the server.
27-
*/
28-
export function History({ onHistoryChangeCallback }: HistoryProps): null {
29-
React.useEffect(() => {
30-
// Register a listener for the "popstate" event and send data back to the server using the `onHistoryChange` callback.
31-
const listener = () => {
32-
onHistoryChangeCallback(createLocationObject());
33-
};
34-
35-
// Register the event listener
36-
window.addEventListener("popstate", listener);
37-
38-
// Delete the event listener when the component is unmounted
39-
return () => window.removeEventListener("popstate", listener);
40-
});
41-
42-
// Tell the server about the URL during the initial page load
43-
// FIXME: This code is commented out since it currently runs every time any component
44-
// is mounted due to a ReactPy core rendering bug. `FirstLoad` component is used instead.
45-
// https://github.com/reactive-python/reactpy/pull/1224
46-
// React.useEffect(() => {
47-
// onHistoryChange({
48-
// pathname: window.location.pathname,
49-
// search: window.location.search,
50-
// });
51-
// return () => {};
52-
// }, []);
53-
return null;
54-
}
55-
56-
/**
57-
* Link component that captures clicks on anchor links and notifies the server.
58-
*
59-
* This component is not the actual `<a>` link element. It is just an event
60-
* listener for ReactPy-Router's server-side link component.
61-
*
62-
* @disabled This component is currently unused due to a ReactPy core rendering bug
63-
* which causes duplicate rendering (and thus duplicate event listeners).
64-
*/
65-
export function Link({ onClickCallback, linkClass }: LinkProps): null {
66-
React.useEffect(() => {
67-
// Event function that will tell the server about clicks
68-
const handleClick = (event: MouseEvent) => {
69-
event.preventDefault();
70-
let to = (event.target as HTMLElement).getAttribute("href");
71-
pushState(to);
72-
onClickCallback(createLocationObject());
73-
};
74-
75-
// Register the event listener
76-
let link = document.querySelector(`.${linkClass}`);
77-
if (link) {
78-
link.addEventListener("click", handleClick);
79-
} else {
80-
console.warn(`Link component with class name ${linkClass} not found.`);
81-
}
82-
83-
// Delete the event listener when the component is unmounted
84-
return () => {
85-
let link = document.querySelector(`.${linkClass}`);
86-
if (link) {
87-
link.removeEventListener("click", handleClick);
88-
}
89-
};
90-
});
91-
return null;
92-
}
93-
94-
/**
95-
* Client-side portion of the navigate component, that allows the server to command the client to change URLs.
96-
*/
97-
export function Navigate({
98-
onNavigateCallback,
99-
to,
100-
replace = false,
101-
}: NavigateProps): null {
102-
React.useEffect(() => {
103-
if (replace) {
104-
replaceState(to);
105-
} else {
106-
pushState(to);
107-
}
108-
onNavigateCallback(createLocationObject());
109-
return () => {};
110-
}, []);
111-
112-
return null;
113-
}
114-
115-
/**
116-
* FirstLoad component that captures the URL during the initial page load and notifies the server.
117-
*
118-
* FIXME: This component only exists because of a ReactPy core rendering bug, and should be removed when the bug
119-
* is fixed. In the future, all this logic should be handled by the `History` component.
120-
* https://github.com/reactive-python/reactpy/pull/1224
121-
*/
122-
export function FirstLoad({ onFirstLoadCallback }: FirstLoadProps): null {
123-
React.useEffect(() => {
124-
onFirstLoadCallback(createLocationObject());
125-
return () => {};
126-
}, []);
127-
128-
return null;
129-
}
1+
export { bind, History, Link, Navigate } from "./components";

‎src/js/src/types.ts

Copy file name to clipboardExpand all lines: src/js/src/types.ts
-4Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,3 @@ export interface NavigateProps {
1717
to: string;
1818
replace?: boolean;
1919
}
20-
21-
export interface FirstLoadProps {
22-
onFirstLoadCallback: (location: ReactPyLocation) => void;
23-
}

‎src/js/src/utils.ts

Copy file name to clipboardExpand all lines: src/js/src/utils.ts
+10-2Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,18 @@ export function createLocationObject(): ReactPyLocation {
77
};
88
}
99

10-
export function pushState(to: string): void {
10+
export function pushState(to: any): void {
11+
if (typeof to !== "string") {
12+
console.error("pushState() requires a string argument.");
13+
return;
14+
}
1115
window.history.pushState(null, "", new URL(to, window.location.href));
1216
}
1317

14-
export function replaceState(to: string): void {
18+
export function replaceState(to: any): void {
19+
if (typeof to !== "string") {
20+
console.error("replaceState() requires a string argument.");
21+
return;
22+
}
1523
window.history.replaceState(null, "", new URL(to, window.location.href));
1624
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.