cjm-fasthtml-card-stack 0.0.3__py3-none-any.whl → 0.0.4__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.
- cjm_fasthtml_card_stack/__init__.py +1 -1
- cjm_fasthtml_card_stack/components/viewport.py +11 -13
- cjm_fasthtml_card_stack/js/core.py +25 -3
- cjm_fasthtml_card_stack/routes/handlers.py +4 -4
- cjm_fasthtml_card_stack/routes/router.py +1 -1
- {cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/METADATA +17 -17
- {cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/RECORD +13 -12
- demos/custom_position.py +114 -0
- demos/shared.py +1 -0
- {cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/WHEEL +0 -0
- {cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/entry_points.txt +0 -0
- {cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/licenses/LICENSE +0 -0
- {cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/top_level.txt +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.4"
|
|
@@ -210,19 +210,17 @@ def render_all_slots_oob(
|
|
|
210
210
|
|
|
211
211
|
# %% ../../nbs/components/viewport.ipynb #v1000031
|
|
212
212
|
def _grid_template_rows(
|
|
213
|
-
|
|
214
|
-
visible_count: int, # Number of visible slots
|
|
213
|
+
focus_position: Optional[int] = None, # Focus slot offset (None=center, -1=bottom, 0=top)
|
|
215
214
|
) -> str: # CSS grid-template-rows value
|
|
216
|
-
"""Compute CSS grid-template-rows based on focus
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return "1fr auto" # No after section
|
|
215
|
+
"""Compute CSS grid-template-rows based on focus position intent."""
|
|
216
|
+
if focus_position is None:
|
|
217
|
+
return "1fr auto 1fr" # Center: always 3-section
|
|
218
|
+
elif focus_position == 0:
|
|
219
|
+
return "auto 1fr" # Top: focused first
|
|
220
|
+
elif focus_position < 0:
|
|
221
|
+
return "1fr auto" # Bottom: focused last
|
|
224
222
|
else:
|
|
225
|
-
return "1fr auto 1fr" #
|
|
223
|
+
return "1fr auto 1fr" # Custom positive: always 3-section
|
|
226
224
|
|
|
227
225
|
# %% ../../nbs/components/viewport.ipynb #v1000041
|
|
228
226
|
def render_viewport(
|
|
@@ -285,8 +283,8 @@ def render_viewport(
|
|
|
285
283
|
cls=section_cls(justify.start)
|
|
286
284
|
)
|
|
287
285
|
|
|
288
|
-
# Grid template
|
|
289
|
-
grid_rows = _grid_template_rows(
|
|
286
|
+
# Grid template based on focus position intent (stable across count changes)
|
|
287
|
+
grid_rows = _grid_template_rows(state.focus_position)
|
|
290
288
|
|
|
291
289
|
inner_cls = combine_classes(grid_display, w.full, h.full, m.x.auto, gap(4))
|
|
292
290
|
inner_style = f"grid-template-rows: {grid_rows}; max-width: {state.card_width}rem"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
__all__ = ['global_callback_name', 'generate_card_stack_js']
|
|
7
7
|
|
|
8
8
|
# %% ../../nbs/js/core.ipynb #jc000003
|
|
9
|
-
from typing import Any, Tuple
|
|
9
|
+
from typing import Any, Optional, Tuple
|
|
10
10
|
|
|
11
11
|
from fasthtml.common import Script
|
|
12
12
|
|
|
@@ -166,7 +166,7 @@ def _generate_card_count_mgmt_js(
|
|
|
166
166
|
if ('{urls.update_viewport}') {{
|
|
167
167
|
htmx.ajax('POST', '{urls.update_viewport}', {{
|
|
168
168
|
target: '#' + '{ids.card_stack}',
|
|
169
|
-
swap: '
|
|
169
|
+
swap: 'none',
|
|
170
170
|
values: {{ visible_count: count }}
|
|
171
171
|
}});
|
|
172
172
|
}}
|
|
@@ -184,15 +184,36 @@ def _generate_card_count_mgmt_js(
|
|
|
184
184
|
def _generate_coordinator_js(
|
|
185
185
|
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
186
186
|
config: CardStackConfig, # Config for prefix-unique listener guards
|
|
187
|
+
focus_position: Optional[int] = None, # Focus slot offset (None=center, -1=bottom, 0=top)
|
|
187
188
|
) -> str: # JS code fragment for master coordinator
|
|
188
189
|
"""Generate JS for the master coordinator and HTMX listener."""
|
|
189
190
|
guard_var = f"_csMasterListener_{config.prefix.replace('-', '_')}"
|
|
191
|
+
js_focus_pos = "null" if focus_position is None else str(focus_position)
|
|
190
192
|
return f"""
|
|
193
|
+
// === Grid Template Management ===
|
|
194
|
+
ns.applyGridTemplate = function() {{
|
|
195
|
+
const inner = document.getElementById('{ids.card_stack_inner}');
|
|
196
|
+
if (!inner) return;
|
|
197
|
+
const focusPosRaw = {js_focus_pos};
|
|
198
|
+
let tmpl;
|
|
199
|
+
if (focusPosRaw === null) {{
|
|
200
|
+
tmpl = '1fr auto 1fr';
|
|
201
|
+
}} else if (focusPosRaw === 0) {{
|
|
202
|
+
tmpl = 'auto 1fr';
|
|
203
|
+
}} else if (focusPosRaw < 0) {{
|
|
204
|
+
tmpl = '1fr auto';
|
|
205
|
+
}} else {{
|
|
206
|
+
tmpl = '1fr auto 1fr';
|
|
207
|
+
}}
|
|
208
|
+
inner.style.gridTemplateRows = tmpl;
|
|
209
|
+
}};
|
|
210
|
+
|
|
191
211
|
// === Master Coordinator ===
|
|
192
212
|
ns.applyAllViewportSettings = function() {{
|
|
193
213
|
requestAnimationFrame(function() {{
|
|
194
214
|
if (ns.applyWidth) ns.applyWidth();
|
|
195
215
|
if (ns.applyScale) ns.applyScale();
|
|
216
|
+
if (ns.applyGridTemplate) ns.applyGridTemplate();
|
|
196
217
|
if (ns.recalculateHeight) ns.recalculateHeight();
|
|
197
218
|
|
|
198
219
|
const cs = document.getElementById('{ids.card_stack}');
|
|
@@ -272,6 +293,7 @@ def generate_card_stack_js(
|
|
|
272
293
|
urls: CardStackUrls, # URL bundle for routing
|
|
273
294
|
container_id: str = "", # Consumer's parent container ID (for height calc)
|
|
274
295
|
extra_scripts: Tuple[str, ...] = (), # Additional JS to include in the IIFE
|
|
296
|
+
focus_position: Optional[int] = None, # Focus slot offset (None=center, -1=bottom, 0=top)
|
|
275
297
|
) -> Any: # Script element with all card stack JavaScript
|
|
276
298
|
"""Compose all card stack JS into a single namespaced IIFE."""
|
|
277
299
|
prefix = config.prefix
|
|
@@ -285,7 +307,7 @@ def generate_card_stack_js(
|
|
|
285
307
|
scale_js = _generate_scale_mgmt_js(ids, config, urls)
|
|
286
308
|
count_js = _generate_card_count_mgmt_js(ids, config, urls)
|
|
287
309
|
global_cbs_js = _generate_global_callbacks_js(config)
|
|
288
|
-
coordinator_js = _generate_coordinator_js(ids, config)
|
|
310
|
+
coordinator_js = _generate_coordinator_js(ids, config, focus_position)
|
|
289
311
|
|
|
290
312
|
return Script(f"""(function() {{
|
|
291
313
|
window.cardStacks = window.cardStacks || {{}};
|
|
@@ -127,17 +127,17 @@ def card_stack_update_viewport(
|
|
|
127
127
|
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
128
128
|
urls: CardStackUrls, # URL bundle for navigation
|
|
129
129
|
render_card: Callable, # Card renderer callback
|
|
130
|
-
) ->
|
|
131
|
-
"""Update viewport with new card count. Mutates state.visible_count in place."""
|
|
130
|
+
) -> Tuple: # OOB section elements (3 viewport sections)
|
|
131
|
+
"""Update viewport with new card count via OOB section swaps. Mutates state.visible_count in place."""
|
|
132
132
|
state.visible_count = visible_count
|
|
133
|
-
return
|
|
133
|
+
return tuple(build_slots_response(
|
|
134
134
|
card_items=card_items,
|
|
135
135
|
state=state,
|
|
136
136
|
config=config,
|
|
137
137
|
ids=ids,
|
|
138
138
|
urls=urls,
|
|
139
139
|
render_card=render_card,
|
|
140
|
-
)
|
|
140
|
+
))
|
|
141
141
|
|
|
142
142
|
# %% ../../nbs/routes/handlers.ipynb #h1000013
|
|
143
143
|
def card_stack_save_width(
|
|
@@ -100,7 +100,7 @@ def init_card_stack_router(
|
|
|
100
100
|
|
|
101
101
|
@router
|
|
102
102
|
def update_viewport(visible_count: int) -> Any:
|
|
103
|
-
"""Update viewport with new card count (
|
|
103
|
+
"""Update viewport with new card count (OOB section swaps)."""
|
|
104
104
|
state = state_getter()
|
|
105
105
|
items = get_items()
|
|
106
106
|
result = card_stack_update_viewport(
|
{cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cjm-fasthtml-card-stack
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A fixed-viewport card stack component for FastHTML with keyboard navigation, scroll-to-nav, configurable focus position, and HTMX-driven OOB updates.
|
|
5
5
|
Home-page: https://github.com/cj-mills/cjm-fasthtml-card-stack
|
|
6
6
|
Author: Christian J. Mills
|
|
@@ -102,38 +102,38 @@ graph LR
|
|
|
102
102
|
components_states --> core_html_ids
|
|
103
103
|
components_viewport --> core_config
|
|
104
104
|
components_viewport --> core_html_ids
|
|
105
|
-
components_viewport --> components_states
|
|
106
|
-
components_viewport --> core_constants
|
|
107
105
|
components_viewport --> core_models
|
|
108
106
|
components_viewport --> helpers_focus
|
|
107
|
+
components_viewport --> core_constants
|
|
108
|
+
components_viewport --> components_states
|
|
109
109
|
helpers_focus --> core_html_ids
|
|
110
110
|
js_core --> core_constants
|
|
111
111
|
js_core --> core_config
|
|
112
112
|
js_core --> core_html_ids
|
|
113
|
-
js_core --> js_viewport
|
|
114
|
-
js_core --> js_navigation
|
|
115
|
-
js_core --> js_scroll
|
|
116
113
|
js_core --> core_models
|
|
114
|
+
js_core --> js_scroll
|
|
115
|
+
js_core --> js_navigation
|
|
116
|
+
js_core --> js_viewport
|
|
117
117
|
js_core --> core_button_ids
|
|
118
118
|
js_navigation --> core_button_ids
|
|
119
119
|
js_scroll --> core_constants
|
|
120
|
-
js_scroll --> core_button_ids
|
|
121
120
|
js_scroll --> core_html_ids
|
|
121
|
+
js_scroll --> core_button_ids
|
|
122
122
|
js_viewport --> core_html_ids
|
|
123
|
+
keyboard_actions --> js_core
|
|
123
124
|
keyboard_actions --> core_config
|
|
124
125
|
keyboard_actions --> core_html_ids
|
|
125
|
-
keyboard_actions --> js_core
|
|
126
126
|
keyboard_actions --> core_models
|
|
127
127
|
keyboard_actions --> core_button_ids
|
|
128
|
-
routes_handlers --> core_models
|
|
129
128
|
routes_handlers --> core_config
|
|
130
129
|
routes_handlers --> core_html_ids
|
|
131
|
-
routes_handlers -->
|
|
132
|
-
routes_handlers --> helpers_focus
|
|
130
|
+
routes_handlers --> core_models
|
|
133
131
|
routes_handlers --> components_viewport
|
|
132
|
+
routes_handlers --> helpers_focus
|
|
133
|
+
routes_handlers --> components_progress
|
|
134
|
+
routes_router --> routes_handlers
|
|
134
135
|
routes_router --> core_config
|
|
135
136
|
routes_router --> core_html_ids
|
|
136
|
-
routes_router --> routes_handlers
|
|
137
137
|
routes_router --> core_models
|
|
138
138
|
```
|
|
139
139
|
|
|
@@ -514,6 +514,7 @@ def _generate_card_count_mgmt_js(
|
|
|
514
514
|
def _generate_coordinator_js(
|
|
515
515
|
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
516
516
|
config: CardStackConfig, # Config for prefix-unique listener guards
|
|
517
|
+
focus_position: Optional[int] = None, # Focus slot offset (None=center, -1=bottom, 0=top)
|
|
517
518
|
) -> str: # JS code fragment for master coordinator
|
|
518
519
|
"Generate JS for the master coordinator and HTMX listener."
|
|
519
520
|
```
|
|
@@ -670,8 +671,8 @@ def card_stack_update_viewport(
|
|
|
670
671
|
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
671
672
|
urls: CardStackUrls, # URL bundle for navigation
|
|
672
673
|
render_card: Callable, # Card renderer callback
|
|
673
|
-
) ->
|
|
674
|
-
"Update viewport with new card count. Mutates state.visible_count in place."
|
|
674
|
+
) -> Tuple: # OOB section elements (3 viewport sections)
|
|
675
|
+
"Update viewport with new card count via OOB section swaps. Mutates state.visible_count in place."
|
|
675
676
|
```
|
|
676
677
|
|
|
677
678
|
``` python
|
|
@@ -1082,10 +1083,9 @@ def render_all_slots_oob(
|
|
|
1082
1083
|
|
|
1083
1084
|
``` python
|
|
1084
1085
|
def _grid_template_rows(
|
|
1085
|
-
|
|
1086
|
-
visible_count: int, # Number of visible slots
|
|
1086
|
+
focus_position: Optional[int] = None, # Focus slot offset (None=center, -1=bottom, 0=top)
|
|
1087
1087
|
) -> str: # CSS grid-template-rows value
|
|
1088
|
-
"Compute CSS grid-template-rows based on focus
|
|
1088
|
+
"Compute CSS grid-template-rows based on focus position intent."
|
|
1089
1089
|
```
|
|
1090
1090
|
|
|
1091
1091
|
``` python
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
cjm_fasthtml_card_stack/__init__.py,sha256=
|
|
1
|
+
cjm_fasthtml_card_stack/__init__.py,sha256=1mptEzQihbdyqqzMgdns_j5ZGK9gz7hR2bsgA_TnjO4,22
|
|
2
2
|
cjm_fasthtml_card_stack/_modidx.py,sha256=jYNj6LPbD5I8yDghD3Lx9XWsDnWqc_oNCESguSF8blA,25105
|
|
3
3
|
cjm_fasthtml_card_stack/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
cjm_fasthtml_card_stack/components/controls.py,sha256=C3iRvaiRe0ti8S7ogTSKRRRdw-z6vz77mkuvKVmIz-Y,4093
|
|
5
5
|
cjm_fasthtml_card_stack/components/progress.py,sha256=BBeR4EWEikA54bVG41iLdlyyGRFwMJtEh8-cX9L9QLI,1500
|
|
6
6
|
cjm_fasthtml_card_stack/components/states.py,sha256=ClgpdS3e19ccO6VHU6ajulU_yb7c1bQVZf9nSsd5QPc,3797
|
|
7
|
-
cjm_fasthtml_card_stack/components/viewport.py,sha256=
|
|
7
|
+
cjm_fasthtml_card_stack/components/viewport.py,sha256=LOocpEh4V_pkTqpdOyWBX50ctil5rf6l7NQl8QBNcGs,11794
|
|
8
8
|
cjm_fasthtml_card_stack/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
cjm_fasthtml_card_stack/core/button_ids.py,sha256=elKKLBrt-Hu6vgWhrcVlCXgdUeYXHQyfn3NyhhEmxW4,2123
|
|
10
10
|
cjm_fasthtml_card_stack/core/config.py,sha256=XYaptNnzZ8sRcCdxWA_RB1FwBiXU8Ko6mEsNSbxhtb0,1714
|
|
@@ -14,23 +14,24 @@ cjm_fasthtml_card_stack/core/models.py,sha256=soivwLNBbSNQmcCb9EpFz09gaDhb9HesXb
|
|
|
14
14
|
cjm_fasthtml_card_stack/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
cjm_fasthtml_card_stack/helpers/focus.py,sha256=PTbsZagutEM8pwpLUKvM8Gjhv7rVNMbNbO27x5uC3YM,2626
|
|
16
16
|
cjm_fasthtml_card_stack/js/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
cjm_fasthtml_card_stack/js/core.py,sha256=
|
|
17
|
+
cjm_fasthtml_card_stack/js/core.py,sha256=k6s4sYMz19-5FzFk7MqmrrR8SUHtJnslqm_sfXE6qVs,13486
|
|
18
18
|
cjm_fasthtml_card_stack/js/navigation.py,sha256=471F3NDxdH-BQPLPQuUvtyACqi6g6EOcELYs_sP1hyk,1288
|
|
19
19
|
cjm_fasthtml_card_stack/js/scroll.py,sha256=X6j5JMOjzgH57sDK1fazNRyGYH2GGEDQeZ0dURFEjC4,3062
|
|
20
20
|
cjm_fasthtml_card_stack/js/viewport.py,sha256=hFWr3zt9odyuaYdRdJmhKJcS_vsJyLRhz-HlZPvGh54,5653
|
|
21
21
|
cjm_fasthtml_card_stack/keyboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
cjm_fasthtml_card_stack/keyboard/actions.py,sha256=rfCD8lLVXXVPrML-8dj48bT_zOKN7g8VmPMNE21IAJI,7573
|
|
23
23
|
cjm_fasthtml_card_stack/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
cjm_fasthtml_card_stack/routes/handlers.py,sha256=
|
|
25
|
-
cjm_fasthtml_card_stack/routes/router.py,sha256=
|
|
26
|
-
cjm_fasthtml_card_stack-0.0.
|
|
24
|
+
cjm_fasthtml_card_stack/routes/handlers.py,sha256=Rnfie_QcvyIA5P4PqXOX8FjZFA1ohzbLga4k9HoUGhY,6861
|
|
25
|
+
cjm_fasthtml_card_stack/routes/router.py,sha256=88a0NEnPX5agS_VsNUcxZ8pC8c0P-eaFy0ZZoIn0UP4,5129
|
|
26
|
+
cjm_fasthtml_card_stack-0.0.4.dist-info/licenses/LICENSE,sha256=xV8xoN4VOL0uw9X8RSs2IMuD_Ss_a9yAbtGNeBWZwnw,11337
|
|
27
27
|
demos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
demos/basic.py,sha256=8GVV7eSWOzcz67L6XiPqwKFy886AT5y7Ok-T2ZPX32Q,3815
|
|
29
29
|
demos/bottom.py,sha256=ZLpgpUfOvCzM3rVf3ufvZnSAxuzOQ5gND7RsSUgNpJo,3341
|
|
30
|
+
demos/custom_position.py,sha256=wra57olAq-RuzcEIl4Jwl_-EeXHx0LW0YLqFB8-w-BI,3613
|
|
30
31
|
demos/data.py,sha256=8yVV_E0nLcr0L1PcrTG-DaT-V36apF74ALKNIZuSQ70,4471
|
|
31
|
-
demos/shared.py,sha256=
|
|
32
|
-
cjm_fasthtml_card_stack-0.0.
|
|
33
|
-
cjm_fasthtml_card_stack-0.0.
|
|
34
|
-
cjm_fasthtml_card_stack-0.0.
|
|
35
|
-
cjm_fasthtml_card_stack-0.0.
|
|
36
|
-
cjm_fasthtml_card_stack-0.0.
|
|
32
|
+
demos/shared.py,sha256=88vNOVODcauWHoswzsZt9vQhEPsam8uWGujSgisVxhU,5433
|
|
33
|
+
cjm_fasthtml_card_stack-0.0.4.dist-info/METADATA,sha256=xHG3PiHNzISON6yxr3paljb6d1yLWFYF06oUyztc8CM,36946
|
|
34
|
+
cjm_fasthtml_card_stack-0.0.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
35
|
+
cjm_fasthtml_card_stack-0.0.4.dist-info/entry_points.txt,sha256=qTw6qaijkEUH1AcPQhpTqJ8TPiUSMnpn0Cvg4hXoX90,68
|
|
36
|
+
cjm_fasthtml_card_stack-0.0.4.dist-info/top_level.txt,sha256=ydBTJsY2ONaDryp85HkjhdGHEuuUOoMGdrOgFA2ddyg,30
|
|
37
|
+
cjm_fasthtml_card_stack-0.0.4.dist-info/RECORD,,
|
demos/custom_position.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Custom focus position demo (focus at slot 1)."""
|
|
2
|
+
|
|
3
|
+
from fasthtml.common import Div, P, Span
|
|
4
|
+
|
|
5
|
+
from cjm_fasthtml_daisyui.components.data_display.card import card, card_body
|
|
6
|
+
from cjm_fasthtml_daisyui.components.data_display.badge import badge, badge_colors, badge_styles
|
|
7
|
+
from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui, text_dui
|
|
8
|
+
from cjm_fasthtml_tailwind.utilities.spacing import p, m
|
|
9
|
+
from cjm_fasthtml_tailwind.utilities.sizing import w
|
|
10
|
+
from cjm_fasthtml_tailwind.core.base import combine_classes
|
|
11
|
+
|
|
12
|
+
from cjm_fasthtml_card_stack.core.config import CardStackConfig
|
|
13
|
+
from cjm_fasthtml_card_stack.core.models import CardStackState, CardRenderContext
|
|
14
|
+
from cjm_fasthtml_card_stack.core.html_ids import CardStackHtmlIds
|
|
15
|
+
from cjm_fasthtml_card_stack.core.button_ids import CardStackButtonIds
|
|
16
|
+
from cjm_fasthtml_card_stack.routes.router import init_card_stack_router
|
|
17
|
+
|
|
18
|
+
from demos.data import SAMPLE_ITEMS
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def render_card(item, context: CardRenderContext):
|
|
22
|
+
"""Render a card with position info for the custom focus demo."""
|
|
23
|
+
is_focused = context.card_role == "focused"
|
|
24
|
+
|
|
25
|
+
index_badge = Span(
|
|
26
|
+
f"#{context.index + 1}",
|
|
27
|
+
cls=combine_classes(
|
|
28
|
+
badge,
|
|
29
|
+
badge_colors.accent if is_focused else badge_colors.neutral,
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
distance_label = Span(
|
|
34
|
+
f"slot offset: {context.distance_from_focus:+d}",
|
|
35
|
+
cls=combine_classes(
|
|
36
|
+
badge, badge_styles.ghost,
|
|
37
|
+
)
|
|
38
|
+
) if not is_focused else Span(
|
|
39
|
+
"focused",
|
|
40
|
+
cls=combine_classes(badge, badge_colors.accent),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return Div(
|
|
44
|
+
Div(
|
|
45
|
+
Div(index_badge, distance_label, cls=combine_classes(m.b(1))),
|
|
46
|
+
P(
|
|
47
|
+
item,
|
|
48
|
+
cls=combine_classes(text_dui.base_content),
|
|
49
|
+
style="font-size: calc(0.875rem * var(--card-stack-scale, 100) / 100)",
|
|
50
|
+
),
|
|
51
|
+
cls=combine_classes(card_body, p(3)),
|
|
52
|
+
),
|
|
53
|
+
cls=combine_classes(
|
|
54
|
+
card,
|
|
55
|
+
bg_dui.base_100,
|
|
56
|
+
w.full,
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def setup(route_prefix="/custom"):
|
|
62
|
+
"""Set up the custom focus position demo.
|
|
63
|
+
|
|
64
|
+
Returns dict with config, ids, btn_ids, router, urls, state management,
|
|
65
|
+
and page rendering dependencies.
|
|
66
|
+
"""
|
|
67
|
+
config = CardStackConfig(prefix="custom", click_to_focus=True)
|
|
68
|
+
ids = CardStackHtmlIds(prefix=config.prefix)
|
|
69
|
+
btn_ids = CardStackButtonIds(prefix=config.prefix)
|
|
70
|
+
|
|
71
|
+
state = CardStackState(
|
|
72
|
+
visible_count=5,
|
|
73
|
+
card_width=60,
|
|
74
|
+
focus_position=1, # Second slot from top
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def get_state():
|
|
78
|
+
return state
|
|
79
|
+
|
|
80
|
+
def set_state(s):
|
|
81
|
+
nonlocal state
|
|
82
|
+
state.focused_index = s.focused_index
|
|
83
|
+
state.visible_count = s.visible_count
|
|
84
|
+
state.card_width = s.card_width
|
|
85
|
+
state.card_scale = s.card_scale
|
|
86
|
+
state.active_mode = s.active_mode
|
|
87
|
+
state.focus_position = s.focus_position
|
|
88
|
+
|
|
89
|
+
def get_items():
|
|
90
|
+
return SAMPLE_ITEMS
|
|
91
|
+
|
|
92
|
+
router, urls = init_card_stack_router(
|
|
93
|
+
config=config,
|
|
94
|
+
state_getter=get_state,
|
|
95
|
+
state_setter=set_state,
|
|
96
|
+
get_items=get_items,
|
|
97
|
+
render_card=render_card,
|
|
98
|
+
route_prefix=route_prefix,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return dict(
|
|
102
|
+
config=config,
|
|
103
|
+
ids=ids,
|
|
104
|
+
btn_ids=btn_ids,
|
|
105
|
+
router=router,
|
|
106
|
+
urls=urls,
|
|
107
|
+
get_state=get_state,
|
|
108
|
+
get_items=get_items,
|
|
109
|
+
render_card=render_card,
|
|
110
|
+
container_id="custom-demo-container",
|
|
111
|
+
title="Custom Focus Position",
|
|
112
|
+
description="Focus at slot 1 (second from top). One context card above, rest below.",
|
|
113
|
+
progress_label="Item",
|
|
114
|
+
)
|
demos/shared.py
CHANGED
|
File without changes
|
{cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{cjm_fasthtml_card_stack-0.0.3.dist-info → cjm_fasthtml_card_stack-0.0.4.dist-info}/top_level.txt
RENAMED
|
File without changes
|