Add SPA dispatch events and auto-close modals on navigation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2026-01-28 21:45:34 +00:00
parent d5572e6081
commit 2ff2609971
3 changed files with 86 additions and 2 deletions

View File

@@ -577,6 +577,10 @@ class Spa {
// (Browser refresh scroll is handled separately by Rsx._restore_scroll_on_refresh)
Rsx.reset_pending_scroll();
// Trigger spa_dispatch_start event - allows cleanup before navigation
// Use case: close modals, cancel pending operations, etc.
Rsx.trigger('spa_dispatch_start', { url });
try {
const opts = {
history: options.history || 'auto',
@@ -943,10 +947,13 @@ class Spa {
// This is the action - set reference but don't wait
Spa._action = component;
// After action is fully ready (on_load + on_ready complete), retry scroll restoration
// This handles cases where on_load fetches data that increases page height
// After action is fully ready (on_load + on_ready complete)
component.ready().then(() => {
// Retry scroll restoration - handles cases where on_load fetches data that increases page height
Rsx.try_restore_scroll();
// Trigger spa_dispatch_ready event - action is fully loaded and rendered
Rsx.trigger('spa_dispatch_ready', { url, action: component });
});
} else {
// This is a layout

View File

@@ -692,6 +692,29 @@ Returns: void (does not wait for close)
Note: Call Modal.close() to dismiss the modal programmatically.
================================================================================
SPA INTEGRATION
================================================================================
The Modal system automatically integrates with SPA navigation:
Auto-Close on Navigation
When SPA navigation begins (spa_dispatch_start event), any open modal
is automatically closed. This prevents stale modals from persisting
across page transitions.
This behavior is automatic - no configuration required.
Modal Flow and Navigation
If you need to navigate after a modal action, use Spa.dispatch():
const result = await Modal.form({...});
if (result) {
Spa.dispatch(Rsx.Route('Target_Action'));
}
The modal closes first, then navigation proceeds.
================================================================================
MODAL STATE MANAGEMENT
================================================================================

View File

@@ -580,6 +580,60 @@ NAVIGATION
generated by Rsx.Route(), so it never appears in the browser address
bar or generated links.
SPA EVENTS
The SPA system fires global events that can be used to hook into the
navigation lifecycle. Register handlers using Rsx.on():
spa_dispatch_start
Fired at the beginning of navigation, before the old action is destroyed.
Use for cleanup before page transition.
Rsx.on('spa_dispatch_start', (data) => {
console.log('Navigating to:', data.url);
// Close modals, cancel pending operations, etc.
});
Data payload:
url: Target URL being navigated to
Common uses:
- Close open modals (Modal system does this automatically)
- Cancel pending Ajax requests
- Save unsaved form state
- Stop video/audio playback
spa_dispatch_ready
Fired after the new action has fully loaded (on_ready complete).
Use for post-navigation setup that needs the action to be ready.
Rsx.on('spa_dispatch_ready', (data) => {
console.log('Action ready:', data.action.constructor.name);
// Perform post-navigation actions
});
Data payload:
url: Current URL
action: The action component instance (fully loaded)
Common uses:
- Analytics page view tracking
- Focus management after navigation
- Lazy-load additional resources
Example: Custom navigation tracking
class Analytics_Tracker {
static on_app_modules_init() {
Rsx.on('spa_dispatch_start', () => {
Analytics.track_page_exit();
});
Rsx.on('spa_dispatch_ready', (data) => {
Analytics.track_page_view(data.url);
});
}
}
SESSION VALIDATION
After each SPA navigation (except initial load and back/forward), the client
validates its state against the server by calling Rsx.validate_session().