pyreact-framework 1.0.1__tar.gz → 1.0.2__tar.gz
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.
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/PKG-INFO +35 -2
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/README.md +32 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyproject.toml +3 -2
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/__init__.py +42 -19
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/cli/main.py +64 -1
- pyreact_framework-1.0.2/pyreact/core/router.py +258 -0
- pyreact_framework-1.0.2/pyreact/core/theme.py +107 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/PKG-INFO +35 -2
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/SOURCES.txt +2 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/LICENSE +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/cli/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/component.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/context.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/element.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/error_boundary.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/hooks.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/memo.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/portal.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/reconciler.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/refs.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/renderer.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/core/scheduler.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/devtools/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/devtools/debugger.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/devtools/profiler.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/dom/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/dom/attributes.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/dom/dom_operations.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/dom/events.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/server/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/server/hydration.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/server/ssr.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/styles/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/styles/css_module.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/styles/styled.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/testing/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/testing/fire_event.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/testing/screen.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/testing/test_renderer.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/utils/__init__.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/utils/diff.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact/utils/object_pool.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/dependency_links.txt +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/entry_points.txt +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/requires.txt +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/top_level.txt +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/setup.cfg +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/tests/test_core.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/tests/test_dom.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/tests/test_hooks.py +0 -0
- {pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/tests/test_ssr.py +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyreact-framework
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Framework web declarativo inspirado no React, construído nativamente para Python
|
|
5
5
|
Author-email: wanbnn <wanbnn@users.noreply.github.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/wanbnn/pyreact
|
|
8
|
-
Project-URL: Documentation, https://pyreact.readthedocs.io
|
|
8
|
+
Project-URL: Documentation, https://pyreact-framework.readthedocs.io
|
|
9
9
|
Project-URL: Repository, https://github.com/wanbnn/pyreact
|
|
10
10
|
Project-URL: Issues, https://github.com/wanbnn/pyreact/issues
|
|
11
|
+
Project-URL: Changelog, https://pyreact-framework.readthedocs.io/en/latest/resources/changelog.html
|
|
11
12
|
Keywords: react,ui,frontend,declarative,components,virtual-dom,web,framework
|
|
12
13
|
Classifier: Development Status :: 4 - Beta
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
@@ -159,6 +160,38 @@ def MyComponent(props):
|
|
|
159
160
|
|
|
160
161
|
## 📖 Documentação
|
|
161
162
|
|
|
163
|
+
### 📚 Documentação Completa
|
|
164
|
+
|
|
165
|
+
A documentação completa está disponível no **Read the Docs**:
|
|
166
|
+
|
|
167
|
+
🔗 **https://pyreact-framework.readthedocs.io/**
|
|
168
|
+
|
|
169
|
+
#### Conteúdo da Documentação:
|
|
170
|
+
|
|
171
|
+
- **Getting Started**
|
|
172
|
+
- Installation
|
|
173
|
+
- Quick Start
|
|
174
|
+
- Tutorial (Todo App)
|
|
175
|
+
|
|
176
|
+
- **Core Concepts**
|
|
177
|
+
- Components
|
|
178
|
+
- Props
|
|
179
|
+
- State
|
|
180
|
+
- Events
|
|
181
|
+
- Lifecycle
|
|
182
|
+
|
|
183
|
+
- **Advanced**
|
|
184
|
+
- Server-Side Rendering (SSR)
|
|
185
|
+
- Routing
|
|
186
|
+
- Styling
|
|
187
|
+
- Testing
|
|
188
|
+
|
|
189
|
+
- **API Reference**
|
|
190
|
+
- Element API
|
|
191
|
+
- Component API
|
|
192
|
+
- Hooks API
|
|
193
|
+
- CLI API
|
|
194
|
+
|
|
162
195
|
### CLI Commands
|
|
163
196
|
|
|
164
197
|
```bash
|
|
@@ -123,6 +123,38 @@ def MyComponent(props):
|
|
|
123
123
|
|
|
124
124
|
## 📖 Documentação
|
|
125
125
|
|
|
126
|
+
### 📚 Documentação Completa
|
|
127
|
+
|
|
128
|
+
A documentação completa está disponível no **Read the Docs**:
|
|
129
|
+
|
|
130
|
+
🔗 **https://pyreact-framework.readthedocs.io/**
|
|
131
|
+
|
|
132
|
+
#### Conteúdo da Documentação:
|
|
133
|
+
|
|
134
|
+
- **Getting Started**
|
|
135
|
+
- Installation
|
|
136
|
+
- Quick Start
|
|
137
|
+
- Tutorial (Todo App)
|
|
138
|
+
|
|
139
|
+
- **Core Concepts**
|
|
140
|
+
- Components
|
|
141
|
+
- Props
|
|
142
|
+
- State
|
|
143
|
+
- Events
|
|
144
|
+
- Lifecycle
|
|
145
|
+
|
|
146
|
+
- **Advanced**
|
|
147
|
+
- Server-Side Rendering (SSR)
|
|
148
|
+
- Routing
|
|
149
|
+
- Styling
|
|
150
|
+
- Testing
|
|
151
|
+
|
|
152
|
+
- **API Reference**
|
|
153
|
+
- Element API
|
|
154
|
+
- Component API
|
|
155
|
+
- Hooks API
|
|
156
|
+
- CLI API
|
|
157
|
+
|
|
126
158
|
### CLI Commands
|
|
127
159
|
|
|
128
160
|
```bash
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyreact-framework"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "Framework web declarativo inspirado no React, construído nativamente para Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -51,9 +51,10 @@ dev = [
|
|
|
51
51
|
|
|
52
52
|
[project.urls]
|
|
53
53
|
Homepage = "https://github.com/wanbnn/pyreact"
|
|
54
|
-
Documentation = "https://pyreact.readthedocs.io"
|
|
54
|
+
Documentation = "https://pyreact-framework.readthedocs.io"
|
|
55
55
|
Repository = "https://github.com/wanbnn/pyreact"
|
|
56
56
|
Issues = "https://github.com/wanbnn/pyreact/issues"
|
|
57
|
+
Changelog = "https://pyreact-framework.readthedocs.io/en/latest/resources/changelog.html"
|
|
57
58
|
|
|
58
59
|
[project.scripts]
|
|
59
60
|
pyreact-framework = "pyreact.cli.main:main"
|
|
@@ -8,25 +8,25 @@ componentizadas e modernas, sem precisar aprender JavaScript/TypeScript.
|
|
|
8
8
|
|
|
9
9
|
Quick Start:
|
|
10
10
|
from pyreact import h, render, use_state
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
def Counter(props):
|
|
13
13
|
count, set_count = use_state(0)
|
|
14
14
|
return h('div', {'className': 'counter'},
|
|
15
15
|
h('span', None, f"Count: {count}"),
|
|
16
16
|
h('button', {'onClick': lambda _: set_count(count + 1)}, '+')
|
|
17
17
|
)
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
render(h(Counter, None), document.getElementById('root'))
|
|
20
20
|
|
|
21
21
|
Princípios:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
- Declaratividade: A UI é uma função do estado
|
|
23
|
+
- Componentização: Tudo é um componente
|
|
24
|
+
- Reatividade: Mudanças de estado disparam re-renderizações
|
|
25
|
+
- Isomorfismo: Suporte a Server-Side Rendering
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
__version__ = '1.0.
|
|
29
|
-
__author__ = '
|
|
28
|
+
__version__ = '1.0.2'
|
|
29
|
+
__author__ = 'wanbnn'
|
|
30
30
|
|
|
31
31
|
# Core
|
|
32
32
|
from .core.element import VNode, h, create_element, is_valid_element, clone_element
|
|
@@ -66,6 +66,13 @@ from .core.memo import memo, lazy
|
|
|
66
66
|
# Error Boundary
|
|
67
67
|
from .core.error_boundary import ErrorBoundary
|
|
68
68
|
|
|
69
|
+
# Router
|
|
70
|
+
from .core.router import Router, Route, Link, Redirect, Switch
|
|
71
|
+
from .core.router import useParams, useLocation, useHistory
|
|
72
|
+
|
|
73
|
+
# Theme
|
|
74
|
+
from .core.theme import ThemeProvider, ThemeContext, use_theme
|
|
75
|
+
|
|
69
76
|
# Scheduler
|
|
70
77
|
from .core.scheduler import Scheduler, Priority
|
|
71
78
|
|
|
@@ -78,10 +85,11 @@ from .server.ssr import render_to_string, render_to_static_markup
|
|
|
78
85
|
# DOM
|
|
79
86
|
from .dom.dom_operations import document
|
|
80
87
|
|
|
88
|
+
|
|
81
89
|
__all__ = [
|
|
82
90
|
# Version
|
|
83
91
|
'__version__',
|
|
84
|
-
|
|
92
|
+
|
|
85
93
|
# Core
|
|
86
94
|
'VNode',
|
|
87
95
|
'h',
|
|
@@ -94,7 +102,7 @@ __all__ = [
|
|
|
94
102
|
'hydrate',
|
|
95
103
|
'create_root',
|
|
96
104
|
'Reconciler',
|
|
97
|
-
|
|
105
|
+
|
|
98
106
|
# Hooks
|
|
99
107
|
'use_state',
|
|
100
108
|
'use_reducer',
|
|
@@ -109,36 +117,51 @@ __all__ = [
|
|
|
109
117
|
'use_id',
|
|
110
118
|
'use_transition',
|
|
111
119
|
'use_deferred_value',
|
|
112
|
-
|
|
120
|
+
|
|
113
121
|
# Context
|
|
114
122
|
'create_context',
|
|
115
|
-
|
|
123
|
+
|
|
116
124
|
# Refs
|
|
117
125
|
'create_ref',
|
|
118
126
|
'forward_ref',
|
|
119
|
-
|
|
127
|
+
|
|
120
128
|
# Portal
|
|
121
129
|
'create_portal',
|
|
122
|
-
|
|
130
|
+
|
|
123
131
|
# Memo
|
|
124
132
|
'memo',
|
|
125
133
|
'lazy',
|
|
126
|
-
|
|
134
|
+
|
|
127
135
|
# Error Boundary
|
|
128
136
|
'ErrorBoundary',
|
|
129
|
-
|
|
137
|
+
|
|
138
|
+
# Router
|
|
139
|
+
'Router',
|
|
140
|
+
'Route',
|
|
141
|
+
'Link',
|
|
142
|
+
'Redirect',
|
|
143
|
+
'Switch',
|
|
144
|
+
'useParams',
|
|
145
|
+
'useLocation',
|
|
146
|
+
'useHistory',
|
|
147
|
+
|
|
148
|
+
# Theme
|
|
149
|
+
'ThemeProvider',
|
|
150
|
+
'ThemeContext',
|
|
151
|
+
'use_theme',
|
|
152
|
+
|
|
130
153
|
# Scheduler
|
|
131
154
|
'Scheduler',
|
|
132
155
|
'Priority',
|
|
133
|
-
|
|
156
|
+
|
|
134
157
|
# Styles
|
|
135
158
|
'styled',
|
|
136
159
|
'css_module',
|
|
137
|
-
|
|
160
|
+
|
|
138
161
|
# Server
|
|
139
162
|
'render_to_string',
|
|
140
163
|
'render_to_static_markup',
|
|
141
|
-
|
|
164
|
+
|
|
142
165
|
# DOM
|
|
143
166
|
'document',
|
|
144
167
|
]
|
|
@@ -453,17 +453,63 @@ def build_project() -> None:
|
|
|
453
453
|
def run_tests() -> None:
|
|
454
454
|
"""Run tests"""
|
|
455
455
|
import subprocess
|
|
456
|
-
|
|
457
456
|
print("Running tests...")
|
|
458
457
|
result = subprocess.run(['pytest', 'tests/', '-v'])
|
|
459
458
|
sys.exit(result.returncode)
|
|
460
459
|
|
|
460
|
+
def serve_build(port: int = 5000, directory: str = 'dist') -> None:
|
|
461
|
+
"""Serve production build"""
|
|
462
|
+
import http.server
|
|
463
|
+
import socketserver
|
|
464
|
+
|
|
465
|
+
# Check if directory exists
|
|
466
|
+
if not Path(directory).exists():
|
|
467
|
+
print(f"Error: Directory '{directory}' not found")
|
|
468
|
+
print("Run 'pyreact build' first")
|
|
469
|
+
sys.exit(1)
|
|
470
|
+
|
|
471
|
+
# Change to directory
|
|
472
|
+
original_dir = os.getcwd()
|
|
473
|
+
os.chdir(directory)
|
|
474
|
+
|
|
475
|
+
# Create handler
|
|
476
|
+
class Handler(http.server.SimpleHTTPRequestHandler):
|
|
477
|
+
def log_message(self, format, *args):
|
|
478
|
+
print(f"[{self.log_date_time_string()}] {format % args}")
|
|
479
|
+
|
|
480
|
+
try:
|
|
481
|
+
socketserver.TCPServer.allow_reuse_address = True
|
|
482
|
+
with socketserver.TCPServer(("", port), Handler) as httpd:
|
|
483
|
+
url = f"http://localhost:{port}"
|
|
484
|
+
print(f"[OK] Serving production build at {url}")
|
|
485
|
+
print(f"Directory: {directory}")
|
|
486
|
+
print(f"\nPress Ctrl+C to stop")
|
|
487
|
+
httpd.serve_forever()
|
|
488
|
+
except OSError as e:
|
|
489
|
+
os.chdir(original_dir)
|
|
490
|
+
if 'Address already in use' in str(e) or e.errno == 10048:
|
|
491
|
+
print(f"Error: Port {port} is already in use")
|
|
492
|
+
print(f"Try: pyreact serve --port {port + 1}")
|
|
493
|
+
else:
|
|
494
|
+
print(f"Error: {e}")
|
|
495
|
+
sys.exit(1)
|
|
496
|
+
except KeyboardInterrupt:
|
|
497
|
+
print("\n\n[OK] Server stopped")
|
|
498
|
+
finally:
|
|
499
|
+
os.chdir(original_dir)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def show_version():
|
|
503
|
+
"""Display PyReact version"""
|
|
504
|
+
print("PyReact Framework v1.0.2")
|
|
505
|
+
|
|
461
506
|
|
|
462
507
|
def main():
|
|
463
508
|
"""Main CLI entry point"""
|
|
464
509
|
parser = argparse.ArgumentParser(
|
|
465
510
|
description='PyReact - Framework Web Declarativo para Python'
|
|
466
511
|
)
|
|
512
|
+
parser.add_argument('-v', '--version', action='store_true', help='Show version')
|
|
467
513
|
|
|
468
514
|
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
|
469
515
|
|
|
@@ -481,6 +527,11 @@ def main():
|
|
|
481
527
|
# Test command
|
|
482
528
|
subparsers.add_parser('test', help='Run tests')
|
|
483
529
|
|
|
530
|
+
# Serve command
|
|
531
|
+
serve_parser = subparsers.add_parser('serve', help='Serve production build')
|
|
532
|
+
serve_parser.add_argument('--port', type=int, default=5000, help='Port number')
|
|
533
|
+
serve_parser.add_argument('--dir', default='dist', help='Directory to serve')
|
|
534
|
+
|
|
484
535
|
# Generate command
|
|
485
536
|
gen_parser = subparsers.add_parser('generate', help='Generate component or hook')
|
|
486
537
|
gen_parser.add_argument('type', choices=['component', 'hook'], help='Type to generate')
|
|
@@ -490,6 +541,11 @@ def main():
|
|
|
490
541
|
|
|
491
542
|
args = parser.parse_args()
|
|
492
543
|
|
|
544
|
+
# Handle --version flag
|
|
545
|
+
if hasattr(args, 'version') and args.version:
|
|
546
|
+
show_version()
|
|
547
|
+
return
|
|
548
|
+
|
|
493
549
|
if args.command == 'create':
|
|
494
550
|
create_project(args.name)
|
|
495
551
|
elif args.command == 'dev':
|
|
@@ -498,15 +554,22 @@ def main():
|
|
|
498
554
|
build_project()
|
|
499
555
|
elif args.command == 'test':
|
|
500
556
|
run_tests()
|
|
557
|
+
elif args.command == 'serve':
|
|
558
|
+
serve_build(args.port, args.dir)
|
|
501
559
|
elif args.command == 'generate':
|
|
502
560
|
if args.type == 'component':
|
|
503
561
|
component_type = 'class' if args.class_type else 'functional'
|
|
504
562
|
generate_component(args.name, component_type)
|
|
505
563
|
elif args.type == 'hook':
|
|
506
564
|
generate_hook(args.name)
|
|
565
|
+
else:
|
|
566
|
+
parser.print_help()
|
|
507
567
|
else:
|
|
508
568
|
parser.print_help()
|
|
509
569
|
|
|
510
570
|
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
|
|
511
574
|
if __name__ == '__main__':
|
|
512
575
|
main()
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Router Module
|
|
3
|
+
=============
|
|
4
|
+
|
|
5
|
+
Declarative routing for Single Page Applications.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
9
|
+
from .element import VNode, h
|
|
10
|
+
from .component import Component
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Router(Component):
|
|
14
|
+
"""
|
|
15
|
+
Main router component that manages navigation state.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
element(Router, {},
|
|
19
|
+
element(Route, {'path': '/', 'component': Home}),
|
|
20
|
+
element(Route, {'path': '/about', 'component': About})
|
|
21
|
+
)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, props: Dict[str, Any]):
|
|
25
|
+
super().__init__(props)
|
|
26
|
+
self.state = {
|
|
27
|
+
'path': self._get_current_path()
|
|
28
|
+
}
|
|
29
|
+
self._setup_listeners()
|
|
30
|
+
|
|
31
|
+
def _get_current_path(self) -> str:
|
|
32
|
+
"""Get current path from window location"""
|
|
33
|
+
# In browser environment, get from window.location
|
|
34
|
+
# For SSR, get from props or default to '/'
|
|
35
|
+
return self.props.get('initialPath', '/')
|
|
36
|
+
|
|
37
|
+
def _setup_listeners(self):
|
|
38
|
+
"""Setup popstate listener for browser navigation"""
|
|
39
|
+
# In browser, would add event listener
|
|
40
|
+
# For now, this is a placeholder
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
def navigate(self, path: str):
|
|
44
|
+
"""Navigate to a new path"""
|
|
45
|
+
self.setState({'path': path})
|
|
46
|
+
|
|
47
|
+
def get_context_value(self) -> Dict[str, Any]:
|
|
48
|
+
"""Get router context value"""
|
|
49
|
+
return {
|
|
50
|
+
'path': self.state['path'],
|
|
51
|
+
'navigate': self.navigate,
|
|
52
|
+
'params': {},
|
|
53
|
+
'query': {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def render(self) -> VNode:
|
|
57
|
+
"""Render router and children"""
|
|
58
|
+
children = self.props.get('children', [])
|
|
59
|
+
|
|
60
|
+
# Find matching route
|
|
61
|
+
for child in children:
|
|
62
|
+
if hasattr(child, 'props') and child.props.get('path') == self.state['path']:
|
|
63
|
+
component = child.props.get('component')
|
|
64
|
+
if component:
|
|
65
|
+
if callable(component):
|
|
66
|
+
return component()
|
|
67
|
+
else:
|
|
68
|
+
return h(component, {})
|
|
69
|
+
|
|
70
|
+
# No matching route, render 404 or nothing
|
|
71
|
+
return h('div', {}, '404 - Page Not Found')
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class Route(Component):
|
|
75
|
+
"""
|
|
76
|
+
Route component that defines a path and its component.
|
|
77
|
+
|
|
78
|
+
Usage:
|
|
79
|
+
element(Route, {'path': '/', 'component': Home})
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def render(self) -> VNode:
|
|
83
|
+
"""Route doesn't render directly, it's used by Router"""
|
|
84
|
+
# This is a declarative component, Router handles rendering
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Link(Component):
|
|
89
|
+
"""
|
|
90
|
+
Navigation link component.
|
|
91
|
+
|
|
92
|
+
Usage:
|
|
93
|
+
element(Link, {'to': '/about'}, 'About Page')
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(self, props: Dict[str, Any]):
|
|
97
|
+
super().__init__(props)
|
|
98
|
+
self.handleClick = self._handle_click
|
|
99
|
+
|
|
100
|
+
def _handle_click(self, event):
|
|
101
|
+
"""Handle click event"""
|
|
102
|
+
# Prevent default link behavior
|
|
103
|
+
if hasattr(event, 'preventDefault'):
|
|
104
|
+
event.preventDefault()
|
|
105
|
+
|
|
106
|
+
# Navigate to new path
|
|
107
|
+
to = self.props.get('to', '/')
|
|
108
|
+
|
|
109
|
+
# In browser, would use history.pushState
|
|
110
|
+
# For now, just update state
|
|
111
|
+
print(f"Navigate to: {to}")
|
|
112
|
+
|
|
113
|
+
def render(self) -> VNode:
|
|
114
|
+
"""Render link element"""
|
|
115
|
+
to = self.props.get('to', '/')
|
|
116
|
+
children = self.props.get('children', [])
|
|
117
|
+
|
|
118
|
+
return h('a', {
|
|
119
|
+
'href': to,
|
|
120
|
+
'onClick': self.handleClick,
|
|
121
|
+
'style': {'cursor': 'pointer'}
|
|
122
|
+
}, *children)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class Redirect(Component):
|
|
126
|
+
"""
|
|
127
|
+
Redirect component that navigates to a new path.
|
|
128
|
+
|
|
129
|
+
Usage:
|
|
130
|
+
element(Redirect, {'to': '/login'})
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def component_did_mount(self):
|
|
134
|
+
"""Redirect when mounted"""
|
|
135
|
+
to = self.props.get('to', '/')
|
|
136
|
+
# In browser, would use history.replaceState
|
|
137
|
+
print(f"Redirect to: {to}")
|
|
138
|
+
|
|
139
|
+
def render(self) -> VNode:
|
|
140
|
+
"""Redirect doesn't render anything"""
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class Switch(Component):
|
|
145
|
+
"""
|
|
146
|
+
Switch component that renders the first matching Route.
|
|
147
|
+
|
|
148
|
+
Usage:
|
|
149
|
+
element(Switch, {},
|
|
150
|
+
element(Route, {'path': '/', 'component': Home}),
|
|
151
|
+
element(Route, {'path': '/about', 'component': About}),
|
|
152
|
+
element(Route, {'component': NotFound}) # 404 fallback
|
|
153
|
+
)
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
def render(self) -> VNode:
|
|
157
|
+
"""Render first matching route"""
|
|
158
|
+
children = self.props.get('children', [])
|
|
159
|
+
|
|
160
|
+
for child in children:
|
|
161
|
+
if hasattr(child, 'props'):
|
|
162
|
+
path = child.props.get('path')
|
|
163
|
+
component = child.props.get('component')
|
|
164
|
+
|
|
165
|
+
# If no path, it's a fallback (404)
|
|
166
|
+
if path is None:
|
|
167
|
+
if component:
|
|
168
|
+
if callable(component):
|
|
169
|
+
return component()
|
|
170
|
+
else:
|
|
171
|
+
return h(component, {})
|
|
172
|
+
|
|
173
|
+
# Check if path matches
|
|
174
|
+
# TODO: Implement proper path matching with params
|
|
175
|
+
if path == self._get_current_path():
|
|
176
|
+
if component:
|
|
177
|
+
if callable(component):
|
|
178
|
+
return component()
|
|
179
|
+
else:
|
|
180
|
+
return h(component, {})
|
|
181
|
+
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
def _get_current_path(self) -> str:
|
|
185
|
+
"""Get current path"""
|
|
186
|
+
# TODO: Get from Router context
|
|
187
|
+
return '/'
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Hook functions for functional components
|
|
191
|
+
|
|
192
|
+
def useParams() -> Dict[str, str]:
|
|
193
|
+
"""
|
|
194
|
+
Get route parameters.
|
|
195
|
+
|
|
196
|
+
Usage:
|
|
197
|
+
params = useParams()
|
|
198
|
+
user_id = params['id']
|
|
199
|
+
"""
|
|
200
|
+
# TODO: Implement with context
|
|
201
|
+
return {}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def useLocation() -> Dict[str, Any]:
|
|
205
|
+
"""
|
|
206
|
+
Get current location information.
|
|
207
|
+
|
|
208
|
+
Usage:
|
|
209
|
+
location = useLocation()
|
|
210
|
+
query = location.get('query', '')
|
|
211
|
+
"""
|
|
212
|
+
# TODO: Implement with context
|
|
213
|
+
return {
|
|
214
|
+
'pathname': '/',
|
|
215
|
+
'search': '',
|
|
216
|
+
'hash': ''
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def useHistory():
|
|
221
|
+
"""
|
|
222
|
+
Get history object for programmatic navigation.
|
|
223
|
+
|
|
224
|
+
Usage:
|
|
225
|
+
history = useHistory()
|
|
226
|
+
history.push('/new-path')
|
|
227
|
+
"""
|
|
228
|
+
class History:
|
|
229
|
+
def push(self, path: str):
|
|
230
|
+
"""Navigate to new path"""
|
|
231
|
+
print(f"Navigate to: {path}")
|
|
232
|
+
|
|
233
|
+
def replace(self, path: str):
|
|
234
|
+
"""Replace current path"""
|
|
235
|
+
print(f"Replace with: {path}")
|
|
236
|
+
|
|
237
|
+
def go_back(self):
|
|
238
|
+
"""Go back in history"""
|
|
239
|
+
print("Go back")
|
|
240
|
+
|
|
241
|
+
def go_forward(self):
|
|
242
|
+
"""Go forward in history"""
|
|
243
|
+
print("Go forward")
|
|
244
|
+
|
|
245
|
+
return History()
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# Export router components
|
|
249
|
+
__all__ = [
|
|
250
|
+
'Router',
|
|
251
|
+
'Route',
|
|
252
|
+
'Link',
|
|
253
|
+
'Redirect',
|
|
254
|
+
'Switch',
|
|
255
|
+
'useParams',
|
|
256
|
+
'useLocation',
|
|
257
|
+
'useHistory',
|
|
258
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Theme Provider
|
|
3
|
+
==============
|
|
4
|
+
|
|
5
|
+
Context-based theming system for PyReact applications.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
from .context import Context, create_context
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Create theme context
|
|
13
|
+
ThemeContext = create_context({})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ThemeProvider:
|
|
17
|
+
"""
|
|
18
|
+
Theme provider component that makes theme available to all descendants.
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
element(ThemeProvider, {'theme': {'primary': '#007bff'}},
|
|
22
|
+
element(App, {})
|
|
23
|
+
)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, props: Dict[str, Any]):
|
|
27
|
+
self.props = props
|
|
28
|
+
self.theme = props.get('theme', {})
|
|
29
|
+
|
|
30
|
+
def render(self):
|
|
31
|
+
"""Render with theme context"""
|
|
32
|
+
from .element import element
|
|
33
|
+
|
|
34
|
+
children = self.props.get('children', [])
|
|
35
|
+
|
|
36
|
+
return element(ThemeContext.Provider, {
|
|
37
|
+
'value': self.theme
|
|
38
|
+
}, *children)
|
|
39
|
+
|
|
40
|
+
def get_child_context(self) -> Dict[str, Any]:
|
|
41
|
+
"""Get context for children"""
|
|
42
|
+
return {
|
|
43
|
+
'theme': self.theme
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def use_theme() -> Dict[str, Any]:
|
|
48
|
+
"""
|
|
49
|
+
Hook to access current theme.
|
|
50
|
+
|
|
51
|
+
Usage:
|
|
52
|
+
theme = use_theme()
|
|
53
|
+
color = theme['primary']
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Current theme object
|
|
57
|
+
"""
|
|
58
|
+
# This would use useContext in a real implementation
|
|
59
|
+
# For now, return default theme
|
|
60
|
+
return {
|
|
61
|
+
'primary': '#007bff',
|
|
62
|
+
'secondary': '#6c757d',
|
|
63
|
+
'success': '#28a745',
|
|
64
|
+
'danger': '#dc3545',
|
|
65
|
+
'warning': '#ffc107',
|
|
66
|
+
'info': '#17a2b8',
|
|
67
|
+
'light': '#f8f9fa',
|
|
68
|
+
'dark': '#343a40'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Default theme
|
|
73
|
+
DEFAULT_THEME = {
|
|
74
|
+
'primary': '#007bff',
|
|
75
|
+
'secondary': '#6c757d',
|
|
76
|
+
'success': '#28a745',
|
|
77
|
+
'danger': '#dc3545',
|
|
78
|
+
'warning': '#ffc107',
|
|
79
|
+
'info': '#17a2b8',
|
|
80
|
+
'light': '#f8f9fa',
|
|
81
|
+
'dark': '#343a40',
|
|
82
|
+
'fontFamily': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
83
|
+
'fontSize': '16px',
|
|
84
|
+
'borderRadius': '4px',
|
|
85
|
+
'spacing': {
|
|
86
|
+
'xs': '4px',
|
|
87
|
+
'sm': '8px',
|
|
88
|
+
'md': '16px',
|
|
89
|
+
'lg': '24px',
|
|
90
|
+
'xl': '32px'
|
|
91
|
+
},
|
|
92
|
+
'breakpoints': {
|
|
93
|
+
'sm': '576px',
|
|
94
|
+
'md': '768px',
|
|
95
|
+
'lg': '992px',
|
|
96
|
+
'xl': '1200px'
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Export
|
|
102
|
+
__all__ = [
|
|
103
|
+
'ThemeProvider',
|
|
104
|
+
'ThemeContext',
|
|
105
|
+
'use_theme',
|
|
106
|
+
'DEFAULT_THEME',
|
|
107
|
+
]
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyreact-framework
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: Framework web declarativo inspirado no React, construído nativamente para Python
|
|
5
5
|
Author-email: wanbnn <wanbnn@users.noreply.github.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/wanbnn/pyreact
|
|
8
|
-
Project-URL: Documentation, https://pyreact.readthedocs.io
|
|
8
|
+
Project-URL: Documentation, https://pyreact-framework.readthedocs.io
|
|
9
9
|
Project-URL: Repository, https://github.com/wanbnn/pyreact
|
|
10
10
|
Project-URL: Issues, https://github.com/wanbnn/pyreact/issues
|
|
11
|
+
Project-URL: Changelog, https://pyreact-framework.readthedocs.io/en/latest/resources/changelog.html
|
|
11
12
|
Keywords: react,ui,frontend,declarative,components,virtual-dom,web,framework
|
|
12
13
|
Classifier: Development Status :: 4 - Beta
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
@@ -159,6 +160,38 @@ def MyComponent(props):
|
|
|
159
160
|
|
|
160
161
|
## 📖 Documentação
|
|
161
162
|
|
|
163
|
+
### 📚 Documentação Completa
|
|
164
|
+
|
|
165
|
+
A documentação completa está disponível no **Read the Docs**:
|
|
166
|
+
|
|
167
|
+
🔗 **https://pyreact-framework.readthedocs.io/**
|
|
168
|
+
|
|
169
|
+
#### Conteúdo da Documentação:
|
|
170
|
+
|
|
171
|
+
- **Getting Started**
|
|
172
|
+
- Installation
|
|
173
|
+
- Quick Start
|
|
174
|
+
- Tutorial (Todo App)
|
|
175
|
+
|
|
176
|
+
- **Core Concepts**
|
|
177
|
+
- Components
|
|
178
|
+
- Props
|
|
179
|
+
- State
|
|
180
|
+
- Events
|
|
181
|
+
- Lifecycle
|
|
182
|
+
|
|
183
|
+
- **Advanced**
|
|
184
|
+
- Server-Side Rendering (SSR)
|
|
185
|
+
- Routing
|
|
186
|
+
- Styling
|
|
187
|
+
- Testing
|
|
188
|
+
|
|
189
|
+
- **API Reference**
|
|
190
|
+
- Element API
|
|
191
|
+
- Component API
|
|
192
|
+
- Hooks API
|
|
193
|
+
- CLI API
|
|
194
|
+
|
|
162
195
|
### CLI Commands
|
|
163
196
|
|
|
164
197
|
```bash
|
|
@@ -15,7 +15,9 @@ pyreact/core/portal.py
|
|
|
15
15
|
pyreact/core/reconciler.py
|
|
16
16
|
pyreact/core/refs.py
|
|
17
17
|
pyreact/core/renderer.py
|
|
18
|
+
pyreact/core/router.py
|
|
18
19
|
pyreact/core/scheduler.py
|
|
20
|
+
pyreact/core/theme.py
|
|
19
21
|
pyreact/devtools/__init__.py
|
|
20
22
|
pyreact/devtools/debugger.py
|
|
21
23
|
pyreact/devtools/profiler.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{pyreact_framework-1.0.1 → pyreact_framework-1.0.2}/pyreact_framework.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|