cjm-fasthtml-card-stack 0.0.1__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 -0
- cjm_fasthtml_card_stack/_modidx.py +151 -0
- cjm_fasthtml_card_stack-0.0.1.dist-info/METADATA +1123 -0
- cjm_fasthtml_card_stack-0.0.1.dist-info/RECORD +8 -0
- cjm_fasthtml_card_stack-0.0.1.dist-info/WHEEL +5 -0
- cjm_fasthtml_card_stack-0.0.1.dist-info/entry_points.txt +2 -0
- cjm_fasthtml_card_stack-0.0.1.dist-info/licenses/LICENSE +201 -0
- cjm_fasthtml_card_stack-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1123 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cjm-fasthtml-card-stack
|
|
3
|
+
Version: 0.0.1
|
|
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
|
+
Home-page: https://github.com/cj-mills/cjm-fasthtml-card-stack
|
|
6
|
+
Author: Christian J. Mills
|
|
7
|
+
Author-email: "Christian J. Mills" <9126128+cj-mills@users.noreply.github.com>
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Project-URL: Repository, https://github.com/cj-mills/cjm-fasthtml-card-stack
|
|
10
|
+
Project-URL: Documentation, https://cj-mills.github.io/cjm-fasthtml-card-stack
|
|
11
|
+
Keywords: nbdev,jupyter,notebook,python
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: python-fasthtml
|
|
21
|
+
Requires-Dist: cjm-fasthtml-tailwind
|
|
22
|
+
Requires-Dist: cjm-fasthtml-daisyui
|
|
23
|
+
Requires-Dist: cjm-fasthtml-keyboard-navigation
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: home-page
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
|
|
29
|
+
# cjm-fasthtml-card-stack
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
``` bash
|
|
37
|
+
pip install cjm_fasthtml_card_stack
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Project Structure
|
|
41
|
+
|
|
42
|
+
nbs/
|
|
43
|
+
├── components/ (4)
|
|
44
|
+
│ ├── controls.ipynb # Width slider, scale slider, and card count selector components.
|
|
45
|
+
│ ├── progress.ipynb # Progress indicator showing the current position within the card stack.
|
|
46
|
+
│ ├── states.ipynb # Loading, empty, and placeholder card components for the card stack viewport.
|
|
47
|
+
│ └── viewport.ipynb # Card stack viewport with 3-section CSS Grid layout, slot rendering,
|
|
48
|
+
├── core/ (5)
|
|
49
|
+
│ ├── button_ids.ipynb # Prefix-based IDs for hidden keyboard action buttons.
|
|
50
|
+
│ ├── config.ipynb # Configuration dataclass for card stack initialization.
|
|
51
|
+
│ ├── constants.ipynb # CSS class constants, type aliases, and default values for the card stack library.
|
|
52
|
+
│ ├── html_ids.ipynb # Prefix-based HTML ID generator for card stack DOM elements.
|
|
53
|
+
│ └── models.ipynb # Core dataclasses for card stack state, render context, and URL routing.
|
|
54
|
+
├── helpers/ (1)
|
|
55
|
+
│ └── focus.ipynb # Focus position resolution, viewport window calculation, and OOB focus sync.
|
|
56
|
+
├── js/ (4)
|
|
57
|
+
│ ├── core.ipynb # Master composer for card stack JavaScript. Combines viewport height,
|
|
58
|
+
│ ├── navigation.ipynb # JavaScript generator for page jump and first/last navigation helpers.
|
|
59
|
+
│ ├── scroll.ipynb # JavaScript generator for scroll-to-nav conversion.
|
|
60
|
+
│ └── viewport.ipynb # JavaScript generator for dynamic viewport height calculation.
|
|
61
|
+
├── keyboard/ (1)
|
|
62
|
+
│ └── actions.ipynb # Keyboard navigation focus zone and action factories for the card stack.
|
|
63
|
+
└── routes/ (2)
|
|
64
|
+
├── handlers.ipynb # Response builder functions for card stack operations (Tier 1 API).
|
|
65
|
+
└── router.ipynb # Convenience router factory that wires up standard card stack routes (Tier 2 API).
|
|
66
|
+
|
|
67
|
+
Total: 17 notebooks across 6 directories
|
|
68
|
+
|
|
69
|
+
## Module Dependencies
|
|
70
|
+
|
|
71
|
+
``` mermaid
|
|
72
|
+
graph LR
|
|
73
|
+
components_controls[components.controls<br/>Controls]
|
|
74
|
+
components_progress[components.progress<br/>Progress]
|
|
75
|
+
components_states[components.states<br/>States]
|
|
76
|
+
components_viewport[components.viewport<br/>Viewport]
|
|
77
|
+
core_button_ids[core.button_ids<br/>Button IDs]
|
|
78
|
+
core_config[core.config<br/>Config]
|
|
79
|
+
core_constants[core.constants<br/>Constants]
|
|
80
|
+
core_html_ids[core.html_ids<br/>HTML IDs]
|
|
81
|
+
core_models[core.models<br/>Models]
|
|
82
|
+
helpers_focus[helpers.focus<br/>Focus]
|
|
83
|
+
js_core[js.core<br/>JS: Core]
|
|
84
|
+
js_navigation[js.navigation<br/>JS: Page Navigation]
|
|
85
|
+
js_scroll[js.scroll<br/>JS: Scroll Navigation]
|
|
86
|
+
js_viewport[js.viewport<br/>JS: Viewport Height]
|
|
87
|
+
keyboard_actions[keyboard.actions<br/>Actions]
|
|
88
|
+
routes_handlers[routes.handlers<br/>Handlers]
|
|
89
|
+
routes_router[routes.router<br/>Router]
|
|
90
|
+
|
|
91
|
+
components_controls --> core_config
|
|
92
|
+
components_controls --> core_html_ids
|
|
93
|
+
components_progress --> core_html_ids
|
|
94
|
+
components_states --> core_html_ids
|
|
95
|
+
components_viewport --> core_models
|
|
96
|
+
components_viewport --> core_config
|
|
97
|
+
components_viewport --> core_constants
|
|
98
|
+
components_viewport --> helpers_focus
|
|
99
|
+
components_viewport --> core_html_ids
|
|
100
|
+
components_viewport --> components_states
|
|
101
|
+
helpers_focus --> core_html_ids
|
|
102
|
+
js_core --> core_constants
|
|
103
|
+
js_core --> core_button_ids
|
|
104
|
+
js_core --> core_models
|
|
105
|
+
js_core --> core_config
|
|
106
|
+
js_core --> js_scroll
|
|
107
|
+
js_core --> core_html_ids
|
|
108
|
+
js_core --> js_navigation
|
|
109
|
+
js_core --> js_viewport
|
|
110
|
+
js_navigation --> core_button_ids
|
|
111
|
+
js_scroll --> core_button_ids
|
|
112
|
+
js_scroll --> core_constants
|
|
113
|
+
js_scroll --> core_html_ids
|
|
114
|
+
js_viewport --> core_html_ids
|
|
115
|
+
keyboard_actions --> core_button_ids
|
|
116
|
+
keyboard_actions --> core_models
|
|
117
|
+
keyboard_actions --> core_config
|
|
118
|
+
keyboard_actions --> core_html_ids
|
|
119
|
+
keyboard_actions --> js_core
|
|
120
|
+
routes_handlers --> core_models
|
|
121
|
+
routes_handlers --> components_viewport
|
|
122
|
+
routes_handlers --> helpers_focus
|
|
123
|
+
routes_handlers --> core_config
|
|
124
|
+
routes_handlers --> components_progress
|
|
125
|
+
routes_handlers --> core_html_ids
|
|
126
|
+
routes_router --> routes_handlers
|
|
127
|
+
routes_router --> core_models
|
|
128
|
+
routes_router --> core_config
|
|
129
|
+
routes_router --> core_html_ids
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
*39 cross-module dependencies detected*
|
|
133
|
+
|
|
134
|
+
## CLI Reference
|
|
135
|
+
|
|
136
|
+
No CLI commands found in this project.
|
|
137
|
+
|
|
138
|
+
## Module Overview
|
|
139
|
+
|
|
140
|
+
Detailed documentation for each module in the project:
|
|
141
|
+
|
|
142
|
+
### Actions (`actions.ipynb`)
|
|
143
|
+
|
|
144
|
+
> Keyboard navigation focus zone and action factories for the card
|
|
145
|
+
> stack.
|
|
146
|
+
|
|
147
|
+
#### Import
|
|
148
|
+
|
|
149
|
+
``` python
|
|
150
|
+
from cjm_fasthtml_card_stack.keyboard.actions import (
|
|
151
|
+
create_card_stack_focus_zone,
|
|
152
|
+
create_card_stack_nav_actions,
|
|
153
|
+
build_card_stack_url_map,
|
|
154
|
+
render_card_stack_action_buttons
|
|
155
|
+
)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### Functions
|
|
159
|
+
|
|
160
|
+
``` python
|
|
161
|
+
def create_card_stack_focus_zone(
|
|
162
|
+
ids: CardStackHtmlIds, # HTML IDs for this card stack instance
|
|
163
|
+
on_focus_change: Optional[str] = None, # JS callback name on focus change
|
|
164
|
+
hidden_input_prefix: Optional[str] = None, # Prefix for keyboard nav hidden inputs
|
|
165
|
+
data_attributes: Tuple[str, ...] = (), # Data attributes to track on focused items
|
|
166
|
+
) -> FocusZone: # Configured focus zone for the card stack
|
|
167
|
+
"Create a focus zone for a card stack viewport."
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
``` python
|
|
171
|
+
def create_card_stack_nav_actions(
|
|
172
|
+
zone_id: str, # Focus zone ID to restrict actions to
|
|
173
|
+
button_ids: CardStackButtonIds, # Button IDs for HTMX triggers
|
|
174
|
+
config: CardStackConfig, # Config (for prefix-unique callback names)
|
|
175
|
+
disable_in_modes: Tuple[str, ...] = (), # Mode names that disable navigation
|
|
176
|
+
) -> Tuple[KeyAction, ...]: # Standard card stack navigation actions
|
|
177
|
+
"Create standard keyboard navigation actions for a card stack."
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
``` python
|
|
181
|
+
def build_card_stack_url_map(
|
|
182
|
+
button_ids: CardStackButtonIds, # Button IDs for this card stack instance
|
|
183
|
+
urls: CardStackUrls, # URL bundle for routing
|
|
184
|
+
) -> Dict[str, str]: # Mapping of button ID -> route URL
|
|
185
|
+
"""
|
|
186
|
+
Build url_map for render_keyboard_system with all card stack navigation buttons.
|
|
187
|
+
|
|
188
|
+
Returns a dict mapping button IDs to URLs for all navigation actions:
|
|
189
|
+
nav_up, nav_down, nav_first, nav_last, nav_page_up, nav_page_down.
|
|
190
|
+
|
|
191
|
+
Merge with consumer's own action URLs when building the keyboard system:
|
|
192
|
+
url_map = {**build_card_stack_url_map(btn_ids, urls), **my_action_urls}
|
|
193
|
+
"""
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
``` python
|
|
197
|
+
def render_card_stack_action_buttons(
|
|
198
|
+
button_ids: CardStackButtonIds, # Button IDs for this card stack instance
|
|
199
|
+
urls: CardStackUrls, # URL bundle for routing
|
|
200
|
+
ids: CardStackHtmlIds, # HTML IDs (for hx-include of focused_index_input)
|
|
201
|
+
) -> 'FT': # Div containing hidden action buttons
|
|
202
|
+
"""
|
|
203
|
+
Render hidden HTMX buttons for JS-callback-triggered navigation actions.
|
|
204
|
+
|
|
205
|
+
Creates buttons for: page_up, page_down, first, last.
|
|
206
|
+
These are clicked programmatically by the card stack's JS functions.
|
|
207
|
+
Must be included in the DOM alongside the keyboard system's own buttons.
|
|
208
|
+
"""
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Button IDs (`button_ids.ipynb`)
|
|
212
|
+
|
|
213
|
+
> Prefix-based IDs for hidden keyboard action buttons.
|
|
214
|
+
|
|
215
|
+
#### Import
|
|
216
|
+
|
|
217
|
+
``` python
|
|
218
|
+
from cjm_fasthtml_card_stack.core.button_ids import (
|
|
219
|
+
CardStackButtonIds
|
|
220
|
+
)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Classes
|
|
224
|
+
|
|
225
|
+
``` python
|
|
226
|
+
@dataclass
|
|
227
|
+
class CardStackButtonIds:
|
|
228
|
+
"Prefix-based IDs for hidden keyboard action buttons."
|
|
229
|
+
|
|
230
|
+
prefix: str # ID prefix for this card stack instance
|
|
231
|
+
|
|
232
|
+
def nav_up(self) -> str: # Navigate to previous item
|
|
233
|
+
"""Navigate up button."""
|
|
234
|
+
return f"{self.prefix}-btn-nav-up"
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def nav_down(self) -> str: # Navigate to next item
|
|
238
|
+
"Navigate up button."
|
|
239
|
+
|
|
240
|
+
def nav_down(self) -> str: # Navigate to next item
|
|
241
|
+
"""Navigate down button."""
|
|
242
|
+
return f"{self.prefix}-btn-nav-down"
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def nav_first(self) -> str: # Navigate to first item
|
|
246
|
+
"Navigate down button."
|
|
247
|
+
|
|
248
|
+
def nav_first(self) -> str: # Navigate to first item
|
|
249
|
+
"""Navigate to first item button."""
|
|
250
|
+
return f"{self.prefix}-btn-nav-first"
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def nav_last(self) -> str: # Navigate to last item
|
|
254
|
+
"Navigate to first item button."
|
|
255
|
+
|
|
256
|
+
def nav_last(self) -> str: # Navigate to last item
|
|
257
|
+
"""Navigate to last item button."""
|
|
258
|
+
return f"{self.prefix}-btn-nav-last"
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def nav_page_up(self) -> str: # Page jump up
|
|
262
|
+
"Navigate to last item button."
|
|
263
|
+
|
|
264
|
+
def nav_page_up(self) -> str: # Page jump up
|
|
265
|
+
"""Page up button."""
|
|
266
|
+
return f"{self.prefix}-btn-nav-page-up"
|
|
267
|
+
|
|
268
|
+
@property
|
|
269
|
+
def nav_page_down(self) -> str: # Page jump down
|
|
270
|
+
"Page up button."
|
|
271
|
+
|
|
272
|
+
def nav_page_down(self) -> str: # Page jump down
|
|
273
|
+
"""Page down button."""
|
|
274
|
+
return f"{self.prefix}-btn-nav-page-down"
|
|
275
|
+
|
|
276
|
+
# --- Viewport control buttons ---
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def width_narrow(self) -> str: # Decrease viewport width
|
|
280
|
+
"Page down button."
|
|
281
|
+
|
|
282
|
+
def width_narrow(self) -> str: # Decrease viewport width
|
|
283
|
+
"""Narrow viewport button."""
|
|
284
|
+
return f"{self.prefix}-btn-width-narrow"
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def width_widen(self) -> str: # Increase viewport width
|
|
288
|
+
"Narrow viewport button."
|
|
289
|
+
|
|
290
|
+
def width_widen(self) -> str: # Increase viewport width
|
|
291
|
+
"""Widen viewport button."""
|
|
292
|
+
return f"{self.prefix}-btn-width-widen"
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def scale_decrease(self) -> str: # Decrease content scale
|
|
296
|
+
"Widen viewport button."
|
|
297
|
+
|
|
298
|
+
def scale_decrease(self) -> str: # Decrease content scale
|
|
299
|
+
"""Decrease scale button."""
|
|
300
|
+
return f"{self.prefix}-btn-scale-decrease"
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def scale_increase(self) -> str: # Increase content scale
|
|
304
|
+
"Decrease scale button."
|
|
305
|
+
|
|
306
|
+
def scale_increase(self) -> str: # Increase content scale
|
|
307
|
+
"Increase scale button."
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Config (`config.ipynb`)
|
|
311
|
+
|
|
312
|
+
> Configuration dataclass for card stack initialization.
|
|
313
|
+
|
|
314
|
+
#### Import
|
|
315
|
+
|
|
316
|
+
``` python
|
|
317
|
+
from cjm_fasthtml_card_stack.core.config import (
|
|
318
|
+
CardStackConfig
|
|
319
|
+
)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
#### Functions
|
|
323
|
+
|
|
324
|
+
``` python
|
|
325
|
+
def _auto_prefix() -> str: # Unique prefix string (e.g., "cs0", "cs1")
|
|
326
|
+
"""Generate an auto-incrementing unique prefix."""
|
|
327
|
+
global _prefix_counter
|
|
328
|
+
p = f"cs{_prefix_counter}"
|
|
329
|
+
_prefix_counter += 1
|
|
330
|
+
return p
|
|
331
|
+
|
|
332
|
+
def _reset_prefix_counter() -> None
|
|
333
|
+
"Generate an auto-incrementing unique prefix."
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
``` python
|
|
337
|
+
def _reset_prefix_counter() -> None
|
|
338
|
+
"Reset the prefix counter (for testing only)."
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### Classes
|
|
342
|
+
|
|
343
|
+
``` python
|
|
344
|
+
@dataclass
|
|
345
|
+
class CardStackConfig:
|
|
346
|
+
"Initialization-time settings for a card stack instance."
|
|
347
|
+
|
|
348
|
+
prefix: str = field(...) # HTML ID prefix (auto-generated if omitted)
|
|
349
|
+
visible_count_options: Tuple[int, ...] = (1, 3, 5, 7, 9) # Choices for card count dropdown
|
|
350
|
+
card_width_min: int = 30 # Width slider minimum (rem)
|
|
351
|
+
card_width_max: int = 120 # Width slider maximum (rem)
|
|
352
|
+
card_width_step: int = 5 # Width slider step (rem)
|
|
353
|
+
card_scale_min: int = 50 # Scale slider minimum (%)
|
|
354
|
+
card_scale_max: int = 200 # Scale slider maximum (%)
|
|
355
|
+
card_scale_step: int = 10 # Scale slider step (%)
|
|
356
|
+
click_to_focus: bool = False # Whether context cards get transparent click overlay
|
|
357
|
+
disable_scroll_in_modes: Tuple[str, ...] = () # Mode names where scroll-to-nav is suppressed
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### Variables
|
|
361
|
+
|
|
362
|
+
``` python
|
|
363
|
+
_prefix_counter: int = 0
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Constants (`constants.ipynb`)
|
|
367
|
+
|
|
368
|
+
> CSS class constants, type aliases, and default values for the card
|
|
369
|
+
> stack library.
|
|
370
|
+
|
|
371
|
+
#### Import
|
|
372
|
+
|
|
373
|
+
``` python
|
|
374
|
+
from cjm_fasthtml_card_stack.core.constants import (
|
|
375
|
+
CardRole,
|
|
376
|
+
SCROLL_THRESHOLD,
|
|
377
|
+
NAVIGATION_COOLDOWN,
|
|
378
|
+
DEFAULT_VISIBLE_COUNT,
|
|
379
|
+
DEFAULT_CARD_WIDTH,
|
|
380
|
+
DEFAULT_CARD_SCALE,
|
|
381
|
+
width_storage_key,
|
|
382
|
+
scale_storage_key,
|
|
383
|
+
card_count_storage_key
|
|
384
|
+
)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### Functions
|
|
388
|
+
|
|
389
|
+
``` python
|
|
390
|
+
def width_storage_key(
|
|
391
|
+
prefix: str # Card stack instance prefix
|
|
392
|
+
) -> str: # localStorage key for card width
|
|
393
|
+
"Generate localStorage key for card width."
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
``` python
|
|
397
|
+
def scale_storage_key(
|
|
398
|
+
prefix: str # Card stack instance prefix
|
|
399
|
+
) -> str: # localStorage key for card scale
|
|
400
|
+
"Generate localStorage key for card scale."
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
``` python
|
|
404
|
+
def card_count_storage_key(
|
|
405
|
+
prefix: str # Card stack instance prefix
|
|
406
|
+
) -> str: # localStorage key for card count
|
|
407
|
+
"Generate localStorage key for card count."
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### Variables
|
|
411
|
+
|
|
412
|
+
``` python
|
|
413
|
+
SCROLL_THRESHOLD: int = 50
|
|
414
|
+
NAVIGATION_COOLDOWN: int = 100
|
|
415
|
+
DEFAULT_VISIBLE_COUNT: int = 3
|
|
416
|
+
DEFAULT_CARD_WIDTH: int = 80
|
|
417
|
+
DEFAULT_CARD_SCALE: int = 100
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Controls (`controls.ipynb`)
|
|
421
|
+
|
|
422
|
+
> Width slider, scale slider, and card count selector components.
|
|
423
|
+
|
|
424
|
+
#### Import
|
|
425
|
+
|
|
426
|
+
``` python
|
|
427
|
+
from cjm_fasthtml_card_stack.components.controls import (
|
|
428
|
+
render_width_slider,
|
|
429
|
+
render_scale_slider,
|
|
430
|
+
render_card_count_select
|
|
431
|
+
)
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
#### Functions
|
|
435
|
+
|
|
436
|
+
``` python
|
|
437
|
+
def render_width_slider(
|
|
438
|
+
config: CardStackConfig, # Card stack configuration
|
|
439
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
440
|
+
card_width: int = 80, # Current card width in rem
|
|
441
|
+
) -> Any: # Width slider component
|
|
442
|
+
"Render the card stack width slider control."
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
``` python
|
|
446
|
+
def render_scale_slider(
|
|
447
|
+
config: CardStackConfig, # Card stack configuration
|
|
448
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
449
|
+
card_scale: int = 100, # Current scale percentage
|
|
450
|
+
) -> Any: # Scale slider component
|
|
451
|
+
"Render the card stack scale slider control."
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
``` python
|
|
455
|
+
def render_card_count_select(
|
|
456
|
+
config: CardStackConfig, # Card stack configuration
|
|
457
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
458
|
+
current_count: int = 3, # Currently selected card count
|
|
459
|
+
) -> Any: # Card count dropdown component
|
|
460
|
+
"Render the card count dropdown selector."
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### JS: Core (`core.ipynb`)
|
|
464
|
+
|
|
465
|
+
> Master composer for card stack JavaScript. Combines viewport height,
|
|
466
|
+
|
|
467
|
+
#### Import
|
|
468
|
+
|
|
469
|
+
``` python
|
|
470
|
+
from cjm_fasthtml_card_stack.js.core import (
|
|
471
|
+
global_callback_name,
|
|
472
|
+
generate_card_stack_js
|
|
473
|
+
)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### Functions
|
|
477
|
+
|
|
478
|
+
``` python
|
|
479
|
+
def _generate_width_mgmt_js(
|
|
480
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
481
|
+
config: CardStackConfig, # Config with slider bounds
|
|
482
|
+
urls: CardStackUrls, # URL bundle (save_width)
|
|
483
|
+
) -> str: # JS code fragment for width management
|
|
484
|
+
"Generate JS for width slider management."
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
``` python
|
|
488
|
+
def _generate_scale_mgmt_js(
|
|
489
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
490
|
+
config: CardStackConfig, # Config with slider bounds
|
|
491
|
+
urls: CardStackUrls, # URL bundle (save_scale)
|
|
492
|
+
) -> str: # JS code fragment for scale management
|
|
493
|
+
"Generate JS for scale slider management."
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
``` python
|
|
497
|
+
def _generate_card_count_mgmt_js(
|
|
498
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
499
|
+
config: CardStackConfig, # Config with count options
|
|
500
|
+
urls: CardStackUrls, # URL bundle (update_viewport)
|
|
501
|
+
) -> str: # JS code fragment for card count management
|
|
502
|
+
"Generate JS for card count selector management."
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
``` python
|
|
506
|
+
def _generate_coordinator_js(
|
|
507
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
508
|
+
config: CardStackConfig, # Config for prefix-unique listener guards
|
|
509
|
+
) -> str: # JS code fragment for master coordinator
|
|
510
|
+
"Generate JS for the master coordinator and HTMX listener."
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
``` python
|
|
514
|
+
def global_callback_name(
|
|
515
|
+
prefix: str, # Card stack instance prefix
|
|
516
|
+
callback: str, # Base callback name (e.g., "jumpPageUp")
|
|
517
|
+
) -> str: # Global function name (e.g., "cs0_jumpPageUp")
|
|
518
|
+
"Generate a prefix-unique global callback name for keyboard navigation."
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
``` python
|
|
522
|
+
def _generate_global_callbacks_js(
|
|
523
|
+
config: CardStackConfig, # Config with prefix
|
|
524
|
+
) -> str: # JS code fragment registering global wrappers
|
|
525
|
+
"Register global wrappers for keyboard navigation system."
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
``` python
|
|
529
|
+
def generate_card_stack_js(
|
|
530
|
+
"Compose all card stack JS into a single namespaced IIFE."
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
#### Variables
|
|
534
|
+
|
|
535
|
+
``` python
|
|
536
|
+
_GLOBAL_CALLBACKS
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Focus (`focus.ipynb`)
|
|
540
|
+
|
|
541
|
+
> Focus position resolution, viewport window calculation, and OOB focus
|
|
542
|
+
> sync.
|
|
543
|
+
|
|
544
|
+
#### Import
|
|
545
|
+
|
|
546
|
+
``` python
|
|
547
|
+
from cjm_fasthtml_card_stack.helpers.focus import (
|
|
548
|
+
resolve_focus_slot,
|
|
549
|
+
calculate_viewport_window,
|
|
550
|
+
render_focus_oob
|
|
551
|
+
)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
#### Functions
|
|
555
|
+
|
|
556
|
+
``` python
|
|
557
|
+
def resolve_focus_slot(
|
|
558
|
+
focus_position: Optional[int], # Slot offset (None=center, -1=bottom, 0=top)
|
|
559
|
+
visible_count: int, # Number of visible card slots
|
|
560
|
+
) -> int: # Resolved 0-indexed slot position
|
|
561
|
+
"Resolve focus_position to an actual slot index within the viewport."
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
``` python
|
|
565
|
+
def calculate_viewport_window(
|
|
566
|
+
focused_index: int, # Index of the focused item
|
|
567
|
+
total_items: int, # Total number of items
|
|
568
|
+
visible_count: int, # Number of visible card slots
|
|
569
|
+
focus_position: Optional[int] = None, # Focus slot (None=center)
|
|
570
|
+
) -> List[Optional[int]]: # Slot indices (None for placeholder slots)
|
|
571
|
+
"Calculate which item indices should be visible in each viewport slot."
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
``` python
|
|
575
|
+
def render_focus_oob(
|
|
576
|
+
focused_index: int, # The item index to focus
|
|
577
|
+
ids: CardStackHtmlIds, # HTML IDs for this card stack instance
|
|
578
|
+
form_input_name: str = "focused_index", # Field name for the form input
|
|
579
|
+
) -> Tuple[Hidden, ...]: # Hidden inputs with OOB swap
|
|
580
|
+
"Render OOB hidden inputs to synchronize focus after HTMX swap."
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### Handlers (`handlers.ipynb`)
|
|
584
|
+
|
|
585
|
+
> Response builder functions for card stack operations (Tier 1 API).
|
|
586
|
+
|
|
587
|
+
#### Import
|
|
588
|
+
|
|
589
|
+
``` python
|
|
590
|
+
from cjm_fasthtml_card_stack.routes.handlers import (
|
|
591
|
+
build_slots_response,
|
|
592
|
+
build_nav_response,
|
|
593
|
+
card_stack_navigate,
|
|
594
|
+
card_stack_navigate_to_index,
|
|
595
|
+
card_stack_update_viewport,
|
|
596
|
+
card_stack_save_width,
|
|
597
|
+
card_stack_save_scale
|
|
598
|
+
)
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
#### Functions
|
|
602
|
+
|
|
603
|
+
``` python
|
|
604
|
+
def build_slots_response(
|
|
605
|
+
card_items: List[Any], # All data items
|
|
606
|
+
state: CardStackState, # Current card stack state
|
|
607
|
+
config: CardStackConfig, # Card stack configuration
|
|
608
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
609
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
610
|
+
render_card: Callable, # Card renderer callback
|
|
611
|
+
) -> List[Any]: # OOB slot elements (3 viewport sections)
|
|
612
|
+
"Build OOB slot updates for the viewport sections only."
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
``` python
|
|
616
|
+
def build_nav_response(
|
|
617
|
+
card_items: List[Any], # All data items
|
|
618
|
+
state: CardStackState, # Current card stack state
|
|
619
|
+
config: CardStackConfig, # Card stack configuration
|
|
620
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
621
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
622
|
+
render_card: Callable, # Card renderer callback
|
|
623
|
+
progress_label: str = "Item", # Label for progress indicator
|
|
624
|
+
) -> Tuple: # OOB elements (slots + progress + focus)
|
|
625
|
+
"Build full OOB response for navigation: slots + progress + focus inputs."
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
``` python
|
|
629
|
+
def card_stack_navigate(
|
|
630
|
+
direction: str, # "up", "down", "first", "last", "page_up", "page_down"
|
|
631
|
+
card_items: List[Any], # All data items
|
|
632
|
+
state: CardStackState, # Current card stack state (mutated in place)
|
|
633
|
+
config: CardStackConfig, # Card stack configuration
|
|
634
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
635
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
636
|
+
render_card: Callable, # Card renderer callback
|
|
637
|
+
progress_label: str = "Item", # Label for progress indicator
|
|
638
|
+
) -> Tuple: # OOB elements (slots + progress + focus)
|
|
639
|
+
"Navigate to a different item. Mutates state.focused_index in place."
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
``` python
|
|
643
|
+
def card_stack_navigate_to_index(
|
|
644
|
+
target_index: int, # Target item index to navigate to
|
|
645
|
+
card_items: List[Any], # All data items
|
|
646
|
+
state: CardStackState, # Current card stack state (mutated in place)
|
|
647
|
+
config: CardStackConfig, # Card stack configuration
|
|
648
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
649
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
650
|
+
render_card: Callable, # Card renderer callback
|
|
651
|
+
progress_label: str = "Item", # Label for progress indicator
|
|
652
|
+
) -> Tuple: # OOB elements (slots + progress + focus)
|
|
653
|
+
"Navigate to a specific item index. Mutates state.focused_index in place."
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
``` python
|
|
657
|
+
def card_stack_update_viewport(
|
|
658
|
+
visible_count: int, # New number of visible cards
|
|
659
|
+
card_items: List[Any], # All data items
|
|
660
|
+
state: CardStackState, # Current card stack state (mutated in place)
|
|
661
|
+
config: CardStackConfig, # Card stack configuration
|
|
662
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
663
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
664
|
+
render_card: Callable, # Card renderer callback
|
|
665
|
+
) -> Any: # Full viewport component (outerHTML swap)
|
|
666
|
+
"Update viewport with new card count. Mutates state.visible_count in place."
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
``` python
|
|
670
|
+
def card_stack_save_width(
|
|
671
|
+
state: CardStackState, # Current card stack state (mutated in place)
|
|
672
|
+
card_width: int, # Card stack width in rem
|
|
673
|
+
config: CardStackConfig, # Card stack configuration (for clamping bounds)
|
|
674
|
+
) -> None: # No response (swap=none on client)
|
|
675
|
+
"Save card stack width. Mutates state.card_width in place."
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
``` python
|
|
679
|
+
def card_stack_save_scale(
|
|
680
|
+
state: CardStackState, # Current card stack state (mutated in place)
|
|
681
|
+
card_scale: int, # Card stack scale percentage
|
|
682
|
+
config: CardStackConfig, # Card stack configuration (for clamping bounds)
|
|
683
|
+
) -> None: # No response (swap=none on client)
|
|
684
|
+
"Save card stack scale. Mutates state.card_scale in place."
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### HTML IDs (`html_ids.ipynb`)
|
|
688
|
+
|
|
689
|
+
> Prefix-based HTML ID generator for card stack DOM elements.
|
|
690
|
+
|
|
691
|
+
#### Import
|
|
692
|
+
|
|
693
|
+
``` python
|
|
694
|
+
from cjm_fasthtml_card_stack.core.html_ids import (
|
|
695
|
+
CardStackHtmlIds
|
|
696
|
+
)
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
#### Classes
|
|
700
|
+
|
|
701
|
+
``` python
|
|
702
|
+
@dataclass
|
|
703
|
+
class CardStackHtmlIds:
|
|
704
|
+
"Prefix-based HTML ID generator for card stack DOM elements."
|
|
705
|
+
|
|
706
|
+
prefix: str # ID prefix for this card stack instance
|
|
707
|
+
|
|
708
|
+
def card_stack(self) -> str: # Full-width scroll capture container
|
|
709
|
+
"""Outer card stack container."""
|
|
710
|
+
return f"{self.prefix}-card-stack"
|
|
711
|
+
|
|
712
|
+
@property
|
|
713
|
+
def card_stack_inner(self) -> str: # Width-constrained CSS Grid container
|
|
714
|
+
"Outer card stack container."
|
|
715
|
+
|
|
716
|
+
def card_stack_inner(self) -> str: # Width-constrained CSS Grid container
|
|
717
|
+
"""Inner grid container for 3-section layout."""
|
|
718
|
+
return f"{self.prefix}-card-stack-inner"
|
|
719
|
+
|
|
720
|
+
@property
|
|
721
|
+
def card_stack_empty(self) -> str: # Empty state placeholder
|
|
722
|
+
"Inner grid container for 3-section layout."
|
|
723
|
+
|
|
724
|
+
def card_stack_empty(self) -> str: # Empty state placeholder
|
|
725
|
+
"""Empty state container."""
|
|
726
|
+
return f"{self.prefix}-card-stack-empty"
|
|
727
|
+
|
|
728
|
+
# --- Viewport sections ---
|
|
729
|
+
|
|
730
|
+
@property
|
|
731
|
+
def viewport_section_before(self) -> str: # Cards before focused (1fr, justify-end)
|
|
732
|
+
"Empty state container."
|
|
733
|
+
|
|
734
|
+
def viewport_section_before(self) -> str: # Cards before focused (1fr, justify-end)
|
|
735
|
+
"""Viewport section for context cards before focused card."""
|
|
736
|
+
return f"{self.prefix}-viewport-section-before"
|
|
737
|
+
|
|
738
|
+
@property
|
|
739
|
+
def viewport_section_focused(self) -> str: # Focused card (auto)
|
|
740
|
+
"Viewport section for context cards before focused card."
|
|
741
|
+
|
|
742
|
+
def viewport_section_focused(self) -> str: # Focused card (auto)
|
|
743
|
+
"""Viewport section for the focused card."""
|
|
744
|
+
return f"{self.prefix}-viewport-section-focused"
|
|
745
|
+
|
|
746
|
+
@property
|
|
747
|
+
def viewport_section_after(self) -> str: # Cards after focused (1fr, justify-start)
|
|
748
|
+
"Viewport section for the focused card."
|
|
749
|
+
|
|
750
|
+
def viewport_section_after(self) -> str: # Cards after focused (1fr, justify-start)
|
|
751
|
+
"""Viewport section for context cards after focused card."""
|
|
752
|
+
return f"{self.prefix}-viewport-section-after"
|
|
753
|
+
|
|
754
|
+
# --- Dynamic slot IDs ---
|
|
755
|
+
|
|
756
|
+
def viewport_slot(
|
|
757
|
+
self,
|
|
758
|
+
index: int # Slot index within the viewport
|
|
759
|
+
) -> str: # Slot element ID
|
|
760
|
+
"Viewport section for context cards after focused card."
|
|
761
|
+
|
|
762
|
+
def viewport_slot(
|
|
763
|
+
self,
|
|
764
|
+
index: int # Slot index within the viewport
|
|
765
|
+
) -> str: # Slot element ID
|
|
766
|
+
"ID for an individual viewport slot container."
|
|
767
|
+
|
|
768
|
+
def card_count_select(self) -> str: # Card count dropdown
|
|
769
|
+
"""Card count selector dropdown."""
|
|
770
|
+
return f"{self.prefix}-card-count-select"
|
|
771
|
+
|
|
772
|
+
@property
|
|
773
|
+
def width_slider(self) -> str: # Width range slider
|
|
774
|
+
"Card count selector dropdown."
|
|
775
|
+
|
|
776
|
+
def width_slider(self) -> str: # Width range slider
|
|
777
|
+
"""Card stack width slider."""
|
|
778
|
+
return f"{self.prefix}-width-slider"
|
|
779
|
+
|
|
780
|
+
@property
|
|
781
|
+
def scale_slider(self) -> str: # Scale range slider
|
|
782
|
+
"Card stack width slider."
|
|
783
|
+
|
|
784
|
+
def scale_slider(self) -> str: # Scale range slider
|
|
785
|
+
"""Card stack scale slider."""
|
|
786
|
+
return f"{self.prefix}-scale-slider"
|
|
787
|
+
|
|
788
|
+
# --- Status elements ---
|
|
789
|
+
|
|
790
|
+
@property
|
|
791
|
+
def progress(self) -> str: # Progress indicator
|
|
792
|
+
"Card stack scale slider."
|
|
793
|
+
|
|
794
|
+
def progress(self) -> str: # Progress indicator
|
|
795
|
+
"""Progress indicator element."""
|
|
796
|
+
return f"{self.prefix}-progress"
|
|
797
|
+
|
|
798
|
+
@property
|
|
799
|
+
def loading(self) -> str: # Loading state container
|
|
800
|
+
"Progress indicator element."
|
|
801
|
+
|
|
802
|
+
def loading(self) -> str: # Loading state container
|
|
803
|
+
"""Loading state container."""
|
|
804
|
+
return f"{self.prefix}-loading"
|
|
805
|
+
|
|
806
|
+
# --- Hidden inputs ---
|
|
807
|
+
|
|
808
|
+
@property
|
|
809
|
+
def focused_index_input(self) -> str: # Hidden input for keyboard nav focus recovery
|
|
810
|
+
"Loading state container."
|
|
811
|
+
|
|
812
|
+
def focused_index_input(self) -> str: # Hidden input for keyboard nav focus recovery
|
|
813
|
+
"Hidden input storing the focused index for HTMX submissions."
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### Models (`models.ipynb`)
|
|
817
|
+
|
|
818
|
+
> Core dataclasses for card stack state, render context, and URL
|
|
819
|
+
> routing.
|
|
820
|
+
|
|
821
|
+
#### Import
|
|
822
|
+
|
|
823
|
+
``` python
|
|
824
|
+
from cjm_fasthtml_card_stack.core.models import (
|
|
825
|
+
CardStackState,
|
|
826
|
+
CardRenderContext,
|
|
827
|
+
CardStackUrls
|
|
828
|
+
)
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
#### Classes
|
|
832
|
+
|
|
833
|
+
``` python
|
|
834
|
+
@dataclass
|
|
835
|
+
class CardStackState:
|
|
836
|
+
"Viewport state for a card stack instance."
|
|
837
|
+
|
|
838
|
+
focused_index: int = 0 # Index of focused item in the items list
|
|
839
|
+
visible_count: int = 3 # Number of card slots visible in viewport
|
|
840
|
+
card_width: int = 80 # Max width of card stack inner container in rem
|
|
841
|
+
card_scale: int = 100 # Content scale percentage (50-200)
|
|
842
|
+
active_mode: Optional[str] # Current interaction mode name (consumer-defined)
|
|
843
|
+
focus_position: Optional[int] # Slot offset for focused card (None=center, -1=bottom)
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
``` python
|
|
847
|
+
@dataclass
|
|
848
|
+
class CardRenderContext:
|
|
849
|
+
"Context passed to the consumer's render_card callback."
|
|
850
|
+
|
|
851
|
+
card_role: str # "focused" or "context"
|
|
852
|
+
index: int # Item's position in the full items list
|
|
853
|
+
total_items: int # Total item count
|
|
854
|
+
is_first: bool # Whether this is the first item
|
|
855
|
+
is_last: bool # Whether this is the last item
|
|
856
|
+
active_mode: Optional[str] # Current interaction mode
|
|
857
|
+
card_scale: int # Scale percentage (50-200)
|
|
858
|
+
distance_from_focus: int # Signed slot offset from focused card (0=focused)
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
``` python
|
|
862
|
+
@dataclass
|
|
863
|
+
class CardStackUrls:
|
|
864
|
+
"URL bundle for card stack navigation and viewport operations."
|
|
865
|
+
|
|
866
|
+
nav_up: str = '' # Navigate to previous item
|
|
867
|
+
nav_down: str = '' # Navigate to next item
|
|
868
|
+
nav_first: str = '' # Navigate to first item
|
|
869
|
+
nav_last: str = '' # Navigate to last item
|
|
870
|
+
nav_page_up: str = '' # Page jump up
|
|
871
|
+
nav_page_down: str = '' # Page jump down
|
|
872
|
+
nav_to_index: str = '' # Navigate to specific index (click-to-focus)
|
|
873
|
+
update_viewport: str = '' # Change visible_count (full viewport re-render)
|
|
874
|
+
save_width: str = '' # Persist card_width
|
|
875
|
+
save_scale: str = '' # Persist card_scale
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
### JS: Page Navigation (`navigation.ipynb`)
|
|
879
|
+
|
|
880
|
+
> JavaScript generator for page jump and first/last navigation helpers.
|
|
881
|
+
|
|
882
|
+
#### Import
|
|
883
|
+
|
|
884
|
+
``` python
|
|
885
|
+
from cjm_fasthtml_card_stack.js.navigation import (
|
|
886
|
+
generate_page_nav_js
|
|
887
|
+
)
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
#### Functions
|
|
891
|
+
|
|
892
|
+
``` python
|
|
893
|
+
def generate_page_nav_js(
|
|
894
|
+
button_ids: CardStackButtonIds, # Button IDs for navigation triggers
|
|
895
|
+
) -> str: # JavaScript code fragment for page navigation
|
|
896
|
+
"Generate JS for page-based and first/last navigation functions."
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
### Progress (`progress.ipynb`)
|
|
900
|
+
|
|
901
|
+
> Progress indicator showing the current position within the card stack.
|
|
902
|
+
|
|
903
|
+
#### Import
|
|
904
|
+
|
|
905
|
+
``` python
|
|
906
|
+
from cjm_fasthtml_card_stack.components.progress import (
|
|
907
|
+
render_progress_indicator
|
|
908
|
+
)
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
#### Functions
|
|
912
|
+
|
|
913
|
+
``` python
|
|
914
|
+
def render_progress_indicator(
|
|
915
|
+
focused_index: int, # Currently focused item index (0-based)
|
|
916
|
+
total_items: int, # Total number of items
|
|
917
|
+
ids: CardStackHtmlIds, # HTML IDs for this card stack instance
|
|
918
|
+
label: str = "Item", # Label prefix (e.g., "Item", "Segment", "Card")
|
|
919
|
+
oob: bool = False, # Whether to render as OOB swap
|
|
920
|
+
) -> Any: # Progress indicator component
|
|
921
|
+
"Render position indicator showing current item in the collection."
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
### Router (`router.ipynb`)
|
|
925
|
+
|
|
926
|
+
> Convenience router factory that wires up standard card stack routes
|
|
927
|
+
> (Tier 2 API).
|
|
928
|
+
|
|
929
|
+
#### Import
|
|
930
|
+
|
|
931
|
+
``` python
|
|
932
|
+
from cjm_fasthtml_card_stack.routes.router import (
|
|
933
|
+
init_card_stack_router
|
|
934
|
+
)
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
#### Functions
|
|
938
|
+
|
|
939
|
+
``` python
|
|
940
|
+
def init_card_stack_router(
|
|
941
|
+
config: CardStackConfig, # Card stack configuration
|
|
942
|
+
state_getter: Callable[[], CardStackState], # Function to get current state
|
|
943
|
+
state_setter: Callable[[CardStackState], None], # Function to save state
|
|
944
|
+
get_items: Callable[[], List[Any]], # Function to get current items list
|
|
945
|
+
render_card: Callable, # Card renderer callback: (item, CardRenderContext) -> FT
|
|
946
|
+
route_prefix: str = "/card-stack", # Route prefix for all card stack routes
|
|
947
|
+
progress_label: str = "Item", # Label for progress indicator
|
|
948
|
+
) -> Tuple[APIRouter, CardStackUrls]: # (router, urls) tuple
|
|
949
|
+
"Initialize an APIRouter with all standard card stack routes."
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
### JS: Scroll Navigation (`scroll.ipynb`)
|
|
953
|
+
|
|
954
|
+
> JavaScript generator for scroll-to-nav conversion.
|
|
955
|
+
|
|
956
|
+
#### Import
|
|
957
|
+
|
|
958
|
+
``` python
|
|
959
|
+
from cjm_fasthtml_card_stack.js.scroll import (
|
|
960
|
+
generate_scroll_nav_js
|
|
961
|
+
)
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
#### Functions
|
|
965
|
+
|
|
966
|
+
``` python
|
|
967
|
+
def generate_scroll_nav_js(
|
|
968
|
+
ids: CardStackHtmlIds, # HTML IDs for this card stack instance
|
|
969
|
+
button_ids: CardStackButtonIds, # Button IDs for navigation triggers
|
|
970
|
+
disable_in_modes: Tuple[str, ...] = (), # Mode names where scroll nav is suppressed
|
|
971
|
+
) -> str: # JavaScript code fragment for scroll navigation
|
|
972
|
+
"Generate JS for scroll wheel to navigation conversion."
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
### States (`states.ipynb`)
|
|
976
|
+
|
|
977
|
+
> Loading, empty, and placeholder card components for the card stack
|
|
978
|
+
> viewport.
|
|
979
|
+
|
|
980
|
+
#### Import
|
|
981
|
+
|
|
982
|
+
``` python
|
|
983
|
+
from cjm_fasthtml_card_stack.components.states import (
|
|
984
|
+
render_placeholder_card,
|
|
985
|
+
render_loading_state,
|
|
986
|
+
render_empty_state
|
|
987
|
+
)
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
#### Functions
|
|
991
|
+
|
|
992
|
+
``` python
|
|
993
|
+
def render_placeholder_card(
|
|
994
|
+
placeholder_type: Literal["start", "end"], # Which edge of the list
|
|
995
|
+
) -> Any: # Placeholder card component
|
|
996
|
+
"Render a placeholder card for viewport edges."
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
``` python
|
|
1000
|
+
def render_loading_state(
|
|
1001
|
+
ids: CardStackHtmlIds, # HTML IDs for this card stack instance
|
|
1002
|
+
message: str = "Loading...", # Loading message text
|
|
1003
|
+
) -> Any: # Loading component
|
|
1004
|
+
"Render loading state with spinner and message."
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
``` python
|
|
1008
|
+
def render_empty_state(
|
|
1009
|
+
ids: CardStackHtmlIds, # HTML IDs for this card stack instance
|
|
1010
|
+
title: str = "No items available", # Main empty state message
|
|
1011
|
+
subtitle: str = "", # Optional subtitle text
|
|
1012
|
+
) -> Any: # Empty state component
|
|
1013
|
+
"Render empty state when no items exist."
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
### Viewport (`viewport.ipynb`)
|
|
1017
|
+
|
|
1018
|
+
> Card stack viewport with 3-section CSS Grid layout, slot rendering,
|
|
1019
|
+
|
|
1020
|
+
#### Import
|
|
1021
|
+
|
|
1022
|
+
``` python
|
|
1023
|
+
from cjm_fasthtml_card_stack.components.viewport import (
|
|
1024
|
+
render_slot_card,
|
|
1025
|
+
render_all_slots_oob,
|
|
1026
|
+
render_viewport
|
|
1027
|
+
)
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
#### Functions
|
|
1031
|
+
|
|
1032
|
+
``` python
|
|
1033
|
+
def _render_mode_sync_script(
|
|
1034
|
+
active_mode: Optional[str] = None, # Active keyboard mode name (None = navigation)
|
|
1035
|
+
) -> Any: # Script element that syncs keyboard mode state
|
|
1036
|
+
"Generate script to sync keyboard navigation mode with rendered UI state."
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
``` python
|
|
1040
|
+
def _render_click_overlay(
|
|
1041
|
+
item_index: int, # Index of the item this slot represents
|
|
1042
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
1043
|
+
) -> Any: # Transparent click overlay element
|
|
1044
|
+
"Render transparent click-to-focus overlay for a context card slot."
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
``` python
|
|
1048
|
+
def render_slot_card(
|
|
1049
|
+
slot_index: int, # Index of this slot in the viewport (0-based)
|
|
1050
|
+
focus_slot: int, # Which slot is the focused position
|
|
1051
|
+
card_items: List[Any], # Full items list
|
|
1052
|
+
item_index: Optional[int], # Item index for this slot (None for placeholder)
|
|
1053
|
+
render_card: Callable, # Callback: (item, CardRenderContext) -> FT
|
|
1054
|
+
state: CardStackState, # Current card stack state
|
|
1055
|
+
config: CardStackConfig, # Card stack configuration
|
|
1056
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
1057
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
1058
|
+
oob: bool = False, # Whether to render as OOB swap
|
|
1059
|
+
) -> Any: # Slot content wrapper
|
|
1060
|
+
"Render a single card for a viewport slot."
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
``` python
|
|
1064
|
+
def render_all_slots_oob(
|
|
1065
|
+
card_items: List[Any], # All data items
|
|
1066
|
+
state: CardStackState, # Current card stack state
|
|
1067
|
+
config: CardStackConfig, # Card stack configuration
|
|
1068
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
1069
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
1070
|
+
render_card: Callable, # Card renderer callback
|
|
1071
|
+
) -> List[Any]: # List of OOB elements (3 sections)
|
|
1072
|
+
"Render all viewport sections with OOB swap for granular updates."
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
``` python
|
|
1076
|
+
def _grid_template_rows(
|
|
1077
|
+
focus_slot: int, # Resolved focus slot position
|
|
1078
|
+
visible_count: int, # Number of visible slots
|
|
1079
|
+
) -> str: # CSS grid-template-rows value
|
|
1080
|
+
"Compute CSS grid-template-rows based on focus slot position."
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
``` python
|
|
1084
|
+
def render_viewport(
|
|
1085
|
+
card_items: List[Any], # All data items
|
|
1086
|
+
state: CardStackState, # Current card stack state
|
|
1087
|
+
config: CardStackConfig, # Card stack configuration
|
|
1088
|
+
ids: CardStackHtmlIds, # HTML IDs for this instance
|
|
1089
|
+
urls: CardStackUrls, # URL bundle for navigation
|
|
1090
|
+
render_card: Callable, # Card renderer callback
|
|
1091
|
+
form_input_name: str = "focused_index", # Name for the focused index hidden input
|
|
1092
|
+
) -> Any: # Viewport component with 3-section layout
|
|
1093
|
+
"Render the card stack viewport with 3-section CSS Grid layout."
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
### JS: Viewport Height (`viewport.ipynb`)
|
|
1097
|
+
|
|
1098
|
+
> JavaScript generator for dynamic viewport height calculation.
|
|
1099
|
+
|
|
1100
|
+
#### Import
|
|
1101
|
+
|
|
1102
|
+
``` python
|
|
1103
|
+
from cjm_fasthtml_card_stack.js.viewport import (
|
|
1104
|
+
generate_viewport_height_js
|
|
1105
|
+
)
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
#### Functions
|
|
1109
|
+
|
|
1110
|
+
``` python
|
|
1111
|
+
def generate_viewport_height_js(
|
|
1112
|
+
"""
|
|
1113
|
+
Generate JS for dynamic viewport height calculation.
|
|
1114
|
+
|
|
1115
|
+
Uses the browser's layout engine to measure the space consumed by sibling
|
|
1116
|
+
elements rather than summing individual heights. This naturally handles
|
|
1117
|
+
margin collapsing regardless of the container's display type.
|
|
1118
|
+
|
|
1119
|
+
Strategy: temporarily collapse the card stack to 0 height, measure how
|
|
1120
|
+
much vertical space the remaining content occupies, then set the card
|
|
1121
|
+
stack height to fill the remaining viewport space.
|
|
1122
|
+
"""
|
|
1123
|
+
```
|