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.
Files changed (118) hide show
  1. dars/__init__.py +0 -0
  2. dars/all.py +69 -0
  3. dars/cli/__init__.py +0 -0
  4. dars/cli/doctor/__init__.py +1 -0
  5. dars/cli/doctor/detect.py +154 -0
  6. dars/cli/doctor/doctor.py +176 -0
  7. dars/cli/doctor/installers.py +100 -0
  8. dars/cli/doctor/persist.py +62 -0
  9. dars/cli/doctor/preflight.py +33 -0
  10. dars/cli/doctor/ui.py +54 -0
  11. dars/cli/hot_reload.py +33 -0
  12. dars/cli/main.py +1107 -0
  13. dars/cli/preview.py +448 -0
  14. dars/cli/translations.py +531 -0
  15. dars/components/__init__.py +0 -0
  16. dars/components/advanced/__init__.py +8 -0
  17. dars/components/advanced/accordion.py +26 -0
  18. dars/components/advanced/card.py +33 -0
  19. dars/components/advanced/modal.py +45 -0
  20. dars/components/advanced/navbar.py +44 -0
  21. dars/components/advanced/table.py +25 -0
  22. dars/components/advanced/tabs.py +31 -0
  23. dars/components/basic/__init__.py +34 -0
  24. dars/components/basic/button.py +55 -0
  25. dars/components/basic/checkbox.py +35 -0
  26. dars/components/basic/container.py +29 -0
  27. dars/components/basic/datepicker.py +139 -0
  28. dars/components/basic/image.py +36 -0
  29. dars/components/basic/input.py +57 -0
  30. dars/components/basic/link.py +31 -0
  31. dars/components/basic/markdown.py +86 -0
  32. dars/components/basic/page.py +20 -0
  33. dars/components/basic/progressbar.py +18 -0
  34. dars/components/basic/radiobutton.py +35 -0
  35. dars/components/basic/select.py +82 -0
  36. dars/components/basic/slider.py +63 -0
  37. dars/components/basic/spinner.py +12 -0
  38. dars/components/basic/text.py +23 -0
  39. dars/components/basic/textarea.py +46 -0
  40. dars/components/basic/tooltip.py +19 -0
  41. dars/components/layout/__init__.py +0 -0
  42. dars/components/layout/anchor.py +13 -0
  43. dars/components/layout/flex.py +26 -0
  44. dars/components/layout/grid.py +45 -0
  45. dars/config.py +134 -0
  46. dars/core/__init__.py +0 -0
  47. dars/core/app.py +957 -0
  48. dars/core/component.py +284 -0
  49. dars/core/events.py +102 -0
  50. dars/core/js_bridge.py +99 -0
  51. dars/core/properties.py +127 -0
  52. dars/core/state.py +309 -0
  53. dars/dars_tests/apps_test/health_check.py +56 -0
  54. dars/dars_tests/run_tests.py +275 -0
  55. dars/dars_tests/tests/test_advanced_components.py +69 -0
  56. dars/dars_tests/tests/test_basic_components.py +88 -0
  57. dars/dars_tests/tests/test_core_and_cli.py +17 -0
  58. dars/dars_tests/tests/test_layout_components.py +58 -0
  59. dars/dars_tests/tests/test_version_check.py +21 -0
  60. dars/docs/__init__.py +0 -0
  61. dars/docs/app.md +290 -0
  62. dars/docs/cli.md +80 -0
  63. dars/docs/components.md +1679 -0
  64. dars/docs/custom_components.md +30 -0
  65. dars/docs/events.md +45 -0
  66. dars/docs/exporters.md +162 -0
  67. dars/docs/getting_started.md +79 -0
  68. dars/docs/index.md +18 -0
  69. dars/docs/scripts.md +593 -0
  70. dars/docs/state_management.md +57 -0
  71. dars/exporters/__init__.py +0 -0
  72. dars/exporters/base.py +96 -0
  73. dars/exporters/web/OLD/html_css_js_OLD4.py +1538 -0
  74. dars/exporters/web/OLD/html_css_js_old.py +1406 -0
  75. dars/exporters/web/OLD/html_css_js_old2.py +1406 -0
  76. dars/exporters/web/__init__.py +0 -0
  77. dars/exporters/web/html_css_js.py +2675 -0
  78. dars/exporters/web/vdom.py +251 -0
  79. dars/js_lib.py +206 -0
  80. dars/scripts/__init__.py +0 -0
  81. dars/scripts/dscript.py +26 -0
  82. dars/scripts/script.py +39 -0
  83. dars/security.py +195 -0
  84. dars/templates/__init__.py +0 -0
  85. dars/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  86. dars/templates/examples/README.md +4 -0
  87. dars/templates/examples/__pycache__/dynamic_event_demo.cpython-311.pyc +0 -0
  88. dars/templates/examples/advanced/Modal_Demo/advanced_modal_demo.py +275 -0
  89. dars/templates/examples/advanced/SimpleDashboard/dashboard.py +437 -0
  90. dars/templates/examples/advanced/SimpleModermWeb/modern_web_app.py +452 -0
  91. dars/templates/examples/advanced/VariousComponents/all_components_demo.py +87 -0
  92. dars/templates/examples/advanced/__init__.py +0 -0
  93. dars/templates/examples/advanced/dState/state_mods_demo.py +68 -0
  94. dars/templates/examples/basic/Forms/form_components.py +516 -0
  95. dars/templates/examples/basic/Forms/simple_form.py +379 -0
  96. dars/templates/examples/basic/HelloWorld/hello_world.py +56 -0
  97. dars/templates/examples/basic/Layouts/flex_layout_responsive.py +13 -0
  98. dars/templates/examples/basic/Layouts/grid_layout_responsive.py +12 -0
  99. dars/templates/examples/basic/Layouts/layout_multipage_demo.py +23 -0
  100. dars/templates/examples/basic/Multipage/multipage_example.py +67 -0
  101. dars/templates/examples/basic/PWA/icon-192x192.png +0 -0
  102. dars/templates/examples/basic/PWA/icon-512x512.png +0 -0
  103. dars/templates/examples/basic/PWA/pwa_custom_icons.py +33 -0
  104. dars/templates/examples/basic/__init__.py +0 -0
  105. dars/templates/examples/demo/__pycache__/complete_app.cpython-311.pyc +0 -0
  106. dars/templates/examples/demo/complete_app.py +21 -0
  107. dars/templates/examples/markdown/MarkdownTemplate/README.md +159 -0
  108. dars/templates/examples/markdown/MarkdownTemplate/markdown_template.py +21 -0
  109. dars/templates/examples/markdown/MarkdownTemplate/other_docs.md +1 -0
  110. dars/templates/examples/markdown/__init__.py +0 -0
  111. dars/templates/html/__init__.py +0 -0
  112. dars/version.py +2 -0
  113. dars_framework-1.2.3.dist-info/METADATA +15 -0
  114. dars_framework-1.2.3.dist-info/RECORD +118 -0
  115. dars_framework-1.2.3.dist-info/WHEEL +5 -0
  116. dars_framework-1.2.3.dist-info/entry_points.txt +2 -0
  117. dars_framework-1.2.3.dist-info/licenses/LICENSE +21 -0
  118. dars_framework-1.2.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,44 @@
1
+ from dars.core.component import Component
2
+ from typing import Optional, Dict, Any, List
3
+
4
+ class Navbar(Component):
5
+ """Component to create navigation bars."""
6
+ def __init__(
7
+ self,
8
+ *children,
9
+ brand: Optional[str] = None,
10
+ class_name: Optional[str] = None,
11
+ style: Optional[Dict[str, Any]] = None,
12
+ **kwargs
13
+ ):
14
+ # Compatibilidad retro: si 'children' está en kwargs, lo usamos; si no, usamos los posicionales
15
+ children_kwarg = kwargs.pop('children', None)
16
+ from dars.core.component import Component
17
+ if children_kwarg is not None:
18
+ children_final = children_kwarg
19
+ elif len(children) == 1 and isinstance(children[0], list):
20
+ children_final = children[0]
21
+ else:
22
+ children_final = list(children)
23
+ # Filtro: solo instancias válidas de Component
24
+ children_final = [c for c in children_final if isinstance(c, Component)]
25
+ super().__init__(class_name=class_name, style=style, **kwargs)
26
+ self.brand = brand
27
+ for child in children_final:
28
+ self.add_child(child)
29
+
30
+ def render(self) -> str:
31
+ brand_html = f'<div class="dars-navbar-brand">{self.brand}</div>' if self.brand else ''
32
+ children_html = ''.join([child.render() for child in self.children])
33
+
34
+ attrs = []
35
+ if self.class_name: attrs.append(f'class="dars-navbar {self.class_name}"')
36
+ else: attrs.append('class="dars-navbar"')
37
+
38
+ navbar_style = 'display: flex; justify-content: space-between; align-items: center; padding: 1rem; background-color: #f8f9fa; border-bottom: 1px solid #dee2e6'
39
+ if self.style:
40
+ navbar_style += f'; {self.render_styles(self.style)}'
41
+ attrs.append(f'style="{navbar_style}"')
42
+
43
+ return f'<nav {" ".join(attrs)}>{brand_html}<div class="dars-navbar-nav">{children_html}</div></nav>'
44
+
@@ -0,0 +1,25 @@
1
+ from dars.core.component import Component
2
+ from typing import List, Dict, Any, Optional
3
+
4
+ class Table(Component):
5
+ """
6
+ Component to display tabular data with columns, data, pagination, sorting, and filtering.
7
+ columns: List of dictionaries with keys 'title', 'field', 'sortable', 'width', etc.
8
+ data: List of dictionaries (each one is a row)
9
+ page_size: Number of rows per page (optional)
10
+ """
11
+
12
+ def __init__(self, columns: List[Dict[str, Any]], data: List[Dict[str, Any]], page_size: Optional[int]=None, **props):
13
+ super().__init__(**props)
14
+ self.columns = columns
15
+ self.data = data
16
+ self.page_size = page_size
17
+
18
+ def render(self) -> str:
19
+ # Renderiza la tabla en HTML (solo vista simple, sin JS avanzado todavía)
20
+ thead = '<thead><tr>' + ''.join(f'<th>{col["title"]}</th>' for col in self.columns) + '</tr></thead>'
21
+ rows = self.data[:self.page_size] if self.page_size else self.data
22
+ tbody = '<tbody>' + ''.join(
23
+ '<tr>' + ''.join(f'<td>{row.get(col["field"], "")}</td>' for col in self.columns) + '</tr>'
24
+ for row in rows) + '</tbody>'
25
+ return f'<table class="dars-table">{thead}{tbody}</table>'
@@ -0,0 +1,31 @@
1
+ from dars.core.component import Component
2
+ from typing import List, Optional
3
+
4
+ class Tabs(Component):
5
+ """
6
+ Tab navigation component.
7
+ tabs: List of tab titles
8
+ panels: List of components or strings (content of each tab)
9
+ selected: Index of the active tab (optional)
10
+ """
11
+
12
+ def __init__(self, tabs: List[str], panels: List[Component], selected: Optional[int]=0, minimum_logic: bool = True, **props):
13
+ super().__init__(**props)
14
+ self.tabs = tabs
15
+ self.panels = panels
16
+ self.selected = selected or 0
17
+ self.minimum_logic = minimum_logic
18
+ for panel in panels:
19
+ if hasattr(panel, 'render'):
20
+ self.add_child(panel)
21
+
22
+ def render(self) -> str:
23
+ tab_headers = ''.join(
24
+ f'<button class="dars-tab{ " dars-tab-active" if i == self.selected else "" }" data-tab="{i}">{title}</button>'
25
+ for i, title in enumerate(self.tabs)
26
+ )
27
+ panels_html = ''.join(
28
+ f'<div class="dars-tab-panel{ " dars-tab-panel-active" if i == self.selected else "" }">{panel.render() if hasattr(panel, "render") else panel}</div>'
29
+ for i, panel in enumerate(self.panels)
30
+ )
31
+ return f'<div class="dars-tabs"><div class="dars-tabs-header">{tab_headers}</div><div class="dars-tabs-panels">{panels_html}</div></div>'
@@ -0,0 +1,34 @@
1
+ from .text import Text
2
+ from .button import Button
3
+ from .input import Input
4
+ from .container import Container
5
+ from .page import Page
6
+ from .image import Image
7
+ from .link import Link
8
+ from .textarea import Textarea
9
+ from .checkbox import Checkbox
10
+ from .radiobutton import RadioButton
11
+ from .select import Select, SelectOption
12
+ from .slider import Slider
13
+ from .datepicker import DatePicker
14
+
15
+ from .progressbar import ProgressBar
16
+ from .spinner import Spinner
17
+ from .tooltip import Tooltip
18
+
19
+ __all__ = [
20
+ 'Text',
21
+ 'Button',
22
+ 'Input',
23
+ 'Container',
24
+ 'Page',
25
+ 'Image',
26
+ 'Link',
27
+ 'Textarea',
28
+ 'Checkbox',
29
+ 'RadioButton',
30
+ 'Select',
31
+ 'SelectOption',
32
+ 'Slider',
33
+ 'DatePicker'
34
+ ]
@@ -0,0 +1,55 @@
1
+ from dars.core.component import Component
2
+ from dars.core.properties import StyleProps
3
+ from dars.core.events import EventTypes
4
+ from dars.scripts.script import Script
5
+ from typing import Optional, Union, Dict, Any, Callable
6
+
7
+ class Button(Component):
8
+ def __init__(
9
+ self,
10
+ text: str = "Button",
11
+ id: Optional[str] = None,
12
+ class_name: Optional[str] = None,
13
+ style: Optional[Dict[str, Any]] = None,
14
+ disabled: bool = False,
15
+ button_type: str = "button", # "button", "submit", "reset"
16
+ on_click: Optional[Callable] = None,
17
+ on_double_click: Optional[Callable] = None,
18
+ on_mouse_enter: Optional[Callable] = None,
19
+ on_mouse_leave: Optional[Callable] = None,
20
+ on_mouse_down: Optional[Callable] = None,
21
+ on_mouse_up: Optional[Callable] = None,
22
+ on_key_down: Optional[Callable] = None,
23
+ on_key_up: Optional[Callable] = None
24
+ ):
25
+ super().__init__(id=id, class_name=class_name, style=style)
26
+ self.text = text
27
+ self.disabled = disabled
28
+ self.button_type = button_type
29
+
30
+ # Soporte para presets JS editables con dScript u otros Script
31
+ if on_click:
32
+ # Convertir a Script si es necesario
33
+ if not isinstance(on_click, Script) and callable(on_click):
34
+ from dars.scripts.dscript import dScript
35
+ on_click = dScript(on_click.__code__)
36
+ self.set_event(EventTypes.CLICK, on_click)
37
+ if on_double_click:
38
+ self.set_event(EventTypes.DOUBLE_CLICK, on_double_click)
39
+ if on_mouse_enter:
40
+ self.set_event(EventTypes.MOUSE_ENTER, on_mouse_enter)
41
+ if on_mouse_leave:
42
+ self.set_event(EventTypes.MOUSE_LEAVE, on_mouse_leave)
43
+ if on_mouse_down:
44
+ self.set_event(EventTypes.MOUSE_DOWN, on_mouse_down)
45
+ if on_mouse_up:
46
+ self.set_event(EventTypes.MOUSE_UP, on_mouse_up)
47
+ if on_key_down:
48
+ self.set_event(EventTypes.KEY_DOWN, on_key_down)
49
+ if on_key_up:
50
+ self.set_event(EventTypes.KEY_UP, on_key_up)
51
+
52
+ def render(self, exporter: Any) -> str:
53
+ # El método render será implementado por cada exportador
54
+ raise NotImplementedError("El método render debe ser implementado por el exportador")
55
+
@@ -0,0 +1,35 @@
1
+ from dars.core.component import Component
2
+ from dars.core.properties import StyleProps
3
+ from dars.core.events import EventTypes
4
+ from typing import Optional, Union, Dict, Any, Callable
5
+
6
+ class Checkbox(Component):
7
+ def __init__(
8
+ self,
9
+ label: str = "",
10
+ checked: bool = False,
11
+ value: str = "",
12
+ name: Optional[str] = None,
13
+ id: Optional[str] = None,
14
+ class_name: Optional[str] = None,
15
+ style: Optional[Dict[str, Any]] = None,
16
+ disabled: bool = False,
17
+ required: bool = False,
18
+ on_change: Optional[Callable] = None,
19
+ **props
20
+ ):
21
+ super().__init__(id=id, class_name=class_name, style=style, **props)
22
+ self.label = label
23
+ self.checked = checked
24
+ self.value = value or label # Si no se proporciona value, usar label
25
+ self.name = name
26
+ self.disabled = disabled
27
+ self.required = required
28
+
29
+ # Registrar evento de cambio si se proporciona
30
+ if on_change:
31
+ self.set_event(EventTypes.CHANGE, on_change)
32
+
33
+ def render(self, exporter: Any) -> str:
34
+ # El método render será implementado por cada exportador
35
+ raise NotImplementedError("El método render debe ser implementado por el exportador")
@@ -0,0 +1,29 @@
1
+ from dars.core.component import Component
2
+ from dars.core.properties import StyleProps
3
+ from typing import Optional, Union, Dict, Any, List
4
+
5
+ class Container(Component):
6
+ def __init__(
7
+ self,
8
+ *children: Component,
9
+ id: Optional[str] = None,
10
+ class_name: Optional[str] = None,
11
+ style: Optional[Dict[str, Any]] = None,
12
+ additional_children: Optional[List[Component]] = None,
13
+ **props
14
+ ):
15
+ super().__init__(id=id, class_name=class_name, style=style, **props)
16
+
17
+ # Agregar hijos pasados como argumentos posicionales
18
+ for child in children:
19
+ self.add_child(child)
20
+
21
+ # Agregar hijos adicionales si se proporcionan
22
+ if additional_children:
23
+ for child in additional_children:
24
+ self.add_child(child)
25
+
26
+ def render(self, exporter: Any) -> str:
27
+ # El método render será implementado por cada exportador
28
+ raise NotImplementedError("El método render debe ser implementado por el exportador")
29
+
@@ -0,0 +1,139 @@
1
+ from dars.core.component import Component
2
+ from dars.core.properties import StyleProps
3
+ from dars.core.events import EventTypes
4
+ from typing import Optional, Union, Dict, Any, Callable
5
+ from datetime import datetime, date
6
+
7
+ class DatePicker(Component):
8
+ def __init__(
9
+ self,
10
+ value: Optional[Union[str, date, datetime]] = None,
11
+ min_date: Optional[Union[str, date, datetime]] = None,
12
+ max_date: Optional[Union[str, date, datetime]] = None,
13
+ placeholder: str = "Seleccionar fecha",
14
+ format: str = "YYYY-MM-DD", # Formato de fecha
15
+ locale: str = "es", # Idioma para el picker
16
+ show_time: bool = False, # Si incluir selector de tiempo
17
+ inline: bool = False, # Si mostrar inline o como popup
18
+ disabled_dates: Optional[list] = None, # Fechas deshabilitadas
19
+ id: Optional[str] = None,
20
+ class_name: Optional[str] = None,
21
+ style: Optional[Dict[str, Any]] = None,
22
+ disabled: bool = False,
23
+ required: bool = False,
24
+ readonly: bool = False,
25
+ on_change: Optional[Callable] = None,
26
+ on_open: Optional[Callable] = None,
27
+ on_close: Optional[Callable] = None
28
+ ):
29
+ super().__init__(id=id, class_name=class_name, style=style)
30
+
31
+ # Asignar atributos básicos primero
32
+ self.placeholder = placeholder
33
+ self.format = format
34
+ self.locale = locale
35
+ self.show_time = show_time
36
+ self.inline = inline
37
+ self.disabled_dates = disabled_dates or []
38
+ self.disabled = disabled
39
+ self.required = required
40
+ self.readonly = readonly
41
+
42
+ # Validar formato
43
+ valid_formats = ["YYYY-MM-DD", "DD/MM/YYYY", "MM/DD/YYYY", "DD-MM-YYYY"]
44
+ if format not in valid_formats:
45
+ raise ValueError(f"format debe ser uno de: {valid_formats}")
46
+
47
+ # Validar locale
48
+ valid_locales = ["es", "en", "fr", "de", "it", "pt"]
49
+ if locale not in valid_locales:
50
+ raise ValueError(f"locale debe ser uno de: {valid_locales}")
51
+
52
+ # Ahora procesar las fechas (después de asignar self.format)
53
+ self.value = self._process_date(value)
54
+ self.min_date = self._process_date(min_date)
55
+ self.max_date = self._process_date(max_date)
56
+
57
+ # Registrar eventos si se proporcionan
58
+ if on_change:
59
+ self.set_event(EventTypes.CHANGE, on_change)
60
+ if on_open:
61
+ self.set_event("open", on_open)
62
+ if on_close:
63
+ self.set_event("close", on_close)
64
+
65
+ def _process_date(self, date_value: Optional[Union[str, date, datetime]]) -> Optional[str]:
66
+ """Procesa y normaliza el valor de fecha"""
67
+ if date_value is None:
68
+ return None
69
+
70
+ if isinstance(date_value, str):
71
+ # Validar formato de string de fecha
72
+ try:
73
+ # Intentar parsear la fecha para validarla
74
+ if self.format == "YYYY-MM-DD":
75
+ datetime.strptime(date_value, "%Y-%m-%d")
76
+ elif self.format == "DD/MM/YYYY":
77
+ datetime.strptime(date_value, "%d/%m/%Y")
78
+ elif self.format == "MM/DD/YYYY":
79
+ datetime.strptime(date_value, "%m/%d/%Y")
80
+ elif self.format == "DD-MM-YYYY":
81
+ datetime.strptime(date_value, "%d-%m-%Y")
82
+ return date_value
83
+ except ValueError:
84
+ raise ValueError(f"Formato de fecha inválido: {date_value}")
85
+
86
+ elif isinstance(date_value, (date, datetime)):
87
+ # Convertir objeto date/datetime a string según el formato
88
+ if self.format == "YYYY-MM-DD":
89
+ return date_value.strftime("%Y-%m-%d")
90
+ elif self.format == "DD/MM/YYYY":
91
+ return date_value.strftime("%d/%m/%Y")
92
+ elif self.format == "MM/DD/YYYY":
93
+ return date_value.strftime("%m/%d/%Y")
94
+ elif self.format == "DD-MM-YYYY":
95
+ return date_value.strftime("%d-%m-%Y")
96
+
97
+ return str(date_value)
98
+
99
+ def set_value(self, value: Union[str, date, datetime]):
100
+ """Establece el valor de la fecha"""
101
+ self.value = self._process_date(value)
102
+
103
+ def get_date_object(self) -> Optional[datetime]:
104
+ """Obtiene el valor como objeto datetime"""
105
+ if not self.value:
106
+ return None
107
+
108
+ try:
109
+ if self.format == "YYYY-MM-DD":
110
+ return datetime.strptime(self.value, "%Y-%m-%d")
111
+ elif self.format == "DD/MM/YYYY":
112
+ return datetime.strptime(self.value, "%d/%m/%Y")
113
+ elif self.format == "MM/DD/YYYY":
114
+ return datetime.strptime(self.value, "%m/%d/%Y")
115
+ elif self.format == "DD-MM-YYYY":
116
+ return datetime.strptime(self.value, "%d-%m-%Y")
117
+ except ValueError:
118
+ return None
119
+
120
+ def is_date_disabled(self, date_to_check: Union[str, date, datetime]) -> bool:
121
+ """Verifica si una fecha está deshabilitada"""
122
+ check_date = self._process_date(date_to_check)
123
+ return check_date in self.disabled_dates
124
+
125
+ def add_disabled_date(self, date_to_disable: Union[str, date, datetime]):
126
+ """Añade una fecha a la lista de fechas deshabilitadas"""
127
+ disabled_date = self._process_date(date_to_disable)
128
+ if disabled_date and disabled_date not in self.disabled_dates:
129
+ self.disabled_dates.append(disabled_date)
130
+
131
+ def remove_disabled_date(self, date_to_enable: Union[str, date, datetime]):
132
+ """Elimina una fecha de la lista de fechas deshabilitadas"""
133
+ enabled_date = self._process_date(date_to_enable)
134
+ if enabled_date in self.disabled_dates:
135
+ self.disabled_dates.remove(enabled_date)
136
+
137
+ def render(self, exporter: Any) -> str:
138
+ # El método render será implementado por cada exportador
139
+ raise NotImplementedError("El método render debe ser implementado por el exportador")
@@ -0,0 +1,36 @@
1
+ from dars.core.component import Component
2
+ from typing import Optional, Dict, Any
3
+
4
+ class Image(Component):
5
+ """Component to display images."""
6
+ def __init__(
7
+ self,
8
+ src: str,
9
+ alt: str = "",
10
+ width: Optional[str] = None,
11
+ height: Optional[str] = None,
12
+ class_name: Optional[str] = None,
13
+ style: Optional[Dict[str, Any]] = None,
14
+ **kwargs
15
+ ):
16
+ super().__init__(class_name=class_name, style=style, **kwargs)
17
+ self.src = src
18
+ self.alt = alt
19
+ self.width = width
20
+ self.height = height
21
+
22
+ def render(self) -> str:
23
+ attrs = [
24
+ f'src="{self.src}"',
25
+ f'alt="{self.alt}"',
26
+ ]
27
+ if self.width: attrs.append(f'width="{self.width}"')
28
+ if self.height: attrs.append(f'height="{self.height}"')
29
+ if self.class_name: attrs.append(f'class="{self.class_name}"')
30
+ if self.style: attrs.append(f'style="{self.render_styles(self.style)}"')
31
+
32
+ return f'<img {" ".join(attrs)} />'
33
+
34
+
35
+
36
+
@@ -0,0 +1,57 @@
1
+ from dars.core.component import Component
2
+ from dars.core.properties import StyleProps
3
+ from dars.core.events import EventTypes
4
+ from typing import Optional, Union, Dict, Any, Callable
5
+
6
+ class Input(Component):
7
+ def __init__(
8
+ self,
9
+ value: str = "",
10
+ placeholder: str = "",
11
+ input_type: str = "text", # "text", "password", "email", "number", etc.
12
+ id: Optional[str] = None,
13
+ class_name: Optional[str] = None,
14
+ style: Optional[Dict[str, Any]] = None,
15
+ disabled: bool = False,
16
+ readonly: bool = False,
17
+ required: bool = False,
18
+ max_length: Optional[int] = None,
19
+ min_length: Optional[int] = None,
20
+ pattern: Optional[str] = None,
21
+ on_change: Optional[Callable] = None,
22
+ on_input: Optional[Callable] = None,
23
+ on_focus: Optional[Callable] = None,
24
+ on_blur: Optional[Callable] = None,
25
+ on_key_down: Optional[Callable] = None,
26
+ on_key_up: Optional[Callable] = None,
27
+ **props
28
+ ):
29
+ super().__init__(id=id, class_name=class_name, style=style, **props)
30
+ self.value = value
31
+ self.placeholder = placeholder
32
+ self.input_type = input_type
33
+ self.disabled = disabled
34
+ self.readonly = readonly
35
+ self.required = required
36
+ self.max_length = max_length
37
+ self.min_length = min_length
38
+ self.pattern = pattern
39
+
40
+ # Soporte para presets JS editables con dScript u otros Script
41
+ if on_change:
42
+ self.set_event(EventTypes.CHANGE, on_change)
43
+ if on_input:
44
+ self.set_event(EventTypes.INPUT, on_input)
45
+ if on_focus:
46
+ self.set_event(EventTypes.FOCUS, on_focus)
47
+ if on_blur:
48
+ self.set_event(EventTypes.BLUR, on_blur)
49
+ if on_key_down:
50
+ self.set_event(EventTypes.KEY_DOWN, on_key_down)
51
+ if on_key_up:
52
+ self.set_event(EventTypes.KEY_UP, on_key_up)
53
+
54
+ def render(self, exporter: Any) -> str:
55
+ # El método render será implementado por cada exportador
56
+ raise NotImplementedError("El método render debe ser implementado por el exportador")
57
+
@@ -0,0 +1,31 @@
1
+ from dars.core.component import Component
2
+ from typing import Optional, Dict, Any
3
+
4
+ class Link(Component):
5
+ """Component to create links."""
6
+ def __init__(
7
+ self,
8
+ text: str,
9
+ href: str,
10
+ target: str = "_self",
11
+ class_name: Optional[str] = None,
12
+ style: Optional[Dict[str, Any]] = None,
13
+ **kwargs
14
+ ):
15
+ super().__init__(class_name=class_name, style=style, **kwargs)
16
+ self.text = text
17
+ self.href = href
18
+ self.target = target
19
+
20
+ def render(self) -> str:
21
+ attrs = [
22
+ f'href="{self.href}"',
23
+ f'target="{self.target}"',
24
+ ]
25
+ if self.class_name: attrs.append(f'class="{self.class_name}"')
26
+ if self.style: attrs.append(f'style="{self.render_styles(self.style)}"')
27
+
28
+ return f'<a {" ".join(attrs)}>{self.text}</a>'
29
+
30
+
31
+
@@ -0,0 +1,86 @@
1
+ from typing import Optional, Dict, Any, Callable, Union
2
+ import os
3
+ from dars.core.component import Component
4
+
5
+ class Markdown(Component):
6
+ def __init__(
7
+ self,
8
+ content: Optional[str] = None,
9
+ file_path: Optional[str] = None,
10
+ id: Optional[str] = None,
11
+ class_name: Optional[str] = None,
12
+ style: Optional[Dict[str, Any]] = None,
13
+ dark_theme: bool = False,
14
+ **kwargs
15
+ ):
16
+ """
17
+ Markdown component that converts markdown content to HTML.
18
+
19
+ Args:
20
+ content: String with markdown content
21
+ file_path: Path to a .md file to load content from
22
+ id: Component ID
23
+ class_name: CSS class name
24
+ style: CSS styles
25
+ dark_theme: Enable dark theme styling
26
+ """
27
+ super().__init__(id=id, class_name=class_name, style=style, **kwargs)
28
+
29
+ if content and file_path:
30
+ raise ValueError("Only content or file_path can be specified, not both")
31
+
32
+ if not content and not file_path:
33
+ raise ValueError("Either content or file_path must be specified")
34
+
35
+ self.content = content
36
+ self.file_path = file_path
37
+ self.dark_theme = dark_theme
38
+ self.rendered_html = ""
39
+
40
+ # Load and process markdown content
41
+ self._load_and_process_content()
42
+
43
+ def _load_and_process_content(self):
44
+ """Load and process markdown content."""
45
+ if self.file_path:
46
+ if not os.path.exists(self.file_path):
47
+ raise FileNotFoundError(f"File {self.file_path} does not exist")
48
+
49
+ if not self.file_path.endswith('.md'):
50
+ raise ValueError("File must have .md extension")
51
+
52
+ with open(self.file_path, 'r', encoding='utf-8') as f:
53
+ self.content = f.read()
54
+
55
+ def update_content(self, new_content: Optional[str] = None, new_file_path: Optional[str] = None):
56
+ """
57
+ Update the markdown content of the component.
58
+
59
+ Args:
60
+ new_content: New markdown content as string
61
+ new_file_path: New markdown file path
62
+ """
63
+ if new_content and new_file_path:
64
+ raise ValueError("Only new_content or new_file_path can be specified, not both")
65
+
66
+ if new_content:
67
+ self.content = new_content
68
+ self.file_path = None
69
+ elif new_file_path:
70
+ self.file_path = new_file_path
71
+ self.content = None
72
+
73
+ self._load_and_process_content()
74
+
75
+ def set_dark_theme(self, enabled: bool = True):
76
+ """Enable or disable dark theme"""
77
+ self.dark_theme = enabled
78
+ # Add dark theme class dynamically
79
+ if enabled:
80
+ self.class_name = f"{self.class_name or ''} dars-markdown-dark"
81
+ else:
82
+ self.class_name = self.class_name.replace("dars-markdown-dark", "") if self.class_name else ""
83
+
84
+ def render(self, exporter: Any) -> str:
85
+ # El método render será implementado por cada exportador
86
+ raise NotImplementedError("El método render debe ser implementado por el exportador")
@@ -0,0 +1,20 @@
1
+ from dars.core.component import Component
2
+ from typing import Optional, Dict, Any, List
3
+
4
+ class Page(Component):
5
+ """Root component for pages in Dars multipage apps. Allows passing children as positional arguments and scripts per page."""
6
+ def __init__(self, *children: Component, id: Optional[str] = None, class_name: Optional[str] = None, style: Optional[Dict[str, Any]] = None, **props):
7
+ super().__init__(id=id, class_name=class_name, style=style, **props)
8
+ self.scripts = []
9
+ for child in children:
10
+ self.add_child(child)
11
+
12
+ def add_script(self, script):
13
+ self.scripts.append(script)
14
+
15
+ def get_scripts(self):
16
+ return self.scripts
17
+
18
+ def render(self, exporter: Any) -> str:
19
+ # El método render será implementado por el exporter
20
+ raise NotImplementedError("El método render debe ser implementado por el exporter")
@@ -0,0 +1,18 @@
1
+ from dars.core.component import Component
2
+ from typing import Optional
3
+
4
+ class ProgressBar(Component):
5
+ """
6
+ Visual progress bar.
7
+ value: current value (0-100)
8
+ max_value: maximum value (default 100)
9
+ """
10
+
11
+ def __init__(self, value: int, max_value: int = 100, **props):
12
+ super().__init__(**props)
13
+ self.value = value
14
+ self.max_value = max_value
15
+
16
+ def render(self) -> str:
17
+ percent = min(max(self.value / self.max_value * 100, 0), 100)
18
+ return f'<div class="dars-progressbar"><div class="dars-progressbar-bar" style="width: {percent}%;"></div></div>'