st-components 0.1.0__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 (85) hide show
  1. examples/README.md +113 -0
  2. examples/__init__.py +1 -0
  3. examples/_source.py +9 -0
  4. examples/basic.py +30 -0
  5. examples/dashboard.py +288 -0
  6. examples/data_dashboard.py +428 -0
  7. examples/flow.py +131 -0
  8. examples/functional.py +121 -0
  9. examples/functional_typed.py +146 -0
  10. examples/multipage/README.md +31 -0
  11. examples/multipage/app.py +105 -0
  12. examples/multipage/pages/report_page.py +82 -0
  13. examples/multipage/shared.py +60 -0
  14. examples/primitives.py +1228 -0
  15. examples/theme_editor.py +77 -0
  16. examples/typed_props.py +154 -0
  17. examples/typed_state.py +140 -0
  18. st_components/__init__.py +33 -0
  19. st_components/builtins/__init__.py +6 -0
  20. st_components/builtins/flow.py +122 -0
  21. st_components/builtins/theme.py +486 -0
  22. st_components/core/__init__.py +83 -0
  23. st_components/core/access.py +103 -0
  24. st_components/core/app.py +381 -0
  25. st_components/core/base.py +275 -0
  26. st_components/core/context.py +162 -0
  27. st_components/core/function_component.py +74 -0
  28. st_components/core/hooks.py +16 -0
  29. st_components/core/models.py +192 -0
  30. st_components/core/page.py +101 -0
  31. st_components/core/refs.py +46 -0
  32. st_components/core/router.py +55 -0
  33. st_components/core/store.py +167 -0
  34. st_components/elements/__init__.py +27 -0
  35. st_components/elements/_types.py +33 -0
  36. st_components/elements/_utils.py +43 -0
  37. st_components/elements/charts/__init__.py +35 -0
  38. st_components/elements/charts/altair_chart.py +25 -0
  39. st_components/elements/charts/area_chart.py +15 -0
  40. st_components/elements/charts/bar_chart.py +15 -0
  41. st_components/elements/charts/bokeh_chart.py +14 -0
  42. st_components/elements/charts/graphviz_chart.py +15 -0
  43. st_components/elements/charts/line_chart.py +15 -0
  44. st_components/elements/charts/map.py +15 -0
  45. st_components/elements/charts/plotly_chart.py +27 -0
  46. st_components/elements/charts/pydeck_chart.py +25 -0
  47. st_components/elements/charts/pyplot.py +15 -0
  48. st_components/elements/charts/scatter_chart.py +15 -0
  49. st_components/elements/charts/vega_lite_chart.py +27 -0
  50. st_components/elements/display/__init__.py +26 -0
  51. st_components/elements/display/basic.py +75 -0
  52. st_components/elements/display/data.py +86 -0
  53. st_components/elements/display/navigation.py +37 -0
  54. st_components/elements/feedback/__init__.py +17 -0
  55. st_components/elements/feedback/messages.py +50 -0
  56. st_components/elements/feedback/progress.py +41 -0
  57. st_components/elements/input/__init__.py +61 -0
  58. st_components/elements/input/_common.py +109 -0
  59. st_components/elements/input/boolean.py +51 -0
  60. st_components/elements/input/buttons.py +133 -0
  61. st_components/elements/input/data_editor.py +52 -0
  62. st_components/elements/input/datetime_widgets.py +106 -0
  63. st_components/elements/input/files.py +99 -0
  64. st_components/elements/input/select.py +229 -0
  65. st_components/elements/input/textual.py +89 -0
  66. st_components/elements/layout/__init__.py +27 -0
  67. st_components/elements/layout/containers.py +143 -0
  68. st_components/elements/layout/misc.py +27 -0
  69. st_components/elements/layout/overlays.py +80 -0
  70. st_components/elements/media/__init__.py +11 -0
  71. st_components/elements/media/core.py +74 -0
  72. st_components/elements/text/__init__.py +25 -0
  73. st_components/elements/text/_common.py +2 -0
  74. st_components/elements/text/body.py +47 -0
  75. st_components/elements/text/headings.py +31 -0
  76. st_components/elements/text/misc.py +31 -0
  77. st_components/examples/__init__.py +9 -0
  78. st_components/examples/__main__.py +28 -0
  79. st_components/examples/runner.py +48 -0
  80. st_components/py.typed +1 -0
  81. st_components-0.1.0.dist-info/METADATA +664 -0
  82. st_components-0.1.0.dist-info/RECORD +85 -0
  83. st_components-0.1.0.dist-info/WHEEL +5 -0
  84. st_components-0.1.0.dist-info/licenses/LICENSE +21 -0
  85. st_components-0.1.0.dist-info/top_level.txt +2 -0
examples/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # Examples
2
+
3
+ Each example is a self-contained Streamlit app.
4
+
5
+ Installed package:
6
+
7
+ ```bash
8
+ python -m st_components.examples <name>
9
+ python -m st_components.examples --list
10
+ ```
11
+
12
+ From the repository root, you can also run the files directly with `streamlit run`.
13
+
14
+ ## basic.py
15
+
16
+ The minimal entry point. A single stateful `Counter` component — nothing else.
17
+
18
+ ```bash
19
+ python -m st_components.examples basic
20
+ streamlit run examples/basic.py
21
+ ```
22
+
23
+ ## data_dashboard.py
24
+
25
+ A data-science oriented dashboard that shows how Python computation (numpy signal analysis, histogram stats, least-squares regression) lives inside **component methods and callbacks**, keeping the render layer thin. Three panels: Signal Explorer (FFT + rolling mean), Distribution Analyzer (descriptive stats + histogram), Regression Playground (polyfit + R²).
26
+
27
+ ```bash
28
+ python -m st_components.examples data_dashboard
29
+ streamlit run examples/data_dashboard.py
30
+ ```
31
+
32
+ ## dashboard.py
33
+
34
+ A fuller tutorial that walks through the core patterns: local component state, `on_change(value)` callbacks, `Ref`, and layout composition.
35
+
36
+ ```bash
37
+ python -m st_components.examples dashboard
38
+ streamlit run examples/dashboard.py
39
+ ```
40
+
41
+ ## flow.py
42
+
43
+ Small demo of the flow built-ins: `Conditional`, `KeepAlive`, `Case`, and `Switch`/`Match`/`Default`.
44
+
45
+ ```bash
46
+ python -m st_components.examples flow
47
+ streamlit run examples/flow.py
48
+ ```
49
+
50
+ ## functional_typed.py
51
+
52
+ Typed state and props for functional components: `use_state()` with a `State` instance, `@component` with a typed `Props` annotation, and combining both.
53
+
54
+ ```bash
55
+ python -m st_components.examples functional_typed
56
+ streamlit run examples/functional_typed.py
57
+ ```
58
+
59
+ ## functional.py
60
+
61
+ Demonstrates the `@component` decorator and `use_state` hook for defining components as plain functions instead of classes.
62
+
63
+ ```bash
64
+ python -m st_components.examples functional
65
+ streamlit run examples/functional.py
66
+ ```
67
+
68
+ ## primitives.py
69
+
70
+ A kitchen-sink reference of every built-in element wrapper: inputs, layout, display, media, feedback, and charts.
71
+
72
+ ```bash
73
+ python -m st_components.examples primitives
74
+ streamlit run examples/primitives.py
75
+ ```
76
+
77
+ ## typed_state.py
78
+
79
+ Demonstrates typed state schemas using nested `State` subclasses: single-field state without `__init__`, multi-field structured state, and type validation at definition and assignment time.
80
+
81
+ ```bash
82
+ python -m st_components.examples typed_state
83
+ streamlit run examples/typed_state.py
84
+ ```
85
+
86
+ ## typed_props.py
87
+
88
+ Demonstrates typed props schemas using nested `Props` subclasses: default prop values, `extra="ignore"` for permissive pass-through components, and `extra="forbid"` for strict interface enforcement.
89
+
90
+ ```bash
91
+ python -m st_components.examples typed_props
92
+ streamlit run examples/typed_props.py
93
+ ```
94
+
95
+ ## theme_editor.py
96
+
97
+ Minimal demo of the `ThemeEditorButton` builtin. Open the dialog from a prewired button and edit the current `App` theme live.
98
+
99
+ ```bash
100
+ python -m st_components.examples theme_editor
101
+ streamlit run examples/theme_editor.py
102
+ ```
103
+
104
+ ## multipage/
105
+
106
+ A multipage app using `Router`, `Page`, and `shared_state`. Shows both inline component pages and file-backed pages synchronized through shared state.
107
+
108
+ ```bash
109
+ python -m st_components.examples multipage
110
+ streamlit run examples/multipage/app.py
111
+ ```
112
+
113
+ See [multipage/README.md](multipage/README.md) for details.
examples/__init__.py ADDED
@@ -0,0 +1 @@
1
+
examples/_source.py ADDED
@@ -0,0 +1,9 @@
1
+ from pathlib import Path
2
+
3
+ from st_components.elements import code, expander
4
+
5
+
6
+ def source_view(path, *, key="source", label="View source"):
7
+ return expander(key=key, label=label, expanded=False)(
8
+ code(key="code", language="python")(Path(path).read_text())
9
+ )
examples/basic.py ADDED
@@ -0,0 +1,30 @@
1
+ from st_components import Component, App
2
+ from st_components.elements import button, container
3
+
4
+ from examples._source import source_view
5
+
6
+
7
+ class Counter(Component):
8
+
9
+ def __init__(self, **props):
10
+ super().__init__(**props)
11
+ self.state = dict(count=0)
12
+
13
+ def increment(self):
14
+ self.state.count += 1
15
+
16
+ def render(self):
17
+ return button(key="btn", on_click=self.increment)(
18
+ f"Clicked {self.state.count} times"
19
+ )
20
+
21
+
22
+ class BasicDemo(Component):
23
+ def render(self):
24
+ return container(key="page")(
25
+ Counter(key="counter"),
26
+ source_view(__file__),
27
+ )
28
+
29
+
30
+ App(root=BasicDemo(key="app")).render()
examples/dashboard.py ADDED
@@ -0,0 +1,288 @@
1
+ import datetime
2
+ from st_components import App, Component, Ref, get_component_state, get_element_value
3
+ from st_components.elements import (
4
+ button, caption, code, columns, container, divider, expander, header, info,
5
+ json, markdown, metric, sidebar, slider, subheader, success, tabs, text,
6
+ text_area, text_input, title, toggle, warning,
7
+ )
8
+
9
+ from examples._source import source_view
10
+
11
+
12
+ class CounterCard(Component):
13
+ """Simple stateful component used across the tutorial."""
14
+
15
+ def __init__(self, **props):
16
+ super().__init__(**props)
17
+ self.state = dict(value=self.props.get("initial", 0))
18
+
19
+ def increment(self):
20
+ self.state.value += 1
21
+
22
+ def decrement(self):
23
+ self.state.value = max(0, self.state.value - 1)
24
+
25
+ def reset(self):
26
+ self.state.value = 0
27
+
28
+ def render(self):
29
+ return container(key="card", border=True)(
30
+ metric(key="metric", label=self.props.label, value=self.state.value),
31
+ columns(key="actions")(
32
+ button(key="dec", on_click=self.decrement, disabled=self.state.value == 0, width="stretch")("−"),
33
+ button(key="inc", on_click=self.increment, width="stretch")("+"),
34
+ button(key="rst", on_click=self.reset, width="stretch")("↺"),
35
+ ),
36
+ )
37
+
38
+
39
+ class IntroCard(Component):
40
+ def render(self):
41
+ today = datetime.date.today().strftime("%A, %B %d")
42
+ return container(key="intro", border=True)(
43
+ title(key="title")("st-components tutorial dashboard"),
44
+ caption(key="date")(today),
45
+ markdown(key="body")(
46
+ "`st-components` gives Streamlit a small component model:\n\n"
47
+ "- `Component` keeps local state across reruns.\n"
48
+ "- `Element` wraps Streamlit primitives.\n"
49
+ "- `on_change` handlers receive the current widget value.\n"
50
+ "- `Ref()` lets you reach a component or stateful element later without hardcoding full paths."
51
+ ),
52
+ info(key="tip")(
53
+ "Use the tabs below in order. Each section is interactive and illustrates one part of the API."
54
+ ),
55
+ )
56
+
57
+
58
+ class LocalStateDemo(Component):
59
+ def render(self):
60
+ return container(key="panel", border=True)(
61
+ subheader(key="h")("1. Local component state"),
62
+ caption(key="c")(
63
+ "Each CounterCard is recreated every rerun, but its state is restored from its fiber."
64
+ ),
65
+ columns(key="cards")(
66
+ CounterCard(key="tasks", label="Tasks done", initial=2),
67
+ CounterCard(key="reviews", label="PR reviews", initial=1),
68
+ CounterCard(key="bugs", label="Bugs fixed", initial=0),
69
+ ),
70
+ code(key="snippet", language="python")(
71
+ "class CounterCard(Component):\n"
72
+ " def __init__(self, **props):\n"
73
+ " super().__init__(**props)\n"
74
+ " self.state = dict(value=0)\n"
75
+ "\n"
76
+ " def increment(self):\n"
77
+ " self.state.value += 1"
78
+ ),
79
+ )
80
+
81
+
82
+ class ElementValueDemo(Component):
83
+ def __init__(self, **props):
84
+ super().__init__(**props)
85
+ self.state = dict(
86
+ task="Prototype refs",
87
+ focus=7,
88
+ blocked=False,
89
+ notes="",
90
+ last_event="Waiting for a widget change",
91
+ )
92
+
93
+ def sync_task(self, value):
94
+ self.state.task = value
95
+ self.state.last_event = f"task -> {value!r}"
96
+
97
+ def sync_focus(self, value):
98
+ self.state.focus = value
99
+ self.state.last_event = f"focus -> {value}"
100
+
101
+ def sync_blocked(self, value):
102
+ self.state.blocked = value
103
+ self.state.last_event = f"blocked -> {value}"
104
+
105
+ def sync_notes(self, value):
106
+ self.state.notes = value
107
+ self.state.last_event = f"notes -> {len(value)} chars"
108
+
109
+ def render(self):
110
+ snapshot = {
111
+ "task": self.state.task,
112
+ "focus": self.state.focus,
113
+ "blocked": self.state.blocked,
114
+ "notes": self.state.notes,
115
+ "last_event": self.state.last_event,
116
+ }
117
+
118
+ return container(key="panel", border=True)(
119
+ subheader(key="h")("2. Widget values in callbacks"),
120
+ caption(key="c")(
121
+ "Stateful Elements pass their current value to `on_change`, so simple widget-to-state sync stays explicit and lightweight."
122
+ ),
123
+ text_input(key="task", value=self.state.task, on_change=self.sync_task)("Current task"),
124
+ slider(key="focus", min_value=0, max_value=10, value=self.state.focus, on_change=self.sync_focus)("Focus level"),
125
+ toggle(key="blocked", value=self.state.blocked, on_change=self.sync_blocked)("Blocked right now"),
126
+ text_area(
127
+ key="notes",
128
+ value=self.state.notes,
129
+ height=120,
130
+ on_change=self.sync_notes,
131
+ placeholder="Type here to update component state from a widget callback.",
132
+ )("Notes"),
133
+ columns(key="metrics")(
134
+ metric(key="task_metric", label="Task", value=self.state.task or "None"),
135
+ metric(key="focus_metric", label="Focus", value=self.state.focus),
136
+ metric(key="blocked_metric", label="Blocked", value="Yes" if self.state.blocked else "No"),
137
+ ),
138
+ success(key="hint")(
139
+ f"Last callback: {self.state.last_event}"
140
+ ),
141
+ json(key="snapshot")(snapshot),
142
+ code(key="snippet", language="python")(
143
+ "def sync_task(self, value):\n"
144
+ " self.state.task = value\n"
145
+ "\n"
146
+ "def render(self):\n"
147
+ " return text_input(key=\"task\", on_change=self.sync_task)(\"Current task\")"
148
+ ),
149
+ )
150
+
151
+
152
+ class RefInspectorDemo(Component):
153
+ def __init__(self, **props):
154
+ super().__init__(**props)
155
+ self.name_ref = Ref()
156
+ self.notes_ref = Ref()
157
+ self.counter_ref = Ref()
158
+ self.state = dict(
159
+ name="Shipping tutorial dashboard",
160
+ notes="Refs resolve to stable logical paths.",
161
+ snapshot={},
162
+ )
163
+
164
+ def capture_refs(self):
165
+ self.state.snapshot = {
166
+ "name_ref_path": self.name_ref.path,
167
+ "name_ref_value": get_element_value(self.name_ref),
168
+ "notes_ref_path": self.notes_ref.path,
169
+ "notes_ref_value": get_element_value(self.notes_ref),
170
+ "counter_ref_path": self.counter_ref.path,
171
+ "counter_ref_state": dict(get_component_state(self.counter_ref)),
172
+ }
173
+
174
+ def render(self):
175
+ return container(key="panel", border=True)(
176
+ subheader(key="h")("3. Reach things later with Ref()"),
177
+ caption(key="c")(
178
+ "Refs store logical paths, not Python instances. Render first, then pass them to helpers like get_element_value(...) or get_component_state(...)."
179
+ ),
180
+ text_input(
181
+ key="name",
182
+ ref=self.name_ref,
183
+ value=self.state.name,
184
+ on_change=self.sync_state("name"),
185
+ )("Title tracked by ref"),
186
+ text_area(
187
+ key="notes",
188
+ ref=self.notes_ref,
189
+ value=self.state.notes,
190
+ height=100,
191
+ on_change=self.sync_state("notes"),
192
+ )("Notes tracked by ref"),
193
+ CounterCard(key="counter", ref=self.counter_ref, label="Counter reached through ref", initial=3),
194
+ button(key="read_refs", on_click=self.capture_refs, type="primary")("Read refs now"),
195
+ json(key="snapshot")(self.state.snapshot or {"status": "Click 'Read refs now' after changing the inputs or counter."}),
196
+ code(key="snippet", language="python")(
197
+ "name_ref = Ref()\n"
198
+ "counter_ref = Ref()\n"
199
+ "\n"
200
+ "text_input(key=\"name\", ref=name_ref)(\"Name\")\n"
201
+ "CounterCard(key=\"counter\", ref=counter_ref)\n"
202
+ "\n"
203
+ "snapshot = {\n"
204
+ " \"name\": get_element_value(name_ref),\n"
205
+ " \"count\": get_component_state(counter_ref).value,\n"
206
+ "}"
207
+ ),
208
+ )
209
+
210
+
211
+ class CompositionDemo(Component):
212
+ def __init__(self, **props):
213
+ super().__init__(**props)
214
+ self.state = dict(saved=False)
215
+
216
+ def mark_dirty(self, value):
217
+ self.state.saved = False
218
+
219
+ def mark_saved(self):
220
+ self.state.saved = True
221
+
222
+ def render(self):
223
+ return container(key="panel", border=True)(
224
+ subheader(key="h")("4. Compose simple Elements into richer Components"),
225
+ caption(key="c")(
226
+ "The library stays small by making each base Element wrap one Streamlit primitive. Rich behavior lives in Components."
227
+ ),
228
+ expander(key="notes_box", label="Mini editor built by composition", expanded=True)(
229
+ text_area(
230
+ key="draft",
231
+ height=140,
232
+ placeholder="Write something, then click save.",
233
+ on_change=self.mark_dirty,
234
+ )("Draft"),
235
+ columns(key="row")(
236
+ button(key="save", on_click=self.mark_saved, type="primary")("Save"),
237
+ success(key="saved")("Saved") if self.state.saved else warning(key="dirty")("Unsaved changes"),
238
+ ),
239
+ ),
240
+ markdown(key="body")(
241
+ "This mini editor is not a special built-in widget. It is just:\n\n"
242
+ "- `text_area`\n"
243
+ "- `button`\n"
244
+ "- feedback Elements\n"
245
+ "- a small `Component` holding local state"
246
+ ),
247
+ )
248
+
249
+
250
+ class SidebarGuide(Component):
251
+ def render(self):
252
+ return sidebar(key="sidebar")(
253
+ header(key="h")("st-components"),
254
+ caption(key="c")("React-like patterns for Streamlit"),
255
+ divider(key="d1"),
256
+ markdown(key="agenda")(
257
+ "This dashboard demonstrates:\n\n"
258
+ "1. local component state\n"
259
+ "2. widget values in callbacks\n"
260
+ "3. `Ref()`\n"
261
+ "4. composition of simple Elements"
262
+ ),
263
+ divider(key="d2"),
264
+ text(key="rule")("Mental model"),
265
+ markdown(key="model")(
266
+ "- Components own state through fibers.\n"
267
+ "- Widgets keep their value in `st.session_state`.\n"
268
+ "- Refs point to paths, not instances."
269
+ ),
270
+ )
271
+
272
+
273
+ class Dashboard(Component):
274
+ def render(self):
275
+ return container(key="page")(
276
+ SidebarGuide(key="guide"),
277
+ IntroCard(key="intro"),
278
+ tabs(key="sections", tabs=["State", "Widget values", "Refs", "Composition"])(
279
+ LocalStateDemo(key="state_demo"),
280
+ ElementValueDemo(key="element_demo"),
281
+ RefInspectorDemo(key="ref_demo"),
282
+ CompositionDemo(key="composition_demo"),
283
+ ),
284
+ source_view(__file__),
285
+ )
286
+
287
+
288
+ App(root=Dashboard(key="dashboard")).render()