dars-framework 1.2.3__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.
- dars/__init__.py +0 -0
- dars/all.py +69 -0
- dars/cli/__init__.py +0 -0
- dars/cli/doctor/__init__.py +1 -0
- dars/cli/doctor/detect.py +154 -0
- dars/cli/doctor/doctor.py +176 -0
- dars/cli/doctor/installers.py +100 -0
- dars/cli/doctor/persist.py +62 -0
- dars/cli/doctor/preflight.py +33 -0
- dars/cli/doctor/ui.py +54 -0
- dars/cli/hot_reload.py +33 -0
- dars/cli/main.py +1107 -0
- dars/cli/preview.py +448 -0
- dars/cli/translations.py +531 -0
- dars/components/__init__.py +0 -0
- dars/components/advanced/__init__.py +8 -0
- dars/components/advanced/accordion.py +26 -0
- dars/components/advanced/card.py +33 -0
- dars/components/advanced/modal.py +45 -0
- dars/components/advanced/navbar.py +44 -0
- dars/components/advanced/table.py +25 -0
- dars/components/advanced/tabs.py +31 -0
- dars/components/basic/__init__.py +34 -0
- dars/components/basic/button.py +55 -0
- dars/components/basic/checkbox.py +35 -0
- dars/components/basic/container.py +29 -0
- dars/components/basic/datepicker.py +139 -0
- dars/components/basic/image.py +36 -0
- dars/components/basic/input.py +57 -0
- dars/components/basic/link.py +31 -0
- dars/components/basic/markdown.py +86 -0
- dars/components/basic/page.py +20 -0
- dars/components/basic/progressbar.py +18 -0
- dars/components/basic/radiobutton.py +35 -0
- dars/components/basic/select.py +82 -0
- dars/components/basic/slider.py +63 -0
- dars/components/basic/spinner.py +12 -0
- dars/components/basic/text.py +23 -0
- dars/components/basic/textarea.py +46 -0
- dars/components/basic/tooltip.py +19 -0
- dars/components/layout/__init__.py +0 -0
- dars/components/layout/anchor.py +13 -0
- dars/components/layout/flex.py +26 -0
- dars/components/layout/grid.py +45 -0
- dars/config.py +134 -0
- dars/core/__init__.py +0 -0
- dars/core/app.py +957 -0
- dars/core/component.py +284 -0
- dars/core/events.py +102 -0
- dars/core/js_bridge.py +99 -0
- dars/core/properties.py +127 -0
- dars/core/state.py +309 -0
- dars/dars_tests/apps_test/health_check.py +56 -0
- dars/dars_tests/run_tests.py +275 -0
- dars/dars_tests/tests/test_advanced_components.py +69 -0
- dars/dars_tests/tests/test_basic_components.py +88 -0
- dars/dars_tests/tests/test_core_and_cli.py +17 -0
- dars/dars_tests/tests/test_layout_components.py +58 -0
- dars/dars_tests/tests/test_version_check.py +21 -0
- dars/docs/__init__.py +0 -0
- dars/docs/app.md +290 -0
- dars/docs/cli.md +80 -0
- dars/docs/components.md +1679 -0
- dars/docs/custom_components.md +30 -0
- dars/docs/events.md +45 -0
- dars/docs/exporters.md +162 -0
- dars/docs/getting_started.md +79 -0
- dars/docs/index.md +18 -0
- dars/docs/scripts.md +593 -0
- dars/docs/state_management.md +57 -0
- dars/exporters/__init__.py +0 -0
- dars/exporters/base.py +96 -0
- dars/exporters/web/OLD/html_css_js_OLD4.py +1538 -0
- dars/exporters/web/OLD/html_css_js_old.py +1406 -0
- dars/exporters/web/OLD/html_css_js_old2.py +1406 -0
- dars/exporters/web/__init__.py +0 -0
- dars/exporters/web/html_css_js.py +2675 -0
- dars/exporters/web/vdom.py +251 -0
- dars/js_lib.py +206 -0
- dars/scripts/__init__.py +0 -0
- dars/scripts/dscript.py +26 -0
- dars/scripts/script.py +39 -0
- dars/security.py +195 -0
- dars/templates/__init__.py +0 -0
- dars/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- dars/templates/examples/README.md +4 -0
- dars/templates/examples/__pycache__/dynamic_event_demo.cpython-311.pyc +0 -0
- dars/templates/examples/advanced/Modal_Demo/advanced_modal_demo.py +275 -0
- dars/templates/examples/advanced/SimpleDashboard/dashboard.py +437 -0
- dars/templates/examples/advanced/SimpleModermWeb/modern_web_app.py +452 -0
- dars/templates/examples/advanced/VariousComponents/all_components_demo.py +87 -0
- dars/templates/examples/advanced/__init__.py +0 -0
- dars/templates/examples/advanced/dState/state_mods_demo.py +68 -0
- dars/templates/examples/basic/Forms/form_components.py +516 -0
- dars/templates/examples/basic/Forms/simple_form.py +379 -0
- dars/templates/examples/basic/HelloWorld/hello_world.py +56 -0
- dars/templates/examples/basic/Layouts/flex_layout_responsive.py +13 -0
- dars/templates/examples/basic/Layouts/grid_layout_responsive.py +12 -0
- dars/templates/examples/basic/Layouts/layout_multipage_demo.py +23 -0
- dars/templates/examples/basic/Multipage/multipage_example.py +67 -0
- dars/templates/examples/basic/PWA/icon-192x192.png +0 -0
- dars/templates/examples/basic/PWA/icon-512x512.png +0 -0
- dars/templates/examples/basic/PWA/pwa_custom_icons.py +33 -0
- dars/templates/examples/basic/__init__.py +0 -0
- dars/templates/examples/demo/__pycache__/complete_app.cpython-311.pyc +0 -0
- dars/templates/examples/demo/complete_app.py +21 -0
- dars/templates/examples/markdown/MarkdownTemplate/README.md +159 -0
- dars/templates/examples/markdown/MarkdownTemplate/markdown_template.py +21 -0
- dars/templates/examples/markdown/MarkdownTemplate/other_docs.md +1 -0
- dars/templates/examples/markdown/__init__.py +0 -0
- dars/templates/html/__init__.py +0 -0
- dars/version.py +2 -0
- dars_framework-1.2.3.dist-info/METADATA +15 -0
- dars_framework-1.2.3.dist-info/RECORD +118 -0
- dars_framework-1.2.3.dist-info/WHEEL +5 -0
- dars_framework-1.2.3.dist-info/entry_points.txt +2 -0
- dars_framework-1.2.3.dist-info/licenses/LICENSE +21 -0
- dars_framework-1.2.3.dist-info/top_level.txt +1 -0
dars/security.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
from typing import Iterable, Set
|
|
4
|
+
from dars.core.js_bridge import esbuild_minify_js as _esbuild_minify_js, esbuild_minify_css as _esbuild_minify_css, esbuild_available as _esbuild_available
|
|
5
|
+
|
|
6
|
+
SAFE_JS_EXT = {'.js', '.mjs', '.cjs'}
|
|
7
|
+
SAFE_CSS_EXT = {'.css'}
|
|
8
|
+
SAFE_HTML_EXT = {'.html', '.htm'}
|
|
9
|
+
|
|
10
|
+
SKIP_PATTERNS = (
|
|
11
|
+
r'^snapshot.*\.json$',
|
|
12
|
+
r'^version.*\.txt$',
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
_pat_compiled = [re.compile(p) for p in SKIP_PATTERNS]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _should_skip(filename: str) -> bool:
|
|
19
|
+
base = os.path.basename(filename)
|
|
20
|
+
for p in _pat_compiled:
|
|
21
|
+
if p.match(base):
|
|
22
|
+
return True
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# --- Minifiers (conservative) ---
|
|
27
|
+
_js_block_comments = re.compile(r"/\*.*?\*/", re.DOTALL)
|
|
28
|
+
_js_line_comments = re.compile(r"(^|[^:\\])//.*?$", re.MULTILINE)
|
|
29
|
+
_js_spaces = re.compile(r"\s+")
|
|
30
|
+
_js_punct_spaces = re.compile(r"\s*([{}\[\](),;:<>+=\-*/%&|^!?])\s*")
|
|
31
|
+
|
|
32
|
+
_css_comments = re.compile(r"/\*.*?\*/", re.DOTALL)
|
|
33
|
+
_css_spaces = re.compile(r"\s+")
|
|
34
|
+
_css_punct_spaces = re.compile(r"\s*([{}:;,>~+])\s*")
|
|
35
|
+
|
|
36
|
+
_html_comments = re.compile(r"<!--(?!\s*\[if).*?-->", re.DOTALL)
|
|
37
|
+
_html_between_tags = re.compile(r">\s+<")
|
|
38
|
+
_js_string_splitter = re.compile(r'(".*?"|\'.*?\'|`.*?`)', re.DOTALL)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def minify_js(src: str) -> str:
|
|
42
|
+
"""Minify a JS source string. Uses esbuild if available; otherwise Python fallback."""
|
|
43
|
+
# Fast path: dump to temp file and use esbuild when available
|
|
44
|
+
if _esbuild_available():
|
|
45
|
+
try:
|
|
46
|
+
import tempfile
|
|
47
|
+
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.js', encoding='utf-8') as tf_in:
|
|
48
|
+
tf_in.write(src)
|
|
49
|
+
in_path = tf_in.name
|
|
50
|
+
with tempfile.NamedTemporaryFile('r', delete=False, suffix='.js', encoding='utf-8') as tf_out:
|
|
51
|
+
out_path = tf_out.name
|
|
52
|
+
if _esbuild_minify_js(in_path, out_path):
|
|
53
|
+
try:
|
|
54
|
+
with open(out_path, 'r', encoding='utf-8') as fr:
|
|
55
|
+
return fr.read()
|
|
56
|
+
finally:
|
|
57
|
+
try: os.remove(in_path)
|
|
58
|
+
except Exception: pass
|
|
59
|
+
try: os.remove(out_path)
|
|
60
|
+
except Exception: pass
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
# Prefer fast/robust rjsmin if available
|
|
64
|
+
try:
|
|
65
|
+
import rjsmin # type: ignore
|
|
66
|
+
return rjsmin.jsmin(src)
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
|
69
|
+
# Conservative regex fallback
|
|
70
|
+
try:
|
|
71
|
+
s = _js_block_comments.sub("", src)
|
|
72
|
+
s = _js_line_comments.sub(lambda m: m.group(1), s)
|
|
73
|
+
parts = _js_string_splitter.split(s)
|
|
74
|
+
for i in range(0, len(parts), 2):
|
|
75
|
+
p = parts[i]
|
|
76
|
+
p = _js_punct_spaces.sub(r"\1", p)
|
|
77
|
+
p = _js_spaces.sub(" ", p)
|
|
78
|
+
parts[i] = p
|
|
79
|
+
s = "".join(parts)
|
|
80
|
+
return s.strip()
|
|
81
|
+
except Exception:
|
|
82
|
+
return src
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def minify_css(src: str) -> str:
|
|
86
|
+
"""Minify a CSS source string. Uses esbuild if available; otherwise Python fallback."""
|
|
87
|
+
if _esbuild_available():
|
|
88
|
+
try:
|
|
89
|
+
import tempfile
|
|
90
|
+
with tempfile.NamedTemporaryFile('w', delete=False, suffix='.css', encoding='utf-8') as tf_in:
|
|
91
|
+
tf_in.write(src)
|
|
92
|
+
in_path = tf_in.name
|
|
93
|
+
with tempfile.NamedTemporaryFile('r', delete=False, suffix='.css', encoding='utf-8') as tf_out:
|
|
94
|
+
out_path = tf_out.name
|
|
95
|
+
if _esbuild_minify_css(in_path, out_path):
|
|
96
|
+
try:
|
|
97
|
+
with open(out_path, 'r', encoding='utf-8') as fr:
|
|
98
|
+
return fr.read()
|
|
99
|
+
finally:
|
|
100
|
+
try: os.remove(in_path)
|
|
101
|
+
except Exception: pass
|
|
102
|
+
try: os.remove(out_path)
|
|
103
|
+
except Exception: pass
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|
|
106
|
+
# Prefer rcssmin if available
|
|
107
|
+
try:
|
|
108
|
+
import rcssmin # type: ignore
|
|
109
|
+
return rcssmin.cssmin(src)
|
|
110
|
+
except Exception:
|
|
111
|
+
pass
|
|
112
|
+
try:
|
|
113
|
+
s = _css_comments.sub("", src)
|
|
114
|
+
s = _css_punct_spaces.sub(r"\1", s)
|
|
115
|
+
s = _css_spaces.sub(" ", s)
|
|
116
|
+
return s.strip()
|
|
117
|
+
except Exception:
|
|
118
|
+
return src
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def minify_html(src: str) -> str:
|
|
122
|
+
# Prefer htmlmin if available
|
|
123
|
+
try:
|
|
124
|
+
import htmlmin # type: ignore
|
|
125
|
+
return htmlmin.minify(src, remove_comments=True, remove_empty_space=True, reduce_boolean_attributes=True)
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
try:
|
|
129
|
+
s = _html_comments.sub("", src)
|
|
130
|
+
s = re.sub(r">\s+<", "><", s)
|
|
131
|
+
s = re.sub(r"\s{2,}", " ", s)
|
|
132
|
+
return s.strip()
|
|
133
|
+
except Exception:
|
|
134
|
+
return src
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def minify_output_dir(output_dir: str, extra_skip: Iterable[str] = None, progress_cb=None) -> int:
|
|
138
|
+
"""
|
|
139
|
+
Minify HTML, CSS, and JS files in-place under output_dir.
|
|
140
|
+
Skips VDOM, snapshot, and version files by default.
|
|
141
|
+
|
|
142
|
+
Returns: number of files minified.
|
|
143
|
+
"""
|
|
144
|
+
# Gather candidates first to allow accurate progress reporting
|
|
145
|
+
extra_skip_set: Set[str] = set(extra_skip or [])
|
|
146
|
+
candidates = []
|
|
147
|
+
for root, _dirs, files in os.walk(output_dir):
|
|
148
|
+
for name in files:
|
|
149
|
+
if name in extra_skip_set:
|
|
150
|
+
continue
|
|
151
|
+
if _should_skip(name):
|
|
152
|
+
continue
|
|
153
|
+
ext = os.path.splitext(name)[1].lower()
|
|
154
|
+
if ext in SAFE_JS_EXT or ext in SAFE_CSS_EXT or ext in SAFE_HTML_EXT:
|
|
155
|
+
candidates.append(os.path.join(root, name))
|
|
156
|
+
|
|
157
|
+
total = len(candidates)
|
|
158
|
+
processed = 0
|
|
159
|
+
written = 0
|
|
160
|
+
for full in candidates:
|
|
161
|
+
ext = os.path.splitext(full)[1].lower()
|
|
162
|
+
try:
|
|
163
|
+
with open(full, 'r', encoding='utf-8') as f:
|
|
164
|
+
content = f.read()
|
|
165
|
+
except Exception:
|
|
166
|
+
processed += 1
|
|
167
|
+
if progress_cb:
|
|
168
|
+
try: progress_cb(processed, total)
|
|
169
|
+
except Exception: pass
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
new_content = None
|
|
173
|
+
if ext in SAFE_JS_EXT:
|
|
174
|
+
new_content = minify_js(content)
|
|
175
|
+
elif ext in SAFE_CSS_EXT:
|
|
176
|
+
new_content = minify_css(content)
|
|
177
|
+
elif ext in SAFE_HTML_EXT:
|
|
178
|
+
new_content = minify_html(content)
|
|
179
|
+
|
|
180
|
+
if new_content is not None and new_content != content:
|
|
181
|
+
try:
|
|
182
|
+
with open(full, 'w', encoding='utf-8') as f:
|
|
183
|
+
f.write(new_content)
|
|
184
|
+
written += 1
|
|
185
|
+
except Exception:
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
processed += 1
|
|
189
|
+
if progress_cb:
|
|
190
|
+
try:
|
|
191
|
+
progress_cb(processed, total)
|
|
192
|
+
except Exception:
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
return written
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
from dars.all import *
|
|
2
|
+
from dars.scripts.script import *
|
|
3
|
+
# Crear la aplicación
|
|
4
|
+
app = App(title="Mi App con Navbar Funcional")
|
|
5
|
+
app.add_script(InlineScript('''
|
|
6
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
7
|
+
var modal = document.getElementById('modal-demo');
|
|
8
|
+
var btnAbrir = document.getElementById('btn-abrir-modal');
|
|
9
|
+
var btnCerrar = document.getElementById('btn-cerrar-modal');
|
|
10
|
+
if (modal && btnAbrir && btnCerrar) {
|
|
11
|
+
btnAbrir.addEventListener('click', function() {
|
|
12
|
+
if (modal.getAttribute('data-enabled') === 'true') {
|
|
13
|
+
modal.style.display = 'flex';
|
|
14
|
+
modal.classList.remove('dars-modal-hidden');
|
|
15
|
+
modal.removeAttribute('hidden');
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
btnCerrar.addEventListener('click', function() {
|
|
19
|
+
modal.style.display = 'none';
|
|
20
|
+
modal.classList.add('dars-modal-hidden');
|
|
21
|
+
modal.setAttribute('hidden', '');
|
|
22
|
+
});
|
|
23
|
+
// Ocultar modal por defecto al cargar
|
|
24
|
+
modal.style.display = 'none';
|
|
25
|
+
modal.classList.add('dars-modal-hidden');
|
|
26
|
+
modal.setAttribute('hidden', '');
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
'''))
|
|
30
|
+
# Función para crear el navbar (reutilizable en todas las páginas)
|
|
31
|
+
def crear_navbar():
|
|
32
|
+
home_link = Link(text="Inicio", href="/", style={"color": "white", "text-decoration": "none", "margin-right": "20px", "padding": "10px 15px", "border-radius": "5px"})
|
|
33
|
+
about_link = Link(text="Acerca de", href="/about.html", style={"color": "white", "text-decoration": "none", "margin-right": "20px", "padding": "10px 15px", "border-radius": "5px"})
|
|
34
|
+
contact_link = Link(text="Contacto", href="/contact.html", style={"color": "white", "text-decoration": "none", "margin-right": "20px", "padding": "10px 15px", "border-radius": "5px"})
|
|
35
|
+
|
|
36
|
+
return Navbar(
|
|
37
|
+
home_link,
|
|
38
|
+
about_link,
|
|
39
|
+
contact_link,
|
|
40
|
+
brand="🚀 DarsApp",
|
|
41
|
+
style={
|
|
42
|
+
"background": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
43
|
+
"padding": "15px 30px",
|
|
44
|
+
"box-shadow": "0 4px 6px rgba(0,0,0,0.1)"
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# PÁGINA DE INICIO
|
|
49
|
+
home_content = Container(
|
|
50
|
+
Text(
|
|
51
|
+
text="¡Bienvenido a DarsApp!",
|
|
52
|
+
style={
|
|
53
|
+
"font-size": "3rem",
|
|
54
|
+
"color": "#2c3e50",
|
|
55
|
+
"text-align": "center",
|
|
56
|
+
"margin": "50px 0 30px 0",
|
|
57
|
+
"font-weight": "bold"
|
|
58
|
+
}
|
|
59
|
+
),
|
|
60
|
+
Text(
|
|
61
|
+
text="Una aplicación de demostración construida con el framework Dars",
|
|
62
|
+
style={
|
|
63
|
+
"font-size": "1.3rem",
|
|
64
|
+
"color": "#7f8c8d",
|
|
65
|
+
"text-align": "center",
|
|
66
|
+
"margin": "0 0 50px 0",
|
|
67
|
+
"line-height": "1.6"
|
|
68
|
+
}
|
|
69
|
+
),
|
|
70
|
+
# --- COMPONENTES AVANZADOS DEMO ---
|
|
71
|
+
Card([
|
|
72
|
+
Text(text="Este es un Card avanzado con hijos", style={"margin-bottom": "10px"}),
|
|
73
|
+
Link(text="Ir a Contacto", href="/contact.html", style={"color": "#667eea"})
|
|
74
|
+
], title="Demo Card", style={"margin": "30px auto", "max-width": "400px"}),
|
|
75
|
+
|
|
76
|
+
# --- Modal con botón para cerrar ---
|
|
77
|
+
Modal([
|
|
78
|
+
Text(text="¡Este es el contenido de un Modal avanzado!", style={"text-align": "center"}),
|
|
79
|
+
Button(text="Cerrar Modal", id="btn-cerrar-modal", style={"margin": "20px auto 0 auto", "display": "block"})
|
|
80
|
+
], title="Demo Modal", is_open=False, id="modal-demo", style={"margin": "30px auto"}),
|
|
81
|
+
Button(
|
|
82
|
+
text="Mostrar Modal",
|
|
83
|
+
id="btn-abrir-modal",
|
|
84
|
+
style={"margin": "20px 0", "padding": "10px 20px", "background": "#667eea", "color": "white", "border": "none", "border-radius": "5px"}
|
|
85
|
+
),
|
|
86
|
+
|
|
87
|
+
Tabs(
|
|
88
|
+
minimum_logic=True,
|
|
89
|
+
tabs=["Tab 1", "Tab 2", "Tab 3"],
|
|
90
|
+
panels=[
|
|
91
|
+
Text(text="Contenido de la pestaña 1"),
|
|
92
|
+
Card([Text(text="Contenido dentro de un Card en Tab 2")]),
|
|
93
|
+
Container(Text(text="Panel 3 con Container"))
|
|
94
|
+
],
|
|
95
|
+
selected=0,
|
|
96
|
+
style={"margin": "30px auto", "max-width": "600px"}
|
|
97
|
+
),
|
|
98
|
+
|
|
99
|
+
Accordion(
|
|
100
|
+
minimum_logic=True,
|
|
101
|
+
sections=[
|
|
102
|
+
("Sección 1", Text(text="Contenido de la sección 1")),
|
|
103
|
+
("Sección 2", Card([Text(text="Contenido de la sección 2 en Card")]))
|
|
104
|
+
],
|
|
105
|
+
open_indices=[0],
|
|
106
|
+
style={"margin": "30px auto", "max-width": "600px"}
|
|
107
|
+
),
|
|
108
|
+
|
|
109
|
+
Table(
|
|
110
|
+
columns=[
|
|
111
|
+
{"title": "Nombre", "field": "nombre"},
|
|
112
|
+
{"title": "Edad", "field": "edad"}
|
|
113
|
+
],
|
|
114
|
+
data=[
|
|
115
|
+
{"nombre": "Ana", "edad": 28},
|
|
116
|
+
{"nombre": "Luis", "edad": 34}
|
|
117
|
+
],
|
|
118
|
+
style={"margin": "30px auto", "max-width": "400px"}
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
home_page = Page(
|
|
123
|
+
crear_navbar(),
|
|
124
|
+
home_content,
|
|
125
|
+
style={
|
|
126
|
+
"font-family": "Arial, sans-serif",
|
|
127
|
+
"background": "linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)",
|
|
128
|
+
"min-height": "100vh"
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# PÁGINA ACERCA DE
|
|
133
|
+
about_content = Container(
|
|
134
|
+
Text(
|
|
135
|
+
text="Acerca de DarsApp",
|
|
136
|
+
style={
|
|
137
|
+
"font-size": "2.5rem",
|
|
138
|
+
"color": "#2c3e50",
|
|
139
|
+
"text-align": "center",
|
|
140
|
+
"margin": "50px 0 30px 0",
|
|
141
|
+
"font-weight": "bold"
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
Text(
|
|
145
|
+
text="Nuestra Historia",
|
|
146
|
+
style={
|
|
147
|
+
"font-size": "1.8rem",
|
|
148
|
+
"color": "#34495e",
|
|
149
|
+
"text-align": "center",
|
|
150
|
+
"margin-bottom": "20px",
|
|
151
|
+
"font-weight": "bold"
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
Text(
|
|
155
|
+
text="DarsApp es una aplicación de demostración que muestra las capacidades del framework Dars para crear interfaces web modernas y funcionales. Nuestro objetivo es proporcionar una experiencia de usuario excepcional a través de un diseño limpio y una navegación intuitiva.",
|
|
156
|
+
style={
|
|
157
|
+
"font-size": "1.1rem",
|
|
158
|
+
"line-height": "1.8",
|
|
159
|
+
"color": "#7f8c8d",
|
|
160
|
+
"text-align": "center",
|
|
161
|
+
"background": "white",
|
|
162
|
+
"padding": "40px",
|
|
163
|
+
"border-radius": "15px",
|
|
164
|
+
"box-shadow": "0 5px 15px rgba(0,0,0,0.1)",
|
|
165
|
+
"max-width": "800px",
|
|
166
|
+
"margin": "0 auto"
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
style={
|
|
170
|
+
"max-width": "1200px",
|
|
171
|
+
"margin": "0 auto",
|
|
172
|
+
"padding": "20px"
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
about_page = Page(
|
|
177
|
+
crear_navbar(),
|
|
178
|
+
about_content,
|
|
179
|
+
style={
|
|
180
|
+
"font-family": "Arial, sans-serif",
|
|
181
|
+
"background": "linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)",
|
|
182
|
+
"min-height": "100vh"
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# PÁGINA CONTACTO
|
|
187
|
+
contact_content = Container(
|
|
188
|
+
Text(
|
|
189
|
+
text="Contáctanos",
|
|
190
|
+
style={
|
|
191
|
+
"font-size": "2.5rem",
|
|
192
|
+
"color": "#2c3e50",
|
|
193
|
+
"text-align": "center",
|
|
194
|
+
"margin": "50px 0 30px 0",
|
|
195
|
+
"font-weight": "bold"
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
Text(
|
|
199
|
+
text="📧 Información de Contacto",
|
|
200
|
+
style={
|
|
201
|
+
"font-size": "1.8rem",
|
|
202
|
+
"color": "#34495e",
|
|
203
|
+
"margin-bottom": "30px",
|
|
204
|
+
"font-weight": "bold",
|
|
205
|
+
"text-align": "center"
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
Text(
|
|
209
|
+
text="Contact INFO",
|
|
210
|
+
style={
|
|
211
|
+
"font-size": "1.1rem",
|
|
212
|
+
"line-height": "2",
|
|
213
|
+
"color": "#7f8c8d",
|
|
214
|
+
"text-align": "center",
|
|
215
|
+
"white-space": "pre-line",
|
|
216
|
+
"background": "white",
|
|
217
|
+
"padding": "40px",
|
|
218
|
+
"border-radius": "15px",
|
|
219
|
+
"box-shadow": "0 5px 15px rgba(0,0,0,0.1)",
|
|
220
|
+
"max-width": "600px",
|
|
221
|
+
"margin": "0 auto"
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
Text(
|
|
225
|
+
text="Horario de Atención:\nLunes a Viernes: 9:00 AM - 6:00 PM\nSábados: 10:00 AM - 2:00 PM",
|
|
226
|
+
style={
|
|
227
|
+
"font-size": "1rem",
|
|
228
|
+
"line-height": "1.6",
|
|
229
|
+
"color": "#95a5a6",
|
|
230
|
+
"text-align": "center",
|
|
231
|
+
"margin-top": "30px",
|
|
232
|
+
"white-space": "pre-line"
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
style={
|
|
236
|
+
"max-width": "1200px",
|
|
237
|
+
"margin": "0 auto",
|
|
238
|
+
"padding": "20px"
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
contact_page = Page(
|
|
243
|
+
crear_navbar(),
|
|
244
|
+
contact_content,
|
|
245
|
+
style={
|
|
246
|
+
"font-family": "Arial, sans-serif",
|
|
247
|
+
"background": "linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)",
|
|
248
|
+
"min-height": "100vh"
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Agregar estilos globales
|
|
253
|
+
app.add_global_style(selector="body", styles={
|
|
254
|
+
"margin": "0",
|
|
255
|
+
"padding": "0",
|
|
256
|
+
"font-family": "Arial, sans-serif"
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
app.add_global_style(selector="a", styles={
|
|
260
|
+
"transition": "all 0.3s ease"
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
app.add_global_style(selector="a:hover", styles={
|
|
264
|
+
"background-color": "rgba(255,255,255,0.2) !important",
|
|
265
|
+
"transform": "translateY(-2px)"
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
# Agregar todas las páginas a la aplicación
|
|
269
|
+
app.add_page(name="index", root=home_page, index=True)
|
|
270
|
+
app.add_page(name="about", root=about_page)
|
|
271
|
+
app.add_page(name="contact", root=contact_page)
|
|
272
|
+
|
|
273
|
+
if __name__ == "__main__":
|
|
274
|
+
app.rTimeCompile() # Preview en vivo
|
|
275
|
+
|